diff --git a/src/auth/base-auth-api.ts b/src/auth/base-auth-api.ts index 12148e21b..a32d836ee 100644 --- a/src/auth/base-auth-api.ts +++ b/src/auth/base-auth-api.ts @@ -1,9 +1,17 @@ import { ResponseError } from '../lib/errors.js'; -import { BaseAPI, ClientOptions } from '../lib/runtime.js'; +import { + BaseAPI, + ClientOptions, + InitOverrideFunction, + JSONApiResponse, + RequestOpts, +} from '../lib/runtime.js'; import { AddClientAuthenticationPayload, addClientAuthentication, } from './client-authentication.js'; +import { IDTokenValidator } from './id-token-validator.js'; +import { GrantOptions, TokenSet } from './oauth.js'; export interface AuthenticationClientOptions extends ClientOptions { domain: string; @@ -115,3 +123,41 @@ export class BaseAuthAPI extends BaseAPI { }); } } + +/** + * @private + * Perform an OAuth 2.0 grant. + */ +export async function grant( + grantType: string, + bodyParameters: Record, + { idTokenValidateOptions, initOverrides }: GrantOptions = {}, + clientId: string, + idTokenValidator: IDTokenValidator, + request: ( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction + ) => Promise +): Promise> { + const response = await request( + { + path: '/oauth/token', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + client_id: clientId, + ...bodyParameters, + grant_type: grantType, + }), + }, + initOverrides + ); + + const res: JSONApiResponse = await JSONApiResponse.fromResponse(response); + if (res.data.id_token) { + await idTokenValidator.validate(res.data.id_token, idTokenValidateOptions); + } + return res; +} diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts index 86df51b82..02c6a8462 100644 --- a/src/auth/oauth.ts +++ b/src/auth/oauth.ts @@ -4,7 +4,7 @@ import { VoidApiResponse, validateRequiredRequestParams, } from '../lib/runtime.js'; -import { BaseAuthAPI, AuthenticationClientOptions } from './base-auth-api.js'; +import { BaseAuthAPI, AuthenticationClientOptions, grant } from './base-auth-api.js'; import { IDTokenValidateOptions, IDTokenValidator } from './id-token-validator.js'; export interface TokenSet { @@ -191,46 +191,6 @@ export class OAuth extends BaseAuthAPI { this.idTokenValidator = new IDTokenValidator(options); } - private async validateIdToken( - tokenSet: TokenSet, - idTokenValidateOptions?: IDTokenValidateOptions - ): Promise { - const { id_token: idToken } = tokenSet; - if (idToken) { - await this.idTokenValidator.validate(idToken, idTokenValidateOptions); - } - } - - /** - * Perform an OAuth 2.0 grant. - * (You should only need this if you can't find the grant you need in this library.) - */ - async grant( - grantType: string, - bodyParameters: Record, - { idTokenValidateOptions, initOverrides }: GrantOptions = {} - ): Promise> { - const response = await this.request( - { - path: '/oauth/token', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - client_id: this.clientId, - ...bodyParameters, - grant_type: grantType, - }), - }, - initOverrides - ); - - const res: JSONApiResponse = await JSONApiResponse.fromResponse(response); - await this.validateIdToken(res.data, idTokenValidateOptions); - return res; - } - /** * This is the flow that regular web apps use to access an API. * @@ -255,10 +215,13 @@ export class OAuth extends BaseAuthAPI { ): Promise> { validateRequiredRequestParams(bodyParameters, ['code']); - return this.grant( + return grant( 'authorization_code', await this.addClientAuthentication(bodyParameters), - options + options, + this.clientId, + this.idTokenValidator, + this.request.bind(this) ); } @@ -289,10 +252,13 @@ export class OAuth extends BaseAuthAPI { ): Promise> { validateRequiredRequestParams(bodyParameters, ['code', 'code_verifier']); - return this.grant( + return grant( 'authorization_code', await this.addClientAuthentication(bodyParameters), - options + options, + this.clientId, + this.idTokenValidator, + this.request.bind(this) ); } @@ -321,10 +287,13 @@ export class OAuth extends BaseAuthAPI { ): Promise> { validateRequiredRequestParams(bodyParameters, ['audience']); - return this.grant( + return grant( 'client_credentials', await this.addClientAuthentication(bodyParameters), - options + options, + this.clientId, + this.idTokenValidator, + this.request.bind(this) ); } @@ -362,10 +331,13 @@ export class OAuth extends BaseAuthAPI { ): Promise> { validateRequiredRequestParams(bodyParameters, ['username', 'password']); - return this.grant( + return grant( bodyParameters.realm ? 'http://auth0.com/oauth/grant-type/password-realm' : 'password', await this.addClientAuthentication(bodyParameters), - options + options, + this.clientId, + this.idTokenValidator, + this.request.bind(this) ); } @@ -391,7 +363,14 @@ export class OAuth extends BaseAuthAPI { ): Promise> { validateRequiredRequestParams(bodyParameters, ['refresh_token']); - return this.grant('refresh_token', await this.addClientAuthentication(bodyParameters), options); + return grant( + 'refresh_token', + await this.addClientAuthentication(bodyParameters), + options, + this.clientId, + this.idTokenValidator, + this.request.bind(this) + ); } /** diff --git a/src/auth/passwordless.ts b/src/auth/passwordless.ts index 788fe5a83..6203430a2 100644 --- a/src/auth/passwordless.ts +++ b/src/auth/passwordless.ts @@ -4,8 +4,9 @@ import { VoidApiResponse, validateRequiredRequestParams, } from '../lib/runtime.js'; -import { BaseAuthAPI, AuthenticationClientOptions } from './base-auth-api.js'; -import { OAuth, ClientCredentials, GrantOptions, TokenSet } from './oauth.js'; +import { BaseAuthAPI, AuthenticationClientOptions, grant } from './base-auth-api.js'; +import { IDTokenValidator } from './id-token-validator.js'; +import { ClientCredentials, GrantOptions, TokenSet } from './oauth.js'; export interface SendEmailLinkRequest { /** @@ -75,10 +76,11 @@ export interface LoginWithSMSRequest extends Omit