Skip to content

Commit

Permalink
chore: improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
atinux committed Sep 2, 2024
1 parent e46e1ec commit f4ba1c6
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 25 deletions.
4 changes: 2 additions & 2 deletions playground/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ const providers = computed(() =>
<template
#default="{ loggedIn, clear }"
>
<WebauthnModal v-if="!loggedIn" />
<PasswordModal v-if="!loggedIn" />
<WebauthnModal />
<PasswordModal />
<UDropdown :items="[providers]">
<UButton
icon="i-heroicons-chevron-down"
Expand Down
2 changes: 1 addition & 1 deletion playground/auth.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
declare module '#auth-utils' {
interface User {
credential?: string
webauthn?: string
password?: string
spotify?: string
github?: string
Expand Down
6 changes: 3 additions & 3 deletions playground/components/PasswordModal.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
const { loggedIn, fetch } = useUserSession()
const { user, loggedIn, fetch } = useUserSession()

Check failure on line 2 in playground/components/PasswordModal.vue

View workflow job for this annotation

GitHub Actions / lint

'loggedIn' is assigned a value but never used. Allowed unused vars must match /^_/u
const show = ref(false)
const logging = ref(false)
const password = ref('')
Expand Down Expand Up @@ -31,12 +31,12 @@ async function login() {

<template>
<UButton
v-if="!loggedIn"
v-if="!user?.password"
size="xs"
color="gray"
@click="show = true"
>
Password
Login with password
</UButton>
<UDashboardModal
v-model="show"
Expand Down
6 changes: 3 additions & 3 deletions playground/components/WebauthnModal.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
const { loggedIn, fetch } = useUserSession()
const { user, fetch } = useUserSession()
const show = ref(false)
const logging = ref(false)
const userName = ref('')
Expand Down Expand Up @@ -56,12 +56,12 @@ async function authenticate() {

<template>
<UButton
v-if="!loggedIn && isSupported()"
v-if="!user?.webauthn && isSupported"
size="xs"
color="gray"
@click="show = true"
>
Webauthn
Login with Webauthn
</UButton>
<UDashboardModal
v-model="show"
Expand Down
2 changes: 1 addition & 1 deletion playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default defineNuxtConfig({
devStorage: {
db: {
driver: 'fs',
base: './data/db',
base: './.data/db',
},
},
},
Expand Down
13 changes: 7 additions & 6 deletions playground/server/api/webauthn/login.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ interface CredentialData {
}

export default defineCredentialAuthenticationEventHandler({
storeChallenge: async (_, options, attemptId) => {
async storeChallenge(_, options, attemptId) {
await useStorage().setItem(`attempt:${attemptId}`, options)
},
getChallenge: async (event, attemptId) => {
async getChallenge(event, attemptId) {
const options = await useStorage<PublicKeyCredentialRequestOptionsJSON>().getItem(`attempt:${attemptId}`)
await useStorage().removeItem(`attempt:${attemptId}`)
if (!options)
throw createError({ statusCode: 400 })
throw createError({ message: 'Challenge not found', statusCode: 400 })

const body = await readBody(event)
const credential = await useStorage<CredentialData>('db').getItem(`users:${body.response.id}`)
if (!credential)
throw createError({ statusCode: 400 })
throw createError({ message: 'Credential not found', statusCode: 400 })

return {
options,
Expand All @@ -33,17 +33,18 @@ export default defineCredentialAuthenticationEventHandler({
},
}
},
onSuccces: async (event, response) => {
async onSuccces(event, response) {
const user = await useStorage<CredentialData>('db').getItem(`users:${response!.credentialID}`)
if (!user)
throw createError({ statusCode: 400 })

console.log('response', response)
user.counter = response!.newCounter
await useStorage('db').setItem(`users:${response!.credentialID}`, user)

await setUserSession(event, {
user: {
credential: response!.credentialID,
webauthn: response!.credentialID,
},
loggedInAt: Date.now(),
})
Expand Down
12 changes: 6 additions & 6 deletions playground/server/api/webauthn/register.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { bufferToBase64URLString } from '@simplewebauthn/browser'
import type { PublicKeyCredentialCreationOptionsJSON } from '@simplewebauthn/types'

export default defineCredentialRegistrationEventHandler({
storeChallenge: async (_, options, attemptId) => {
async storeChallenge(_, options, attemptId) {
await useStorage().setItem(`attempt:${attemptId}`, options)
},
getChallenge: async (_, attemptId) => {
async getChallenge(_, attemptId) {
const options = await useStorage<PublicKeyCredentialCreationOptionsJSON>().getItem(`attempt:${attemptId}`)
await useStorage().removeItem(`attempt:${attemptId}`)
if (!options)
throw createError({ statusCode: 400 })
throw createError({ message: 'Challenge not found', statusCode: 400 })

return options
},
onSuccces: async (event, response, body) => {
async onSuccces(event, response, body) {
const user = {
id: 1,
displayName: body.displayName,
Expand All @@ -24,12 +24,12 @@ export default defineCredentialRegistrationEventHandler({
await useStorage('db').setItem(`users:${response!.credentialID}`, user)
await setUserSession(event, {
user: {
credential: response!.credentialID,
webauthn: response!.credentialID,
},
loggedInAt: Date.now(),
})
},
registrationOptions: async () => {
async registrationOptions() {
return {
rpName: 'My Relying Party Name',
}
Expand Down
7 changes: 6 additions & 1 deletion src/runtime/app/composables/webauthn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@simplewebauthn/browser'
import type { VerifiedAuthenticationResponse, VerifiedRegistrationResponse } from '@simplewebauthn/server'
import type { AuthenticationResponseJSON, PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from '@simplewebauthn/types'
import { ref, onMounted } from '#imports'
import type { WebauthnComposable } from '#auth-utils'

interface RegistrationInitResponse {
Expand Down Expand Up @@ -92,11 +93,15 @@ export function useWebauthn(options: {

return verificationResponse && verificationResponse.verified
}
const isSupported = ref(false)
onMounted(() => {
isSupported.value = browserSupportsWebAuthn()
})

return {
register,
authenticate,
isSupported: browserSupportsWebAuthn,
isSupported,
isAutofillSupported: browserSupportsWebAuthnAutofill,
isPlatformAvailable: platformAuthenticatorIsAvailable,
}
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/types/webauthn.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { Ref } from 'vue'

export interface WebauthnComposable {
/**
* Helper function that checks if the webauthn API is available
* Vue ref (boolean) that checks if the webauthn API is available
*/
isSupported: () => boolean
isSupported: Ref<boolean>
/**
* Helper function that checks if the browser supports "Conditional UI" for webauthn
* @see https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI
Expand Down

0 comments on commit f4ba1c6

Please sign in to comment.