Skip to content

Commit 5db6e58

Browse files
chore: wip
1 parent f60a09e commit 5db6e58

File tree

9 files changed

+109
-6
lines changed

9 files changed

+109
-6
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { generateRegistrationOptions } from '@simplewebauthn/server'
2+
import { Action } from '@stacksjs/actions'
3+
import type { RequestInstance } from '@stacksjs/types'
4+
import User from '../../storage/framework/orm/src/models/User.ts'
5+
6+
export default new Action({
7+
name: 'PasskeyRegistrationAction',
8+
description: 'Register a Passkey',
9+
method: 'POST',
10+
async handle(request: RequestInstance) {
11+
const userPasskeys: any[] = [
12+
{ id: 'passkey1', transports: ['usb'] },
13+
{ id: 'passkey2', transports: ['nfc'] },
14+
]
15+
16+
const user = await User.find(1)
17+
18+
const email = user?.email ?? ''
19+
20+
const options = await generateRegistrationOptions({
21+
rpName: 'Stacks',
22+
rpID: 'localhost',
23+
userName: email,
24+
attestationType: 'none',
25+
excludeCredentials: userPasskeys.map((passkey) => ({
26+
id: passkey.id,
27+
transports: passkey.transports,
28+
})),
29+
authenticatorSelection: {
30+
residentKey: 'preferred',
31+
userVerification: 'preferred',
32+
authenticatorAttachment: 'platform',
33+
},
34+
})
35+
36+
return options
37+
},
38+
})
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { verifyRegistrationResponse } from '@simplewebauthn/server'
2+
import { Action } from '@stacksjs/actions'
3+
import type { RequestInstance } from '@stacksjs/types'
4+
5+
export default new Action({
6+
name: 'PasskeyRegistrationAction',
7+
description: 'Register a Passkey',
8+
method: 'POST',
9+
async handle(request: RequestInstance) {
10+
const body = request.all()
11+
12+
let verification
13+
14+
try {
15+
verification = await verifyRegistrationResponse({
16+
response: body.attResp,
17+
expectedChallenge: body.challenge,
18+
expectedOrigin: 'http://localhost:3333',
19+
expectedRPID: 'localhost',
20+
})
21+
} catch (error) {
22+
console.error(error)
23+
}
24+
25+
return verification
26+
},
27+
})

bun.lockb

6.08 KB
Binary file not shown.

resources/views/auth/login.vue

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
<script setup>
1+
<script setup lang="ts">
2+
import { startRegistration } from '@simplewebauthn/browser'
3+
4+
const successMessage = ref('')
5+
const errorMessage = ref('')
6+
27
import { ref } from 'vue'
38
49
const router = useRouter()
@@ -37,6 +42,33 @@ async function login() {
3742
3843
password.value = ''
3944
}
45+
46+
async function generateRegistration() {
47+
// Reset success/error messages
48+
successMessage.value = ''
49+
errorMessage.value = ''
50+
51+
const resp = await fetch('http://localhost:3008/generate-registration-options')
52+
const options = await resp.json()
53+
54+
const attResp = await startRegistration(options)
55+
56+
const verificationResp = await fetch('http://localhost:3008/verify-registration', {
57+
method: 'POST',
58+
headers: {
59+
'Content-Type': 'application/json',
60+
},
61+
body: JSON.stringify({ challenge: options.challenge, attResp }),
62+
})
63+
64+
const verificationJSON = await verificationResp.json()
65+
66+
if (verificationJSON?.verified) {
67+
successMessage.value = 'Success!'
68+
} else {
69+
errorMessage.value = `Oh no, something went wrong! Response: <pre>${JSON.stringify(verificationJSON)}</pre>`
70+
}
71+
}
4072
</script>
4173

4274
<template>

routes/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ route.get('/hello/world', () => 'hello world, buddy') // $API_URL/hello/world
1414

1515
route.post('/email/subscribe', 'Actions/SubscriberEmailAction')
1616
route.post('/login', 'Actions/LoginAction')
17+
route.get('/generate-registration-options', 'Actions/GenerateRegistrationAction')
18+
route.post('/verify-registration', 'Actions/VerifyRegistrationAction')
1719

1820
// route.email('/welcome')
1921
route.health() // adds a GET `/health` route

storage/framework/.biomelintrc-auto-import.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,4 @@
418418
"wordChar"
419419
]
420420
}
421-
}
421+
}

storage/framework/core/auth/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,9 @@
4242
"@types/qrcode": "^1.5.5",
4343
"otplib": "^12.0.1",
4444
"qrcode": "^1.5.4"
45+
},
46+
"dependencies": {
47+
"@simplewebauthn/browser": "^10.0.0",
48+
"@simplewebauthn/server": "^10.0.1"
4549
}
4650
}

storage/framework/core/auth/src/authentication.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import Team, { type TeamModel } from '../../../orm/src/models/Team'
55
import User from '../../../orm/src/models/User'
66

77
interface Credentials {
8-
password: string
9-
[key: string]: string | number | undefined
8+
password: string | undefined
9+
[key: string]: string | undefined
1010
}
1111

1212
type AuthToken = `${number}:${number}:${string}`

storage/framework/core/types/src/request.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { VineType } from '@stacksjs/types'
22

33
interface RequestData {
4-
[key: string]: string
4+
[key: string]: any
55
}
66

77
type RouteParams = { [key: string]: string | number } | null
@@ -22,7 +22,7 @@ export interface RequestInstance {
2222

2323
addParam(param: RouteParams): void
2424

25-
get(element: string): string | number | undefined
25+
get<T>(element: T): string | undefined
2626

2727
header(element: string): string | number | boolean | null
2828

0 commit comments

Comments
 (0)