Update passkey.service.ts
This commit is contained in:
@@ -30,18 +30,12 @@ const textEncoder = new TextEncoder();
|
|||||||
|
|
||||||
function base64UrlToUint8Array(base64urlString: string): Uint8Array {
|
function base64UrlToUint8Array(base64urlString: string): Uint8Array {
|
||||||
const base64 = base64urlString.replace(/-/g, '+').replace(/_/g, '/');
|
const base64 = base64urlString.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
const padLength = (4 - (base64.length % 4)) % 4;
|
// Buffer.from will handle padding correctly for base64
|
||||||
const paddedBase64 = base64 + '='.repeat(padLength);
|
|
||||||
try {
|
try {
|
||||||
const binaryString = atob(paddedBase64);
|
return Buffer.from(base64, 'base64');
|
||||||
const uint8Array = new Uint8Array(binaryString.length);
|
|
||||||
for (let i = 0; i < binaryString.length; i++) {
|
|
||||||
uint8Array[i] = binaryString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
return uint8Array;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to decode base64url string:", base64urlString, e);
|
console.error("Failed to decode base64url string to Buffer:", base64urlString, e);
|
||||||
throw new Error("Invalid base64url string");
|
throw new Error("Invalid base64url string for Buffer conversion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,35 +177,86 @@ export class PasskeyService {
|
|||||||
expectedChallenge: string
|
expectedChallenge: string
|
||||||
): Promise<VerifiedAuthenticationResponse & { passkey?: Passkey, userId?: number }> {
|
): Promise<VerifiedAuthenticationResponse & { passkey?: Passkey, userId?: number }> {
|
||||||
|
|
||||||
|
console.log('[PasskeyService] Verifying authentication. Client response:', JSON.stringify(authenticationResponseJSON, null, 2));
|
||||||
|
console.log('[PasskeyService] Expected challenge:', expectedChallenge);
|
||||||
|
|
||||||
const credentialIdFromResponse = authenticationResponseJSON.id;
|
const credentialIdFromResponse = authenticationResponseJSON.id;
|
||||||
if (!credentialIdFromResponse) {
|
if (!credentialIdFromResponse) {
|
||||||
|
console.error('[PasskeyService] Credential ID missing from authentication response.');
|
||||||
throw new Error('Credential ID missing from authentication response.');
|
throw new Error('Credential ID missing from authentication response.');
|
||||||
}
|
}
|
||||||
|
console.log('[PasskeyService] Credential ID from response:', credentialIdFromResponse);
|
||||||
|
|
||||||
const passkey = await this.passkeyRepo.getPasskeyByCredentialId(credentialIdFromResponse);
|
const passkey = await this.passkeyRepo.getPasskeyByCredentialId(credentialIdFromResponse);
|
||||||
if (!passkey) {
|
if (!passkey) {
|
||||||
|
console.error('[PasskeyService] Passkey not found for credential ID:', credentialIdFromResponse);
|
||||||
throw new Error('Authentication failed. Passkey not found.');
|
throw new Error('Authentication failed. Passkey not found.');
|
||||||
}
|
}
|
||||||
|
console.log('[PasskeyService] Passkey from DB:', JSON.stringify(passkey, null, 2));
|
||||||
|
|
||||||
// TODO: Re-evaluate the structure of VerifyAuthenticationResponseOpts for @simplewebauthn/server@13.1.1
|
let authenticatorCredentialID: Uint8Array;
|
||||||
// The 'authenticator' field seems to be causing type errors.
|
try {
|
||||||
const verifyOpts: any = { // Using 'any' temporarily to bypass the authenticator structure error
|
authenticatorCredentialID = base64UrlToUint8Array(passkey.credential_id);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('[PasskeyService] Error decoding credential_id to Uint8Array:', passkey.credential_id, e.message);
|
||||||
|
throw new Error('Failed to decode credential_id.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let authenticatorPublicKey: Buffer;
|
||||||
|
try {
|
||||||
|
authenticatorPublicKey = Buffer.from(passkey.public_key, 'base64');
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('[PasskeyService] Error decoding public_key to Buffer:', passkey.public_key, e.message);
|
||||||
|
throw new Error('Failed to decode public_key.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let authenticatorTransports: AuthenticatorTransportFuture[] | undefined;
|
||||||
|
try {
|
||||||
|
authenticatorTransports = passkey.transports ? JSON.parse(passkey.transports) as AuthenticatorTransportFuture[] : undefined;
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('[PasskeyService] Error parsing transports JSON:', passkey.transports, e.message);
|
||||||
|
authenticatorTransports = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authenticatorObject = {
|
||||||
|
credentialID: authenticatorCredentialID,
|
||||||
|
credentialPublicKey: authenticatorPublicKey,
|
||||||
|
counter: passkey.counter,
|
||||||
|
transports: authenticatorTransports,
|
||||||
|
credentialBackedUp: !!passkey.backed_up,
|
||||||
|
// Ensure credentialDeviceType is one of the allowed string literals
|
||||||
|
credentialDeviceType: (passkey.backed_up ? 'multiDevice' : 'singleDevice') as 'multiDevice' | 'singleDevice',
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('[PasskeyService] Authenticator object to be used for verification:');
|
||||||
|
console.log(` - credentialID (type: ${typeof authenticatorObject.credentialID}, instanceof Uint8Array: ${authenticatorObject.credentialID instanceof Uint8Array}, length: ${authenticatorObject.credentialID?.length}):`, authenticatorObject.credentialID);
|
||||||
|
console.log(` - credentialPublicKey (type: ${typeof authenticatorObject.credentialPublicKey}, instanceof Buffer: ${authenticatorObject.credentialPublicKey instanceof Buffer}, length: ${authenticatorObject.credentialPublicKey?.length}):`, authenticatorObject.credentialPublicKey);
|
||||||
|
console.log(` - counter (type: ${typeof authenticatorObject.counter}):`, authenticatorObject.counter);
|
||||||
|
console.log(` - transports (type: ${typeof authenticatorObject.transports}):`, authenticatorObject.transports);
|
||||||
|
console.log(` - credentialBackedUp (type: ${typeof authenticatorObject.credentialBackedUp}):`, authenticatorObject.credentialBackedUp);
|
||||||
|
console.log(` - credentialDeviceType (type: ${typeof authenticatorObject.credentialDeviceType}):`, authenticatorObject.credentialDeviceType);
|
||||||
|
|
||||||
|
// Reverting to 'any' for verifyOpts due to issues with the library's
|
||||||
|
// type definitions for VerifyAuthenticationResponseOpts not recognizing 'authenticator' key.
|
||||||
|
// This aligns with the original code's approach and TODO comment.
|
||||||
|
const verifyOpts: any = {
|
||||||
response: authenticationResponseJSON,
|
response: authenticationResponseJSON,
|
||||||
expectedChallenge,
|
expectedChallenge,
|
||||||
expectedOrigin: RP_ORIGIN,
|
expectedOrigin: RP_ORIGIN,
|
||||||
expectedRPID: RP_ID,
|
expectedRPID: RP_ID,
|
||||||
authenticator: {
|
authenticator: authenticatorObject,
|
||||||
credentialID: base64UrlToUint8Array(passkey.credential_id),
|
|
||||||
credentialPublicKey: Buffer.from(passkey.public_key, 'base64'),
|
|
||||||
counter: passkey.counter,
|
|
||||||
transports: passkey.transports ? JSON.parse(passkey.transports) as AuthenticatorTransportFuture[] : undefined,
|
|
||||||
credentialBackedUp: !!passkey.backed_up,
|
|
||||||
credentialDeviceType: passkey.backed_up ? 'multiDevice' : 'singleDevice',
|
|
||||||
},
|
|
||||||
requireUserVerification: true,
|
requireUserVerification: true,
|
||||||
};
|
};
|
||||||
|
console.log('[PasskeyService] verifyOpts to be passed to @simplewebauthn/server (using type any):', JSON.stringify(verifyOpts, (key, value) => {
|
||||||
|
if (value instanceof Uint8Array || value instanceof Buffer) {
|
||||||
|
// Represent Uint8Array/Buffer as a string indicating its type and length for cleaner logs
|
||||||
|
return `[${value instanceof Buffer ? 'Buffer' : 'Uint8Array'} len:${value.length}]`;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}, 2));
|
||||||
|
|
||||||
const verification = await verifyAuthenticationResponse(verifyOpts as VerifyAuthenticationResponseOpts);
|
// Call without 'as VerifyAuthenticationResponseOpts' since verifyOpts is 'any'
|
||||||
|
const verification = await verifyAuthenticationResponse(verifyOpts);
|
||||||
|
|
||||||
if (verification.verified && verification.authenticationInfo) {
|
if (verification.verified && verification.authenticationInfo) {
|
||||||
const authInfo = verification.authenticationInfo;
|
const authInfo = verification.authenticationInfo;
|
||||||
|
|||||||
Reference in New Issue
Block a user