feat(identity): redesign identity module and introduce RBAC foundation
- Redesign the Identity module with a richer domain model. - Extend the User entity to support username, Authentik integration, activity tracking, and storage information. - Add Role and Permission domain models with many-to-many relationships. - Implement RBAC foundation using UserRole, RolePermission, and UserPermission mappings. - Add user storage quota and usage fields with default values. - Introduce Authentik identifiers and synchronization metadata. - Refactor user domain logic for role and permission management. - Update Prisma schema to support the new identity architecture. - Improve JWT authentication and permission guard integration. - Update repositories, handlers, controllers, mappers, DTOs, and Swagger configuration. - Refresh environment configuration and project dependencies.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
|
||||
import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { AuthService } from './auth.service';
|
||||
import { Response, Request } from 'express';
|
||||
|
||||
@ApiTags('Auth')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@Get('login')
|
||||
@ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' })
|
||||
async login(@Query('returnTo') returnTo: string | undefined, @Res() res: Response) {
|
||||
const redirect = await this.authService.createAuthorizationRedirect(returnTo);
|
||||
return res.redirect(302, redirect);
|
||||
}
|
||||
|
||||
@Get('callback')
|
||||
@ApiOperation({ summary: 'OIDC callback endpoint' })
|
||||
async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) {
|
||||
const result = await this.authService.handleCallback(code, state);
|
||||
|
||||
// set cookies
|
||||
const cookieOptions: any = {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
};
|
||||
|
||||
// access token cookie (internal JWT)
|
||||
res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 });
|
||||
|
||||
// refresh token cookie
|
||||
res.cookie('raylab_refresh', result.refreshToken, { ...cookieOptions, maxAge: result.refreshTtl * 1000 });
|
||||
|
||||
return res.redirect(302, result.returnTo || '/');
|
||||
}
|
||||
|
||||
@Post('logout')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Logout (invalidate internal session and redirect to identity provider logout)' })
|
||||
async logout(@Req() req: Request, @Res() res: Response) {
|
||||
const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken;
|
||||
const redirect = await this.authService.logout(refreshToken);
|
||||
return res.redirect(302, redirect);
|
||||
}
|
||||
|
||||
@Post('refresh')
|
||||
@ApiOperation({ summary: 'Refresh internal JWT using internal refresh token' })
|
||||
async refresh(@Req() req: Request) {
|
||||
const refreshToken = req.cookies?.raylab_refresh || req.body?.refreshToken;
|
||||
const result = await this.authService.refresh(refreshToken);
|
||||
return { success: true, data: result };
|
||||
}
|
||||
|
||||
@Get('me')
|
||||
@ApiOperation({ summary: 'Get current user from internal JWT (cookie or Authorization header)' })
|
||||
async me(@Req() req: Request) {
|
||||
const token = (req.cookies?.raylab_jwt) || (req.headers.authorization && (req.headers.authorization as string).replace(/^Bearer\s+/i, ''));
|
||||
const user = await this.authService.me(token);
|
||||
return { success: true, data: user };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user