import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { UnauthorizedError } from '../src/errors.js'; import { createOidcClient, type OidcClient } from '../src/oidc.js'; import { makeConfig, startStubIdp, type StubIdp } from './helpers.js'; let idp: StubIdp; let oidc: OidcClient; const REDIRECT_URI = 'http://localhost:3000/auth/callback'; const CODE_VERIFIER = 'a'.repeat(64); beforeAll(async () => { idp = await startStubIdp(); oidc = await createOidcClient( makeConfig({ oidc: { issuer: idp.issuer, clientId: idp.clientId, clientSecret: idp.clientSecret, scopes: ['openid', 'profile', 'email'], }, }), ); }); afterAll(async () => { await idp.close(); }); describe('createOidcClient', () => { it('découvre un issuer en http (stub) et expose le scope', () => { expect(oidc.scope).toBe('openid profile email'); }); it('construit une URL d’autorisation PKCE', () => { const url = oidc.authorizationUrl({ redirectUri: REDIRECT_URI, state: 's1', nonce: 'n1', codeChallenge: 'challenge', }); expect(url.origin).toBe(idp.issuer); expect(url.searchParams.get('response_type')).toBe('code'); expect(url.searchParams.get('state')).toBe('s1'); expect(url.searchParams.get('code_challenge_method')).toBe('S256'); expect(url.searchParams.get('redirect_uri')).toBe(REDIRECT_URI); }); it('échange le code et renvoie l’identité', async () => { idp.setNextIdToken({ nonce: 'n2', sub: 'zoe', name: 'Zoé' }); const user = await oidc.handleCallback({ currentUrl: new URL(`${REDIRECT_URI}?code=abc&state=s2`), state: 's2', nonce: 'n2', codeVerifier: CODE_VERIFIER, }); expect(user).toEqual({ sub: 'zoe', name: 'Zoé' }); }); it('rejette un nonce qui ne correspond pas', async () => { idp.setNextIdToken({ nonce: 'autre', sub: 'zoe' }); await expect( oidc.handleCallback({ currentUrl: new URL(`${REDIRECT_URI}?code=abc&state=s3`), state: 's3', nonce: 'n3', codeVerifier: CODE_VERIFIER, }), ).rejects.toBeInstanceOf(UnauthorizedError); }); it('expose une URL de déconnexion RP-initiated', () => { const url = oidc.endSessionUrl('http://localhost:3000/'); expect(url?.origin).toBe(idp.issuer); expect(url?.searchParams.get('post_logout_redirect_uri')).toBe('http://localhost:3000/'); }); });