From 2fe2496816aa19b3df71259506570d99c135d1a5 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 23 Oct 2024 23:14:56 +0200 Subject: [PATCH] test: add regression test for issue 704 --- .github/workflows/test.yml | 7 ++- ava.config.mjs | 2 +- package.json | 2 +- src/index.ts | 3 +- test/issue-704.test.ts | 91 ++++++++++++++++++++++++++++++++++++++ test/tsconfig.json | 14 ++++++ 6 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 test/issue-704.test.ts create mode 100644 test/tsconfig.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d66fb3b..b5fd3dd6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,6 +70,9 @@ jobs: fail-fast: false matrix: node-version: ${{ fromJSON(needs.node-versions.outputs.matrix) }} + suite: + - tap:node + - test runs-on: ubuntu-latest steps: - name: Checkout @@ -82,9 +85,9 @@ jobs: check-latest: true - run: npm clean-install - name: Run Test Suite - run: npm run tap:node + run: npm run ${{ matrix.suite }} - name: Upload server logs - if: ${{ failure() }} + if: ${{ failure() && matrix.suite == 'tap:node' }} id: artifact-upload-step uses: actions/upload-artifact@v4 with: diff --git a/ava.config.mjs b/ava.config.mjs index 86009d6c..90d2ff66 100644 --- a/ava.config.mjs +++ b/ava.config.mjs @@ -3,7 +3,7 @@ export default { ts: 'module', mjs: true, }, - files: ['test/**/*.ts', '!test/**/_*.ts'], + files: ['test/**/*.ts'], workerThreads: false, nodeArguments: ['--enable-source-maps'], } diff --git a/package.json b/package.json index 3088edd3..be27c002 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ ], "scripts": { "_format": "find src test examples tap conformance -type f -name '*.ts' -o -name '*.mjs' -o -name '*.cjs' | xargs prettier --check", - "build": "rm -rf build && tsc && tsc --removeComments false --declaration --emitDeclarationOnly && tsc -p tsconfig.passport.json && tsc -p tsconfig.passport.json --removeComments false --declaration --emitDeclarationOnly && npm run --silent check-build", + "build": "rm -rf build && tsc && tsc --removeComments false --declaration --emitDeclarationOnly && tsc -p tsconfig.passport.json && tsc -p tsconfig.passport.json --removeComments false --declaration --emitDeclarationOnly && tsc -p test && npm run --silent check-build", "check-build": "tsc --noEmit --types node --lib ES2022.Error && tsc -p conformance && tsc -p tap && tsc -p examples", "conformance": "bash -c 'source .node_flags.sh && ava --config conformance/ava.config.ts'", "docs": "patch-package && typedoc", diff --git a/src/index.ts b/src/index.ts index 24d1852c..951a01e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1026,8 +1026,7 @@ function handleEntraId( options?: DiscoveryRequestOptions, ) { if ( - (server.href === 'https://login.microsoftonline.com/common/v2.0' || - server.href === 'https://login.microsoftonline.com/organizations/v2.0') && + server.origin === 'https://login.microsoftonline.com' && (!options?.algorithm || options.algorithm === 'oidc') ) { // @ts-expect-error diff --git a/test/issue-704.test.ts b/test/issue-704.test.ts new file mode 100644 index 00000000..cb81d2fd --- /dev/null +++ b/test/issue-704.test.ts @@ -0,0 +1,91 @@ +// see https://github.com/panva/openid-client/discussions/704 + +import test from 'ava' +import * as client from '../src/index.js' +import * as undici from 'undici' +import * as jose from 'jose' + +const urls = [ + new URL('https://login.microsoftonline.com/common/v2.0'), + new URL('https://login.microsoftonline.com/common/v2.0/'), + new URL('https://login.microsoftonline.com/organizations/v2.0'), + new URL('https://login.microsoftonline.com/organizations/v2.0/'), +] + +let i = 0 +for (const url of urls) { + i++ + test(`handles Entra ID multi-tenant issuer identifiers ${i}/${urls.length}`, async (t) => { + let agent = new undici.MockAgent() + agent.disableNetConnect() + + const wellKnown = new URL( + `${url.pathname}/.well-known/openid-configuration`.replace('//', '/'), + url, + ) + + const mockAgent = agent.get(url.origin) + + mockAgent + .intercept({ + method: 'GET', + path: wellKnown.pathname, + }) + .reply( + 200, + { + issuer: 'https://login.microsoftonline.com/{tenantid}/v2.0', + token_endpoint: 'https://login.microsoftonline.com/token', + id_token_signing_alg_values_supported: ['none'], + }, + { + headers: { + 'content-type': 'application/json', + }, + }, + ) + + mockAgent + .intercept({ + method: 'POST', + path: '/token', + }) + .reply( + 200, + { + access_token: 'foo', + token_type: 'bearer', + id_token: new jose.UnsecuredJWT({ + iss: 'https://login.microsoftonline.com/foobar/v2.0', + tid: 'foobar', + }) + .setAudience('decoy') + .setIssuedAt() + .setExpirationTime('1m') + .setSubject('decoy') + .encode(), + }, + { + headers: { + 'content-type': 'application/json', + }, + }, + ) + + await t.notThrowsAsync(async () => { + const config = await client.discovery(url, 'decoy', 'decoy', undefined, { + // @ts-ignore + [client.customFetch](url, options) { + return undici.fetch(url, { ...options, dispatcher: agent }) + }, + }) + + await client.authorizationCodeGrant( + config, + new URL('https://rp.example.com/cb?code=foo'), + ) + }) + + t.notThrows(() => agent.assertNoPendingInterceptors()) + }) +} diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 00000000..d6c28653 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,14 @@ +{ + "include": ["**/*.test.ts"], + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitAny": true, + "esModuleInterop": true + } +}