UNPKG

@ideem/zsm-client-sdk

Version:

ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no relian

198 lines (182 loc) 10.9 kB
import eventCoordinator from './EventCoordinator.js'; import GLOBAL from './GlobalScoping.js'; import {uid2iid} from './IdentityIndexing.js'; eventCoordinator.update('RelyingPartyBase'); function tracePrimer (obj=false) { const traceBase = [[new Date().toISOString(), "TRACEPRIMER"]] return (obj) ? {"trace": traceBase} : traceBase; } //## BEGIN RelyingPartyBase =================================================================================================================================== class RelyingPartyBase { /** * Constructs a RelyingParty object. * @param {string} host - The URL of the relying party server. * @param {string} apiKey - The API key for authentication. * @param {string} applicationID - The ID of the application. */ constructor(host, apiKey, applicationID, appEnvironment, useOrigin) { this.host = host; this.apiKey = apiKey; this.applicationId = applicationID; this.appEnvironment = appEnvironment || null; this.customerDefinedIdentifier = undefined; this.publicKey = undefined; this.identityID = undefined; this.credentialID = undefined; this.traceId = undefined; this.xhrHeaders = { 'Content-Type' : 'application/json' }; this.addlRelyingPartyBodyProps = useOrigin ? {use_origin: "true"} : {}; eventCoordinator.update('RelyingPartyBase', 'READY'); } get userIdentifier() { return this.customerDefinedIdentifier; } set userIdentifier(v) { return this.customerDefinedIdentifier = v; } /** * @name makePostRequest * @description Performs an HTTP POST request * @param {string} url The URL to send the request to. * @param {Object} body The request body data. * @param {Object} headers Additional headers for the request. * @param {string} method The HTTP method to use (default is 'POST'). * @returns {Promise<Object>} Resolves with the response data. */ makePostRequest (url, body={}, headers={}, method='POST') { url = `${this.host.endsWith('/') ? this.host.slice(0,-1) : this.host}/${url.startsWith('/') ? url.slice(1) : url}`; body = ((typeof body === 'object') ? body : {}); if(this.appEnvironment) body = Object.assign(body, {environment: this.appEnvironment}); body = Object.assign({ customer_defined_identifier : this.customerDefinedIdentifier, application_id : this.applicationId }, body, this.addlRelyingPartyBodyProps, tracePrimer(true)); let fetchObj = { method, headers : Object.assign(this.xhrHeaders, ((!headers?.Authorization) ? {'Authorization': `Bearer ${this.apiKey}`} : {}), headers), body : JSON.stringify(body) }; return fetch(url, fetchObj) .then(response => response.ok ? response.json() : response.json().then(errData => { throw new Error(`[RelyingParty] :: makePostRequest :: Request failed: ${response.statusText}, ${JSON.stringify(errData)}`) }) ) .then(response => { GLOBAL.dispatchEvent(new CustomEvent("AuthResponse", {bubbles: true, cancelable: false, detail: response })); return response; }); } /** * @name clearEnrollmentCredentials * @description Clears the enrollment credentials. * @returns {Promise<ZSMAPI>} */ clearEnrollmentCredentials () { return (this.publicKey = this.credentialID = null) }; /** * @name clearLoginCredentials * @description Clears the login credentials. * @returns {Promise<Void>} * @todo Verify nothing is calling this and remove it outright. */ clearLoginCredentials () {}; /** * @name resetAll * @description Resets all credentials. * @returns {Promise<Void>} */ resetAll () { return (this.clearLoginCredentials(), this.clearEnrollmentCredentials()); } /** * @name registrationStart * @description Sends a login request to the server. * @returns {Promise<Object>} Resolves with publicKey object used in the subsequent Crypto Server API call. */ registrationStart () { return this.makePostRequest("api/webauthn/registration/start") .then(data => { if(data.identity_id){ GLOBAL.mpcConfig.consumer_id = data.identity_id; uid2iid(this.userIdentifier, data.identity_id); } return (this.publicKey = data.ccr.publicKey) }) .then(result => result) .catch(error => Promise.reject((error instanceof Error) ? error : new Error(error))); } /** * @name registrationFinish * @description Completes WebAuthn registration by sending the credential to the server. * @param {Object} credential The credential information to finalize registration. * @returns {Promise<Object>} Resolves with the registered credential. * @throws {Error} Throws an error if the request fails. */ registrationFinish (credential) { return this.makePostRequest("api/webauthn/registration/finish", { credential }) .then(() => (this.credentialID = credential.rawId, credential)) .then(credential => credential) .catch(error => (this.clearEnrollmentCredentials(), Promise.reject((error instanceof Error) ? error : new Error(error)))) } /** * @name authenticationStart * @description Starts the authentication process by sending a request to the server. * @param {String} credential_id The ID of the credential to use for authentication. * @returns {Promise<Object>} Resolves with the authentication challenge. * @throws {Error} Throws an error if the request fails. */ authenticationStart (credential_id=this.credentialID) { return (credential_id != null) ? this.makePostRequest( "api/webauthn/authentication/start", { credential_id }) .then(result => result) : (Promise.reject(new Error("[RelyingParty] :: authenticationStart :: No user ID or credential ID provided"))) } /** * @name authenticationFinish * @description Completes the authentication process by sending the credential to the server. * @param {Object} credential The credential information to finalize authentication. * @returns {Promise<Object>} Resolves with the authentication result. * @throws {Error} Throws an error if the request fails. */ authenticationFinish (credential) { return this.makePostRequest("api/webauthn/authentication/finish", { credential }) .then(result => result) .catch(error => Promise.reject((error instanceof Error) ? error : new Error(error))); } /** * @name createIdentityThenRegistrationStart * @description UMFA-OPTIMIZED FLOW: Creates a server-side Identity and then starts the WebAuthn Registration challenge automatically. * @returns {Promise<Object>} Resolves with an object containing the publicKey and identity_id. * @throws {Error} Throws an error if the request fails. */ createIdentityThenRegistrationStart() { return this.makePostRequest("api/webauthn/registration/create-identity-then-start") .then(data => { if(data.identity_id){ GLOBAL.mpcConfig.consumer_id = data.identity_id; uid2iid(this.userIdentifier, data.identity_id); } return {publicKey:(this.publicKey = data.ccr.publicKey), identity_id:data.identity_id} }) .then(result => result) .catch(error => Promise.reject((error instanceof Error) ? error : new Error(error))); } /** * @name registrationFinishAuthenticationStart * @description UMFA-OPTIMIZED FLOW: Completes WebAuthn registration by sending the credential to the server. * @param {Object} credential The credential information to finalize registration. * @returns {Promise<Object>} Resolves with the registered credential. * @throws {Error} Throws an error if the request fails. */ registrationFinishAuthenticationStart (credential) { return this.makePostRequest("api/webauthn/registration/finish-then-authentication-start", { credential }) .then(result => (this.credentialID = credential.rawId, result)) .then(credential => credential) .catch(error => (this.clearEnrollmentCredentials(), Promise.reject((error instanceof Error) ? error : new Error(error)))); } /** * @name checkServerSideIdentity * @description Checks if an identity exists on the server and creates one if it doesn't. * @param {boolean} createNewIdentity Whether to create a new identity if one doesn't exist. * @returns {Promise<Object>} Resolves with the server response. * @throws {Error} Throws an error if the request fails. */ checkServerSideIdentity (createNewIdentity) { createNewIdentity = createNewIdentity ? { create_new_identity: "true" } : {}; return this.makePostRequest("api/umfa/check-identity", createNewIdentity) .then(result => result) .catch(error => Promise.reject(error)); } } //## END RelyingPartyBase ===================================================================================================================================== export default RelyingPartyBase;