UNPKG

@nopwdio/sdk-js

Version:
267 lines 10.8 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; 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 necessary modules and components from lit and local files import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; import { core } from "../internal/styles/core.styles.js"; import { component } from "../internal/styles/semantic.styles.js"; import styles from "./np-login.styles.js"; import { arrowRightCircle, arrowRightCircleSolid, busy, checkCircle, envelope, exclamationCircle, } from "../internal/styles/icons.styles.js"; import { handleCallbackCode, hasCallbackCode, request } from "../core/email.js"; import { getChallenge, isWebauthnSupported, signChallenge, verifySignature, } from "../core/webauthn.js"; import { create, get } from "../core/session.js"; import { wait } from "../internal/util/wait.js"; import { NetworkError } from "../core/errors.js"; // Define the different states the component can be in export var State; (function (State) { State["EMAIL_SENDING"] = "email:link:sending"; State["EMAIL_SENT"] = "email:link:sent"; State["EMAIL_VERIFYING"] = "email:link:verifying"; State["AUTHENTICATED"] = "authenticated"; State["PASSKEYS_VERIFYING"] = "passkeys:verifying"; State["ERROR"] = "error"; })(State || (State = {})); /** * @summary `np-login` is a custom element for user authentication via email link (magic-link) or Passkeys. * * @description * This component manages the authentication process using WebAuthn passkeys or email link authentication. * It handles sending email links, processing callback codes, and verifying passkey challenges. * The component emits events for successful login and errors, allowing other parts of the application to * respond accordingly. It also provides visual feedback for different states of the authentication process. * * @event np:login - Emitted when the session is successfully created. * @event np:error - Emitted when an error occurs during the authentication process. * * @csspart button - The submit button wrapper. * @csspart input - The email input wrapper. */ let NpLogin = class NpLogin extends LitElement { constructor() { super(...arguments); this.placeholder = "Your email"; this.id = "input"; this.value = ""; } // Lifecycle method called when the element is added to the document connectedCallback() { const _super = Object.create(null, { connectedCallback: { get: () => super.connectedCallback } }); return __awaiter(this, void 0, void 0, function* () { _super.connectedCallback.call(this); this.startConditionalUI(); this.handleCallbackIfNeeded(); }); } disconnectedCallback() { const _super = Object.create(null, { disconnectedCallback: { get: () => super.disconnectedCallback } }); return __awaiter(this, void 0, void 0, function* () { var _a; _super.disconnectedCallback.call(this); (_a = this.abortConditianal) === null || _a === void 0 ? void 0 : _a.abort(); this.abortConditianal = undefined; }); } // Method to handle login with email loginWithEmail() { return __awaiter(this, void 0, void 0, function* () { if ((this.state !== undefined && this.state !== State.EMAIL_SENT) || this.value.length < 3) { return; } try { this.state = State.EMAIL_SENDING; const resp = yield request({ email: this.value }); this.state = State.EMAIL_SENT; return resp; } catch (e) { this.signalError(e); } }); } // Method to handle callback if needed handleCallbackIfNeeded() { return __awaiter(this, void 0, void 0, function* () { if (!hasCallbackCode()) { return false; } try { this.state = State.EMAIL_VERIFYING; const token = yield handleCallbackCode(); const session = yield create(token, this.lifetime, this.idletimeout); yield this.signalSuccess(session); return true; } catch (e) { yield this.signalError(e); return false; } }); } // Method to start conditional UI based on WebAuthn support startConditionalUI() { return __awaiter(this, void 0, void 0, function* () { var _a; this.passkeys = undefined; this.passkeys = yield isWebauthnSupported(); (_a = this.abortConditianal) === null || _a === void 0 ? void 0 : _a.abort(); this.abortConditianal = new AbortController(); for (let i = 0; i <= 20; i++) { try { const { challenge } = yield getChallenge(); this.passkeys = true; const auth = yield signChallenge(challenge, this.abortConditianal.signal); this.state = State.PASSKEYS_VERIFYING; const token = yield verifySignature(auth); const session = yield create(token, this.lifetime, this.idletimeout); yield this.signalSuccess(session); } catch (e) { console.log(e); if (e instanceof NetworkError) { this.state = undefined; yield wait((i + 1) * 1000); } else { yield this.signalError(e); } } } return false; }); } // Method to get the current session getSession() { return __awaiter(this, void 0, void 0, function* () { return get(); }); } // Method to signal successful authentication signalSuccess(session) { return __awaiter(this, void 0, void 0, function* () { this.state = State.AUTHENTICATED; this.dispatchEvent(new CustomEvent("np:login", { composed: true, cancelable: true, bubbles: true, detail: session, })); yield wait(3000); this.state = undefined; }); } // Method to signal an error signalError(e) { return __awaiter(this, void 0, void 0, function* () { this.state = State.ERROR; this.dispatchEvent(new CustomEvent("np:error", { composed: true, cancelable: true, bubbles: true, detail: e, })); yield wait(1000); this.state = undefined; }); } // Method to handle input events onInput() { return __awaiter(this, void 0, void 0, function* () { var _a; const input = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("input"); if (!input) { return; } const oldValue = this.value; this.value = input.value; if (this.state === State.EMAIL_SENT) { this.state = undefined; } // capturing copy/past or password manager if (oldValue.length === 0 && this.value.length >= 3) { this.loginWithEmail(); } }); } // Method to handle key up events onKeyUp(e) { if (e.key === "Enter") { this.loginWithEmail(); } } // Render the component's HTML template render() { return html ` <input type="email" @input=${this.onInput} @keyup=${this.onKeyUp} value=${this.value} placeholder=${this.placeholder} id="${this.id}" autocomplete="username webauthn" part="input" /> <button @click=${this.loginWithEmail} part="button"> ${this.state === State.EMAIL_SENDING ? html `${busy}` : this.state === State.EMAIL_SENT ? html `${envelope}` : this.state === State.EMAIL_VERIFYING ? html `${busy}` : this.state === State.PASSKEYS_VERIFYING ? html `${busy}` : this.state === State.AUTHENTICATED ? html `${checkCircle}` : this.state === State.ERROR ? html `${exclamationCircle}` : html `${this.passkeys ? arrowRightCircleSolid : arrowRightCircle}`} </button> `; } }; // Apply styles to the component NpLogin.styles = [core, component, styles]; __decorate([ property({ reflect: true }) ], NpLogin.prototype, "state", void 0); __decorate([ property({ reflect: true, type: Boolean }) ], NpLogin.prototype, "passkeys", void 0); __decorate([ property({ type: String }) ], NpLogin.prototype, "placeholder", void 0); __decorate([ property({ type: String }) ], NpLogin.prototype, "id", void 0); __decorate([ property({ type: Number }) ], NpLogin.prototype, "lifetime", void 0); __decorate([ property({ type: Number }) ], NpLogin.prototype, "idletimeout", void 0); __decorate([ property({ type: String }) ], NpLogin.prototype, "value", void 0); NpLogin = __decorate([ customElement("np-login") ], NpLogin); export { NpLogin }; //# sourceMappingURL=np-login.js.map