From 0084ed0b6c1b407a0d02e344e0a702a5516f7768 Mon Sep 17 00:00:00 2001 From: gyaneshgouraw-okta Date: Sat, 27 Apr 2024 16:17:49 +0530 Subject: [PATCH] Added changes to support mTLS authentication --- EXAMPLES.md | 19 ++++++++++ src/auth/client-authentication.ts | 11 +++++- test/auth/client-authentication.test.ts | 46 +++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index c9c510ed0..6ad971b2e 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -6,6 +6,7 @@ - [Use Refresh Tokens](#use-refresh-tokens) - [Complete the Authorization Code flow with PKCE](#complete-the-authorization-code-flow-with-pkce) - [Login with Passwordless](#login-with-passwordless) + - [mTLS request](#mtls-request) - [Management Client](#management-client) - [Paginate through a list of users](#paginate-through-a-list-of-users) - [Paginate through a list of logs using checkpoint pagination](#paginate-through-a-list-of-logs-using-checkpoint-pagination) @@ -129,6 +130,24 @@ const { data: tokens } = await auth.passwordless.loginWithEmail({ }); ``` +### mTLS request + +```js +import { AuthenticationClient } from 'auth0'; + +// add mtls prefix to your domain name +const auth = new AuthenticationClient({ + domain: 'mtls.{YOUR_TENANT_AND REGION}.auth0.com', + clientId: '{YOUR_CLIENT_ID}', + agent: new https.Agent({ ... }), +}); + +const { data: tokens } = await auth.oauth.clientCredentialsGrant({ + audience: 'you-api', +}); + +``` + ## Management Client ### Paginate through a list of users diff --git a/src/auth/client-authentication.ts b/src/auth/client-authentication.ts index 1c10fb9f0..8af14bcac 100644 --- a/src/auth/client-authentication.ts +++ b/src/auth/client-authentication.ts @@ -26,6 +26,7 @@ interface AddClientAuthenticationOptions { * Adds `client_assertion` and `client_assertion_type` for Private Key JWT token endpoint auth method. * * If `clientAssertionSigningKey` is provided it takes precedent over `clientSecret` . + * Also skips `client_secret` & `clientAssertionSigningKey` if request(domain) is of mTLS type */ export const addClientAuthentication = async ({ payload, @@ -55,9 +56,17 @@ export const addClientAuthentication = async ({ } if ( (!payload.client_secret || payload.client_secret.trim().length === 0) && - (!payload.client_assertion || payload.client_assertion.trim().length === 0) + (!payload.client_assertion || payload.client_assertion.trim().length === 0) && + !isMTLSRequest(domain) ) { throw new Error('The client_secret or client_assertion field is required.'); } return payload; }; + +/** + * Checks if domain name starts with mTLS keyword for mTLS requests + */ +const isMTLSRequest = (domain: string): boolean => { + return domain.toLowerCase().startsWith('mtls'); +}; diff --git a/test/auth/client-authentication.test.ts b/test/auth/client-authentication.test.ts index 53000d469..cadb359a5 100644 --- a/test/auth/client-authentication.test.ts +++ b/test/auth/client-authentication.test.ts @@ -205,3 +205,49 @@ describe('client-authentication for par endpoint', () => { }); }); }); + +describe('mTLS-authentication', () => { + const path = jest.fn(); + const body = jest.fn(); + const headers = jest.fn(); + const clientAssertion = jest.fn(); + const URL = 'https://mtls.tenant.auth0.com/'; + + 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 { + access_token: 'test-access-token', + }; + } + + nock(URL, { encodedQueryParams: true }).post('/oauth/token').reply(200, handler).persist(); + }); + + afterEach(() => { + nock.cleanAll(); + jest.clearAllMocks(); + }); + + it('should do client credentials grant without client secret or assertion', async () => { + const auth0 = new AuthenticationClient({ + domain: 'mtls.tenant.auth0.com', + clientId, + }); + await auth0.oauth.clientCredentialsGrant({ + audience: 'my-api', + }); + expect(path).toHaveBeenCalledWith('/oauth/token'); + expect(body).toHaveBeenCalledWith({ + grant_type: 'client_credentials', + client_id: clientId, + audience: 'my-api', + }); + }); +});