From c9855e3dce42f8830636914458d1061668a466a8 Mon Sep 17 00:00:00 2001 From: Romain MARTINEAU Date: Thu, 16 May 2024 10:46:15 +0200 Subject: [PATCH] fix(core): Handle credential in body for oauth2 refresh token (#9179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- .../client-oauth2/src/ClientOAuth2Token.ts | 26 ++++++--- .../test/CredentialsFlow.test.ts | 58 ++++++++++++++++++- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/packages/@n8n/client-oauth2/src/ClientOAuth2Token.ts b/packages/@n8n/client-oauth2/src/ClientOAuth2Token.ts index 9b696dff22b62..505bd7c98286a 100644 --- a/packages/@n8n/client-oauth2/src/ClientOAuth2Token.ts +++ b/packages/@n8n/client-oauth2/src/ClientOAuth2Token.ts @@ -10,6 +10,7 @@ export interface ClientOAuth2TokenData extends Record = { + refresh_token: this.refreshToken, + grant_type: 'refresh_token', + }; + + if (options.authentication === 'body') { + body.client_id = clientId; + body.client_secret = clientSecret; + } else { + headers.Authorization = auth(clientId, clientSecret); + } + const requestOptions = getRequestOptions( { url: options.accessTokenUri, method: 'POST', - headers: { - ...DEFAULT_HEADERS, - Authorization: auth(options.clientId, options.clientSecret), - }, - body: { - refresh_token: this.refreshToken, - grant_type: 'refresh_token', - }, + headers, + body, }, options, ); diff --git a/packages/@n8n/client-oauth2/test/CredentialsFlow.test.ts b/packages/@n8n/client-oauth2/test/CredentialsFlow.test.ts index 9e0749800de99..39978c41f8d08 100644 --- a/packages/@n8n/client-oauth2/test/CredentialsFlow.test.ts +++ b/packages/@n8n/client-oauth2/test/CredentialsFlow.test.ts @@ -130,8 +130,8 @@ describe('CredentialsFlow', () => { }); describe('#refresh', () => { - const mockRefreshCall = () => - nock(config.baseUrl) + const mockRefreshCall = async () => { + const nockScope = nock(config.baseUrl) .post( '/login/oauth/access_token', ({ refresh_token, grant_type }) => @@ -142,6 +142,15 @@ describe('CredentialsFlow', () => { access_token: config.refreshedAccessToken, refresh_token: config.refreshedRefreshToken, }); + return await new Promise<{ headers: Headers; body: unknown }>((resolve) => { + nockScope.once('request', (req) => { + resolve({ + headers: req.headers, + body: req.requestBodyBuffers.toString('utf-8'), + }); + }); + }); + }; it('should make a request to get a new access token', async () => { const authClient = createAuthClient({ scopes: ['notifications'] }); @@ -150,11 +159,54 @@ describe('CredentialsFlow', () => { const token = await authClient.credentials.getToken(); expect(token.accessToken).toEqual(config.accessToken); - mockRefreshCall(); + const requestPromise = mockRefreshCall(); + const token1 = await token.refresh(); + await requestPromise; + + expect(token1).toBeInstanceOf(ClientOAuth2Token); + expect(token1.accessToken).toEqual(config.refreshedAccessToken); + expect(token1.tokenType).toEqual('bearer'); + }); + + it('should make a request to get a new access token with authentication = "body"', async () => { + const authClient = createAuthClient({ scopes: ['notifications'], authentication: 'body' }); + void mockTokenCall({ requestedScope: 'notifications' }); + + const token = await authClient.credentials.getToken(); + expect(token.accessToken).toEqual(config.accessToken); + + const requestPromise = mockRefreshCall(); const token1 = await token.refresh(); + const { headers, body } = await requestPromise; + + expect(token1).toBeInstanceOf(ClientOAuth2Token); + expect(token1.accessToken).toEqual(config.refreshedAccessToken); + expect(token1.tokenType).toEqual('bearer'); + expect(headers?.authorization).toBe(undefined); + expect(body).toEqual( + 'refresh_token=def456token&grant_type=refresh_token&client_id=abc&client_secret=123', + ); + }); + + it('should make a request to get a new access token with authentication = "header"', async () => { + const authClient = createAuthClient({ + scopes: ['notifications'], + authentication: 'header', + }); + void mockTokenCall({ requestedScope: 'notifications' }); + + const token = await authClient.credentials.getToken(); + expect(token.accessToken).toEqual(config.accessToken); + + const requestPromise = mockRefreshCall(); + const token1 = await token.refresh(); + const { headers, body } = await requestPromise; + expect(token1).toBeInstanceOf(ClientOAuth2Token); expect(token1.accessToken).toEqual(config.refreshedAccessToken); expect(token1.tokenType).toEqual('bearer'); + expect(headers?.authorization).toBe('Basic YWJjOjEyMw=='); + expect(body).toEqual('refresh_token=def456token&grant_type=refresh_token'); }); }); });