refactor(auth): add auth controller, but almost certain that implementation is shaky at best. Will rework structure after further learning NestJS architecture
This commit is contained in:
parent
ab8587e769
commit
5e644b6a6c
29
package-lock.json
generated
29
package-lock.json
generated
|
|
@ -2438,13 +2438,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@nestjs/platform-express": {
|
||||
"version": "11.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz",
|
||||
"integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==",
|
||||
"version": "11.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz",
|
||||
"integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
"express": "5.1.0",
|
||||
"multer": "2.0.1",
|
||||
"multer": "2.0.2",
|
||||
"path-to-regexp": "8.2.0",
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
|
|
@ -4628,7 +4629,8 @@
|
|||
"node_modules/append-field": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/arch": {
|
||||
"version": "3.0.0",
|
||||
|
|
@ -5428,6 +5430,7 @@
|
|||
"engines": [
|
||||
"node >= 6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
|
|
@ -8557,6 +8560,7 @@
|
|||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
|
|
@ -8570,9 +8574,10 @@
|
|||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz",
|
||||
"integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
|
||||
"integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.6.0",
|
||||
|
|
@ -8590,6 +8595,7 @@
|
|||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -8598,6 +8604,7 @@
|
|||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
|
@ -8606,6 +8613,7 @@
|
|||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
|
|
@ -8617,6 +8625,7 @@
|
|||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
|
|
@ -10817,7 +10826,8 @@
|
|||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
|
|
@ -11355,6 +11365,7 @@
|
|||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { AuthentikAuthService } from './services/authentik-auth.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -10,5 +11,8 @@ import { PassportModule } from '@nestjs/passport';
|
|||
signOptions: { expiresIn: '1h' },
|
||||
}),
|
||||
],
|
||||
providers: [ AuthentikAuthService, ],
|
||||
exports: [ AuthentikAuthService ],
|
||||
|
||||
})
|
||||
export class AuthenticationModule {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
@Post('/login')
|
||||
login() {
|
||||
// Passport handles redirection to Authentik
|
||||
}
|
||||
|
||||
@Get('callback')
|
||||
// @UseGuards(AuthGuard('openidconnect'))
|
||||
callback(@Req() req: Request) {
|
||||
console.log('✅ Auth complete, here is req.user:');
|
||||
console.dir(req.user, { depth: null });
|
||||
|
||||
return req.user;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UsersService } from 'src/modules/users-management/services/users.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthentikAuthService {
|
||||
constructor(private usersService: UsersService) {}
|
||||
|
||||
async validateUser(user_email: string): Promise<any> {
|
||||
const user = await this.usersService.findOneByEmail(user_email);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ export class AuthService {
|
|||
) {}
|
||||
|
||||
async validateUser(user_id: UUID): Promise<any> {
|
||||
const user = await this.usersService.findOne(user_id);
|
||||
const user = await this.usersService.findOne( user_id );
|
||||
if (user) {
|
||||
return user;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,48 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Strategy as OIDCStrategy } from 'passport-openidconnect';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy } from 'passport-openidconnect';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { AuthentikAuthService } from '../services/authentik-auth.service';
|
||||
|
||||
export interface AuthentikPayload {
|
||||
//TODO: check Authentik payload contents
|
||||
iss: string; // Issuer
|
||||
sub: string; // Subject (user ID)
|
||||
aud: string; // Audience (client ID)
|
||||
exp: number; // Expiration time (Unix)
|
||||
iat: number; // Issued at time (Unix)
|
||||
auth_time: number; // Time of authentication (Unix)
|
||||
acr?: string; // Auth Context Class Reference
|
||||
amr?: string[]; // Auth Method References (e.g., ['pwd'])
|
||||
email: string;
|
||||
email_verified: boolean;
|
||||
name?: string;
|
||||
given_name?: string;
|
||||
preferred_username?: string;
|
||||
nickname?: string;
|
||||
groups?: string[];
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AuthentikStrategy extends PassportStrategy(Strategy) {
|
||||
constructor() {
|
||||
super({
|
||||
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER MISSING",
|
||||
clientID: process.env.AUTHENTIK_CLIENT_ID || "CLIENT ID MISSING",
|
||||
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || "CLIENT SECRET MISSING",
|
||||
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || "CALLBACK URL MISSING",
|
||||
authorizationURL: `${process.env.AUTHENTIK_ISSUER}/authorize/`,
|
||||
tokenURL: `${process.env.AUTHENTIK_ISSUER}/token/`,
|
||||
userInfoURL: `${process.env.AUTHENTIK_ISSUER}/userinfo/`,
|
||||
scope: [],
|
||||
})
|
||||
}
|
||||
export class AuthentikStrategy extends PassportStrategy(OIDCStrategy) {
|
||||
constructor(private authentikAuthService: AuthentikAuthService) {
|
||||
super({
|
||||
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING",
|
||||
clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING',
|
||||
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING',
|
||||
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING',
|
||||
authorizationURL: `${process.env.AUTHENTIK_ISSUER}/authorize/`,
|
||||
tokenURL: `${process.env.AUTHENTIK_ISSUER}/token/`,
|
||||
userInfoURL: `${process.env.AUTHENTIK_ISSUER}/userinfo/`,
|
||||
scope: ['openid', 'email', 'profile'],
|
||||
});
|
||||
}
|
||||
|
||||
async validate(): Promise<any> {
|
||||
// Check what kind of payload we're actually handling here
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async validate(payload: AuthentikPayload): Promise<any> {
|
||||
// if ( !payload.email_verified ) { throw new UnauthorizedException(); }
|
||||
|
||||
const user = await this.authentikAuthService.validateUser(payload.email);
|
||||
if ( !user ) { throw new UnauthorizedException(); }
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export abstract class AbstractUserService {
|
|||
return this.prisma.users.findMany();
|
||||
}
|
||||
|
||||
async findOne(id: string): Promise<Users> {
|
||||
async findOne( id: string ): Promise<Users> {
|
||||
const user = await this.prisma.users.findUnique({ where: { id } });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`User #${id} not found`);
|
||||
|
|
@ -18,6 +18,14 @@ export abstract class AbstractUserService {
|
|||
return user;
|
||||
}
|
||||
|
||||
async findOneByEmail( email: string ): Promise<Users> {
|
||||
const user = await this.prisma.users.findUnique({ where: { email } });
|
||||
if (!user) {
|
||||
throw new NotFoundException(`No user with email #${email} exists`);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<Users> {
|
||||
await this.findOne(id);
|
||||
return this.prisma.users.delete({ where: { id } });
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user