From 38a7c0bedc39b3fc0a391f9e80f4374c7597e9cf Mon Sep 17 00:00:00 2001 From: Eddie Chayes <35413716+eddiechayes@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:25:56 -0700 Subject: [PATCH] fix splitit oauth bug (#683) * generator changes * more generator changes * integration test --- .../typescript-axios/common.mustache | 22 ++++++++++++------ .../typescript-axios/configuration.mustache | 4 ++++ .../typescript/.konfigignore | 3 ++- .../typescript/client.ts | 11 ++++++--- .../typescript/common.ts | 22 ++++++++++++------ .../typescript/configuration.ts | 4 ++++ .../typescript/index.test.ts | 23 +++++++++++++++++++ .../tests/typescript-oauth-token.test.ts | 9 ++++++++ 8 files changed, 80 insertions(+), 18 deletions(-) diff --git a/generator/konfig-generator-api/src/main/resources/typescript-axios/common.mustache b/generator/konfig-generator-api/src/main/resources/typescript-axios/common.mustache index 0282c212b2..67c8b4586a 100644 --- a/generator/konfig-generator-api/src/main/resources/typescript-axios/common.mustache +++ b/generator/konfig-generator-api/src/main/resources/typescript-axios/common.mustache @@ -108,7 +108,7 @@ export const setOAuthToObject = async function (object: any, name: string, scope // Sets the OAuth2 authentication header for the request and saves the token for the next request const authenticate = async () => { if (configuration.oauthClientId && configuration.oauthClientSecret && configuration.accessToken === undefined) { - const token = await wrapAxiosRequest(async () => { + const tokenResponse = await wrapAxiosRequest(async () => { {{#isAuthorizationOrTokenUrlRelative}} if (configuration.basePath === undefined) { throw new Error("basePath must be set to use the oauth2 authentication"); @@ -136,11 +136,12 @@ export const setOAuthToObject = async function (object: any, name: string, scope } return acc; }, undefined); - return token + return {"token": token, "expires_in": json.expires_in }; }) - if (token === undefined) + if (tokenResponse === undefined || tokenResponse.token === undefined) throw new Error("Token not found in OAuth response") - configuration.accessToken = token + configuration.accessToken = tokenResponse.token + configuration.accessTokenExpiresIn = tokenResponse.expires_in !== undefined ? (Date.now() / 1000) + tokenResponse.expires_in : undefined } } {{/-first}} @@ -160,10 +161,17 @@ export const setOAuthToObject = async function (object: any, name: string, scope await authenticate(); } else { // check that the token is still valid - const decodedToken = jwtDecode(previousToken); + let decodedToken = undefined; const currentTime = Date.now() / 1000; - if (decodedToken.exp !== undefined && decodedToken.exp < currentTime) { - await authenticate(); + try { // If token is jwt + decodedToken = jwtDecode(previousToken); + } catch (e) {} + if (decodedToken !== undefined && decodedToken.exp !== undefined && decodedToken.exp < currentTime + || configuration.accessTokenExpiresIn !== undefined && configuration.accessTokenExpiresIn < currentTime) { + + configuration.accessToken = undefined; + configuration.accessTokenExpiresIn = undefined; + await authenticate(); } } diff --git a/generator/konfig-generator-api/src/main/resources/typescript-axios/configuration.mustache b/generator/konfig-generator-api/src/main/resources/typescript-axios/configuration.mustache index e566e05d90..73a36d0bcc 100644 --- a/generator/konfig-generator-api/src/main/resources/typescript-axios/configuration.mustache +++ b/generator/konfig-generator-api/src/main/resources/typescript-axios/configuration.mustache @@ -103,6 +103,10 @@ export class Configuration { * @memberof Configuration */ accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * When the access token expires, measured in Unix time in seconds + */ + accessTokenExpiresIn?: number; /** * override base path * diff --git a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/.konfigignore b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/.konfigignore index 41de6c442e..a25ae5f0df 100644 --- a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/.konfigignore +++ b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/.konfigignore @@ -1 +1,2 @@ -index.test.ts \ No newline at end of file +index.test.ts +client.ts \ No newline at end of file diff --git a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/client.ts b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/client.ts index dbb931f2e3..009a2388d2 100644 --- a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/client.ts +++ b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/client.ts @@ -17,12 +17,17 @@ import { Configuration, ConfigurationParameters } from "./configuration"; import { TypescriptOauthTokenClientCustom } from "./client-custom"; export class TypescriptOauthTokenClient extends TypescriptOauthTokenClientCustom { - readonly test: TestApi; + test: TestApi; + config: Configuration; constructor(configurationParameters: ConfigurationParameters) { super(configurationParameters); - const configuration = new Configuration(configurationParameters); - this.test = new TestApi(configuration); + this.config = new Configuration(configurationParameters); + this.test = new TestApi(this.config); } + setOauthTokenUrl(url: string) { + this.config.oauthTokenUrl = url; + this.test = new TestApi(this.config); + } } diff --git a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/common.ts b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/common.ts index 6754594c8a..619d296065 100644 --- a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/common.ts +++ b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/common.ts @@ -108,7 +108,7 @@ export const setOAuthToObject = async function (object: any, name: string, scope // Sets the OAuth2 authentication header for the request and saves the token for the next request const authenticate = async () => { if (configuration.oauthClientId && configuration.oauthClientSecret && configuration.accessToken === undefined) { - const token = await wrapAxiosRequest(async () => { + const tokenResponse = await wrapAxiosRequest(async () => { const url = configuration.oauthTokenUrl ?? "http://127.0.0.1:4048/token" const oauthResponse = await axios.request({ url, @@ -128,11 +128,12 @@ export const setOAuthToObject = async function (object: any, name: string, scope } return acc; }, undefined); - return token + return {"token": token, "expires_in": json.expires_in }; }) - if (token === undefined) + if (tokenResponse === undefined || tokenResponse.token === undefined) throw new Error("Token not found in OAuth response") - configuration.accessToken = token + configuration.accessToken = tokenResponse.token + configuration.accessTokenExpiresIn = tokenResponse.expires_in !== undefined ? (Date.now() / 1000) + tokenResponse.expires_in : undefined } } const getToken = async () => { @@ -149,10 +150,17 @@ export const setOAuthToObject = async function (object: any, name: string, scope await authenticate(); } else { // check that the token is still valid - const decodedToken = jwtDecode(previousToken); + let decodedToken = undefined; const currentTime = Date.now() / 1000; - if (decodedToken.exp !== undefined && decodedToken.exp < currentTime) { - await authenticate(); + try { // If token is jwt + decodedToken = jwtDecode(previousToken); + } catch (e) {} + if (decodedToken !== undefined && decodedToken.exp !== undefined && decodedToken.exp < currentTime + || configuration.accessTokenExpiresIn !== undefined && configuration.accessTokenExpiresIn < currentTime) { + + configuration.accessToken = undefined; + configuration.accessTokenExpiresIn = undefined; + await authenticate(); } } diff --git a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/configuration.ts b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/configuration.ts index c7e6543bc7..b2b2edc226 100644 --- a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/configuration.ts +++ b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/configuration.ts @@ -69,6 +69,10 @@ export class Configuration { * @memberof Configuration */ accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * When the access token expires, measured in Unix time in seconds + */ + accessTokenExpiresIn?: number; /** * override base path * diff --git a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/index.test.ts b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/index.test.ts index f76e691d90..0580ac4e6c 100644 --- a/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/index.test.ts +++ b/generator/konfig-integration-tests/sdks/typescript-oauth-token/typescript/index.test.ts @@ -12,4 +12,27 @@ describe("typescript-oauth-token", () => { const token = (result.data as any).headers.authorization; expect(token).toBe("Bearer 1234567890"); }); + it("token that expires", async () => { + const client = new TypescriptOauthTokenClient({ + oauthClientId: "client_id", + oauthClientSecret: "client_secret", + basePath: "http://127.0.0.1:4048", + oauthTokenUrl: "http://127.0.0.1:4048/fast-expiring-token", + }); + const result = await client.test.fetch(); + const token = (result.data as any).headers.authorization; + expect(token).toBe("Bearer 0987654321"); + + client.setOauthTokenUrl("http://127.0.0.1:4048/token") + + await new Promise((resolve) => setTimeout(resolve, 5000)); + const result2 = await client.test.fetch(); + const token2 = (result2.data as any).headers.authorization; + expect(token2).toBe("Bearer 0987654321"); // token should not have expired yet + + await new Promise((resolve) => setTimeout(resolve, 5000)); + const result3 = await client.test.fetch(); + const token3 = (result3.data as any).headers.authorization; + expect(token3).toBe("Bearer 1234567890"); // token should have expired and been re-fetched + }) }); diff --git a/generator/konfig-integration-tests/tests/typescript-oauth-token.test.ts b/generator/konfig-integration-tests/tests/typescript-oauth-token.test.ts index c84c8ac5a2..13cfc63428 100644 --- a/generator/konfig-integration-tests/tests/typescript-oauth-token.test.ts +++ b/generator/konfig-integration-tests/tests/typescript-oauth-token.test.ts @@ -19,6 +19,15 @@ test("typescript-oauth-token", async () => { expires_in: 3600, }, }, + { + path: "/fast-expiring-token", + method: "post", + response: { + access_token: "0987654321", + token_type: "Bearer", + expires_in: 10, + }, + }, ], }, });