UNPKG

@adonisjs/ally

Version:

Social authentication provider for AdonisJS

242 lines (239 loc) 7 kB
import { RedirectRequest } from "./chunk-NK6X76EQ.js"; import { E_OAUTH_MISSING_CODE, E_OAUTH_STATE_MISMATCH } from "./chunk-TSIMPJ6I.js"; // src/abstract_drivers/oauth1.ts import { Exception } from "@adonisjs/core/exceptions"; import { Oauth1Client } from "@poppinss/oauth-client/oauth1"; var Oauth1Driver = class extends Oauth1Client { /** * Create a new OAuth1 driver instance. * * @param ctx - The current HTTP context * @param config - OAuth1 driver configuration */ constructor(ctx, config) { super(config); this.ctx = ctx; this.config = config; } ctx; config; /** * OAuth protocol version identifier */ version = "oauth1"; /** * Cached OAuth token and secret values read from cookies */ oauthTokenCookieValue; oauthSecretCookieValue; /** * The cookie name for storing the OAuth token secret. * Automatically derived from the token cookie name. * * @returns The cookie name used to persist the OAuth token secret. */ get oauthSecretCookieName() { return `${this.oauthTokenCookieName}_secret`; } /** * Creates a URL builder instance for constructing authorization URLs * with scope support. * * @param url - The base authorization URL * @returns A redirect request builder for the given URL. */ urlBuilder(url) { return new RedirectRequest(url, this.scopeParamName, this.scopesSeparator); } /** * Loads the OAuth token and secret from encrypted cookies and immediately * clears the cookies. This must be called by child classes in their * constructor to enable token verification. * * @example * ```ts * constructor(ctx: HttpContext, config: DriverConfig) { * super(ctx, config) * this.loadState() * } * ``` */ loadState() { this.oauthTokenCookieValue = this.ctx.request.encryptedCookie(this.oauthTokenCookieName); this.oauthSecretCookieValue = this.ctx.request.encryptedCookie(this.oauthSecretCookieName); this.ctx.response.clearCookie(this.oauthTokenCookieName); this.ctx.response.clearCookie(this.oauthSecretCookieName); } /** * Stores the OAuth token in an encrypted cookie for later use * * @param token - The request token to persist. */ #persistToken(token) { this.ctx.response.encryptedCookie(this.oauthTokenCookieName, token, { sameSite: false, httpOnly: true }); } /** * Stores the OAuth token secret in an encrypted cookie for later use * * @param secret - The request token secret to persist. */ #persistSecret(secret) { this.ctx.response.encryptedCookie(this.oauthSecretCookieName, secret, { sameSite: false, httpOnly: true }); } /** * OAuth1 does not support stateless authentication due to the * three-legged authentication flow requiring token persistence. * * @returns This method never returns. */ stateless() { throw new Exception("OAuth1 does not support stateless authorization"); } /** * Get the authorization redirect URL without performing the redirect. * Useful when you need to manually handle the redirect or use the URL * in a different context. * * @param callback - Optional callback to customize the redirect request * @returns A promise resolving to the authorization URL. * * @example * ```ts * const url = await ally.use('twitter').redirectUrl() * ``` */ async redirectUrl(callback) { return this.getRedirectUrl(callback); } /** * Redirect the user to the OAuth provider's authorization page. * The request token is automatically obtained and stored in cookies. * * @param callback - Optional callback to customize the redirect request * @returns A promise that resolves after the redirect response is prepared. * * @example * ```ts * await ally.use('twitter').redirect() * ``` */ async redirect(callback) { const { token, secret } = await this.getRequestToken(); this.#persistToken(token); this.#persistSecret(secret); const url = await this.redirectUrl((request) => { request.param(this.oauthTokenParamName, token); if (typeof callback === "function") { callback(request); } }); if ("inertia" in this.ctx && this.ctx.inertia.requestInfo().isInertiaRequest) { this.ctx.inertia.location(url); } else { this.ctx.response.redirect(url); } } /** * Check if the OAuth token from the callback matches the token * stored in the cookie. * * @returns `true` when the callback token does not match the stored token. */ stateMisMatch() { return this.oauthTokenCookieValue !== this.ctx.request.input(this.oauthTokenParamName); } /** * Check if an error was returned by the OAuth provider. * * @returns `true` when an error exists on the callback request. */ hasError() { return !!this.getError(); } /** * Get the error code or message returned by the OAuth provider. * Returns 'unknown_error' if no verifier is present and no error was specified. * * @returns The provider error value when present. */ getError() { const error = this.ctx.request.input(this.errorParamName); if (error) { return error; } if (!this.hasCode()) { return "unknown_error"; } return null; } /** * Get the OAuth verifier from the callback request. * * @returns The OAuth verifier when present. */ getCode() { return this.ctx.request.input(this.oauthTokenVerifierName, null); } /** * Check if the OAuth verifier is present in the callback request. * * @returns `true` when the callback request contains an OAuth verifier. */ hasCode() { return !!this.getCode(); } /** * Exchange the request token and verifier for an access token. * This method validates the token and checks for errors before * making the request. * * @param callback - Optional callback to customize the token request * @returns A promise resolving to the access token payload. * * @example * ```ts * const token = await ally.use('twitter').accessToken() * ``` */ async accessToken(callback) { if (this.hasError()) { throw new E_OAUTH_MISSING_CODE([this.oauthTokenVerifierName]); } if (this.stateMisMatch()) { throw new E_OAUTH_STATE_MISMATCH(); } return this.getAccessToken( { token: this.oauthTokenCookieValue, secret: this.oauthSecretCookieValue }, (request) => { request.oauth1Param(this.oauthTokenVerifierName, this.getCode()); if (typeof callback === "function") { callback(request); } } ); } /** * Not applicable with OAuth1. Use `userFromTokenAndSecret` instead. * * @returns This method never returns. */ async userFromToken() { throw new Exception( '"userFromToken" is not available with Oauth1. Use "userFromTokenAndSecret" instead' ); } }; export { Oauth1Driver };