Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not expose a grant method #904

Merged
merged 4 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion src/auth/base-auth-api.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -117,3 +125,41 @@ export class BaseAuthAPI extends BaseAPI {
});
}
}

/**
* @private
* Perform an OAuth 2.0 grant.
*/
export async function grant(
grantType: string,
bodyParameters: Record<string, any>,
{ idTokenValidateOptions, initOverrides }: GrantOptions = {},
clientId: string,
idTokenValidator: IDTokenValidator,
request: (
context: RequestOpts,
initOverrides?: RequestInit | InitOverrideFunction
) => Promise<Response>
): Promise<JSONApiResponse<TokenSet>> {
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<TokenSet> = await JSONApiResponse.fromResponse(response);
if (res.data.id_token) {
await idTokenValidator.validate(res.data.id_token, idTokenValidateOptions);
}
return res;
}
67 changes: 26 additions & 41 deletions src/auth/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -201,36 +201,6 @@ export class OAuth extends BaseAuthAPI {
}
}

/**
* 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<string, any>,
{ idTokenValidateOptions, initOverrides }: GrantOptions = {}
): Promise<JSONApiResponse<TokenSet>> {
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<TokenSet> = 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.
*
Expand All @@ -255,10 +225,13 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['code']);

return this.grant(
return grant(
'authorization_code',
await this.addClientAuthentication(bodyParameters, true),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand Down Expand Up @@ -289,10 +262,13 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['code', 'code_verifier']);

return this.grant(
return grant(
'authorization_code',
await this.addClientAuthentication(bodyParameters, false),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand Down Expand Up @@ -321,10 +297,13 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['audience']);

return this.grant(
return grant(
'client_credentials',
await this.addClientAuthentication(bodyParameters, true),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand Down Expand Up @@ -362,10 +341,13 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['username', 'password']);

return this.grant(
return grant(
bodyParameters.realm ? 'http://auth0.com/oauth/grant-type/password-realm' : 'password',
await this.addClientAuthentication(bodyParameters, false),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand All @@ -391,10 +373,13 @@ export class OAuth extends BaseAuthAPI {
): Promise<JSONApiResponse<TokenSet>> {
validateRequiredRequestParams(bodyParameters, ['refresh_token']);

return this.grant(
return grant(
'refresh_token',
await this.addClientAuthentication(bodyParameters, false),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand Down
24 changes: 16 additions & 8 deletions src/auth/passwordless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand Down Expand Up @@ -75,10 +76,11 @@ export interface LoginWithSMSRequest extends Omit<LoginWithEmailRequest, 'email'
* Handles passwordless flows using Email and SMS.
*/
export class Passwordless extends BaseAuthAPI {
private oauth: OAuth;
private idTokenValidator: IDTokenValidator;
constructor(configuration: AuthenticationClientOptions) {
super(configuration);
this.oauth = new OAuth(configuration);

this.idTokenValidator = new IDTokenValidator(configuration);
}

/**
Expand Down Expand Up @@ -213,7 +215,7 @@ export class Passwordless extends BaseAuthAPI {

const { email: username, code: otp, ...otherParams } = bodyParameters;

return this.oauth.grant(
return grant(
'http://auth0.com/oauth/grant-type/passwordless/otp',
await this.addClientAuthentication(
{
Expand All @@ -224,7 +226,10 @@ export class Passwordless extends BaseAuthAPI {
},
false
),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}

Expand Down Expand Up @@ -253,7 +258,7 @@ export class Passwordless extends BaseAuthAPI {

const { phone_number: username, code: otp, ...otherParams } = bodyParameters;

return this.oauth.grant(
return grant(
'http://auth0.com/oauth/grant-type/passwordless/otp',
await this.addClientAuthentication(
{
Expand All @@ -264,7 +269,10 @@ export class Passwordless extends BaseAuthAPI {
},
false
),
options
options,
this.clientId,
this.idTokenValidator,
this.request.bind(this)
);
}
}