feat(#4): Implement Authentik OIDC authentication with BetterAuth

- Integrated BetterAuth library for modern authentication
- Added Session, Account, and Verification database tables
- Created complete auth module with service, controller, guards, and decorators
- Implemented shared authentication types in @mosaic/shared package
- Added comprehensive test coverage (26 tests passing)
- Documented type sharing strategy for monorepo
- Updated environment configuration with OIDC and JWT settings

Key architectural decisions:
- BetterAuth over Passport.js for better TypeScript support
- Separation of User (DB entity) vs AuthUser (client-safe subset)
- Shared types package to prevent FE/BE drift
- Factory pattern for auth config to use shared Prisma instance

Ready for frontend integration (Issue #6).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Fixes #4
This commit is contained in:
Jason Woltje
2026-01-28 17:26:34 -06:00
parent 139a16648d
commit 6a038d093b
22 changed files with 2616 additions and 7 deletions

View File

@@ -0,0 +1,42 @@
import { Controller, All, Req, Get, UseGuards } from "@nestjs/common";
import type { AuthUser } from "@mosaic/shared";
import { AuthService } from "./auth.service";
import { AuthGuard } from "./guards/auth.guard";
import { CurrentUser } from "./decorators/current-user.decorator";
@Controller("auth")
export class AuthController {
constructor(private readonly authService: AuthService) {}
/**
* Handle all BetterAuth routes
* BetterAuth provides built-in handlers for:
* - /auth/sign-in
* - /auth/sign-up
* - /auth/sign-out
* - /auth/callback/authentik
* - /auth/session
* etc.
*
* Note: BetterAuth expects a Fetch API-compatible Request object.
* NestJS converts the incoming Express request to be compatible at runtime.
*/
@All("*")
async handleAuth(@Req() req: Request) {
const auth = this.authService.getAuth();
return auth.handler(req);
}
/**
* Get current user profile (protected route example)
*/
@Get("profile")
@UseGuards(AuthGuard)
getProfile(@CurrentUser() user: AuthUser) {
return {
id: user.id,
email: user.email,
name: user.name,
};
}
}