UNPKG

@nopwdio/sdk-js

Version:
175 lines 6.92 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { endpoint } from "../internal/api/endpoint.js"; import { AbortError, NetworkError, NotFoundError, TooManyRequestsError, UnauthorizedError, } from "../internal/api/errors.js"; import { bufferTo64Safe, decodeFromSafe64 } from "../internal/crypto/encoding.js"; import { sha256 } from "../internal/crypto/hash.js"; import { InvalidSignatureError, InvalidTokenError, QuotaError, UnexpectedError, UnknownChallengeOrPasskeyError, WebauthnNotSupportedError, } from "./errors.js"; import { getPayload } from "./token.js"; /** * Register a new Passkey * @param token An access token to prove the user has been already authenticated */ export const register = function (token, signal) { return __awaiter(this, void 0, void 0, function* () { try { if (!(yield isWebauthnSupported())) { throw new WebauthnNotSupportedError(); } const payload = getPayload(token); if (payload.amr.includes("webauthn")) { throw new Error("A passkey already exists for this website"); } if (payload.exp < Date.now() / 1000) { throw new InvalidTokenError(); } // to avoid storing personal identifier in plain text const userId = yield sha256(payload.sub); const cred = (yield navigator.credentials.create({ signal, publicKey: { challenge: decodeFromSafe64(payload.jti), rp: { name: payload.aud, }, user: { id: userId, name: payload.sub, displayName: payload.sub, }, pubKeyCredParams: [{ alg: -7, type: "public-key" }], authenticatorSelection: { userVerification: "required", }, }, })); if (cred === null) { throw new AbortError(); } const { alg } = (yield endpoint({ method: "POST", ressource: "/webauthn/passkeys", data: { client_data: bufferTo64Safe(cred.response.clientDataJSON), attestation_object: bufferTo64Safe(cred.response.attestationObject), access_token: token, }, signal, })); return { id: cred.id, alg: alg, }; } catch (e) { if (e instanceof AbortError || e instanceof NetworkError || e instanceof InvalidTokenError || e instanceof WebauthnNotSupportedError) { throw e; } throw new UnexpectedError(e); } }); }; export const startConditional = (signal) => __awaiter(void 0, void 0, void 0, function* () { const { challenge } = yield getChallenge(signal); const authResponse = yield signChallenge(challenge, signal); return yield verifySignature(authResponse, signal); }); export const getChallenge = (signal) => __awaiter(void 0, void 0, void 0, function* () { try { const { challenge, expires_at } = (yield endpoint({ method: "GET", ressource: "/webauthn/challenge", signal, })); return { challenge, expiresAt: expires_at, }; } catch (e) { if (e instanceof AbortError || e instanceof NetworkError) { throw e; } if (e instanceof TooManyRequestsError) { throw new QuotaError(e.getRetryAt()); } throw new UnexpectedError(e); } }); export const signChallenge = function (challenge, signal) { return __awaiter(this, void 0, void 0, function* () { try { if (!(yield isWebauthnSupported())) { throw new WebauthnNotSupportedError(); } var options = { signal, publicKey: { userVerification: "required", challenge: decodeFromSafe64(challenge), }, mediation: "conditional", }; const result = (yield navigator.credentials.get(options)); if (result === null) { throw new AbortError(); } return { id: result.id, signature: bufferTo64Safe(result.response.signature), authenticatorData: bufferTo64Safe(result.response.authenticatorData), clientData: bufferTo64Safe(result.response.clientDataJSON), }; } catch (e) { if (e instanceof AbortError || e instanceof WebauthnNotSupportedError) { throw e; } throw new UnexpectedError(e); } }); }; export const verifySignature = (params, signal) => __awaiter(void 0, void 0, void 0, function* () { try { const { access_token } = (yield endpoint({ method: "POST", ressource: "/webauthn/tokens", data: { id: params.id, signature: params.signature, authenticator_data: params.authenticatorData, client_data: params.clientData, }, signal, })); return access_token; } catch (e) { if (e instanceof AbortError || e instanceof NetworkError) { throw e; } if (e instanceof NotFoundError) { throw new UnknownChallengeOrPasskeyError(); } if (e instanceof UnauthorizedError) { throw new InvalidSignatureError(); } throw new UnexpectedError(e); } }); export const isWebauthnSupported = () => __awaiter(void 0, void 0, void 0, function* () { var _a, _b; return (window.PublicKeyCredential !== undefined && ((_b = (_a = window.PublicKeyCredential).isConditionalMediationAvailable) === null || _b === void 0 ? void 0 : _b.call(_a))); }); //# sourceMappingURL=webauthn.js.map