mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 00:06:42 +02:00
first auth impl
This commit is contained in:
parent
8f7c63950c
commit
2a002bf6be
79 changed files with 13355 additions and 6076 deletions
|
|
@ -1,53 +1,284 @@
|
|||
import { Controller, Post, Body, UseGuards, Req, Ip, Headers } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { AuthService } from './auth.service';
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Get,
|
||||
Delete,
|
||||
Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
Headers,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { BetterAuthService } from './services/better-auth.service';
|
||||
import { RegisterDto } from './dto/register.dto';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { RefreshTokenDto } from './dto/refresh-token.dto';
|
||||
import { RegisterB2BDto } from './dto/register-b2b.dto';
|
||||
import { InviteEmployeeDto } from './dto/invite-employee.dto';
|
||||
import { AcceptInvitationDto } from './dto/accept-invitation.dto';
|
||||
import { SetActiveOrganizationDto } from './dto/set-active-organization.dto';
|
||||
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
||||
import { CurrentUser, CurrentUserData } from '../common/decorators/current-user.decorator';
|
||||
|
||||
/**
|
||||
* Auth Controller
|
||||
*
|
||||
* Handles authentication and organization management endpoints.
|
||||
*
|
||||
* B2C Endpoints:
|
||||
* - POST /auth/register - Register individual user
|
||||
* - POST /auth/login - Sign in with email/password
|
||||
* - POST /auth/logout - Sign out
|
||||
* - POST /auth/refresh - Refresh access token
|
||||
* - GET /auth/session - Get current session
|
||||
*
|
||||
* B2B Endpoints:
|
||||
* - POST /auth/register/b2b - Register organization with owner
|
||||
* - GET /auth/organizations - List user's organizations
|
||||
* - GET /auth/organizations/:id - Get organization details
|
||||
* - POST /auth/organizations/:id/invite - Invite employee
|
||||
* - POST /auth/organizations/accept-invitation - Accept invitation
|
||||
* - DELETE /auth/organizations/:id/members/:memberId - Remove member
|
||||
* - POST /auth/organizations/set-active - Switch active organization
|
||||
*/
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
constructor(private readonly betterAuthService: BetterAuthService) {}
|
||||
|
||||
// =========================================================================
|
||||
// B2C Authentication Endpoints
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Register a new B2C user (individual)
|
||||
*
|
||||
* Creates a user account and initializes their credit balance.
|
||||
*/
|
||||
@Post('register')
|
||||
async register(
|
||||
@Body() registerDto: RegisterDto,
|
||||
@Ip() ipAddress: string,
|
||||
@Headers('user-agent') userAgent: string
|
||||
) {
|
||||
return this.authService.register(registerDto, ipAddress, userAgent);
|
||||
async register(@Body() registerDto: RegisterDto) {
|
||||
return this.betterAuthService.registerB2C({
|
||||
email: registerDto.email,
|
||||
password: registerDto.password,
|
||||
name: registerDto.name || '',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign in with email and password
|
||||
*
|
||||
* Returns user data and JWT token.
|
||||
*/
|
||||
@Post('login')
|
||||
async login(
|
||||
@Body() loginDto: LoginDto,
|
||||
@Ip() ipAddress: string,
|
||||
@Headers('user-agent') userAgent: string
|
||||
) {
|
||||
return this.authService.login(loginDto, ipAddress, userAgent);
|
||||
}
|
||||
|
||||
@Post('refresh')
|
||||
async refresh(
|
||||
@Body() refreshTokenDto: RefreshTokenDto,
|
||||
@Ip() ipAddress: string,
|
||||
@Headers('user-agent') userAgent: string
|
||||
) {
|
||||
return this.authService.refreshToken(refreshTokenDto.refreshToken, ipAddress, userAgent);
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async login(@Body() loginDto: LoginDto) {
|
||||
return this.betterAuthService.signIn({
|
||||
email: loginDto.email,
|
||||
password: loginDto.password,
|
||||
deviceId: loginDto.deviceId,
|
||||
deviceName: loginDto.deviceName,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign out current user
|
||||
*
|
||||
* Invalidates the user's session.
|
||||
*/
|
||||
@Post('logout')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async logout(@Req() req: Request & { user: CurrentUserData }) {
|
||||
// Extract sessionId from JWT (would need to be added to the CurrentUserData interface)
|
||||
// For now, we'll use a placeholder
|
||||
return this.authService.logout('session-id');
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async logout(@Headers('authorization') authorization: string) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.signOut(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh access token
|
||||
*
|
||||
* Uses refresh token rotation to issue new access and refresh tokens.
|
||||
*/
|
||||
@Post('refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async refresh(@Body() refreshTokenDto: RefreshTokenDto) {
|
||||
return this.betterAuthService.refreshToken(refreshTokenDto.refreshToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current session
|
||||
*
|
||||
* Returns the current user and session data.
|
||||
*/
|
||||
@Get('session')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async getSession(@Headers('authorization') authorization: string) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.getSession(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a token
|
||||
*
|
||||
* Checks if a token is valid and returns the payload.
|
||||
*/
|
||||
@Post('validate')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async validate(@Body() body: { token: string }) {
|
||||
return this.authService.validateToken(body.token);
|
||||
return this.betterAuthService.validateToken(body.token);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// B2B Registration
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Register a new B2B organization
|
||||
*
|
||||
* Creates an organization with the registering user as owner.
|
||||
* Also creates organization credit balance.
|
||||
*/
|
||||
@Post('register/b2b')
|
||||
async registerB2B(@Body() registerDto: RegisterB2BDto) {
|
||||
return this.betterAuthService.registerB2B(registerDto);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Organization Management Endpoints
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* List user's organizations
|
||||
*
|
||||
* Returns all organizations the current user is a member of.
|
||||
*/
|
||||
@Get('organizations')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async listOrganizations(@Headers('authorization') authorization: string) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.listOrganizations(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get organization details
|
||||
*
|
||||
* Returns full organization info including members.
|
||||
*/
|
||||
@Get('organizations/:id')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async getOrganization(
|
||||
@Param('id') organizationId: string,
|
||||
@Headers('authorization') authorization: string
|
||||
) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.getOrganization(organizationId, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get organization members
|
||||
*
|
||||
* Returns all members of an organization with their roles.
|
||||
*/
|
||||
@Get('organizations/:id/members')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async getOrganizationMembers(@Param('id') organizationId: string) {
|
||||
return this.betterAuthService.getOrganizationMembers(organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invite employee to organization
|
||||
*
|
||||
* Sends an invitation email to join the organization.
|
||||
* Requires owner or admin role.
|
||||
*/
|
||||
@Post('organizations/:id/invite')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async inviteEmployee(
|
||||
@Param('id') organizationId: string,
|
||||
@Body() inviteDto: InviteEmployeeDto,
|
||||
@Headers('authorization') authorization: string
|
||||
) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.inviteEmployee({
|
||||
organizationId,
|
||||
employeeEmail: inviteDto.employeeEmail,
|
||||
role: inviteDto.role,
|
||||
inviterToken: token,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept organization invitation
|
||||
*
|
||||
* Accepts a pending invitation and adds user to organization.
|
||||
*/
|
||||
@Post('organizations/accept-invitation')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async acceptInvitation(
|
||||
@Body() acceptDto: AcceptInvitationDto,
|
||||
@Headers('authorization') authorization: string
|
||||
) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.acceptInvitation({
|
||||
invitationId: acceptDto.invitationId,
|
||||
userToken: token,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove member from organization
|
||||
*
|
||||
* Removes a member from the organization.
|
||||
* Requires owner or admin role.
|
||||
*/
|
||||
@Delete('organizations/:id/members/:memberId')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async removeMember(
|
||||
@Param('id') organizationId: string,
|
||||
@Param('memberId') memberId: string,
|
||||
@Headers('authorization') authorization: string
|
||||
) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.removeMember({
|
||||
organizationId,
|
||||
memberId,
|
||||
removerToken: token,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active organization
|
||||
*
|
||||
* Switches the user's active organization context.
|
||||
* Affects JWT claims and credit balance.
|
||||
*/
|
||||
@Post('organizations/set-active')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async setActiveOrganization(
|
||||
@Body() setActiveDto: SetActiveOrganizationDto,
|
||||
@Headers('authorization') authorization: string
|
||||
) {
|
||||
const token = this.extractToken(authorization);
|
||||
return this.betterAuthService.setActiveOrganization({
|
||||
organizationId: setActiveDto.organizationId,
|
||||
userToken: token,
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Helper Methods
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Extract token from Authorization header
|
||||
*/
|
||||
private extractToken(authorization: string): string {
|
||||
if (!authorization) {
|
||||
return '';
|
||||
}
|
||||
// Handle both "Bearer token" and raw token formats
|
||||
if (authorization.startsWith('Bearer ')) {
|
||||
return authorization.substring(7);
|
||||
}
|
||||
return authorization;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue