From 72f223033506a40cb4012e772bf58cf33f9b7dd9 Mon Sep 17 00:00:00 2001 From: Gyanesh Gouraw Date: Wed, 10 Apr 2024 20:53:45 +0530 Subject: [PATCH] Added new authorization_details parameter to support RAR requests --- src/auth/client-authentication.ts | 3 ++ src/auth/oauth.ts | 18 +++++++ test/auth/client-authentication.test.ts | 72 +++++++++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/src/auth/client-authentication.ts b/src/auth/client-authentication.ts index 1c10fb9f0..ed93055d9 100644 --- a/src/auth/client-authentication.ts +++ b/src/auth/client-authentication.ts @@ -36,6 +36,9 @@ export const addClientAuthentication = async ({ clientSecret, }: AddClientAuthenticationOptions): Promise> => { const cid = payload.client_id || clientId; + if (payload.authorization_details) { + payload.authorization_details = JSON.stringify(payload.authorization_details); + } if (clientAssertionSigningKey && !payload.client_assertion) { const alg = clientAssertionSigningAlg || 'RS256'; const privateKey = await jose.importPKCS8(clientAssertionSigningKey, alg); diff --git a/src/auth/oauth.ts b/src/auth/oauth.ts index 736a0a1ac..29a89997d 100644 --- a/src/auth/oauth.ts +++ b/src/auth/oauth.ts @@ -91,6 +91,19 @@ export interface ClientCredentialsGrantRequest extends ClientCredentials { audience: string; } +interface AuthorizationDetails { + /** + * An identifier for the authorization details + */ + type: string; + + /** + * Allow for any custom property to be sent to Auth0 + * It represents data to specify the authorization requirements for a certain type of resource. + */ + [key: string]: any; +} + export interface PushedAuthorizationRequest extends ClientCredentials { /** * URI to redirect to. @@ -146,6 +159,11 @@ export interface PushedAuthorizationRequest extends ClientCredentials { */ code_challenge?: string; + /** + * Can carry fine-grained authorization data in OAuth messages as part of Rich Authorization Requests(RAR) {@link https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow/authorization-code-flow-with-rar | Reference} + */ + authorization_details?: Required[]; + /** * Allow for any custom property to be sent to Auth0 */ diff --git a/test/auth/client-authentication.test.ts b/test/auth/client-authentication.test.ts index 24d6583fd..7679fc98d 100644 --- a/test/auth/client-authentication.test.ts +++ b/test/auth/client-authentication.test.ts @@ -3,6 +3,7 @@ import { jest } from '@jest/globals'; import * as jose from 'jose'; import { AuthenticationClient } from '../../src/index.js'; import { TEST_PUBLIC_KEY, TEST_PRIVATE_KEY } from '../constants.js'; +import { addClientAuthentication } from '../../src/auth/client-authentication.js'; const URL = 'https://tenant.auth0.com/'; const clientId = 'test-client-id'; @@ -152,3 +153,74 @@ describe('client-authentication', () => { }); }); }); + +describe('client-authentication for par endpoint', () => { + const path = jest.fn(); + const body = jest.fn(); + const headers = jest.fn(); + const clientAssertion = jest.fn(); + + beforeEach(() => { + async function handler(this: any, pathIn: unknown, bodyIn: string) { + const bodyParsed = Object.fromEntries(new URLSearchParams(bodyIn)); + path(pathIn); + body(bodyParsed); + headers(this.req.headers); + if ((bodyParsed as any).client_assertion) { + clientAssertion(await verify(bodyParsed.client_assertion, TEST_PUBLIC_KEY, verifyOpts)); + } + return { + data: { + request_uri: 'https://www.request.uri', + expires_in: 86400, + }, + }; + } + + nock(URL, { encodedQueryParams: true }).post('/oauth/par').reply(200, handler).persist(); + }); + + afterEach(() => { + nock.cleanAll(); + jest.clearAllMocks(); + }); + + it('should allow you to call with cliendId & clientSecret combination', async () => { + const auth0 = new AuthenticationClient({ + domain: 'tenant.auth0.com', + clientId, + clientSecret: 'foo', + }); + await auth0.oauth.pushedAuthorization({ + client_id: 'test-client-id', + response_type: 'code', + redirect_uri: 'https://example.com', + }); + expect(path).toHaveBeenCalledWith('/oauth/par'); + + expect(body).toHaveBeenCalledWith({ + client_id: 'test-client-id', + client_secret: 'foo', + redirect_uri: 'https://example.com', + response_type: 'code', + }); + }); +}); + +describe('Validate addClientAuthentication function', () => { + it('should stringify authorization_details property of payload parameter', async () => { + const authorization_details = [{ type: 'payment_initiation', actions: ['write'] }]; + const authenticatedPayload = await addClientAuthentication({ + payload: { + authorization_details: authorization_details, + client_secret: 'foo', + }, + domain: 'tenant.auth0.com', + clientId, + }); + + expect(authenticatedPayload.authorization_details).toEqual( + JSON.stringify(authorization_details) + ); + }); +});