UNPKG

@turnkey/react-native-passkey-stamper

Version:

Passkey stamper for React Native

136 lines (132 loc) 5.78 kB
'use strict'; var reactNativePasskey = require('react-native-passkey'); var encoding = require('@turnkey/encoding'); var util = require('./util.js'); exports.AuthenticatorTransport = void 0; (function (AuthenticatorTransport) { AuthenticatorTransport["usb"] = "usb"; AuthenticatorTransport["nfc"] = "nfc"; AuthenticatorTransport["ble"] = "ble"; AuthenticatorTransport["smartCard"] = "smart-card"; AuthenticatorTransport["hybrid"] = "hybrid"; AuthenticatorTransport["internal"] = "internal"; })(exports.AuthenticatorTransport || (exports.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 reactNativePasskey.Passkey.isSupported(); } /** * Creates a passkey and returns authenticator params */ async function createPasskey(config, options) { const challenge = config.challenge || util.getRandomChallenge(); let createFn = options?.withPlatformKey ? reactNativePasskey.Passkey.createPlatformKey : options?.withSecurityKey ? reactNativePasskey.Passkey.createSecurityKey : reactNativePasskey.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: encoding.base64StringToBase64UrlEncodedString(registrationResult.id), clientDataJson: encoding.base64StringToBase64UrlEncodedString(registrationResult.response.clientDataJSON), attestationObject: encoding.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 = util.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 ? reactNativePasskey.Passkey.getPlatformKey : this.forceSecurityKey ? reactNativePasskey.Passkey.getSecurityKey : reactNativePasskey.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: encoding.base64StringToBase64UrlEncodedString(authenticationResult.response.authenticatorData), clientDataJson: encoding.base64StringToBase64UrlEncodedString(authenticationResult.response.clientDataJSON), credentialId: encoding.base64StringToBase64UrlEncodedString(authenticationResult.id), signature: encoding.base64StringToBase64UrlEncodedString(authenticationResult.response.signature), }; return { stampHeaderName: stampHeaderName, stampHeaderValue: JSON.stringify(stamp), }; } } exports.PasskeyStamper = PasskeyStamper; exports.createPasskey = createPasskey; exports.isSupported = isSupported; //# sourceMappingURL=index.js.map