61 lines
2.5 KiB
TypeScript
61 lines
2.5 KiB
TypeScript
import { describe, it, expect } from 'bun:test';
|
|
import { oidcDiscoveryUrls, fetchOidcDiscovery } from '../src/lib/utils/oidc-discovery-url';
|
|
|
|
const ok = (statusText = 'OK') => ({ ok: true, statusText });
|
|
const notOk = (statusText: string) => ({ ok: false, statusText });
|
|
|
|
describe('oidcDiscoveryUrls', () => {
|
|
it('tries the canonical (no trailing slash) URL first, then the trailing-slash variant', () => {
|
|
expect(oidcDiscoveryUrls('https://idp.example.com/realm')).toEqual([
|
|
'https://idp.example.com/realm/.well-known/openid-configuration',
|
|
'https://idp.example.com/realm/.well-known/openid-configuration/'
|
|
]);
|
|
});
|
|
|
|
it('normalises an issuer that already ends with a slash (no double slash)', () => {
|
|
expect(oidcDiscoveryUrls('https://idp.example.com/realm/')).toEqual([
|
|
'https://idp.example.com/realm/.well-known/openid-configuration',
|
|
'https://idp.example.com/realm/.well-known/openid-configuration/'
|
|
]);
|
|
});
|
|
|
|
it('handles a bare-host issuer', () => {
|
|
expect(oidcDiscoveryUrls('https://idp.example.com')).toEqual([
|
|
'https://idp.example.com/.well-known/openid-configuration',
|
|
'https://idp.example.com/.well-known/openid-configuration/'
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('fetchOidcDiscovery', () => {
|
|
it('returns the FIRST url on OK - a compliant provider never sees the fallback', async () => {
|
|
const tried: string[] = [];
|
|
const r = await fetchOidcDiscovery(['a', 'b'], async (u) => { tried.push(u); return ok(); });
|
|
expect(tried).toEqual(['a']);
|
|
expect(r.ok).toBe(true);
|
|
});
|
|
|
|
it('falls back to the trailing-slash url when the canonical one is 404 (#1368)', async () => {
|
|
const tried: string[] = [];
|
|
const r = await fetchOidcDiscovery(['canon', 'canon/'], async (u) => {
|
|
tried.push(u);
|
|
return u.endsWith('/') ? ok() : notOk('Not Found');
|
|
});
|
|
expect(tried).toEqual(['canon', 'canon/']);
|
|
expect(r.ok).toBe(true);
|
|
});
|
|
|
|
it('throws with the last statusText when every url is not-ok', async () => {
|
|
await expect(fetchOidcDiscovery(['a', 'b'], async () => notOk('Not Found')))
|
|
.rejects.toThrow('Failed to fetch OIDC discovery document: Not Found');
|
|
});
|
|
|
|
it('a network error is fatal - does NOT fall through to the next url', async () => {
|
|
const tried: string[] = [];
|
|
const boom = Object.assign(new TypeError('fetch failed'), { cause: { code: 'ENOTFOUND' } });
|
|
await expect(fetchOidcDiscovery(['a', 'b'], async (u) => { tried.push(u); throw boom; }))
|
|
.rejects.toThrow('Failed to reach OIDC issuer at a: fetch failed (ENOTFOUND)');
|
|
expect(tried).toEqual(['a']); // stopped at the first, did not try 'b'
|
|
});
|
|
});
|