UNPKG

@ownid/azure-b2c

Version:

Server-side library for integrating OwnID passwordless authentication with Azure Active Directory B2C

111 lines (110 loc) 4.73 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OwnIDB2CAuth = void 0; const microsoft_graph_client_1 = require("@microsoft/microsoft-graph-client"); const identity_1 = require("@azure/identity"); const crypto_1 = __importDefault(require("crypto")); /** * Authentication and validation utilities for OwnID Azure B2C integration */ class OwnIDB2CAuth { constructor(config) { this.config = config; } /** * Verify that a request came from OwnID by checking the signature * * @param body - The request body * @param headers - The request headers containing ownid-signature and ownid-timestamp * @returns true if the signature is valid, throws an error otherwise */ verifyOwnIdRequest(body, headers) { // Skip verification only if explicitly disabled if (this.config.disableRequestVerification === true) { return true; } // Require a shared secret for verification if (!this.config.ownIdSharedSecret) { throw new Error("Request verification failed: No shared secret provided. Set ownIdSharedSecret or explicitly disable verification with disableRequestVerification=true."); } const keyBuffer = Buffer.from(this.config.ownIdSharedSecret, 'base64'); const bodyString = JSON.stringify(body); // Handle header values that might be strings or arrays const ownIdSignature = this.getHeaderValue(headers['ownid-signature']); const ownIdTimestamp = this.getHeaderValue(headers['ownid-timestamp']); if (!ownIdSignature || !ownIdTimestamp) { throw new Error("Request rejected: Missing required OwnID headers."); } const expirationTimeInSeconds = 60000; // 1 minute if (Math.abs(Date.now() - parseInt(ownIdTimestamp)) > expirationTimeInSeconds) { throw new Error("Request rejected: Signature has expired."); } const dataToSign = `${bodyString}.${ownIdTimestamp}`; const hmac = crypto_1.default.createHmac('sha256', keyBuffer); hmac.update(dataToSign); const signature = hmac.digest('base64'); if (signature !== ownIdSignature) { throw new Error("Request rejected: Invalid signature."); } return true; } /** * Safely gets a single string value from a header that might be a string or string array * @param headerValue - The header value which might be a string, string array, or undefined * @returns A single string value or undefined */ getHeaderValue(headerValue) { if (!headerValue) { return undefined; } if (Array.isArray(headerValue)) { return headerValue[0]; } return headerValue; } /** * Get a Microsoft Graph API client with proper authentication * @returns Authenticated Microsoft Graph client */ getGraphClient() { const credential = new identity_1.ClientSecretCredential(this.config.azureTenantId, this.config.azureClientId, this.config.azureClientSecret); return microsoft_graph_client_1.Client.initWithMiddleware({ authProvider: { getAccessToken: async () => (await credential.getToken("https://graph.microsoft.com/.default")).token } }); } /** * Get Azure AD B2C authentication tokens for a user * * @param userId - Azure B2C user ID * @param email - User's email address * @returns Authentication tokens */ async getTokens(userId, email) { const credential = new identity_1.ClientSecretCredential(this.config.azureTenantId, this.config.azureClientId, this.config.azureClientSecret); const response = await credential.getToken("https://graph.microsoft.com/.default"); return { accessToken: response.token, expiresOn: new Date(Date.now() + (response.expiresOnTimestamp - Date.now())), scopes: ["https://graph.microsoft.com/.default"], account: { homeAccountId: userId, environment: "login.microsoftonline.com", tenantId: this.config.azureTenantId, username: email } }; } /** * Get the attribute name for storing OwnID data in Azure B2C * @returns The extension attribute name */ getOwnIdDataAttributeName() { return `extension_${this.config.azureB2cExtensionAppId.replace(/-/g, '')}_ownIdData`; } } exports.OwnIDB2CAuth = OwnIDB2CAuth;