mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-25 00:13:00 +00:00
chore: move oidc backend to proper DI
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
/**
|
||||
* OIDC e2e — exercises the migrated /api/auth/oidc flow with the real cookie
|
||||
* service. The OIDC service is mocked and the auth toggles are stubbed on the
|
||||
* container's real AuthService instance (OidcService injects it since the auth
|
||||
* DI fold — the old services/authService path mock would silently miss); this
|
||||
* proves the flow is unauthenticated, the sso-disabled 403, the login redirect,
|
||||
* and that /exchange sets the httpOnly trek_session cookie from a valid auth code.
|
||||
* service. The OIDC methods and the auth toggles are stubbed with vi.spyOn on
|
||||
* the container's real OidcService/AuthService instances (the domain is
|
||||
* DI-native since the oidc fold — a services/oidcService path mock would
|
||||
* silently miss); this proves the flow is unauthenticated, the sso-disabled
|
||||
* 403, the login redirect, and that /exchange sets the httpOnly trek_session
|
||||
* cookie from a valid auth code.
|
||||
*/
|
||||
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest';
|
||||
import request from 'supertest';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import type { Server } from 'http';
|
||||
import type { MockInstance } from 'vitest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
vi.mock('../../src/app-config', async (importOriginal) => {
|
||||
@@ -20,9 +22,10 @@ vi.mock('../../src/app-config', async (importOriginal) => {
|
||||
const { db } = vi.hoisted(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const Database = require('better-sqlite3');
|
||||
// Bare :memory: db — nothing in these flows queries it (the toggles are
|
||||
// stubbed on the AuthService instance below), the container just needs a
|
||||
// connection to construct the auth/permissions/atlas providers.
|
||||
// Bare :memory: db — nothing in these flows queries it (the db-touching
|
||||
// OidcService methods and the toggles are stubbed on the container
|
||||
// instances below), the container just needs a connection to construct the
|
||||
// oidc/auth/permissions/atlas providers.
|
||||
return { db: new Database(':memory:') };
|
||||
});
|
||||
vi.mock('../../src/db/database', () => ({
|
||||
@@ -38,16 +41,8 @@ vi.mock('../../src/nest/audit/audit-log.logger', () => ({ LOG_LEVEL: 'error', lo
|
||||
|
||||
const toggles = { oidc_login: true };
|
||||
|
||||
const { oidcSvc } = vi.hoisted(() => ({
|
||||
oidcSvc: {
|
||||
getOidcConfig: vi.fn(), discover: vi.fn(), createState: vi.fn(), consumeState: vi.fn(), createAuthCode: vi.fn(),
|
||||
consumeAuthCode: vi.fn(), exchangeCodeForToken: vi.fn(), getUserInfo: vi.fn(), verifyIdToken: vi.fn(),
|
||||
findOrCreateUser: vi.fn(), touchLastLogin: vi.fn(), generateToken: vi.fn(), frontendUrl: (p: string) => 'https://app' + p,
|
||||
},
|
||||
}));
|
||||
vi.mock('../../src/services/oidcService', () => oidcSvc);
|
||||
|
||||
import { OidcModule } from '../../src/nest/oidc/oidc.module';
|
||||
import { OidcService } from '../../src/nest/oidc/oidc.service';
|
||||
import { DatabaseModule } from '../../src/nest/database/database.module';
|
||||
import { AuthService } from '../../src/nest/auth/auth.service';
|
||||
import { TrekExceptionFilter } from '../../src/nest/common/trek-exception.filter';
|
||||
@@ -55,6 +50,7 @@ import { TrekExceptionFilter } from '../../src/nest/common/trek-exception.filter
|
||||
describe('OIDC e2e (real cookie service)', () => {
|
||||
let server: Server;
|
||||
let app: Awaited<ReturnType<typeof build>>;
|
||||
let consumeAuthCode: MockInstance;
|
||||
|
||||
async function build() {
|
||||
const moduleRef = await Test.createTestingModule({ imports: [DatabaseModule, OidcModule] }).compile();
|
||||
@@ -70,10 +66,11 @@ describe('OIDC e2e (real cookie service)', () => {
|
||||
app = await build();
|
||||
server = app.getHttpServer();
|
||||
vi.spyOn(app.get(AuthService), 'resolveAuthToggles').mockImplementation(() => toggles as never);
|
||||
oidcSvc.getOidcConfig.mockReturnValue({ issuer: 'https://idp', clientId: 'c', clientSecret: 's', discoveryUrl: null });
|
||||
oidcSvc.discover.mockResolvedValue({ authorization_endpoint: 'https://idp/auth', userinfo_endpoint: 'https://idp/ui', issuer: 'https://idp' });
|
||||
oidcSvc.createState.mockReturnValue({ state: 'st', codeChallenge: 'cc' });
|
||||
oidcSvc.consumeAuthCode.mockReturnValue({ token: 'jwt.value' });
|
||||
const oidc = app.get(OidcService);
|
||||
vi.spyOn(oidc, 'getOidcConfig').mockReturnValue({ issuer: 'https://idp', clientId: 'c', clientSecret: 's', displayName: 'SSO', discoveryUrl: null });
|
||||
vi.spyOn(oidc, 'discover').mockResolvedValue({ authorization_endpoint: 'https://idp/auth', userinfo_endpoint: 'https://idp/ui', issuer: 'https://idp' } as never);
|
||||
vi.spyOn(oidc, 'createState').mockReturnValue({ state: 'st', codeChallenge: 'cc' });
|
||||
consumeAuthCode = vi.spyOn(oidc, 'consumeAuthCode').mockReturnValue({ token: 'jwt.value' });
|
||||
});
|
||||
|
||||
beforeEach(() => { toggles.oidc_login = true; });
|
||||
@@ -102,7 +99,7 @@ describe('OIDC e2e (real cookie service)', () => {
|
||||
});
|
||||
|
||||
it('GET /exchange sets the httpOnly trek_session cookie + returns the token', async () => {
|
||||
oidcSvc.consumeAuthCode.mockReturnValue({ token: 'jwt.value' });
|
||||
consumeAuthCode.mockReturnValue({ token: 'jwt.value' });
|
||||
const res = await request(server).get('/api/auth/oidc/exchange').query({ code: 'good' });
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ token: 'jwt.value' });
|
||||
|
||||
Reference in New Issue
Block a user