-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
ac4f35d
commit 691acab
Showing
11 changed files
with
222 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 4 additions & 5 deletions
9
apps/server/src/modules/authentication/authentication-api.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,11 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { AuthenticationModule } from './authentication.module'; | ||
import { LoginController } from './controllers/login.controller'; | ||
import { LoginUc } from './uc/login.uc'; | ||
import { LoginController, LogoutController } from './controllers'; | ||
import { LoginUc, LogoutUc } from './uc'; | ||
|
||
@Module({ | ||
imports: [AuthenticationModule], | ||
providers: [LoginUc], | ||
controllers: [LoginController], | ||
exports: [], | ||
providers: [LoginUc, LogoutUc], | ||
controllers: [LoginController, LogoutController], | ||
}) | ||
export class AuthenticationApiModule {} |
72 changes: 72 additions & 0 deletions
72
apps/server/src/modules/authentication/controllers/api-test/logout.api.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { EntityManager } from '@mikro-orm/mongodb'; | ||
import { ServerTestModule } from '@modules/server/server.module'; | ||
import { CACHE_MANAGER } from '@nestjs/cache-manager'; | ||
import { HttpStatus, INestApplication } from '@nestjs/common'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { cleanupCollections, TestApiClient, UserAndAccountTestFactory } from '@shared/testing'; | ||
import { Cache } from 'cache-manager'; | ||
import { Response } from 'supertest'; | ||
|
||
describe('Logout Controller (api)', () => { | ||
const baseRouteName = '/logout'; | ||
|
||
let app: INestApplication; | ||
let em: EntityManager; | ||
let cacheManager: Cache; | ||
let testApiClient: TestApiClient; | ||
|
||
beforeAll(async () => { | ||
const moduleFixture: TestingModule = await Test.createTestingModule({ | ||
imports: [ServerTestModule], | ||
}).compile(); | ||
|
||
app = moduleFixture.createNestApplication(); | ||
await app.init(); | ||
em = app.get(EntityManager); | ||
cacheManager = app.get(CACHE_MANAGER); | ||
testApiClient = new TestApiClient(app, baseRouteName); | ||
}); | ||
|
||
beforeEach(async () => { | ||
await cleanupCollections(em); | ||
}); | ||
|
||
afterAll(async () => { | ||
await app.close(); | ||
}); | ||
|
||
describe('logout', () => { | ||
describe('when a valid jwt is provided', () => { | ||
const setup = async () => { | ||
const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); | ||
|
||
await em.persistAndFlush([studentAccount, studentUser]); | ||
em.clear(); | ||
|
||
const loggedInClient = await testApiClient.login(studentAccount); | ||
|
||
return { | ||
loggedInClient, | ||
studentAccount, | ||
}; | ||
}; | ||
|
||
it('should log out the user', async () => { | ||
const { loggedInClient, studentAccount } = await setup(); | ||
|
||
const response: Response = await loggedInClient.post(''); | ||
|
||
expect(response.status).toEqual(HttpStatus.OK); | ||
expect(await cacheManager.store.keys(`jwt:${studentAccount.id}:*`)).toHaveLength(0); | ||
}); | ||
}); | ||
|
||
describe('when the user is not logged in', () => { | ||
it('should return unauthorized', async () => { | ||
const response: Response = await testApiClient.post(''); | ||
|
||
expect(response.status).toEqual(HttpStatus.UNAUTHORIZED); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { LogoutController } from './logout.controller'; | ||
export { LoginController } from './login.controller'; |
20 changes: 20 additions & 0 deletions
20
apps/server/src/modules/authentication/controllers/logout.controller.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { 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'; | ||
|
||
@ApiTags('Authentication') | ||
@Controller('logout') | ||
export class LogoutController { | ||
constructor(private readonly logoutUc: LogoutUc) {} | ||
|
||
@JwtAuthentication() | ||
@Post() | ||
@HttpCode(HttpStatus.OK) | ||
@ApiOperation({ summary: 'Logs out a user.' }) | ||
@ApiOkResponse({ description: 'Logout was successful.' }) | ||
@ApiUnauthorizedResponse({ description: 'There has been an error while logging out.' }) | ||
async logout(@JWT() jwt: string): Promise<void> { | ||
await this.logoutUc.logout(jwt); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 10 additions & 14 deletions
24
apps/server/src/modules/authentication/helper/jwt-whitelist.adapter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,23 @@ | ||
import { CacheService } from '@infra/cache'; | ||
import { CacheStoreType } from '@infra/cache/interface/cache-store-type.enum'; | ||
import { CACHE_MANAGER } from '@nestjs/cache-manager'; | ||
import { Inject, Injectable } from '@nestjs/common'; | ||
import { addTokenToWhitelist, createRedisIdentifierFromJwtData } from '@src/imports-from-feathers'; | ||
import { createRedisIdentifierFromJwtData, getRedisData, JwtRedisData } from '@src/imports-from-feathers'; | ||
import { Cache } from 'cache-manager'; | ||
|
||
@Injectable() | ||
export class JwtWhitelistAdapter { | ||
constructor( | ||
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache, | ||
private readonly cacheService: CacheService | ||
) {} | ||
constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) {} | ||
|
||
async addToWhitelist(accountId: string, jti: string): Promise<void> { | ||
const redisIdentifier = createRedisIdentifierFromJwtData(accountId, jti); | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call | ||
await addTokenToWhitelist(redisIdentifier); | ||
const redisIdentifier: string = createRedisIdentifierFromJwtData(accountId, jti); | ||
const redisData: JwtRedisData = getRedisData({}); | ||
const expirationInMilliseconds: number = redisData.expirationInSeconds * 1000; | ||
|
||
await this.cacheManager.set(redisIdentifier, redisData, expirationInMilliseconds); | ||
} | ||
|
||
async removeFromWhitelist(accountId: string, jti: string): Promise<void> { | ||
if (this.cacheService.getStoreType() === CacheStoreType.REDIS) { | ||
const redisIdentifier: string = createRedisIdentifierFromJwtData(accountId, jti); | ||
await this.cacheManager.del(redisIdentifier); | ||
} | ||
const redisIdentifier: string = createRedisIdentifierFromJwtData(accountId, jti); | ||
|
||
await this.cacheManager.del(redisIdentifier); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { LoginDto } from './dto'; | ||
export { LoginUc } from './login.uc'; | ||
export { LogoutUc } from './logout.uc'; |
47 changes: 47 additions & 0 deletions
47
apps/server/src/modules/authentication/uc/logout.uc.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { createMock, DeepMocked } from '@golevelup/ts-jest'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { JwtTestFactory } from '@shared/testing'; | ||
import { AuthenticationService } from '../services'; | ||
import { LogoutUc } from './logout.uc'; | ||
|
||
describe(LogoutUc.name, () => { | ||
let module: TestingModule; | ||
let logoutUc: LogoutUc; | ||
|
||
let authenticationService: DeepMocked<AuthenticationService>; | ||
|
||
beforeAll(async () => { | ||
module = await Test.createTestingModule({ | ||
providers: [ | ||
LogoutUc, | ||
{ | ||
provide: AuthenticationService, | ||
useValue: createMock<AuthenticationService>(), | ||
}, | ||
], | ||
}).compile(); | ||
|
||
logoutUc = await module.get(LogoutUc); | ||
authenticationService = await module.get(AuthenticationService); | ||
}); | ||
|
||
describe('logout', () => { | ||
describe('when a jwt is given', () => { | ||
const setup = () => { | ||
const jwt = JwtTestFactory.createJwt(); | ||
|
||
return { | ||
jwt, | ||
}; | ||
}; | ||
|
||
it('should remove the user from the whitelist', async () => { | ||
const { jwt } = setup(); | ||
|
||
await logoutUc.logout(jwt); | ||
|
||
expect(authenticationService.removeJwtFromWhitelist).toHaveBeenCalledWith(jwt); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { AuthenticationService } from '../services'; | ||
|
||
@Injectable() | ||
export class LogoutUc { | ||
constructor(private readonly authenticationService: AuthenticationService) {} | ||
|
||
async logout(jwt: string): Promise<void> { | ||
await this.authenticationService.removeJwtFromWhitelist(jwt); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters