Skip to content

Commit

Permalink
fix/issue 158 Added encryption to the access token (#160)
Browse files Browse the repository at this point in the history
* fix/issue-158
- Added encryption to access token similar to the refresh tokens
- Changed authguard to resolve access token from cookies
- Changed req interface to incorporate separate iv's for refresh and access tokens
- Minor refactoring

* updated screenshots

* Update README.md

* excluded out-of-office events from calender events list

* added searchPeople method in mock service

* fix/issue-158 Updated contribution docs

---------

Co-authored-by: Ali Ahnaf <[email protected]>
Co-authored-by: Propo41 <[email protected]>
  • Loading branch information
3 people authored Jan 12, 2025
1 parent 7e4dd4e commit a923b66
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 29 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Note that in production, the client build files are served directly from the ser

### Encryption key

The app uses an encryption service to encrypt the refresh token (provided by google). It implements AES (Advanced Encryption Standard) encryption to secure sensitive data. Specifically, it uses the `aes-256-cbc` algorithm, which is a symmetric block cipher with the following characteristics:
The app uses an encryption service to encrypt the access and refresh tokens (provided by google). It implements AES (Advanced Encryption Standard) encryption to secure sensitive data. Specifically, it uses the `aes-256-cbc` algorithm, which is a symmetric block cipher with the following characteristics:

- 256-bit Key: Ensures a high level of security by requiring a 32-byte key.
- CBC (Cipher Block Chaining) Mode: Enhances security by chaining blocks together, using an Initialization Vector (IV) to ensure randomness.
Expand Down
38 changes: 25 additions & 13 deletions server/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiResponse } from '@quickmeet/shared';
import { Body, Controller, Get, Inject, Post, Req, Res, Logger, Query } from '@nestjs/common';
import { Body, Controller, Get, Inject, Post, Req, Res, Logger, Query, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
import { _OAuth2Client } from './decorators';
import { createResponse } from 'src/helpers/payload.util';
Expand All @@ -20,14 +20,16 @@ export class AuthController {

@Post('/oauth2/callback')
async oAuthCallback(@Body('code') code: string, @Res({ passthrough: true }) res: Response): Promise<ApiResponse<Boolean>> {
const { accessToken, refreshToken, hd, iv, email } = await this.authService.login(code);
const { accessToken, accessTokenIv, refreshToken, refreshTokenIv, hd, email } = await this.authService.login(code);

if (refreshToken && iv) {
if (refreshToken && refreshTokenIv) {
this.setCookie(res, 'refreshToken', refreshToken);
this.setCookie(res, 'iv', iv);
this.setCookie(res, 'refreshTokenIv', refreshTokenIv);
}

this.setCookie(res, 'accessToken', accessToken, toMs('1h'));
this.setCookie(res, 'accessTokenIv', accessTokenIv, toMs('1h'));

this.setCookie(res, 'hd', hd);
this.setCookie(res, 'email', email);

Expand All @@ -38,18 +40,25 @@ export class AuthController {
@Post('/logout')
async logout(@Req() req: _Request, @Res({ passthrough: true }) res: Response, @Body('revokeToken') revokeToken?: boolean): Promise<ApiResponse<boolean>> {
res.clearCookie('accessToken');
res.clearCookie('accessTokenIv');
res.clearCookie('hd');
res.clearCookie('email');

if (revokeToken) {
this.logger.debug("revoking refresh token")
this.logger.debug('revoking refresh token');
res.clearCookie('refreshToken');
res.clearCookie('iv');
res.clearCookie('refreshTokenIv');

const { accessToken, accessTokenIv } = req.cookies;
if (accessToken && accessTokenIv) {
const decryptedAccessToken = await this.encryptionService.decrypt(accessToken, accessTokenIv);

const client = this.googleApiService.getOAuthClient();

const client = this.googleApiService.getOAuthClient();
client.setCredentials({ access_token: req.cookies.accessToken });
client.setCredentials({ access_token: decryptedAccessToken });

await this.authService.logout(client);
await this.authService.logout(client);
}
}

return createResponse(true);
Expand All @@ -63,13 +72,16 @@ export class AuthController {
}

@Get('/token/refresh')
async refreshAppToken(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<ApiResponse<string>> {
const refreshToken = await this.encryptionService.decrypt(req.cookies.refreshToken, req.cookies.iv);
async refreshAppToken(@Req() req: Request, @Res({ passthrough: true }) res: Response): Promise<ApiResponse<Boolean>> {
const refreshToken = await this.encryptionService.decrypt(req.cookies.refreshToken, req.cookies.refreshTokenIv);
const accessToken = await this.authService.refreshAppToken(refreshToken);

this.setCookie(res, 'accessToken', accessToken, toMs('1h'));
const { iv, encryptedData } = await this.encryptionService.encrypt(accessToken);

return createResponse(accessToken);
this.setCookie(res, 'accessToken', encryptedData, toMs('1h'));
this.setCookie(res, 'accessTokenIv', iv, toMs('1h'));

return createResponse(true);
}

private setCookie(res: Response, name: string, value: string, maxAge: number = toMs('30d')): void {
Expand Down
17 changes: 8 additions & 9 deletions server/src/auth/auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,16 @@ export class AuthGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request: _Request = context.switchToHttp().getRequest();

if (!request.cookies.accessToken) {
throw new UnauthorizedException();
if (!request.cookies.accessToken || !request.cookies.accessTokenIv) {
throw new UnauthorizedException('No access token found');
}

request.accessToken = request.cookies.accessToken;
request.hd = request.cookies.hd;
request.email = request.cookies.email;

if (request.cookies.iv && request.cookies.refreshToken) {
request.iv = request.cookies.iv;
request.refreshToken = await this.encryptionService.decrypt(request.cookies.refreshToken, request.cookies.iv);
try {
request.accessToken = await this.encryptionService.decrypt(request.cookies.accessToken, request.cookies.accessTokenIv);
request.hd = request.cookies.hd;
request.email = request.cookies.email;
} catch (error) {
throw new UnauthorizedException('Invalid access token');
}

return true;
Expand Down
12 changes: 7 additions & 5 deletions server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ export class AuthService {

this.logger.log(`User logged in: ${JSON.stringify(userInfo)}`);

const data = await this.encryptionService.encrypt(tokens.refresh_token);
const encryptedAccessToken = await this.encryptionService.encrypt(tokens.access_token);
const encryptedRefreshToken = await this.encryptionService.encrypt(tokens.refresh_token);

return {
accessToken: tokens.access_token,
accessToken: encryptedAccessToken.encryptedData,
accessTokenIv: encryptedAccessToken.iv,
refreshToken: encryptedRefreshToken?.encryptedData,
refreshTokenIv: encryptedRefreshToken?.iv,
hd: userInfo.hd,
email: userInfo.email,
refreshToken: data?.encryptedData,
iv: data?.iv,
email: userInfo.email
};
}

Expand Down
3 changes: 2 additions & 1 deletion server/src/auth/interfaces/request.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Request } from 'express';
export interface _Request extends Request {
hd?: string;
accessToken?: string;
accessTokenIv?: string;
refreshToken?: string;
refreshTokenIv?: string;
oauth2Client?: OAuth2Client;
iv?: string;
email?: string;
}

0 comments on commit a923b66

Please sign in to comment.