@mrgnw/anahtar 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/passkey.d.ts +2 -2
- package/dist/passkey.js +32 -20
- package/package.json +1 -1
package/dist/passkey.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthenticationResponseJSON, RegistrationResponseJSON } from
|
|
2
|
-
import type { AuthDB, ResolvedConfig } from
|
|
1
|
+
import type { AuthenticationResponseJSON, RegistrationResponseJSON } from "@simplewebauthn/server";
|
|
2
|
+
import type { AuthDB, ResolvedConfig } from "./types.js";
|
|
3
3
|
export declare function getWebAuthnConfig(requestUrl: URL): {
|
|
4
4
|
rpID: string;
|
|
5
5
|
origin: string;
|
package/dist/passkey.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { randomUUID } from
|
|
2
|
-
import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse as verifyAuthResponse, verifyRegistrationResponse as verifyRegResponse } from
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse as verifyAuthResponse, verifyRegistrationResponse as verifyRegResponse, } from "@simplewebauthn/server";
|
|
3
3
|
const CHALLENGE_EXPIRY_MS = 5 * 60 * 1000;
|
|
4
4
|
export function getWebAuthnConfig(requestUrl) {
|
|
5
5
|
const envOrigin = process.env.ORIGIN;
|
|
@@ -14,18 +14,21 @@ export async function generateRegistrationChallenge(db, user, requestUrl, config
|
|
|
14
14
|
const existingPasskeys = await db.getUserPasskeys(user.id);
|
|
15
15
|
const excludeCredentials = existingPasskeys.map((pk) => ({
|
|
16
16
|
id: pk.credentialId,
|
|
17
|
-
transports: pk.transports
|
|
17
|
+
transports: pk.transports
|
|
18
|
+
? JSON.parse(pk.transports)
|
|
19
|
+
: undefined,
|
|
18
20
|
}));
|
|
19
21
|
const options = await generateRegistrationOptions({
|
|
20
22
|
rpName: config.rpName,
|
|
21
23
|
rpID,
|
|
22
24
|
userName: user.email,
|
|
25
|
+
userDisplayName: user.email,
|
|
23
26
|
userID: new TextEncoder().encode(user.id),
|
|
24
27
|
authenticatorSelection: {
|
|
25
|
-
residentKey:
|
|
26
|
-
userVerification:
|
|
28
|
+
residentKey: "required",
|
|
29
|
+
userVerification: "preferred",
|
|
27
30
|
},
|
|
28
|
-
excludeCredentials
|
|
31
|
+
excludeCredentials,
|
|
29
32
|
});
|
|
30
33
|
await db.storeChallenge(options.challenge, user.id, Date.now() + CHALLENGE_EXPIRY_MS);
|
|
31
34
|
return options;
|
|
@@ -34,7 +37,7 @@ export async function verifyRegistrationResponse(db, userId, response, requestUr
|
|
|
34
37
|
const { rpID, origin } = getWebAuthnConfig(requestUrl);
|
|
35
38
|
let challenge;
|
|
36
39
|
try {
|
|
37
|
-
const clientData = JSON.parse(Buffer.from(response.response.clientDataJSON,
|
|
40
|
+
const clientData = JSON.parse(Buffer.from(response.response.clientDataJSON, "base64url").toString());
|
|
38
41
|
challenge = clientData.challenge;
|
|
39
42
|
}
|
|
40
43
|
catch (e) {
|
|
@@ -42,20 +45,24 @@ export async function verifyRegistrationResponse(db, userId, response, requestUr
|
|
|
42
45
|
}
|
|
43
46
|
const stored = await db.consumeChallenge(challenge);
|
|
44
47
|
if (!stored)
|
|
45
|
-
return { ok: false, reason:
|
|
48
|
+
return { ok: false, reason: "challenge not found or expired" };
|
|
46
49
|
if (stored.userId !== userId)
|
|
47
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
reason: `userId mismatch: challenge=${stored.userId} session=${userId}`,
|
|
53
|
+
};
|
|
48
54
|
try {
|
|
49
55
|
const verification = await verifyRegResponse({
|
|
50
56
|
response,
|
|
51
57
|
expectedChallenge: challenge,
|
|
52
58
|
expectedOrigin: origin,
|
|
53
|
-
expectedRPID: rpID
|
|
59
|
+
expectedRPID: rpID,
|
|
60
|
+
requireUserVerification: false,
|
|
54
61
|
});
|
|
55
62
|
if (!verification.verified)
|
|
56
|
-
return { ok: false, reason:
|
|
63
|
+
return { ok: false, reason: "verification not verified" };
|
|
57
64
|
if (!verification.registrationInfo)
|
|
58
|
-
return { ok: false, reason:
|
|
65
|
+
return { ok: false, reason: "no registrationInfo" };
|
|
59
66
|
const { credential } = verification.registrationInfo;
|
|
60
67
|
await db.storePasskey({
|
|
61
68
|
id: randomUUID(),
|
|
@@ -63,8 +70,10 @@ export async function verifyRegistrationResponse(db, userId, response, requestUr
|
|
|
63
70
|
credentialId: credential.id,
|
|
64
71
|
publicKey: new Uint8Array(credential.publicKey),
|
|
65
72
|
counter: credential.counter,
|
|
66
|
-
transports: response.response.transports
|
|
67
|
-
|
|
73
|
+
transports: response.response.transports
|
|
74
|
+
? JSON.stringify(response.response.transports)
|
|
75
|
+
: null,
|
|
76
|
+
name,
|
|
68
77
|
});
|
|
69
78
|
return { ok: true };
|
|
70
79
|
}
|
|
@@ -77,9 +86,9 @@ export async function generateAuthenticationChallenge(db, requestUrl) {
|
|
|
77
86
|
const options = await generateAuthenticationOptions({
|
|
78
87
|
rpID,
|
|
79
88
|
allowCredentials: [],
|
|
80
|
-
userVerification:
|
|
89
|
+
userVerification: "preferred",
|
|
81
90
|
});
|
|
82
|
-
await db.storeChallenge(options.challenge,
|
|
91
|
+
await db.storeChallenge(options.challenge, "anonymous", Date.now() + CHALLENGE_EXPIRY_MS);
|
|
83
92
|
return options;
|
|
84
93
|
}
|
|
85
94
|
export async function verifyAuthenticationResponse(db, response, requestUrl) {
|
|
@@ -87,7 +96,7 @@ export async function verifyAuthenticationResponse(db, response, requestUrl) {
|
|
|
87
96
|
const passkey = await db.getPasskeyByCredentialId(response.id);
|
|
88
97
|
if (!passkey)
|
|
89
98
|
return null;
|
|
90
|
-
const challenge = JSON.parse(Buffer.from(response.response.clientDataJSON,
|
|
99
|
+
const challenge = JSON.parse(Buffer.from(response.response.clientDataJSON, "base64url").toString()).challenge;
|
|
91
100
|
const stored = await db.consumeChallenge(challenge);
|
|
92
101
|
if (!stored)
|
|
93
102
|
return null;
|
|
@@ -97,18 +106,21 @@ export async function verifyAuthenticationResponse(db, response, requestUrl) {
|
|
|
97
106
|
expectedChallenge: challenge,
|
|
98
107
|
expectedOrigin: origin,
|
|
99
108
|
expectedRPID: rpID,
|
|
109
|
+
requireUserVerification: false,
|
|
100
110
|
credential: {
|
|
101
111
|
id: passkey.credentialId,
|
|
102
112
|
publicKey: new Uint8Array(passkey.publicKey),
|
|
103
113
|
counter: passkey.counter,
|
|
104
|
-
transports: passkey.transports
|
|
105
|
-
|
|
114
|
+
transports: passkey.transports
|
|
115
|
+
? JSON.parse(passkey.transports)
|
|
116
|
+
: undefined,
|
|
117
|
+
},
|
|
106
118
|
});
|
|
107
119
|
if (!verification.verified)
|
|
108
120
|
return null;
|
|
109
121
|
await db.updatePasskeyCounter(passkey.id, verification.authenticationInfo.newCounter);
|
|
110
122
|
return {
|
|
111
|
-
user: { id: passkey.userId, email: passkey.email }
|
|
123
|
+
user: { id: passkey.userId, email: passkey.email },
|
|
112
124
|
};
|
|
113
125
|
}
|
|
114
126
|
catch {
|