@turnkey/react-native-passkey-stamper
Version:
Passkey stamper for React Native
136 lines (132 loc) • 5.78 kB
JavaScript
;
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