UNPKG

@turnkey/react-native-passkey-stamper

Version:

Passkey stamper for React Native

132 lines (129 loc) 5.55 kB
import { Passkey } from 'react-native-passkey'; import { base64StringToBase64UrlEncodedString } from '@turnkey/encoding'; import { getChallengeFromPayload, getRandomChallenge } from './util.mjs'; var AuthenticatorTransport; (function (AuthenticatorTransport) { AuthenticatorTransport["usb"] = "usb"; AuthenticatorTransport["nfc"] = "nfc"; AuthenticatorTransport["ble"] = "ble"; AuthenticatorTransport["smartCard"] = "smart-card"; AuthenticatorTransport["hybrid"] = "hybrid"; AuthenticatorTransport["internal"] = "internal"; })(AuthenticatorTransport || (AuthenticatorTransport = {})); /** * Header name for a webauthn stamp */ const stampHeaderName = "X-Stamp-Webauthn"; const defaultTimeout = 5 * 60 * 1000; // five minutes const defaultUserVerification = "preferred"; /** * Re-export of the underlying library's `isSupported` method */ function isSupported() { return Passkey.isSupported(); } /** * Creates a passkey and returns authenticator params */ async function createPasskey(config, options) { const challenge = config.challenge || getRandomChallenge(); let createFn = options?.withPlatformKey ? Passkey.createPlatformKey : options?.withSecurityKey ? Passkey.createSecurityKey : Passkey.create; let registrationResult = await createFn({ challenge: challenge, rp: config.rp, user: config.user, excludeCredentials: config.excludeCredentials || [], authenticatorSelection: config.authenticatorSelection || { requireResidentKey: true, residentKey: "required", userVerification: "preferred", }, attestation: config.attestation || "none", extensions: config.extensions || {}, // All algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms // We only support ES256 and RS256, which are listed below pubKeyCredParams: [ { type: "public-key", alg: -7, }, { type: "public-key", alg: -257, }, ], }); // See https://github.com/f-23/react-native-passkey/issues/54 // On Android the typedef lies. Registration result is actually a string! // TODO: remove me once the above is resolved. const brokenRegistrationResult = registrationResult; if (typeof brokenRegistrationResult === "string") { registrationResult = JSON.parse(brokenRegistrationResult); } return { authenticatorName: config.authenticatorName, challenge: challenge, attestation: { credentialId: base64StringToBase64UrlEncodedString(registrationResult.id), clientDataJson: base64StringToBase64UrlEncodedString(registrationResult.response.clientDataJSON), attestationObject: base64StringToBase64UrlEncodedString(registrationResult.response.attestationObject), // TODO: can we infer the transport from the registration result? // In all honesty this isn't critical so we default to "hybrid" because that's the transport used by passkeys. transports: ["AUTHENTICATOR_TRANSPORT_HYBRID"], }, }; } /** * Stamper to use with `@turnkey/http`'s `TurnkeyClient` */ class PasskeyStamper { constructor(config) { this.rpId = config.rpId; this.timeout = config.timeout || defaultTimeout; this.userVerification = config.userVerification || defaultUserVerification; this.allowCredentials = config.allowCredentials || []; this.extensions = config.extensions || {}; this.forcePlatformKey = !!config.withPlatformKey; this.forceSecurityKey = !!config.withSecurityKey; } async stamp(payload) { const challenge = getChallengeFromPayload(payload); const signingOptions = { challenge: challenge, rpId: this.rpId, timeout: this.timeout, allowCredentials: this.allowCredentials, userVerification: this.userVerification, extensions: this.extensions, }; let passkeyGetfn = this.forcePlatformKey ? Passkey.getPlatformKey : this.forceSecurityKey ? Passkey.getSecurityKey : Passkey.get; let authenticationResult = await passkeyGetfn(signingOptions); // See https://github.com/f-23/react-native-passkey/issues/54 // On Android the typedef lies. Authentication result is actually a string! // TODO: remove me once the above is resolved. const brokenAuthenticationResult = authenticationResult; if (typeof brokenAuthenticationResult === "string") { authenticationResult = JSON.parse(brokenAuthenticationResult); } const stamp = { authenticatorData: base64StringToBase64UrlEncodedString(authenticationResult.response.authenticatorData), clientDataJson: base64StringToBase64UrlEncodedString(authenticationResult.response.clientDataJSON), credentialId: base64StringToBase64UrlEncodedString(authenticationResult.id), signature: base64StringToBase64UrlEncodedString(authenticationResult.response.signature), }; return { stampHeaderName: stampHeaderName, stampHeaderValue: JSON.stringify(stamp), }; } } export { AuthenticatorTransport, PasskeyStamper, createPasskey, isSupported }; //# sourceMappingURL=index.mjs.map