From 78c2d5147a698939a7d965786b4620712d90b28a Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Sat, 18 May 2024 05:36:47 +0000 Subject: [PATCH 01/39] feat: support extra parameter of client authentication method --- src/auth/oauth2client.ts | 26 +++++++++++++++++++++++--- src/index.ts | 1 + test/test.oauth2.ts | 19 ++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index ce1fb829..e8b9707e 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -68,6 +68,12 @@ export enum CertificateFormat { JWK = 'JWK', } +export enum ClientAuthentication { + ClientSecretPost, + ClientSecretBasic, + None, +} + export interface GetTokenOptions { code: string; codeVerifier?: string; @@ -475,6 +481,11 @@ export interface OAuth2ClientOptions extends AuthClientOptions { * The allowed OAuth2 token issuers. */ issuers?: string[]; + /** + * The client_authentication choice from baisc/post/none + * https://docs.authlib.org/en/latest/client/oauth2.html#client-authentication + */ + client_authentication?: ClientAuthentication; } // Re-exporting here for backwards compatibility @@ -491,6 +502,7 @@ export class OAuth2Client extends AuthClient { protected refreshTokenPromises = new Map>(); readonly endpoints: Readonly; readonly issuers: string[]; + private client_authentication: ClientAuthentication; // TODO: refactor tests to make this private _clientId?: string; @@ -542,6 +554,7 @@ export class OAuth2Client extends AuthClient { oauth2IapPublicKeyUrl: 'https://www.gstatic.com/iap/verify/public_key', ...opts.endpoints, }; + this.client_authentication = opts.client_authentication || ClientAuthentication.ClientSecretPost; this.issuers = opts.issuers || [ 'accounts.google.com', @@ -660,20 +673,27 @@ export class OAuth2Client extends AuthClient { options: GetTokenOptions ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); - const values = { + let headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; + if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { + const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); + headers.Authorization = basic_auth; + } + let values: any = { code: options.code, client_id: options.client_id || this._clientId, - client_secret: this._clientSecret, redirect_uri: options.redirect_uri || this.redirectUri, grant_type: 'authorization_code', code_verifier: options.codeVerifier, }; + if (this.client_authentication === ClientAuthentication.ClientSecretPost) { + values.client_secret = this._clientSecret; + } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, method: 'POST', url, data: querystring.stringify(values), - headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + headers: headers, }); const tokens = res.data as Credentials; if (res.data && res.data.expires_in) { diff --git a/src/index.ts b/src/index.ts index 1ab32948..fe3b69eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,6 +44,7 @@ export { RefreshOptions, TokenInfo, VerifyIdTokenOptions, + ClientAuthentication, } from './auth/oauth2client'; export {LoginTicket, TokenPayload} from './auth/loginticket'; export { diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index b836656f..911378c6 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -23,7 +23,7 @@ import * as path from 'path'; import * as qs from 'querystring'; import * as sinon from 'sinon'; -import {CodeChallengeMethod, Credentials, OAuth2Client} from '../src'; +import {CodeChallengeMethod, Credentials, OAuth2Client, ClientAuthentication} from '../src'; import {LoginTicket} from '../src/auth/loginticket'; nock.disableNetConnect(); @@ -1421,6 +1421,23 @@ describe('oauth2', () => { assert.strictEqual(params.client_id, 'overridden'); }); + it('getToken should use basic header auth if provided in options', async () => { + const opts = { + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: REDIRECT_URI, + endpoints: { + oauth2TokenUrl: 'mytokenurl' + }, + client_authentication: ClientAuthentication.ClientSecretBasic, + } + const oauth2client = new OAuth2Client(opts); + const res = oauth2client.getToken({ + code: 'code here', + client_id: CLIENT_ID, + }); + }); + it('should return expiry_date', done => { const now = new Date().getTime(); const scope = nock(baseUrl, { From 9fb45d55038ed72f7b8e84699829d7d00ab3f63e Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Sat, 18 May 2024 05:54:03 +0000 Subject: [PATCH 02/39] fix lint --- src/auth/oauth2client.ts | 7 ++++--- test/test.oauth2.ts | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index e8b9707e..8c7a9385 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -554,7 +554,8 @@ export class OAuth2Client extends AuthClient { oauth2IapPublicKeyUrl: 'https://www.gstatic.com/iap/verify/public_key', ...opts.endpoints, }; - this.client_authentication = opts.client_authentication || ClientAuthentication.ClientSecretPost; + this.client_authentication = + opts.client_authentication || ClientAuthentication.ClientSecretPost; this.issuers = opts.issuers || [ 'accounts.google.com', @@ -673,12 +674,12 @@ export class OAuth2Client extends AuthClient { options: GetTokenOptions ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); - let headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; + const headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); headers.Authorization = basic_auth; } - let values: any = { + const values: any = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 911378c6..be034b94 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1427,12 +1427,12 @@ describe('oauth2', () => { clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, endpoints: { - oauth2TokenUrl: 'mytokenurl' + oauth2TokenUrl: 'mytokenurl', }, client_authentication: ClientAuthentication.ClientSecretBasic, - } + }; const oauth2client = new OAuth2Client(opts); - const res = oauth2client.getToken({ + const _ = oauth2client.getToken({ code: 'code here', client_id: CLIENT_ID, }); From 5c829b31de6a4facb09d3265b4bcb0b041d4d553 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Sat, 18 May 2024 05:57:34 +0000 Subject: [PATCH 03/39] fix lint --- test/test.oauth2.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index be034b94..b97113e3 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -23,7 +23,12 @@ import * as path from 'path'; import * as qs from 'querystring'; import * as sinon from 'sinon'; -import {CodeChallengeMethod, Credentials, OAuth2Client, ClientAuthentication} from '../src'; +import { + CodeChallengeMethod, + Credentials, + OAuth2Client, + ClientAuthentication +} from '../src'; import {LoginTicket} from '../src/auth/loginticket'; nock.disableNetConnect(); From d16618ceb66e5af6fb73bedcf25668cb8f33e6ec Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Sat, 18 May 2024 06:00:21 +0000 Subject: [PATCH 04/39] fix lint --- src/auth/oauth2client.ts | 3 ++- test/test.oauth2.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 8c7a9385..527d6f63 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -676,7 +676,8 @@ export class OAuth2Client extends AuthClient { const url = this.endpoints.oauth2TokenUrl.toString(); const headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { - const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); + const basic_auth + = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); headers.Authorization = basic_auth; } const values: any = { diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index b97113e3..c654bb3e 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -27,7 +27,7 @@ import { CodeChallengeMethod, Credentials, OAuth2Client, - ClientAuthentication + ClientAuthentication, } from '../src'; import {LoginTicket} from '../src/auth/loginticket'; From 08c6b6780297e60fc68e693e136eb6b23560a940 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Sat, 18 May 2024 06:02:59 +0000 Subject: [PATCH 05/39] fix lint --- src/auth/oauth2client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 527d6f63..e5bb52e9 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -676,8 +676,8 @@ export class OAuth2Client extends AuthClient { const url = this.endpoints.oauth2TokenUrl.toString(); const headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { - const basic_auth - = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); + const basic_auth = + 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); headers.Authorization = basic_auth; } const values: any = { From 77386a88dcd68604f097276aa0a87aa8bfd06779 Mon Sep 17 00:00:00 2001 From: Jin Date: Mon, 20 May 2024 11:01:24 -0700 Subject: [PATCH 06/39] Update src/auth/oauth2client.ts Co-authored-by: aeitzman <12433791+aeitzman@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index e5bb52e9..55aa5a00 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -482,7 +482,7 @@ export interface OAuth2ClientOptions extends AuthClientOptions { */ issuers?: string[]; /** - * The client_authentication choice from baisc/post/none + * The client authentication type. Support values are basic, post, and none. * https://docs.authlib.org/en/latest/client/oauth2.html#client-authentication */ client_authentication?: ClientAuthentication; From f19d49d4dcf194752cee764ba99a041bd8345e04 Mon Sep 17 00:00:00 2001 From: Jin Date: Tue, 21 May 2024 16:34:31 -0700 Subject: [PATCH 07/39] Update src/auth/oauth2client.ts Co-authored-by: aeitzman <12433791+aeitzman@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 55aa5a00..e4e82d09 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -482,7 +482,7 @@ export interface OAuth2ClientOptions extends AuthClientOptions { */ issuers?: string[]; /** - * The client authentication type. Support values are basic, post, and none. + * The client authentication type. Supported values are basic, post, and none. If no type is provided, will default to post. * https://docs.authlib.org/en/latest/client/oauth2.html#client-authentication */ client_authentication?: ClientAuthentication; From 1d8edafe2aa91ed11c47ab1b392f266f94272d67 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Tue, 21 May 2024 23:34:15 +0000 Subject: [PATCH 08/39] fix the 'any' type into strict check --- src/auth/oauth2client.ts | 14 ++++++++----- test/test.oauth2.ts | 45 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index e4e82d09..e331239a 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -67,7 +67,10 @@ export enum CertificateFormat { PEM = 'PEM', JWK = 'JWK', } - + /** + * The client authentication type. Support values are basic, post, and none. + * https://datatracker.ietf.org/doc/html/rfc7591#section-2 + */ export enum ClientAuthentication { ClientSecretPost, ClientSecretBasic, @@ -482,8 +485,9 @@ export interface OAuth2ClientOptions extends AuthClientOptions { */ issuers?: string[]; /** - * The client authentication type. Supported values are basic, post, and none. If no type is provided, will default to post. - * https://docs.authlib.org/en/latest/client/oauth2.html#client-authentication + * The client authentication type. Supported values are basic, post, and none. + * If no type is provided, will default to post. + * https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ client_authentication?: ClientAuthentication; } @@ -674,13 +678,13 @@ export class OAuth2Client extends AuthClient { options: GetTokenOptions ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); - const headers: any = {'Content-Type': 'application/x-www-form-urlencoded'}; + const headers: {[key: string]: string} = {'Content-Type': 'application/x-www-form-urlencoded'}; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); headers.Authorization = basic_auth; } - const values: any = { + const values: {[key: string]: string | undefined} = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index c654bb3e..412bf7c5 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1427,12 +1427,24 @@ describe('oauth2', () => { }); it('getToken should use basic header auth if provided in options', async () => { + const authurl = 'https://auth.cloud.google/authorize'; + const scope = nock(authurl, { + reqheaders: {'Content-Type': 'application/x-www-form-urlencoded'}, + }) + .post('/token') + .reply(200, { + access_token: 'abc', + refresh_token: '123', + expires_in: 10, + }); const opts = { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, endpoints: { + oauth2AuthBaseUrl: authurl, oauth2TokenUrl: 'mytokenurl', + tokenInfoUrl: 'https://sts.googleapis.com/v1/introspect', }, client_authentication: ClientAuthentication.ClientSecretBasic, }; @@ -1443,6 +1455,39 @@ describe('oauth2', () => { }); }); + it('getToken should not use basic header auth if provided none in options', async () => { + const opts = { + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: REDIRECT_URI, + endpoints: { + oauth2TokenUrl: 'mytokenurl', + }, + client_authentication: ClientAuthentication.None, + }; + const oauth2client = new OAuth2Client(opts); + const _ = oauth2client.getToken({ + code: 'code here', + client_id: CLIENT_ID, + }); + }); + + it('getToken should use auth secret post if not provided in options', async () => { + const opts = { + clientId: CLIENT_ID, + clientSecret: CLIENT_SECRET, + redirectUri: REDIRECT_URI, + endpoints: { + oauth2TokenUrl: 'mytokenurl', + }, + }; + const oauth2client = new OAuth2Client(opts); + const _ = oauth2client.getToken({ + code: 'code here', + client_id: CLIENT_ID, + }); + }); + it('should return expiry_date', done => { const now = new Date().getTime(); const scope = nock(baseUrl, { From b8d3b8c7839e19ee2797b63e727d2506e0883549 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Tue, 21 May 2024 23:42:52 +0000 Subject: [PATCH 09/39] fix lint --- src/auth/oauth2client.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index e331239a..ba050d14 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -67,10 +67,11 @@ export enum CertificateFormat { PEM = 'PEM', JWK = 'JWK', } - /** - * The client authentication type. Support values are basic, post, and none. - * https://datatracker.ietf.org/doc/html/rfc7591#section-2 - */ + +/** + * The client authentication type. Support values are basic, post, and none. + * https://datatracker.ietf.org/doc/html/rfc7591#section-2 + */ export enum ClientAuthentication { ClientSecretPost, ClientSecretBasic, @@ -678,7 +679,9 @@ export class OAuth2Client extends AuthClient { options: GetTokenOptions ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); - const headers: {[key: string]: string} = {'Content-Type': 'application/x-www-form-urlencoded'}; + const headers: {[key: string]: string} = { + 'Content-Type': 'application/x-www-form-urlencoded' + }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); From 9ca3d3bc4fabac7a9d41722c83bf7060264572e0 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Tue, 21 May 2024 23:45:30 +0000 Subject: [PATCH 10/39] fix lint --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index ba050d14..9dd8a2ec 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -680,7 +680,7 @@ export class OAuth2Client extends AuthClient { ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); const headers: {[key: string]: string} = { - 'Content-Type': 'application/x-www-form-urlencoded' + 'Content-Type': 'application/x-www-form-urlencoded', }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = From 19639ab34159b1fafbe016cefd830566c3964fa9 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:53:04 -0700 Subject: [PATCH 11/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 9dd8a2ec..b8da960a 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -487,7 +487,7 @@ export interface OAuth2ClientOptions extends AuthClientOptions { issuers?: string[]; /** * The client authentication type. Supported values are basic, post, and none. - * If no type is provided, will default to post. + * Defaults to post if not provided. * https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ client_authentication?: ClientAuthentication; From 9bfdecb4c22cee46dc42f9f48b8581561661c447 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:53:17 -0700 Subject: [PATCH 12/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index b8da960a..4db4f81c 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -685,7 +685,7 @@ export class OAuth2Client extends AuthClient { if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); - headers.Authorization = basic_auth; + headers['Authorization'] = basic_auth; } const values: {[key: string]: string | undefined} = { code: options.code, From 4af818bb03a1931899cc9cba2ad782fa45bd62e8 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:53:52 -0700 Subject: [PATCH 13/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 4db4f81c..2957bc24 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -679,7 +679,7 @@ export class OAuth2Client extends AuthClient { options: GetTokenOptions ): Promise { const url = this.endpoints.oauth2TokenUrl.toString(); - const headers: {[key: string]: string} = { + const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { From 32f73ed0dff3156d1a312406f2b5619df0fd0832 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:54:24 -0700 Subject: [PATCH 14/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 2957bc24..8976cb6d 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -695,7 +695,7 @@ export class OAuth2Client extends AuthClient { code_verifier: options.codeVerifier, }; if (this.client_authentication === ClientAuthentication.ClientSecretPost) { - values.client_secret = this._clientSecret; + Object.assign(values, {client_secret: this._clientSecret}); } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, From c316e9c890772c729eb7d91e3306b542bd195a22 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:54:43 -0700 Subject: [PATCH 15/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 8976cb6d..05540bfd 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -687,7 +687,7 @@ export class OAuth2Client extends AuthClient { 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); headers['Authorization'] = basic_auth; } - const values: {[key: string]: string | undefined} = { + const values = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, From af077ca27c4cb94bbb2ade3a0ff3893dd7b2ce1b Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 22 May 2024 23:54:51 -0700 Subject: [PATCH 16/39] Update src/auth/oauth2client.ts Co-authored-by: aeitzman <12433791+aeitzman@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 05540bfd..2333c922 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -69,7 +69,7 @@ export enum CertificateFormat { } /** - * The client authentication type. Support values are basic, post, and none. + * The client authentication type. Supported values are basic, post, and none. * https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ export enum ClientAuthentication { From 8082f2bbc00ebb41711a60c2d86f69fcf0f6a316 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Thu, 23 May 2024 07:05:01 +0000 Subject: [PATCH 17/39] address comments --- src/auth/oauth2client.ts | 4 ++-- test/test.oauth2.ts | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 2333c922..38c02ac0 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -507,7 +507,7 @@ export class OAuth2Client extends AuthClient { protected refreshTokenPromises = new Map>(); readonly endpoints: Readonly; readonly issuers: string[]; - private client_authentication: ClientAuthentication; + readonly client_authentication: ClientAuthentication; // TODO: refactor tests to make this private _clientId?: string; @@ -695,7 +695,7 @@ export class OAuth2Client extends AuthClient { code_verifier: options.codeVerifier, }; if (this.client_authentication === ClientAuthentication.ClientSecretPost) { - Object.assign(values, {client_secret: this._clientSecret}); + Object.assign(values, {client_secret: this._clientSecret}); } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 412bf7c5..5f83bcd4 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1427,11 +1427,11 @@ describe('oauth2', () => { }); it('getToken should use basic header auth if provided in options', async () => { - const authurl = 'https://auth.cloud.google/authorize'; - const scope = nock(authurl, { - reqheaders: {'Content-Type': 'application/x-www-form-urlencoded'}, - }) - .post('/token') + const authurl = 'https://sts.googleapis.com/v1/'; + const basic_auth = 'basic ' + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`); + const scope = nock(authurl) + .post('/oauthtoken') + .matchHeader('authorization', basic_auth) .reply(200, { access_token: 'abc', refresh_token: '123', @@ -1442,17 +1442,20 @@ describe('oauth2', () => { clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, endpoints: { - oauth2AuthBaseUrl: authurl, - oauth2TokenUrl: 'mytokenurl', + oauth2AuthBaseUrl: 'https://auth.cloud.google/authorize', + oauth2TokenUrl: 'https://sts.googleapis.com/v1/oauthtoken', tokenInfoUrl: 'https://sts.googleapis.com/v1/introspect', }, client_authentication: ClientAuthentication.ClientSecretBasic, }; const oauth2client = new OAuth2Client(opts); - const _ = oauth2client.getToken({ + const res = await oauth2client.getToken({ code: 'code here', client_id: CLIENT_ID, }); + scope.done(); + assert(res.res); + assert(res.res.data.access_token === 'abc'); }); it('getToken should not use basic header auth if provided none in options', async () => { @@ -1466,10 +1469,7 @@ describe('oauth2', () => { client_authentication: ClientAuthentication.None, }; const oauth2client = new OAuth2Client(opts); - const _ = oauth2client.getToken({ - code: 'code here', - client_id: CLIENT_ID, - }); + assert(oauth2client.client_authentication === ClientAuthentication.None); }); it('getToken should use auth secret post if not provided in options', async () => { @@ -1482,10 +1482,10 @@ describe('oauth2', () => { }, }; const oauth2client = new OAuth2Client(opts); - const _ = oauth2client.getToken({ - code: 'code here', - client_id: CLIENT_ID, - }); + assert( + oauth2client.client_authentication === + ClientAuthentication.ClientSecretPost + ); }); it('should return expiry_date', done => { From 31a97d99d5d2d444396f2756cf3ca32f9be0755b Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Thu, 23 May 2024 18:51:45 +0000 Subject: [PATCH 18/39] fix tests --- test/test.oauth2.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 5f83bcd4..54fb459d 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1371,6 +1371,7 @@ describe('oauth2', () => { reqheaders: {'Content-Type': 'application/x-www-form-urlencoded'}, }) .post('/token') + .matchHeader('authorization', value => value === undefined) .reply(200, { access_token: 'abc', refresh_token: '123', @@ -1428,7 +1429,7 @@ describe('oauth2', () => { it('getToken should use basic header auth if provided in options', async () => { const authurl = 'https://sts.googleapis.com/v1/'; - const basic_auth = 'basic ' + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`); + const basic_auth = 'basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ='; const scope = nock(authurl) .post('/oauthtoken') .matchHeader('authorization', basic_auth) From 50ef2339f2286bf9958bd80540c2f187c9fe592a Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Thu, 23 May 2024 18:53:02 +0000 Subject: [PATCH 19/39] fix tests --- test/test.oauth2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 54fb459d..a365c0b3 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1429,6 +1429,7 @@ describe('oauth2', () => { it('getToken should use basic header auth if provided in options', async () => { const authurl = 'https://sts.googleapis.com/v1/'; + // basic auth of base64 on `{CLIENT_ID}:{CLIENT_SECRET}` const basic_auth = 'basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ='; const scope = nock(authurl) .post('/oauthtoken') From 5d4c1d4c7ed425d777cd6c0d8b4fc3c44b05f8f0 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Thu, 23 May 2024 20:09:20 +0000 Subject: [PATCH 20/39] fix tests and lint --- src/auth/oauth2client.ts | 5 ++++- test/test.oauth2.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 38c02ac0..8cbb2cc9 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -684,7 +684,10 @@ export class OAuth2Client extends AuthClient { }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = - 'basic ' + btoa(`${this._clientId}:${this._clientSecret}`); + 'basic ' + + Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( + 'base64' + ); headers['Authorization'] = basic_auth; } const values = { diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index a365c0b3..e9cebd13 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1429,8 +1429,9 @@ describe('oauth2', () => { it('getToken should use basic header auth if provided in options', async () => { const authurl = 'https://sts.googleapis.com/v1/'; - // basic auth of base64 on `{CLIENT_ID}:{CLIENT_SECRET}` - const basic_auth = 'basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ='; + const basic_auth = + 'basic ' + + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'); const scope = nock(authurl) .post('/oauthtoken') .matchHeader('authorization', basic_auth) From 1e0df4e0913120045a405e59a908d6e54ae43273 Mon Sep 17 00:00:00 2001 From: Jin Date: Tue, 28 May 2024 21:35:13 -0700 Subject: [PATCH 21/39] Update src/auth/oauth2client.ts Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 8cbb2cc9..9e1743cd 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -684,7 +684,7 @@ export class OAuth2Client extends AuthClient { }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = - 'basic ' + + 'Basic ' + Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( 'base64' ); From 6712b7d3be36d61961a8f8d4cf6e0e8e041c6c68 Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Wed, 29 May 2024 05:13:58 +0000 Subject: [PATCH 22/39] addressing comments --- src/auth/oauth2client.ts | 19 ++++++++++--------- test/test.oauth2.ts | 21 ++++++++++++++++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 9e1743cd..dbffca30 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -682,14 +682,6 @@ export class OAuth2Client extends AuthClient { const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { - const basic_auth = - 'Basic ' + - Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( - 'base64' - ); - headers['Authorization'] = basic_auth; - } const values = { code: options.code, client_id: options.client_id || this._clientId, @@ -697,7 +689,16 @@ export class OAuth2Client extends AuthClient { grant_type: 'authorization_code', code_verifier: options.codeVerifier, }; - if (this.client_authentication === ClientAuthentication.ClientSecretPost) { + if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { + const basic_auth = + 'Basic ' + + Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( + 'base64' + ); + headers['Authorization'] = basic_auth; + } else if ( + this.client_authentication === ClientAuthentication.ClientSecretPost + ) { Object.assign(values, {client_secret: this._clientSecret}); } const res = await this.transporter.request({ diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index e9cebd13..992533bc 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1430,7 +1430,7 @@ describe('oauth2', () => { it('getToken should use basic header auth if provided in options', async () => { const authurl = 'https://sts.googleapis.com/v1/'; const basic_auth = - 'basic ' + + 'Basic ' + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'); const scope = nock(authurl) .post('/oauthtoken') @@ -1461,18 +1461,33 @@ describe('oauth2', () => { assert(res.res.data.access_token === 'abc'); }); - it('getToken should not use basic header auth if provided none in options', async () => { + it('getToken should not use basic header auth if provided none in options and fail', async () => { + const authurl = 'https://some.example.auth/'; + const scope = nock(authurl).post('/token').reply(401); const opts = { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, endpoints: { - oauth2TokenUrl: 'mytokenurl', + oauth2AuthBaseUrl: 'https://auth.cloud.google/authorize', + oauth2TokenUrl: 'https://some.example.auth/token', }, client_authentication: ClientAuthentication.None, }; const oauth2client = new OAuth2Client(opts); assert(oauth2client.client_authentication === ClientAuthentication.None); + + try { + await oauth2client.getToken({ + code: 'code here', + client_id: CLIENT_ID, + }); + } catch (err) { + scope.done(); + const e = err as GaxiosError; + assert(e); + assert.strictEqual(e.response!.status, 401); + } }); it('getToken should use auth secret post if not provided in options', async () => { From e76b1cd7fd41999585252612d9203fe63e86219f Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Wed, 29 May 2024 21:30:39 +0000 Subject: [PATCH 23/39] adding validation of no auth header --- test/test.oauth2.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 992533bc..3fee96f3 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1463,7 +1463,10 @@ describe('oauth2', () => { it('getToken should not use basic header auth if provided none in options and fail', async () => { const authurl = 'https://some.example.auth/'; - const scope = nock(authurl).post('/token').reply(401); + const scope = nock(authurl) + .post('/token') + .matchHeader('Authorization', val => val === undefined) + .reply(401); const opts = { clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, From a1187ea68b41358464ac3c999847add492b5ea67 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:54:34 -0700 Subject: [PATCH 24/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index dbffca30..46cfd8e9 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -73,9 +73,9 @@ export enum CertificateFormat { * https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ export enum ClientAuthentication { - ClientSecretPost, - ClientSecretBasic, - None, + ClientSecretPost = 'ClientSecretPost', + ClientSecretBasic = 'ClientSecretBasic', + None = 'None', } export interface GetTokenOptions { From 067ede8b7609d7f3ecb02175de32949a748c9924 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:54:45 -0700 Subject: [PATCH 25/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 46cfd8e9..ec6d04da 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -507,7 +507,7 @@ export class OAuth2Client extends AuthClient { protected refreshTokenPromises = new Map>(); readonly endpoints: Readonly; readonly issuers: string[]; - readonly client_authentication: ClientAuthentication; + readonly clientAuthentication: ClientAuthentication; // TODO: refactor tests to make this private _clientId?: string; From f2c642aed69d1c42a22e04c980cfabedf2217f98 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:54:57 -0700 Subject: [PATCH 26/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index ec6d04da..9e17855a 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -699,7 +699,7 @@ export class OAuth2Client extends AuthClient { } else if ( this.client_authentication === ClientAuthentication.ClientSecretPost ) { - Object.assign(values, {client_secret: this._clientSecret}); + values.client_secret = this._clientSecret; } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, From 26e7f5c1d09023490cdf86a79c448785949ff71c Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:55:19 -0700 Subject: [PATCH 27/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 9e17855a..3e38c0a4 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -706,7 +706,7 @@ export class OAuth2Client extends AuthClient { method: 'POST', url, data: querystring.stringify(values), - headers: headers, + headers, }); const tokens = res.data as Credentials; if (res.data && res.data.expires_in) { From 61e127bead3371d8204970b34592d01308a14a13 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:55:26 -0700 Subject: [PATCH 28/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 3e38c0a4..0db5dcf4 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -682,7 +682,7 @@ export class OAuth2Client extends AuthClient { const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - const values = { + const values: Omit = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, From a000b4eb0583c044516884dda703105c15194245 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:55:37 -0700 Subject: [PATCH 29/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 0db5dcf4..9d9b613a 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -81,6 +81,11 @@ export enum ClientAuthentication { export interface GetTokenOptions { code: string; codeVerifier?: string; + /** + * @alias GetTokenOptions['codeVerifier'] + **/ + code_verifier?: string; + grant_type?: 'authorization_code'; /** * The client ID for your application. The value passed into the constructor * will be used if not provided. Must match any client_id option passed to From bb43d01f05d247bf06021b7b3300a8bb22a5f0fa Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:56:01 -0700 Subject: [PATCH 30/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 9d9b613a..95ff14ca 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -691,8 +691,8 @@ export class OAuth2Client extends AuthClient { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, - grant_type: 'authorization_code', - code_verifier: options.codeVerifier, + grant_type: options.grant_type || 'authorization_code', + code_verifier: options.codeVerifier || options.code_verifier, }; if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = From 4605d4c16b7903dbce82b5258fbe3e74d6cd480c Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:56:27 -0700 Subject: [PATCH 31/39] Update test/test.oauth2.ts Co-authored-by: Daniel Bankhead --- test/test.oauth2.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 3fee96f3..240adaf1 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1485,11 +1485,12 @@ describe('oauth2', () => { code: 'code here', client_id: CLIENT_ID, }); + throw new Error('Expected an error'); } catch (err) { + assert(err instanceof GaxiosError); + assert.equal(err.response?.status, 401); + } finally { scope.done(); - const e = err as GaxiosError; - assert(e); - assert.strictEqual(e.response!.status, 401); } }); From 959dfbfdd30a29384a67d1390c8e200aaf9c7622 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:56:36 -0700 Subject: [PATCH 32/39] Update test/test.oauth2.ts Co-authored-by: Daniel Bankhead --- test/test.oauth2.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 240adaf1..f2691563 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1504,10 +1504,7 @@ describe('oauth2', () => { }, }; const oauth2client = new OAuth2Client(opts); - assert( - oauth2client.client_authentication === - ClientAuthentication.ClientSecretPost - ); + assert.equal(oauth2client.client_authentication, ClientAuthentication.ClientSecretPost); }); it('should return expiry_date', done => { From f945b24199ac5fe757de14b6123fbfdc8aa27413 Mon Sep 17 00:00:00 2001 From: Jin Date: Wed, 29 May 2024 20:56:45 -0700 Subject: [PATCH 33/39] Update test/test.oauth2.ts Co-authored-by: Daniel Bankhead --- test/test.oauth2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index f2691563..913da14d 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1458,7 +1458,7 @@ describe('oauth2', () => { }); scope.done(); assert(res.res); - assert(res.res.data.access_token === 'abc'); + assert.equal(res.res.data.access_token, 'abc'); }); it('getToken should not use basic header auth if provided none in options and fail', async () => { From 45f8942a00e653f201770c67de3ea19deaed804d Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Thu, 30 May 2024 04:46:53 +0000 Subject: [PATCH 34/39] fix CI after apply changes --- src/auth/oauth2client.ts | 19 +++++++++---------- test/test.oauth2.ts | 16 +++++++++++----- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 95ff14ca..7118cb57 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -82,8 +82,8 @@ export interface GetTokenOptions { code: string; codeVerifier?: string; /** - * @alias GetTokenOptions['codeVerifier'] - **/ + * @alias GetTokenOptions['codeVerifier'] + **/ code_verifier?: string; grant_type?: 'authorization_code'; /** @@ -495,7 +495,7 @@ export interface OAuth2ClientOptions extends AuthClientOptions { * Defaults to post if not provided. * https://datatracker.ietf.org/doc/html/rfc7591#section-2 */ - client_authentication?: ClientAuthentication; + clientAuthentication?: ClientAuthentication; } // Re-exporting here for backwards compatibility @@ -564,8 +564,8 @@ export class OAuth2Client extends AuthClient { oauth2IapPublicKeyUrl: 'https://www.gstatic.com/iap/verify/public_key', ...opts.endpoints, }; - this.client_authentication = - opts.client_authentication || ClientAuthentication.ClientSecretPost; + this.clientAuthentication = + opts.clientAuthentication || ClientAuthentication.ClientSecretPost; this.issuers = opts.issuers || [ 'accounts.google.com', @@ -694,17 +694,16 @@ export class OAuth2Client extends AuthClient { grant_type: options.grant_type || 'authorization_code', code_verifier: options.codeVerifier || options.code_verifier, }; - if (this.client_authentication === ClientAuthentication.ClientSecretBasic) { + if (this.clientAuthentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = 'Basic ' + Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( 'base64' ); headers['Authorization'] = basic_auth; - } else if ( - this.client_authentication === ClientAuthentication.ClientSecretPost - ) { - values.client_secret = this._clientSecret; + } + if (this.clientAuthentication === ClientAuthentication.ClientSecretPost) { + Object.assign(values, {client_secret: this._clientSecret}); } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, diff --git a/test/test.oauth2.ts b/test/test.oauth2.ts index 913da14d..ebd42ef0 100644 --- a/test/test.oauth2.ts +++ b/test/test.oauth2.ts @@ -1434,7 +1434,7 @@ describe('oauth2', () => { Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64'); const scope = nock(authurl) .post('/oauthtoken') - .matchHeader('authorization', basic_auth) + .matchHeader('Authorization', basic_auth) .reply(200, { access_token: 'abc', refresh_token: '123', @@ -1449,7 +1449,7 @@ describe('oauth2', () => { oauth2TokenUrl: 'https://sts.googleapis.com/v1/oauthtoken', tokenInfoUrl: 'https://sts.googleapis.com/v1/introspect', }, - client_authentication: ClientAuthentication.ClientSecretBasic, + clientAuthentication: ClientAuthentication.ClientSecretBasic, }; const oauth2client = new OAuth2Client(opts); const res = await oauth2client.getToken({ @@ -1475,10 +1475,13 @@ describe('oauth2', () => { oauth2AuthBaseUrl: 'https://auth.cloud.google/authorize', oauth2TokenUrl: 'https://some.example.auth/token', }, - client_authentication: ClientAuthentication.None, + clientAuthentication: ClientAuthentication.None, }; const oauth2client = new OAuth2Client(opts); - assert(oauth2client.client_authentication === ClientAuthentication.None); + assert.equal( + oauth2client.clientAuthentication, + ClientAuthentication.None + ); try { await oauth2client.getToken({ @@ -1504,7 +1507,10 @@ describe('oauth2', () => { }, }; const oauth2client = new OAuth2Client(opts); - assert.equal(oauth2client.client_authentication, ClientAuthentication.ClientSecretPost); + assert.equal( + oauth2client.clientAuthentication, + ClientAuthentication.ClientSecretPost + ); }); it('should return expiry_date', done => { From e142c196a71da0e0de523acfd15cdea227930d90 Mon Sep 17 00:00:00 2001 From: Jin Date: Thu, 30 May 2024 21:39:48 -0700 Subject: [PATCH 35/39] Update src/auth/oauth2client.ts Co-authored-by: Daniel Bankhead --- src/auth/oauth2client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 7118cb57..7a66ec96 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -703,7 +703,7 @@ export class OAuth2Client extends AuthClient { headers['Authorization'] = basic_auth; } if (this.clientAuthentication === ClientAuthentication.ClientSecretPost) { - Object.assign(values, {client_secret: this._clientSecret}); + values.client_secret = this._clientSecret; } const res = await this.transporter.request({ ...OAuth2Client.RETRY_CONFIG, From 8ef641788f4cba6f578a5655fc85650c7461bb9a Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Fri, 31 May 2024 04:53:46 +0000 Subject: [PATCH 36/39] fix syntax of post value --- src/auth/oauth2client.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 7a66ec96..9deb2923 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -687,12 +687,13 @@ export class OAuth2Client extends AuthClient { const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - const values: Omit = { + const values: Omit & {client_secret : string | undefined} = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, grant_type: options.grant_type || 'authorization_code', code_verifier: options.codeVerifier || options.code_verifier, + client_secret: undefined, }; if (this.clientAuthentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = From 021b96910a67bfa373b38ea8afbedd31b405da0f Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Fri, 31 May 2024 05:10:36 +0000 Subject: [PATCH 37/39] fix lint --- src/auth/oauth2client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 9deb2923..26452f87 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -687,7 +687,9 @@ export class OAuth2Client extends AuthClient { const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - const values: Omit & {client_secret : string | undefined} = { + const values: Omit & { + client_secret: string | undefined; + } = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, From 461e648aaa91509408804c554ce24c578354a8bc Mon Sep 17 00:00:00 2001 From: Jin Qin Date: Fri, 31 May 2024 22:09:17 +0000 Subject: [PATCH 38/39] fix the client_secret field --- src/auth/oauth2client.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 26452f87..3f4ffd4d 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -688,14 +688,13 @@ export class OAuth2Client extends AuthClient { 'Content-Type': 'application/x-www-form-urlencoded', }; const values: Omit & { - client_secret: string | undefined; + client_secret?: string; } = { code: options.code, client_id: options.client_id || this._clientId, redirect_uri: options.redirect_uri || this.redirectUri, grant_type: options.grant_type || 'authorization_code', code_verifier: options.codeVerifier || options.code_verifier, - client_secret: undefined, }; if (this.clientAuthentication === ClientAuthentication.ClientSecretBasic) { const basic_auth = From 81a23d3bcf32969fb71df79c55318a634c6efd8e Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Fri, 31 May 2024 16:32:24 -0700 Subject: [PATCH 39/39] refactor: interface and readability --- src/auth/oauth2client.ts | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/auth/oauth2client.ts b/src/auth/oauth2client.ts index 3f4ffd4d..4c60c5f9 100644 --- a/src/auth/oauth2client.ts +++ b/src/auth/oauth2client.ts @@ -81,11 +81,6 @@ export enum ClientAuthentication { export interface GetTokenOptions { code: string; codeVerifier?: string; - /** - * @alias GetTokenOptions['codeVerifier'] - **/ - code_verifier?: string; - grant_type?: 'authorization_code'; /** * The client ID for your application. The value passed into the constructor * will be used if not provided. Must match any client_id option passed to @@ -101,6 +96,19 @@ export interface GetTokenOptions { redirect_uri?: string; } +/** + * An interface for preparing {@link GetTokenOptions} as a querystring. + */ +interface GetTokenQuery { + client_id?: string; + client_secret?: string; + code_verifier?: string; + code: string; + grant_type: 'authorization_code'; + redirect_uri?: string; + [key: string]: string | undefined; +} + export interface TokenInfo { /** * The application that is the intended user of the access token. @@ -687,22 +695,17 @@ export class OAuth2Client extends AuthClient { const headers: Headers = { 'Content-Type': 'application/x-www-form-urlencoded', }; - const values: Omit & { - client_secret?: string; - } = { - code: options.code, + const values: GetTokenQuery = { client_id: options.client_id || this._clientId, + code_verifier: options.codeVerifier, + code: options.code, + grant_type: 'authorization_code', redirect_uri: options.redirect_uri || this.redirectUri, - grant_type: options.grant_type || 'authorization_code', - code_verifier: options.codeVerifier || options.code_verifier, }; if (this.clientAuthentication === ClientAuthentication.ClientSecretBasic) { - const basic_auth = - 'Basic ' + - Buffer.from(`${this._clientId}:${this._clientSecret}`).toString( - 'base64' - ); - headers['Authorization'] = basic_auth; + const basic = Buffer.from(`${this._clientId}:${this._clientSecret}`); + + headers['Authorization'] = `Basic ${basic.toString('base64')}`; } if (this.clientAuthentication === ClientAuthentication.ClientSecretPost) { values.client_secret = this._clientSecret;