Skip to content
This repository has been archived by the owner on Jun 10, 2024. It is now read-only.

Commit

Permalink
feat: tokens endpoint (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
eddiejaoude committed Jul 25, 2021
1 parent 63315b2 commit 3157929
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 183 deletions.
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { LoggerMiddleware } from './logger/logger.middleware';
import { CalendarModule } from './calendar/calendar.module';
import { AstraModule } from '@cahllagerfeld/nestjs-astra';
import { AstraConfigService } from './astra/astra-config.service';
import { AstraApiModule } from './astra/astra-api.module';

@Module({
imports: [
Expand All @@ -24,6 +25,7 @@ import { AstraConfigService } from './astra/astra-config.service';
isGlobal: true,
}),
CalendarModule,
AstraApiModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
5 changes: 3 additions & 2 deletions src/astra/astra-api.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { HttpModule, Module } from '@nestjs/common';
import { AstraService } from './astra.service';
import { KeyspaceService } from './keyspace.service';

@Module({
imports: [HttpModule],
providers: [KeyspaceService],
exports: [KeyspaceService],
providers: [KeyspaceService, AstraService],
exports: [KeyspaceService, AstraService],
})
export class AstraApiModule {}
48 changes: 0 additions & 48 deletions src/auth/auth.controller.ts

This file was deleted.

15 changes: 11 additions & 4 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Module } from '@nestjs/common';
import { TokenStrategy } from './token.strategy';
import { ValidationService } from './header-validation.service';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TokensController } from './tokens.controller';
import { TokensService } from './tokens.service';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AstraService } from '../astra/astra.service';

@Module({
imports: [
Expand All @@ -17,8 +18,14 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
inject: [ConfigService],
}),
],
providers: [TokenStrategy, ValidationService, AuthService, JwtStrategy],
providers: [
TokenStrategy,
ValidationService,
TokensService,
JwtStrategy,
AstraService,
],
exports: [ValidationService],
controllers: [AuthController],
controllers: [TokensController],
})
export class AuthModule {}
71 changes: 0 additions & 71 deletions src/auth/auth.service.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard, PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';
import { AuthService } from './auth.service';
import { TokensService } from './tokens.service';
import { TokenPayload } from './interfaces/token-payload.interface';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly authService: AuthService) {
constructor(private readonly tokensService: TokensService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.SECRET,
});
}
validate(payload: TokenPayload): TokenPayload {
if (!this.authService.validateClient(payload))
if (!this.tokensService.validateClient(payload))
throw new UnauthorizedException();
return payload;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { JwtModule } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TokensController } from './tokens.controller';
import { TokensService } from './tokens.service';

describe('AuthController', () => {
let controller: AuthController;
let controller: TokensController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [JwtModule.register({ secret: 'Test' })],
controllers: [AuthController],
providers: [AuthService],
controllers: [TokensController],
providers: [TokensService],
}).compile();

controller = module.get<AuthController>(AuthController);
controller = module.get<TokensController>(TokensController);
});

it('should be defined', () => {
Expand Down
50 changes: 50 additions & 0 deletions src/auth/tokens.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
Body,
Controller,
Delete,
Get,
HttpCode,
Param,
Post,
UseGuards,
} from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { TokensService } from './tokens.service';
import { AuthDTO } from './dto/auth.dto';
import { TokenGuard } from './token.strategy';

@ApiTags('Tokens')
@Controller('auth/tokens')
export class TokensController {
constructor(private readonly tokensService: TokensService) {}

@Get(':keyspace')
@UseGuards(TokenGuard)
@ApiSecurity('token')
getTokens(@Param('keyspace') keyspace: string) {
return this.tokensService.findAll(keyspace);
}

@Post()
@UseGuards(TokenGuard)
@ApiSecurity('token')
register(@Body() body: AuthDTO) {
return this.tokensService.register(body);
}

@Post('validate')
@UseGuards(TokenGuard)
@ApiSecurity('token')
@HttpCode(200)
validateClient(@Body() body) {
return this.tokensService.validateClient(body);
}

@Delete(':token')
@UseGuards(TokenGuard)
@ApiSecurity('token')
@HttpCode(204)
deleteClient(@Param('token') token: string) {
return this.tokensService.removeClient(token);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { JwtModule } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
import { TokensService } from './tokens.service';

describe('AuthService', () => {
let service: AuthService;
let service: TokensService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [JwtModule.register({ secret: 'Test' })],
providers: [AuthService],
providers: [TokensService],
}).compile();

service = module.get<AuthService>(AuthService);
service = module.get<TokensService>(TokensService);
});

it('should be defined', () => {
Expand Down
79 changes: 79 additions & 0 deletions src/auth/tokens.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { TokenPayload } from './interfaces/token-payload.interface';
import { JwtService } from '@nestjs/jwt';
import { v4 as uuidv4 } from 'uuid';
import { AuthDTO } from './dto/auth.dto';
import { AstraService } from '../astra/astra.service';
import { catchError } from 'rxjs/operators';
import { from } from 'rxjs';

@Injectable()
export class TokensService {
constructor(
private readonly jwtService: JwtService,
private readonly astraService: AstraService,
) {}

public async findAll(keyspace: string) {
return this.astraService
.find<AuthDTO>(keyspace, 'tokens')
.pipe(catchError(() => from([{}])));
}

public register(body: AuthDTO) {
const clientId = uuidv4();
const { serverId, scopes } = body;

const payload: TokenPayload = {
clientId,
keyspace: serverId,
scopes,
};

//TODO token-expiry
const signedToken = this.jwtService.sign(payload, { expiresIn: '1y' });
const decoded: any = this.jwtService.decode(signedToken);
const expiresIn: number = decoded.exp - Math.round(Date.now() / 1000);
const response = { ...payload, accessToken: signedToken, expiresIn };

this.astraService.create(response, serverId, 'tokens');

return response;
}

public async validateClient(payload: TokenPayload): Promise<boolean> {
const { keyspace, clientId } = payload;

try {
const token = await this.astraService.findOne(
{ clientId: { $eq: clientId } },
keyspace,
'tokens',
);
if (token) {
return true;
}
} catch (e) {
return false;
}

return false;
}

public async removeClient(token: string) {
if (!token)
throw new HttpException('Please provide token', HttpStatus.BAD_REQUEST);

const { clientId, keyspace } = this.jwtService.decode(
token,
) as TokenPayload;

try {
await this.astraService.delete(clientId, keyspace, 'tokens');
} catch (e) {
throw new HttpException('Invalid client id', HttpStatus.BAD_REQUEST);
}

return;
}
}
Loading

0 comments on commit 3157929

Please sign in to comment.