Skip to content

Commit

Permalink
N21-2136 wip new external system logout endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
GordonNicholasCap committed Oct 29, 2024
1 parent 034ebcd commit 706a392
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { authConfig, AuthGuardModule } from '@infra/auth-guard';
import { CacheWrapperModule } from '@infra/cache';
import { EncryptionModule } from '@infra/encryption';
import { IdentityManagementModule } from '@infra/identity-management';
import { UserModule } from '@modules/user';
import { AccountModule } from '@modules/account';
import { OauthModule } from '@modules/oauth/oauth.module';
import { RoleModule } from '@modules/role';
import { SystemModule } from '@modules/system';
import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { LegacySchoolRepo, UserRepo } from '@shared/repo';
Expand Down Expand Up @@ -44,6 +47,9 @@ const jwtModuleOptions: JwtModuleOptions = {
IdentityManagementModule,
CacheWrapperModule,
AuthGuardModule,
UserModule,
HttpModule,
EncryptionModule,
],
providers: [
UserRepo,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JWT, JwtAuthentication } from '@infra/auth-guard';
import { CurrentUser, ICurrentUser, JWT, JwtAuthentication } from '@infra/auth-guard';
import { Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { LogoutUc } from '../uc';
Expand All @@ -17,4 +17,11 @@ export class LogoutController {
async logout(@JWT() jwt: string): Promise<void> {
await this.logoutUc.logout(jwt);
}

@JwtAuthentication()
@Post('/external')
@HttpCode(HttpStatus.OK)
async logoutTest(@CurrentUser() user: ICurrentUser): Promise<void> {
await this.logoutUc.externalSystemLogout(user);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { CreateJwtPayload, ICurrentUser, JwtPayloadFactory } from '@infra/auth-guard';
import { DefaultEncryptionService, EncryptionService } from '@infra/encryption';
import { Account, AccountService } from '@modules/account';
import { Injectable } from '@nestjs/common';
import { UserService } from '@modules/user';
import { System, SystemService } from '@modules/system';
import { HttpService } from '@nestjs/axios';
import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { User } from '@shared/domain/entity';
import { UserDO } from '@shared/domain/domainobject';
import { Logger } from '@src/core/logger';
import { randomUUID } from 'crypto';
import jwt, { JwtPayload } from 'jsonwebtoken';
import { firstValueFrom } from 'rxjs';
import { AxiosHeaders, AxiosRequestConfig } from 'axios';
import { AuthenticationConfig } from '../authentication-config';
import { BruteForceError, UnauthorizedLoggableException } from '../errors';
import { JwtWhitelistAdapter } from '../helper/jwt-whitelist.adapter';
Expand All @@ -21,6 +28,10 @@ export class AuthenticationService {
private readonly jwtWhitelistAdapter: JwtWhitelistAdapter,
private readonly accountService: AccountService,
private readonly configService: ConfigService<AuthenticationConfig, true>,
private readonly userService: UserService,
private readonly systemService: SystemService,
private readonly httpService: HttpService,
@Inject(DefaultEncryptionService) private readonly oAuthEncryptionService: EncryptionService,
private readonly logger: Logger
) {
this.logger.setContext(AuthenticationService.name);
Expand Down Expand Up @@ -123,4 +134,58 @@ export class AuthenticationService {
public normalizePassword(password: string): string {
return password.trim();
}

public async logoutFromExternalSystem(userId: string, systemId: string): Promise<void> {
const user: UserDO = await this.userService.findById(userId);
const system: System | null = await this.systemService.findById(systemId);

if (!user.sessionToken) {
return;
}

if (this.hasSessionTokenExpired(user.sessionToken)) {
return;
}

const endSessionDto = {
refresh_token: user.sessionToken,
};

const headers: AxiosHeaders = new AxiosHeaders();
headers.setContentType('application/x-www-form-urlencoded');

const config: AxiosRequestConfig = {
auth: {
username: system?.oauthConfig?.clientId as string,
password: this.oAuthEncryptionService.decrypt(system?.oauthConfig?.clientSecret as string),
},
headers,
};

if (!system?.oauthConfig?.endSessionEndpoint) {
return;
}

const response = await firstValueFrom(
this.httpService.post(system?.oauthConfig?.endSessionEndpoint, endSessionDto, config)
);

if (response.status !== 204) {
// TODO throw error
return;
}

user.sessionToken = undefined;
await this.userService.save(user);
}

private hasSessionTokenExpired(sessionToken: string): boolean {
const decodedJwt: JwtPayload | null = jwt.decode(sessionToken, { json: true });
const now: number = new Date().getTime();
if (!decodedJwt?.exp) {
return true;
}

return decodedJwt.exp > now;
}
}
8 changes: 8 additions & 0 deletions apps/server/src/modules/authentication/uc/logout.uc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Injectable } from '@nestjs/common';
import { ICurrentUser } from '@infra/auth-guard';
import { AuthenticationService } from '../services';

@Injectable()
Expand All @@ -8,4 +9,11 @@ export class LogoutUc {
async logout(jwt: string): Promise<void> {
await this.authenticationService.removeJwtFromWhitelist(jwt);
}

async externalSystemLogout(user: ICurrentUser): Promise<void> {
if (!user.systemId) return;
// TODO support for now only moin.schule

await this.authenticationService.logoutFromExternalSystem(user.userId, user.systemId);
}
}

0 comments on commit 706a392

Please sign in to comment.