initial
This commit is contained in:
27
apps/api/src/auth/auth.controller.ts
Normal file
27
apps/api/src/auth/auth.controller.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
import { RequestUser } from '../common/request-user';
|
||||
import { AuthService } from './auth.service';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly auth: AuthService) {}
|
||||
|
||||
@Post('login')
|
||||
login(@Body() dto: LoginDto, @Req() request: Request) {
|
||||
return this.auth.login(
|
||||
dto.username,
|
||||
dto.password,
|
||||
request.ip,
|
||||
request.headers['user-agent'],
|
||||
);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('me')
|
||||
me(@Req() request: Request & { user: RequestUser }) {
|
||||
return { user: request.user };
|
||||
}
|
||||
}
|
||||
26
apps/api/src/auth/auth.module.ts
Normal file
26
apps/api/src/auth/auth.module.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { AuditModule } from '../audit/audit.module';
|
||||
import { LldapModule } from '../lldap/lldap.module';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtAuthGuard } from './jwt-auth.guard';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
JwtModule.registerAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.getOrThrow<string>('JWT_SECRET'),
|
||||
signOptions: { expiresIn: '8h' },
|
||||
}),
|
||||
}),
|
||||
LldapModule,
|
||||
AuditModule,
|
||||
],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, JwtAuthGuard],
|
||||
exports: [AuthService, JwtAuthGuard, JwtModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
27
apps/api/src/auth/auth.service.ts
Normal file
27
apps/api/src/auth/auth.service.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { AuditService } from '../audit/audit.service';
|
||||
import { LdapAuthService } from '../lldap/ldap-auth.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly ldapAuth: LdapAuthService,
|
||||
private readonly jwt: JwtService,
|
||||
private readonly audit: AuditService,
|
||||
) {}
|
||||
|
||||
async login(username: string, password: string, ipAddress?: string, userAgent?: string) {
|
||||
const valid = await this.ldapAuth.verifyPassword(username, password);
|
||||
if (!valid) {
|
||||
await this.audit.record({ type: 'auth.login_failed', username, ipAddress, userAgent });
|
||||
throw new UnauthorizedException('Ungueltige Zugangsdaten.');
|
||||
}
|
||||
|
||||
await this.audit.record({ type: 'auth.login_success', username, ipAddress, userAgent });
|
||||
return {
|
||||
accessToken: await this.jwt.signAsync({ sub: username, username }),
|
||||
user: { username },
|
||||
};
|
||||
}
|
||||
}
|
||||
11
apps/api/src/auth/dto/login.dto.ts
Normal file
11
apps/api/src/auth/dto/login.dto.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { IsString, Length } from 'class-validator';
|
||||
|
||||
export class LoginDto {
|
||||
@IsString()
|
||||
@Length(1, 128)
|
||||
username!: string;
|
||||
|
||||
@IsString()
|
||||
@Length(1, 256)
|
||||
password!: string;
|
||||
}
|
||||
34
apps/api/src/auth/jwt-auth.guard.ts
Normal file
34
apps/api/src/auth/jwt-auth.guard.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Request } from 'express';
|
||||
import { RequestUser } from '../common/request-user';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard implements CanActivate {
|
||||
constructor(private readonly jwt: JwtService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request & { user?: RequestUser }>();
|
||||
const token = this.extractBearerToken(request);
|
||||
if (!token) {
|
||||
throw new UnauthorizedException('Nicht angemeldet.');
|
||||
}
|
||||
|
||||
try {
|
||||
request.user = await this.jwt.verifyAsync<RequestUser>(token);
|
||||
return true;
|
||||
} catch {
|
||||
throw new UnauthorizedException('Session ist ungueltig oder abgelaufen.');
|
||||
}
|
||||
}
|
||||
|
||||
private extractBearerToken(request: Request): string | undefined {
|
||||
const header = request.headers.authorization;
|
||||
if (!header) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [type, token] = header.split(' ');
|
||||
return type?.toLowerCase() === 'bearer' ? token : undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user