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": {
|
"node_modules/@nestjs/platform-express": {
|
||||||
"version": "11.1.3",
|
"version": "11.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.5.tgz",
|
||||||
"integrity": "sha512-hEDNMlaPiBO72fxxX/CuRQL3MEhKRc/sIYGVoXjrnw6hTxZdezvvM6A95UaLsYknfmcZZa/CdG1SMBZOu9agHQ==",
|
"integrity": "sha512-OsoiUBY9Shs5IG3uvDIt9/IDfY5OlvWBESuB/K4Eun8xILw1EK5d5qMfC3d2sIJ+kA3l+kBR1d/RuzH7VprLIg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"express": "5.1.0",
|
"express": "5.1.0",
|
||||||
"multer": "2.0.1",
|
"multer": "2.0.2",
|
||||||
"path-to-regexp": "8.2.0",
|
"path-to-regexp": "8.2.0",
|
||||||
"tslib": "2.8.1"
|
"tslib": "2.8.1"
|
||||||
},
|
},
|
||||||
|
|
@ -4628,7 +4629,8 @@
|
||||||
"node_modules/append-field": {
|
"node_modules/append-field": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
"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": {
|
"node_modules/arch": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
|
@ -5428,6 +5430,7 @@
|
||||||
"engines": [
|
"engines": [
|
||||||
"node >= 6.0"
|
"node >= 6.0"
|
||||||
],
|
],
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer-from": "^1.0.0",
|
"buffer-from": "^1.0.0",
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
|
|
@ -8557,6 +8560,7 @@
|
||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
|
|
@ -8570,9 +8574,10 @@
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
},
|
},
|
||||||
"node_modules/multer": {
|
"node_modules/multer": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz",
|
||||||
"integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==",
|
"integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"append-field": "^1.0.0",
|
"append-field": "^1.0.0",
|
||||||
"busboy": "^1.6.0",
|
"busboy": "^1.6.0",
|
||||||
|
|
@ -8590,6 +8595,7 @@
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
|
|
@ -8598,6 +8604,7 @@
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
|
|
@ -8606,6 +8613,7 @@
|
||||||
"version": "2.1.35",
|
"version": "2.1.35",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mime-db": "1.52.0"
|
"mime-db": "1.52.0"
|
||||||
},
|
},
|
||||||
|
|
@ -8617,6 +8625,7 @@
|
||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"media-typer": "0.3.0",
|
"media-typer": "0.3.0",
|
||||||
"mime-types": "~2.1.24"
|
"mime-types": "~2.1.24"
|
||||||
|
|
@ -10817,7 +10826,8 @@
|
||||||
"node_modules/typedarray": {
|
"node_modules/typedarray": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"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": {
|
"node_modules/typescript": {
|
||||||
"version": "5.8.3",
|
"version": "5.8.3",
|
||||||
|
|
@ -11355,6 +11365,7 @@
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { PassportModule } from '@nestjs/passport';
|
import { PassportModule } from '@nestjs/passport';
|
||||||
|
import { AuthentikAuthService } from './services/authentik-auth.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -10,5 +11,8 @@ import { PassportModule } from '@nestjs/passport';
|
||||||
signOptions: { expiresIn: '1h' },
|
signOptions: { expiresIn: '1h' },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
providers: [ AuthentikAuthService, ],
|
||||||
|
exports: [ AuthentikAuthService ],
|
||||||
|
|
||||||
})
|
})
|
||||||
export class AuthenticationModule {}
|
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> {
|
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) {
|
if (user) {
|
||||||
return 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 { 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 {
|
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()
|
@Injectable()
|
||||||
export class AuthentikStrategy extends PassportStrategy(Strategy) {
|
export class AuthentikStrategy extends PassportStrategy(OIDCStrategy) {
|
||||||
constructor() {
|
constructor(private authentikAuthService: AuthentikAuthService) {
|
||||||
super({
|
super({
|
||||||
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER MISSING",
|
issuer: process.env.AUTHENTIK_ISSUER || "ISSUER_MISSING",
|
||||||
clientID: process.env.AUTHENTIK_CLIENT_ID || "CLIENT ID MISSING",
|
clientID: process.env.AUTHENTIK_CLIENT_ID || 'CLIENT_ID_MISSING',
|
||||||
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || "CLIENT SECRET MISSING",
|
clientSecret: process.env.AUTHENTIK_CLIENT_SECRET || 'CLIENT_SECRET_MISSING',
|
||||||
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || "CALLBACK URL MISSING",
|
callbackURL: process.env.AUTHENTIK_CALLBACK_URL || 'CALLBACK_URL_MISSING',
|
||||||
authorizationURL: `${process.env.AUTHENTIK_ISSUER}/authorize/`,
|
authorizationURL: `${process.env.AUTHENTIK_ISSUER}/authorize/`,
|
||||||
tokenURL: `${process.env.AUTHENTIK_ISSUER}/token/`,
|
tokenURL: `${process.env.AUTHENTIK_ISSUER}/token/`,
|
||||||
userInfoURL: `${process.env.AUTHENTIK_ISSUER}/userinfo/`,
|
userInfoURL: `${process.env.AUTHENTIK_ISSUER}/userinfo/`,
|
||||||
scope: [],
|
scope: ['openid', 'email', 'profile'],
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async validate(): Promise<any> {
|
async validate(payload: AuthentikPayload): Promise<any> {
|
||||||
// Check what kind of payload we're actually handling here
|
// if ( !payload.email_verified ) { throw new UnauthorizedException(); }
|
||||||
return null;
|
|
||||||
}
|
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();
|
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 } });
|
const user = await this.prisma.users.findUnique({ where: { id } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new NotFoundException(`User #${id} not found`);
|
throw new NotFoundException(`User #${id} not found`);
|
||||||
|
|
@ -18,6 +18,14 @@ export abstract class AbstractUserService {
|
||||||
return user;
|
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> {
|
async remove(id: string): Promise<Users> {
|
||||||
await this.findOne(id);
|
await this.findOne(id);
|
||||||
return this.prisma.users.delete({ where: { id } });
|
return this.prisma.users.delete({ where: { id } });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user