UNPKG

auth0

Version:
106 lines 4.28 kB
// Wednesday, 8 January, 2025 // Client Initiated Backchannel Authentication (CIBA) // CIBA is an OpenID Foundation standard for a decoupled authentication flow. It enables // solution developers to build authentication flows where the user logging in does not do so // directly on the device that receives the ID or access tokens (the “Consumption Device”), but // instead on a separate “Authorization Device”. import { JSONApiResponse } from '../lib/models.js'; import { BaseAuthAPI } from './base-auth-api.js'; /** * Generates the login hint for the user. * * @param {string} userId - The user ID. * @param {string} domain - The tenant domain. * @returns {string} - The login hint as a JSON string. */ const getLoginHint = (userId, domain) => { // remove trailing '/' from domain, added later for uniformity const trimmedDomain = domain.endsWith('/') ? domain.slice(0, -1) : domain; const loginHint = { format: 'iss_sub', iss: `https://${trimmedDomain}/`, sub: `${userId}`, }; return JSON.stringify(loginHint); }; const CIBA_GRANT_TYPE = 'urn:openid:params:grant-type:ciba'; const CIBA_AUTHORIZE_URL = '/bc-authorize'; const CIBA_TOKEN_URL = '/oauth/token'; /** * Class implementing the backchannel authentication flow. */ export class Backchannel extends BaseAuthAPI { /** * Initiates a CIBA authorization request. * * @param {AuthorizeOptions} options - The options for the request. * @returns {Promise<AuthorizeResponse>} - The authorization response. * * @throws {Error} - If the request fails. */ async authorize({ userId, ...options }) { const body = { ...options, login_hint: getLoginHint(userId, this.domain), client_id: this.clientId, }; await this.addClientAuthentication(body); const response = await this.request.bind(this)({ path: CIBA_AUTHORIZE_URL, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(body), }, {}); const r = await JSONApiResponse.fromResponse(response); return r.data; } /** * Handles the backchannel grant flow for authentication. Client can poll this method at regular intervals to check if the backchannel auth request has been approved. * * @param {string} auth_req_id - The authorization request ID. This value is returned from the call to /bc-authorize. Once you have exchanged an auth_req_id for an ID and access token, it is no longer usable. * @returns {Promise<TokenResponse>} - A promise that resolves to the token response. * * @throws {Error} - Throws an error if the request fails. * * If the authorizing user has not yet approved or rejected the request, you will receive a response like this: * ```json * { * "error": "authorization_pending", * "error_description": "The end-user authorization is pending" * } * ``` * * If the authorizing user rejects the request, you will receive a response like this: * ```json * { * "error": "access_denied", * "error_description": "The end-user denied the authorization request or it has been expired" * } * ``` * * If you are polling too quickly (faster than the interval value returned from /bc-authorize), you will receive a response like this: * ```json * { * "error": "slow_down", * "error_description": "You are polling faster than allowed. Try again in 10 seconds." * } * ``` */ async backchannelGrant({ auth_req_id }) { const body = { client_id: this.clientId, auth_req_id, grant_type: CIBA_GRANT_TYPE, }; await this.addClientAuthentication(body); const response = await this.request.bind(this)({ path: CIBA_TOKEN_URL, method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(body), }, {}); const r = await JSONApiResponse.fromResponse(response); return r.data; } } //# sourceMappingURL=backchannel.js.map