From bcf5ae2e0d93c6889b330c1bdf195a6906e72713 Mon Sep 17 00:00:00 2001 From: Dan Radenkovic Date: Fri, 26 Jun 2026 14:53:23 +0200 Subject: [PATCH] fix(auth): stop sending apiKey as client_secret in OAuth token exchange body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDK was auto-populating clientSecret in the POST body from apiKey during exchangeCodeForToken and refreshAccessToken, even though the apiKey is already sent via the Authorization: Bearer header. This broke reverse proxy setups where the proxy rewrites the Authorization header for credential injection — the placeholder key in the body leaked through and caused 'Invalid credentials' failures. Fixes https://github.com/nylas/nylas-nodejs/issues/703 Jira: TW-5724 Co-Authored-By: Claude Sonnet 4.6 --- src/resources/auth.ts | 8 -------- tests/resources/auth.spec.ts | 6 ++---- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/resources/auth.ts b/src/resources/auth.ts index b1449aa9..4bc62e43 100644 --- a/src/resources/auth.ts +++ b/src/resources/auth.ts @@ -48,10 +48,6 @@ export class Auth extends Resource { public exchangeCodeForToken( request: CodeExchangeRequest ): Promise { - if (!request.clientSecret) { - request.clientSecret = this.apiClient.apiKey; - } - return this.apiClient.request({ method: 'POST', path: makePathParams('/v3/connect/token', {}), @@ -70,10 +66,6 @@ export class Auth extends Resource { public refreshAccessToken( request: TokenExchangeRequest ): Promise { - if (!request.clientSecret) { - request.clientSecret = this.apiClient.apiKey; - } - return this.apiClient.request({ method: 'POST', path: makePathParams('/v3/connect/token', {}), diff --git a/tests/resources/auth.spec.ts b/tests/resources/auth.spec.ts index 578b6b5d..f302e750 100644 --- a/tests/resources/auth.spec.ts +++ b/tests/resources/auth.spec.ts @@ -46,7 +46,7 @@ describe('Auth', () => { }); }); - it('should default clientSecret to the API key', async () => { + it('should not include clientSecret in body when not provided', async () => { const payload: CodeExchangeRequest = { clientId: 'clientId', redirectUri: 'https://redirect.uri/path', @@ -59,7 +59,6 @@ describe('Auth', () => { path: '/v3/connect/token', body: { clientId: 'clientId', - clientSecret: 'apiKey', redirectUri: 'https://redirect.uri/path', code: 'code', grantType: 'authorization_code', @@ -115,7 +114,7 @@ describe('Auth', () => { }); }); - it('should default clientSecret to the API key', async () => { + it('should not include clientSecret in body when not provided', async () => { const payload: TokenExchangeRequest = { clientId: 'clientId', redirectUri: 'https://redirect.uri/path', @@ -128,7 +127,6 @@ describe('Auth', () => { path: '/v3/connect/token', body: { clientId: 'clientId', - clientSecret: 'apiKey', redirectUri: 'https://redirect.uri/path', refreshToken: 'refreshToken', grantType: 'refresh_token',