conductor

CI task system
git clone git://git.finwo.net/app/conductor
Log | Files | Refs | README | LICENSE

oidc.introspection.test.js (3484B)


      1 // test/oidc.introspection.test.js - unit coverage for opaque token introspection
      2 //
      3 // The provider is stubbed with a fake fetch so these run without docker and
      4 // without a real identity provider. They pin the contract: an introspection
      5 // endpoint that reports active:true yields a profile, active:false is
      6 // rejected, and a provider without an endpoint falls back to JWKS.
      7 
      8 import test from 'node:test';
      9 import assert from 'node:assert/strict';
     10 import { createOidc, fetchDiscovery } from '../src/lib/auth/oidc.js';
     11 
     12 function discovery(overrides = {}) {
     13   return {
     14     issuer: 'https://idp.example',
     15     authorization_endpoint: 'https://idp.example/auth',
     16     token_endpoint: 'https://idp.example/token',
     17     jwks_uri: 'https://idp.example/jwks',
     18     ...overrides,
     19   };
     20 }
     21 
     22 function makeFetch(handler) {
     23   return async (url, opts) => handler(String(url), opts || {});
     24 }
     25 
     26 test('introspection success builds a profile from the response claims', async () => {
     27   const doc = discovery({ introspection_endpoint: 'https://idp.example/introspect' });
     28   const fetchImpl = makeFetch((url) => {
     29     if (url.endsWith('/.well-known/openid-configuration')) {
     30       return new Response(JSON.stringify(doc), { status: 200, headers: { 'content-type': 'application/json' } });
     31     }
     32     return new Response(JSON.stringify({
     33       active: true,
     34       sub: 'user-1',
     35       preferred_username: 'alice',
     36       groups: ['conductor-admin'],
     37     }), { status: 200, headers: { 'content-type': 'application/json' } });
     38   });
     39 
     40   const cfg = { auth: { oidc: { discovery_url: 'https://idp.example/.well-known/openid-configuration', client_id: 'conductor', scopes: 'openid', admin_role: 'conductor-admin' } } };
     41   const oidc = createOidc(cfg, { fetchImpl });
     42   const profile = await oidc.verify('opaque-token');
     43   assert.equal(profile.subject, 'user-1');
     44   assert.equal(profile.username, 'alice');
     45   assert.equal(profile.role, 'admin');
     46 });
     47 
     48 test('introspection reporting active:false is rejected', async () => {
     49   const doc = discovery({ introspection_endpoint: 'https://idp.example/introspect' });
     50   const fetchImpl = makeFetch((url) => {
     51     if (url.endsWith('/.well-known/openid-configuration')) {
     52       return new Response(JSON.stringify(doc), { status: 200, headers: { 'content-type': 'application/json' } });
     53     }
     54     return new Response(JSON.stringify({ active: false }), { status: 200, headers: { 'content-type': 'application/json' } });
     55   });
     56 
     57   const cfg = { auth: { oidc: { discovery_url: 'https://idp.example/.well-known/openid-configuration', client_id: 'conductor', scopes: 'openid', admin_role: 'conductor-admin' } } };
     58   const oidc = createOidc(cfg, { fetchImpl });
     59   await assert.rejects(() => oidc.verify('opaque-token'), /not active/);
     60 });
     61 
     62 test('a provider without an introspection endpoint uses JWKS verification', async () => {
     63   const doc = discovery(); // no introspection_endpoint
     64   // jwks verification requires a real jose key set; instead confirm fetchDiscovery
     65   // does not require introspection_endpoint and the document loads.
     66   const fetchImpl = makeFetch((url) => {
     67     if (url.endsWith('/.well-known/openid-configuration')) {
     68       return new Response(JSON.stringify(doc), { status: 200, headers: { 'content-type': 'application/json' } });
     69     }
     70     return new Response('nope', { status: 404 });
     71   });
     72   const loaded = await fetchDiscovery('https://idp.example/.well-known/openid-configuration', { fetchImpl });
     73   assert.equal(loaded.introspection_endpoint, undefined);
     74 });