diff --git a/.github/workflows/oidc-test.yml b/.github/workflows/oidc-test.yml index a9fc84196..d05577f3e 100644 --- a/.github/workflows/oidc-test.yml +++ b/.github/workflows/oidc-test.yml @@ -66,7 +66,6 @@ jobs: "repository": "${{ github.repository_owner }}/frogbot" }, "token_spec": { - "username": "admin", "scope": "applied-permissions/admin", "audience": "*@*", "expires_in": 1200 diff --git a/.gitignore b/.gitignore index c53a965cc..95b6bf4be 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,6 @@ coverage /testdata/vcsclientmock.go /action/lib/src/* /action/lib/test/* +/action/node_modules/ CLAUDE.md .claude \ No newline at end of file diff --git a/action/.husky/pre-commit b/action/.husky/pre-commit index ba9122b51..08b62685d 100755 --- a/action/.husky/pre-commit +++ b/action/.husky/pre-commit @@ -5,5 +5,6 @@ if [ "$(git status action --porcelain=v1)" ]; then cd action || exit npm i npm prune --production - git add node_modules lib -fi \ No newline at end of file + git add -f node_modules lib + npm i +fi diff --git a/action/lib/utils.js b/action/lib/utils.js index faf8952e8..1e7921c40 100644 --- a/action/lib/utils.js +++ b/action/lib/utils.js @@ -283,14 +283,22 @@ class Utils { }; const response = yield httpClient.post(exchangeUrl, data, additionalHeaders); const responseString = yield response.readBody(); - const responseJson = JSON.parse(responseString); - process.env.JF_ACCESS_TOKEN = responseJson.access_token; - if (responseJson.access_token) { - core.setSecret(responseJson.access_token); + const statusCode = response.message.statusCode; + if (!statusCode || statusCode < 200 || statusCode >= 300) { + throw new Error(`Token exchange failed with HTTP ${statusCode}: ${responseString}`); } + const responseJson = JSON.parse(responseString); if (responseJson.errors) { throw new Error(`${JSON.stringify(responseJson.errors)}`); } + if (responseJson.error) { + throw new Error(`${responseJson.error}${responseJson.error_description ? ': ' + responseJson.error_description : ''}`); + } + if (!responseJson.access_token) { + throw new Error(`Token exchange response is missing access_token. Full response: ${responseString}`); + } + core.setSecret(responseJson.access_token); + process.env.JF_ACCESS_TOKEN = responseJson.access_token; }); } } diff --git a/action/src/utils.ts b/action/src/utils.ts index 58de399f8..fb0608f68 100644 --- a/action/src/utils.ts +++ b/action/src/utils.ts @@ -262,17 +262,29 @@ export class Utils { const response: HttpClientResponse = await httpClient.post(exchangeUrl, data, additionalHeaders); const responseString: string = await response.readBody(); - const responseJson: TokenExchangeResponseData = JSON.parse(responseString); - process.env.JF_ACCESS_TOKEN = responseJson.access_token; - if (responseJson.access_token) { - core.setSecret(responseJson.access_token); + const statusCode: number | undefined = response.message.statusCode; + if (!statusCode || statusCode < 200 || statusCode >= 300) { + throw new Error(`Token exchange failed with HTTP ${statusCode}: ${responseString}`); } + const responseJson: TokenExchangeResponseData = JSON.parse(responseString); if (responseJson.errors) { throw new Error(`${JSON.stringify(responseJson.errors)}`); } + if (responseJson.error) { + throw new Error( + `${responseJson.error}${responseJson.error_description ? ': ' + responseJson.error_description : ''}`, + ); + } + if (!responseJson.access_token) { + throw new Error(`Token exchange response is missing access_token. Full response: ${responseString}`); + } + core.setSecret(responseJson.access_token); + process.env.JF_ACCESS_TOKEN = responseJson.access_token; } } export interface TokenExchangeResponseData { access_token: string; errors: string; + error: string; + error_description: string; } diff --git a/action/test/main.spec.ts b/action/test/main.spec.ts index 3ddc8c58a..2d450688e 100644 --- a/action/test/main.spec.ts +++ b/action/test/main.spec.ts @@ -7,7 +7,7 @@ describe('Frogbot Action Tests', () => { afterEach(() => { delete process.env.JF_ACCESS_TOKEN; delete process.env.JF_USER; - delete process.env.PASSWORD; + delete process.env.JF_PASSWORD; delete process.env.JF_GIT_PROVIDER; delete process.env.JF_GIT_OWNER; delete process.env.GITHUB_REPOSITORY_OWNER; @@ -114,25 +114,25 @@ describe('Frogbot Action Tests', () => { describe('Generate auth string', () => { it('Should return an empty string if releasesRepo is falsy', () => { - const result = Utils.generateAuthString(''); + const result: string = Utils.generateAuthString(''); expect(result).toBe(''); }); it('Should generate a Bearer token if accessToken is provided', () => { process.env.JF_ACCESS_TOKEN = 'yourAccessToken'; - const result = Utils.generateAuthString('yourReleasesRepo'); + const result: string = Utils.generateAuthString('yourReleasesRepo'); expect(result).toBe('Bearer yourAccessToken'); }); it('Should generate a Basic token if username and password are provided', () => { process.env.JF_USER = 'yourUsername'; process.env.JF_PASSWORD = 'yourPassword'; - const result = Utils.generateAuthString('yourReleasesRepo'); + const result: string = Utils.generateAuthString('yourReleasesRepo'); expect(result).toBe('Basic eW91clVzZXJuYW1lOnlvdXJQYXNzd29yZA=='); }); it('Should return an empty string if no credentials are provided', () => { - const result = Utils.generateAuthString('yourReleasesRepo'); + const result: string = Utils.generateAuthString('yourReleasesRepo'); expect(result).toBe(''); }); });