Files
tranquil-pds/frontend/src/lib/webauthn.ts
T
2026-01-05 00:48:18 +02:00

159 lines
4.3 KiB
TypeScript

export interface PublicKeyCredentialDescriptorJSON {
type: "public-key";
id: string;
transports?: AuthenticatorTransport[];
}
export interface PublicKeyCredentialUserEntityJSON {
id: string;
name: string;
displayName: string;
}
export interface PublicKeyCredentialRpEntityJSON {
name: string;
id?: string;
}
export interface PublicKeyCredentialParametersJSON {
type: "public-key";
alg: number;
}
export interface AuthenticatorSelectionCriteriaJSON {
authenticatorAttachment?: AuthenticatorAttachment;
residentKey?: ResidentKeyRequirement;
requireResidentKey?: boolean;
userVerification?: UserVerificationRequirement;
}
export interface PublicKeyCredentialCreationOptionsJSON {
rp: PublicKeyCredentialRpEntityJSON;
user: PublicKeyCredentialUserEntityJSON;
challenge: string;
pubKeyCredParams: PublicKeyCredentialParametersJSON[];
timeout?: number;
excludeCredentials?: PublicKeyCredentialDescriptorJSON[];
authenticatorSelection?: AuthenticatorSelectionCriteriaJSON;
attestation?: AttestationConveyancePreference;
}
export interface PublicKeyCredentialRequestOptionsJSON {
challenge: string;
timeout?: number;
rpId?: string;
allowCredentials?: PublicKeyCredentialDescriptorJSON[];
userVerification?: UserVerificationRequirement;
}
export interface WebAuthnCreationOptionsResponse {
publicKey: PublicKeyCredentialCreationOptionsJSON;
}
export interface WebAuthnRequestOptionsResponse {
publicKey: PublicKeyCredentialRequestOptionsJSON;
}
export interface CredentialAssertionJSON {
id: string;
type: string;
rawId: string;
response: {
clientDataJSON: string;
authenticatorData: string;
signature: string;
userHandle: string | null;
};
}
export interface CredentialAttestationJSON {
id: string;
type: string;
rawId: string;
response: {
clientDataJSON: string;
attestationObject: string;
};
}
export function base64UrlToArrayBuffer(base64url: string): ArrayBuffer {
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4);
const binary = atob(padded);
return Uint8Array.from(binary, (char) => char.charCodeAt(0)).buffer;
}
export function arrayBufferToBase64Url(buffer: ArrayBuffer): string {
const bytes = new Uint8Array(buffer);
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join(
"",
);
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
export function prepareCreationOptions(
options: WebAuthnCreationOptionsResponse,
): PublicKeyCredentialCreationOptions {
const pk = options.publicKey;
return {
...pk,
challenge: base64UrlToArrayBuffer(pk.challenge),
user: {
...pk.user,
id: base64UrlToArrayBuffer(pk.user.id),
},
excludeCredentials: (pk.excludeCredentials ?? []).map((cred) => ({
...cred,
id: base64UrlToArrayBuffer(cred.id),
})),
};
}
export function prepareRequestOptions(
options: WebAuthnRequestOptionsResponse,
): PublicKeyCredentialRequestOptions {
const pk = options.publicKey;
return {
...pk,
challenge: base64UrlToArrayBuffer(pk.challenge),
allowCredentials: (pk.allowCredentials ?? []).map((cred) => ({
...cred,
id: base64UrlToArrayBuffer(cred.id),
})),
};
}
export function serializeAttestationResponse(
credential: PublicKeyCredential,
): CredentialAttestationJSON {
const response = credential.response as AuthenticatorAttestationResponse;
return {
id: credential.id,
type: credential.type,
rawId: arrayBufferToBase64Url(credential.rawId),
response: {
clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
attestationObject: arrayBufferToBase64Url(response.attestationObject),
},
};
}
export function serializeAssertionResponse(
credential: PublicKeyCredential,
): CredentialAssertionJSON {
const response = credential.response as AuthenticatorAssertionResponse;
return {
id: credential.id,
type: credential.type,
rawId: arrayBufferToBase64Url(credential.rawId),
response: {
clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
authenticatorData: arrayBufferToBase64Url(response.authenticatorData),
signature: arrayBufferToBase64Url(response.signature),
userHandle: response.userHandle
? arrayBufferToBase64Url(response.userHandle)
: null,
},
};
}