diff --git a/nest-cli.json b/nest-cli.json index a8170d1..8914b80 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -3,6 +3,7 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { - "deleteOutDir": true + "deleteOutDir": true, + "plugins": ["@nestjs/swagger"] } } diff --git a/src/app.module.ts b/src/app.module.ts index ef10bde..2cd6209 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -19,6 +19,8 @@ import { BullModule } from '@nestjs/bullmq'; import { BullConfigService } from './redis/service/bull-config.service'; import { RedisService } from './redis/service/redis.service'; import { OrganizationModule } from './organization/organization.module'; +import { ClientModule } from './client/client.module'; +import { ViewModule } from './view/view.module'; @Module({ imports: [ @@ -68,6 +70,8 @@ import { OrganizationModule } from './organization/organization.module'; UserModule, AuthModule, OrganizationModule, + ClientModule, + ViewModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index ad8642c..8582673 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Get, Post, Query, Render, Req, Res, UseGuards } from '@nestjs/common'; -import { ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger'; +import { ApiExcludeEndpoint, ApiResponse, ApiTags } from '@nestjs/swagger'; import { AuthService } from '../services/auth.service'; import { ForgotPasswordDto } from '../dto/forgotPassword.dto'; @@ -8,6 +8,7 @@ import { LoginUserDto } from '../dto/loginUser.dto'; import { User } from '../decorators/user.decorator'; import { LoginGuard } from '../guard/login.guard'; import { Response, Request } from 'express'; +import { LoginResponse } from '../dto/loginResponse.dto'; // TODO: Implement RateLimit @Controller('auth') @@ -20,7 +21,7 @@ export class AuthController { @Body() body: LoginUserDto, @Res({ passthrough: true }) res: Response, @Req() req: Request, - ): Promise { + ): Promise { const userId = await this.authService.login(body.username, body.password); const interaction = await this.authService.checkInteractionStatus(req, res); @@ -49,50 +50,6 @@ export class AuthController { // ==== Render pages ==== // - @Get('login') - @UseGuards(LoginGuard) - @Render('auth/login') - @ApiExcludeEndpoint() - public async getHello(): Promise { - return { - forgot_password: 'forgot-password', - register: 'register', - login_url: '/auth/login', - //background_image: 'https://waterwolf.club/static/img/portal/portal7.jpg', - }; - } - - @Get('login/totp') - @UseGuards(LoginGuard) - @Render('auth/login-totp') - @ApiExcludeEndpoint() - public async getLoginTotp(): Promise { - return { - login: 'login', - methods: ['authenticator', 'email'], - }; - } - - @Get('register') - @UseGuards(LoginGuard) - @Render('auth/register') - @ApiExcludeEndpoint() - public async getRegister(): Promise { - return { - login: 'login', - }; - } - - @Get('forgot-password') - @UseGuards(LoginGuard) - @Render('auth/forgot-password') - @ApiExcludeEndpoint() - public async getForgotPassword(): Promise { - return { - login: 'login', - }; - } - @Get('verify-email') @UseGuards(LoginGuard) @ApiExcludeEndpoint() @@ -121,10 +78,4 @@ export class AuthController { response.redirect('/auth/login'); } - - @Get('auth-test') - @ApiExcludeEndpoint() - public async getAuthTest(@User() user: any): Promise { - return user; - } } diff --git a/src/auth/controllers/interaction.controller.ts b/src/auth/controllers/interaction.controller.ts index df14fbc..6e19304 100644 --- a/src/auth/controllers/interaction.controller.ts +++ b/src/auth/controllers/interaction.controller.ts @@ -38,7 +38,6 @@ export class InteractionController { prompt: any; params: InteractionParams; } = await this.interactionService.get(req, res).catch((err: unknown) => { - console.log(err); //TODO: Handle error in a nice way. throw new BadRequestException("Couldn't get interaction details", { cause: err }); }); @@ -46,24 +45,14 @@ export class InteractionController { const { uid, prompt, params } = details; switch (prompt.name) { case 'login': { - //Set a login redirect cookie to redirect the user back to the interaction page after login. - // Take the interaction cookies and write them to /auth/login - // _interaction, _interaction.sig, interactionId - if (!uid) { throw new InternalServerErrorException('No uid found'); } - const cookies = req.cookies; - - // Log in console when the packet expires - - console.log(cookies); - return await this.interactionService.interactionRedirectToLoginPage( res, uid, - cookies['_interaction.sig'], + req.cookies['_interaction.sig'], ); } diff --git a/src/auth/dto/loginResponse.dto.ts b/src/auth/dto/loginResponse.dto.ts new file mode 100644 index 0000000..5cb7049 --- /dev/null +++ b/src/auth/dto/loginResponse.dto.ts @@ -0,0 +1,14 @@ +export class LoginResponse { + status: LoginStatusResponse; + + message: string; + + sessionId?: string; + + redirectUrl?: string; +} + +export enum LoginStatusResponse { + SUCCESS = 'success', + REDIRECT = 'redirect', +} diff --git a/src/auth/guard/login.guard.ts b/src/auth/guard/login.guard.ts index 11f2a8e..2187bac 100644 --- a/src/auth/guard/login.guard.ts +++ b/src/auth/guard/login.guard.ts @@ -10,6 +10,7 @@ export class LoginGuard implements CanActivate { const authType = this.clsService.get('authType'); const response = context.switchToHttp().getResponse() as Response; + console.log('test'); if (authType === 'session') { response.redirect('/auth/auth-test'); return false; diff --git a/src/auth/oidc/helper/scopes.helper.ts b/src/auth/oidc/helper/scopes.helper.ts new file mode 100644 index 0000000..a0a7b97 --- /dev/null +++ b/src/auth/oidc/helper/scopes.helper.ts @@ -0,0 +1,10 @@ +export const scopesArray = { + profile: { + scope: 'profile', + displayName: 'General Profile Information', + }, + email: { + scope: 'email', + displayName: '', + }, +}; diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index 5476d46..9d94f41 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -1,4 +1,9 @@ -import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + InternalServerErrorException, + UnauthorizedException, +} from '@nestjs/common'; import { Response, Request } from 'express'; import { UserService } from '../../user/service/user.service'; @@ -14,6 +19,7 @@ import { import { OidcService } from '../oidc/service/core.service'; import { InteractionService } from '../oidc/service/interaction.service'; import { Interaction } from '../oidc/types/interaction.type'; +import { LoginResponse, LoginStatusResponse } from '../dto/loginResponse.dto'; @Injectable() export class AuthService { @@ -84,14 +90,13 @@ export class AuthService { return user.id; } - //TODO: Make response DTOs /** * Create a session for a user and set the session copokies. * @param response The Response object * @param userId The User's ID * @returns */ - public async createSession(res: Response, userId: string) { + public async createSession(res: Response, userId: string): Promise { const sessionData = await this.oidcService.createSession(userId); sessionData.cookiesForms.forEach((cookie) => { @@ -102,7 +107,7 @@ export class AuthService { }); return { - status: 'success', + status: LoginStatusResponse.SUCCESS, message: 'Login Successful', sessionId: sessionData.sessionId, }; @@ -114,11 +119,19 @@ export class AuthService { * @param userId The User's ID * @return */ - public async loginExistingInteraction(req: Request, res: Response, userId: string) { + public async loginExistingInteraction( + req: Request, + res: Response, + userId: string, + ): Promise { const redirectUrl = await this.interactionService.login(req, res, userId, true); + if (!redirectUrl) { + throw new InternalServerErrorException('No redirect URL after building interaction'); + } + return { - status: 'redirect', + status: LoginStatusResponse.REDIRECT, message: 'Login Successful, Redirect to session page', redirectUrl, }; diff --git a/src/client/client.module.ts b/src/client/client.module.ts index eacc6ba..73f7b4b 100644 --- a/src/client/client.module.ts +++ b/src/client/client.module.ts @@ -10,4 +10,4 @@ import { DATABASE_ENTITIES } from 'src/database/database.entities'; providers: [], exports: [], }) -export class UserModule {} +export class ClientModule {} diff --git a/src/client/controller/client.controller.ts b/src/client/controller/client.controller.ts new file mode 100644 index 0000000..e88320a --- /dev/null +++ b/src/client/controller/client.controller.ts @@ -0,0 +1,6 @@ +import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; + +@Controller('organization') +@ApiTags('Clients') +export class ClientsController {} diff --git a/src/client/dto/listClient.dto.ts b/src/client/dto/listClient.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/client/service/client.service.ts b/src/client/service/client.service.ts index e69de29..546fc15 100644 --- a/src/client/service/client.service.ts +++ b/src/client/service/client.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@nestjs/common'; + +import { OidcClient } from '../../database/models/oidc_client.model'; +import { Repository } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; + +@Injectable() +export class ClientService { + constructor( + @InjectRepository(OidcClient) + private readonly clientRepository: Repository, + ) {} + + public async getClients() {} +} diff --git a/src/view/controllers/view.controller.ts b/src/view/controllers/view.controller.ts new file mode 100644 index 0000000..2770c96 --- /dev/null +++ b/src/view/controllers/view.controller.ts @@ -0,0 +1,59 @@ +import { Controller, Get, Render, UseGuards } from '@nestjs/common'; +import { ApiExcludeController, ApiExcludeEndpoint } from '@nestjs/swagger'; + +import { LoginGuard } from '../../auth/guard/login.guard'; +import { User } from '../../auth/decorators/user.decorator'; + +@ApiExcludeController() +@Controller() +export class ViewController { + @Get('auth/login') + @Render('auth/login') + @ApiExcludeEndpoint() + @UseGuards(LoginGuard) + public async loginView(): Promise { + return { + forgot_password: 'forgot-password', + register: 'register', + login_url: '/auth/login', + //background_image: 'https://waterwolf.club/static/img/portal/portal7.jpg', + }; + } + + @Get('auth/login/totp') + @Render('auth/login-totp') + @ApiExcludeEndpoint() + @UseGuards(LoginGuard) + public async getLoginTotp(): Promise { + return { + login: 'login', + methods: ['authenticator', 'email'], + }; + } + + @Get('auth/register') + @Render('auth/register') + @ApiExcludeEndpoint() + @UseGuards(LoginGuard) + public async getRegister(): Promise { + return { + login: 'login', + }; + } + + @Get('auth/forgot-password') + @UseGuards(LoginGuard) + @Render('auth/forgot-password') + @ApiExcludeEndpoint() + public async getForgotPassword(): Promise { + return { + login: 'login', + }; + } + + @Get('auth/auth-test') + @ApiExcludeEndpoint() + public async getAuthTest(@User() user: any): Promise { + return user; + } +} diff --git a/src/view/view.module.ts b/src/view/view.module.ts new file mode 100644 index 0000000..b007405 --- /dev/null +++ b/src/view/view.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ViewController } from './controllers/view.controller'; + +@Module({ + imports: [], + controllers: [ViewController], + providers: [], + exports: [], +}) +export class ViewModule {}