Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
65 changes: 64 additions & 1 deletion src/server/auth-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ ca/T0LLtgmbMmxSv/MmzIg==
expect(authClient.handleProfile).toHaveBeenCalled();
});

it("should call the access token handler if the path is /auth/access-token", async () => {
it("should call the handleAccessToken method if the path is /auth/access-token and enableAccessTokenEndpoint is true", async () => {
const secret = await generateSecret(32);
const transactionStore = new TransactionStore({
secret
Expand All @@ -342,6 +342,69 @@ ca/T0LLtgmbMmxSv/MmzIg==

secret,
appBaseUrl: DEFAULT.appBaseUrl,
enableAccessTokenEndpoint: true,

fetch: getMockAuthorizationServer()
});
const request = new NextRequest("https://example.com/auth/access-token", {
method: "GET"
});
authClient.handleAccessToken = vi.fn();
await authClient.handler(request);
expect(authClient.handleAccessToken).toHaveBeenCalled();
});

it("should not call the handleAccessToken method if the path is /auth/access-token but enableAccessTokenEndpoint is false", async () => {
const secret = await generateSecret(32);
const transactionStore = new TransactionStore({
secret
});
const sessionStore = new StatelessSessionStore({
secret
});
const authClient = new AuthClient({
transactionStore,
sessionStore,

domain: DEFAULT.domain,
clientId: DEFAULT.clientId,
clientSecret: DEFAULT.clientSecret,

secret,
appBaseUrl: DEFAULT.appBaseUrl,
enableAccessTokenEndpoint: false,

fetch: getMockAuthorizationServer()
});
const request = new NextRequest("https://example.com/auth/access-token", {
method: "GET"
});
authClient.handleAccessToken = vi.fn();
const response = await authClient.handler(request);
expect(authClient.handleAccessToken).not.toHaveBeenCalled();
// When a route doesn't match, the handler returns a NextResponse.next() with status 200
expect(response.status).toBe(200);
});

it("should use the default value (true) for enableAccessTokenEndpoint when not explicitly provided", async () => {
const secret = await generateSecret(32);
const transactionStore = new TransactionStore({
secret
});
const sessionStore = new StatelessSessionStore({
secret
});
const authClient = new AuthClient({
transactionStore,
sessionStore,

domain: DEFAULT.domain,
clientId: DEFAULT.clientId,
clientSecret: DEFAULT.clientSecret,

secret,
appBaseUrl: DEFAULT.appBaseUrl,
// enableAccessTokenEndpoint not specified, should default to true

fetch: getMockAuthorizationServer()
});
Expand Down
8 changes: 7 additions & 1 deletion src/server/auth-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface AuthClientOptions {
allowInsecureRequests?: boolean;
httpTimeout?: number;
enableTelemetry?: boolean;
enableAccessTokenEndpoint?: boolean;
}

function createRouteUrl(url: string, base: string) {
Expand Down Expand Up @@ -133,6 +134,8 @@ export class AuthClient {

private authorizationServerMetadata?: oauth.AuthorizationServer;

private readonly enableAccessTokenEndpoint: boolean;

constructor(options: AuthClientOptions) {
// dependencies
this.fetch = options.fetch || fetch;
Expand Down Expand Up @@ -219,6 +222,8 @@ export class AuthClient {
process.env.NEXT_PUBLIC_ACCESS_TOKEN_ROUTE || "/auth/access-token",
...options.routes
};

this.enableAccessTokenEndpoint = options.enableAccessTokenEndpoint ?? true;
}

async handler(req: NextRequest): Promise<NextResponse> {
Expand All @@ -236,7 +241,8 @@ export class AuthClient {
return this.handleProfile(req);
} else if (
method === "GET" &&
sanitizedPathname === this.routes.accessToken
sanitizedPathname === this.routes.accessToken &&
this.enableAccessTokenEndpoint
) {
return this.handleAccessToken(req);
} else if (
Expand Down
13 changes: 13 additions & 0 deletions src/server/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ export interface Auth0ClientOptions {
* via the `Auth0-Client` header. Defaults to `true`.
*/
enableTelemetry?: boolean;

/**
* Boolean value to enable the `/auth/access-token` endpoint for use in the client app.
*
* Defaults to `true`.
*
* NOTE: Set this to `false` if your client does not need to directly interact with resource servers (Token Mediating Backend). This will be false for most apps.
*
* A security best practice is to disable this to avoid exposing access tokens to the client app.
*
* See: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-token-mediating-backend
*/
enableAccessTokenEndpoint?: boolean;
}

export type PagesRouterRequest = IncomingMessage | NextApiRequest;
Expand Down