From 44508aaf3d68336acd82281b7b372c3db42ca633 Mon Sep 17 00:00:00 2001 From: vishwas1 Date: Tue, 7 Nov 2023 19:28:23 +0530 Subject: [PATCH] seperating out app oauth module from app module --- src/app-auth/app-auth.module.ts | 12 +-- .../controllers/app-auth.controller.ts | 52 ++---------- src/app-oauth/app-oauth.controller.spec.ts | 19 +++++ src/app-oauth/app-oauth.controller.ts | 72 ++++++++++++++++ src/app-oauth/app-oauth.module.ts | 14 ++++ src/app-oauth/dtos/app-sercret.decorator.ts | 31 +++++++ src/app-oauth/dtos/generate-token.dto.ts | 82 +++++++++++++++++++ src/app.module.ts | 2 + src/main.ts | 46 +++++++++-- 9 files changed, 270 insertions(+), 60 deletions(-) create mode 100644 src/app-oauth/app-oauth.controller.spec.ts create mode 100644 src/app-oauth/app-oauth.controller.ts create mode 100644 src/app-oauth/app-oauth.module.ts create mode 100644 src/app-oauth/dtos/app-sercret.decorator.ts create mode 100644 src/app-oauth/dtos/generate-token.dto.ts diff --git a/src/app-auth/app-auth.module.ts b/src/app-auth/app-auth.module.ts index f825aa6c..043e7043 100644 --- a/src/app-auth/app-auth.module.ts +++ b/src/app-auth/app-auth.module.ts @@ -6,10 +6,7 @@ import { } from '@nestjs/common'; import { AppAuthService } from './services/app-auth.service'; -import { - AppAuthController, - AppOAuthController, -} from './controllers/app-auth.controller'; +import { AppAuthController } from './controllers/app-auth.controller'; import { MongooseModule } from '@nestjs/mongoose'; import { App, AppSchema } from './schemas/app.schema'; @@ -40,15 +37,12 @@ import { TrimMiddleware } from 'src/utils/middleware/trim.middleware'; JwtStrategyApp, AppAuthApiKeyService, ], - controllers: [AppAuthController, AppOAuthController], + controllers: [AppAuthController], - exports: [AppAuthService, AppRepository, AppAuthApiKeyService], + exports: [AppAuthService, AppRepository, AppAuthApiKeyService, AppAuthModule], }) export class AppAuthModule implements NestModule { configure(consumer: MiddlewareConsumer) { - consumer - .apply(WhitelistAppCorsMiddleware) - .forRoutes(AppAuthController, AppOAuthController); consumer .apply(TrimMiddleware) .exclude( diff --git a/src/app-auth/controllers/app-auth.controller.ts b/src/app-auth/controllers/app-auth.controller.ts index d9197c59..756de4a2 100644 --- a/src/app-auth/controllers/app-auth.controller.ts +++ b/src/app-auth/controllers/app-auth.controller.ts @@ -26,6 +26,7 @@ import { import { AppAuthService } from 'src/app-auth/services/app-auth.service'; import { ApiBadRequestResponse, + ApiBasicAuth, ApiBearerAuth, ApiCreatedResponse, ApiExcludeController, @@ -49,12 +50,14 @@ import { } from '../decorator/app-sercret.decorator'; import { TransformResponseInterceptor } from '../interceptors/transformResponse.interseptor'; import { JwtGuard } from '../guard/jwt.guard'; +import { BasicAuthVerificationGuard } from '../guard/basic-auth-verification.guard'; @UseFilters(AllExceptionsFilter) @Controller('app') -@ApiExcludeController() -@ApiBearerAuth('Authorization') -@UseGuards(JwtGuard) +// @ApiExcludeController() +// @ApiBearerAuth('Authorization') +@ApiBasicAuth('Basic Auth') +@UseGuards(BasicAuthVerificationGuard) export class AppAuthController { constructor(private readonly appAuthService: AppAuthService) {} @UseInterceptors( @@ -243,46 +246,3 @@ export class AppAuthController { return this.appAuthService.reGenerateAppSecretKey(app, userId); } } - -@UseFilters(AllExceptionsFilter) -@ApiTags('App') -@Controller('app') -export class AppOAuthController { - constructor(private readonly appAuthService: AppAuthService) {} - - @ApiHeader({ - name: 'X-Api-Secret-Key', - description: 'Provide Api Secret key to get access token', - required: true, - }) - @ApiHeader({ - name: 'Origin', - description: 'Origin as you set in application cors', - required: false, - }) - @ApiBadRequestResponse({ - status: 400, - description: 'Error occured at the time of generating access token', - type: AppError, - }) - @Post('oauth') - @HttpCode(200) - @ApiResponse({ - status: 200, - description: 'AccessToken generated', - type: GenerateTokenResponse, - }) - @ApiUnauthorizedResponse({ - description: 'Unauthorized', - type: GenerateTokenError, - }) - @UsePipes(ValidationPipe) - generateAccessToken( - @Headers('X-Api-Secret-Key') apiSectretKey: string, - @AppSecretHeader() appSecreatKey, - @AppSubdomainHeader() appSubdomain, - ): Promise<{ access_token; expiresIn; tokenType }> { - Logger.log('reGenerateAppSecretKey() method: starts', 'AppOAuthController'); - return this.appAuthService.generateAccessToken(appSecreatKey, appSubdomain); - } -} diff --git a/src/app-oauth/app-oauth.controller.spec.ts b/src/app-oauth/app-oauth.controller.spec.ts new file mode 100644 index 00000000..37d16243 --- /dev/null +++ b/src/app-oauth/app-oauth.controller.spec.ts @@ -0,0 +1,19 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppOauthController } from './app-oauth.controller'; + +describe('AppOauthController', () => { + let controller: AppOauthController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AppOauthController], + providers: [], + }).compile(); + + controller = module.get(AppOauthController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/src/app-oauth/app-oauth.controller.ts b/src/app-oauth/app-oauth.controller.ts new file mode 100644 index 00000000..9da3d3b7 --- /dev/null +++ b/src/app-oauth/app-oauth.controller.ts @@ -0,0 +1,72 @@ +import { + Controller, + ValidationPipe, + Post, + UsePipes, + HttpCode, + UseFilters, + Headers, + Logger, +} from '@nestjs/common'; + +import { AppAuthService } from 'src/app-auth/services/app-auth.service'; +import { + ApiBadRequestResponse, + ApiHeader, + ApiResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; +import { AllExceptionsFilter } from 'src/utils/utils'; +import { + AppSecretHeader, + AppSubdomainHeader, +} from './dtos/app-sercret.decorator'; +import { + GenerateTokenError, + GenerateTokenResponse, + AppError, +} from './dtos/generate-token.dto'; + +@UseFilters(AllExceptionsFilter) +@ApiTags('App') +@Controller('app') +export class AppOauthController { + constructor(private readonly appAuthService: AppAuthService) {} + + @ApiHeader({ + name: 'X-Api-Secret-Key', + description: 'Provide Api Secret key to get access token', + required: true, + }) + @ApiHeader({ + name: 'Origin', + description: 'Origin as you set in application cors', + required: false, + }) + @ApiBadRequestResponse({ + status: 400, + description: 'Error occured at the time of generating access token', + type: AppError, + }) + @Post('oauth') + @HttpCode(200) + @ApiResponse({ + status: 200, + description: 'AccessToken generated', + type: GenerateTokenResponse, + }) + @ApiUnauthorizedResponse({ + description: 'Unauthorized', + type: GenerateTokenError, + }) + @UsePipes(ValidationPipe) + generateAccessToken( + @Headers('X-Api-Secret-Key') apiSectretKey: string, + @AppSecretHeader() appSecreatKey, + @AppSubdomainHeader() appSubdomain, + ): Promise<{ access_token; expiresIn; tokenType }> { + Logger.log('reGenerateAppSecretKey() method: starts', 'AppOAuthController'); + return this.appAuthService.generateAccessToken(appSecreatKey, appSubdomain); + } +} diff --git a/src/app-oauth/app-oauth.module.ts b/src/app-oauth/app-oauth.module.ts new file mode 100644 index 00000000..7739adf3 --- /dev/null +++ b/src/app-oauth/app-oauth.module.ts @@ -0,0 +1,14 @@ +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; +import { AppOauthController } from './app-oauth.controller'; +import { AppAuthModule } from 'src/app-auth/app-auth.module'; +import { WhitelistSSICorsMiddleware } from 'src/utils/middleware/cors.middleware'; +@Module({ + imports: [AppAuthModule], + controllers: [AppOauthController], + providers: [], +}) +export class AppOauthModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(WhitelistSSICorsMiddleware).forRoutes(AppOauthController); + } +} diff --git a/src/app-oauth/dtos/app-sercret.decorator.ts b/src/app-oauth/dtos/app-sercret.decorator.ts new file mode 100644 index 00000000..1312803e --- /dev/null +++ b/src/app-oauth/dtos/app-sercret.decorator.ts @@ -0,0 +1,31 @@ +import { + createParamDecorator, + ExecutionContext, + UnauthorizedException, +} from '@nestjs/common'; + +export const AppSecretHeader = createParamDecorator( + (_data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + if ( + !request.headers['x-api-secret-key'] || + request.headers['x-api-secret-key'] == undefined + ) { + throw new UnauthorizedException(['x-api-secret-key header is missing']); + } + return request.headers['x-api-secret-key']; + }, +); + +export const AppSubdomainHeader = createParamDecorator( + (_data: unknown, ctx: ExecutionContext) => { + const request = ctx.switchToHttp().getRequest(); + if ( + !request.headers['x-subdomain'] || + request.headers['x-subdomain'] == undefined + ) { + throw new UnauthorizedException(['x-subdomain header is missing']); + } + return request.headers['x-subdomain']; + }, +); diff --git a/src/app-oauth/dtos/generate-token.dto.ts b/src/app-oauth/dtos/generate-token.dto.ts new file mode 100644 index 00000000..5db13166 --- /dev/null +++ b/src/app-oauth/dtos/generate-token.dto.ts @@ -0,0 +1,82 @@ +import { IsNotEmpty, IsString, IsNumber } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class GenerateTokenError { + @ApiProperty({ + description: 'statusCode', + example: 401, + }) + @IsNumber() + statusCode: number; + + @ApiProperty({ + description: 'message', + example: ['access_denied'], + }) + @IsString() + message: string; + + @ApiProperty({ + description: 'Unauthorized', + example: 'Unauthorized', + }) + @IsString() + error: 'Unauthorized'; +} +export class GenerateTokenResponse { + @ApiProperty({ + description: 'accessToken', + example: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBJZCI6IjRkNjhmMjNmLTcwZjQtNDFhZC1hMGViLTU3MjA4YTZlOTcxMSIsImFwcFNlY3JldCI6IjNjN2NiNTY1LTZmNWQtNGY2MC1hMjQ2LTZhOGFjYWVhMmY0MyIsImdyYW50VHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsImlhdCI6MTY3NDAyMDY3NCwiZXhwIjoxNjc0MDM1MDc0fQ.P-AbheTJMxQNGLTkGWOsnct4M0nKCd-7oUFGqMCpIDM', + }) + @IsNotEmpty() + @IsString() + access_token: string; + + @ApiProperty({ + description: 'Type of token', + example: 'Bearer', + }) + @IsNotEmpty() + @IsString() + tokenType: string; + + @ApiProperty({ + description: 'Token expiry time', + example: 14400, + }) + @IsNotEmpty() + @IsNumber() + expiresIn: number; +} + +export class RegenrateAppApiSecretResponse { + @ApiProperty({ + description: 'apiSecretKey for getting access token', + example: 'xyz.ert34nbhjf48959', + }) + apiSecretKey: string; +} + +export class AppError { + @ApiProperty({ + description: 'statusCode', + example: 400, + }) + @IsNumber() + statusCode: number; + + @ApiProperty({ + description: 'message', + example: ['error message 1', 'error message 2'], + }) + @IsString() + message: Array; + + @ApiProperty({ + description: 'error', + example: 'Bad Request', + }) + @IsString() + error: string; +} diff --git a/src/app.module.ts b/src/app.module.ts index 03141b1b..47bfdf91 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { DidModule } from './did/did.module'; import { SchemaModule } from './schema/schema.module'; import { CredentialModule } from './credential/credential.module'; import { PresentationModule } from './presentation/presentation.module'; +import { AppOauthModule } from './app-oauth/app-oauth.module'; @Module({ imports: [ @@ -21,6 +22,7 @@ import { PresentationModule } from './presentation/presentation.module'; }), MongooseModule.forRoot(process.env.DATABASE_CONNECTION_PATH), EdvModule, + AppOauthModule, DidModule, SchemaModule, CredentialModule, diff --git a/src/main.ts b/src/main.ts index 23e49969..184971a8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,12 @@ import { Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { EdvClientKeysManager } from './edv/services/edv.singleton'; import { VaultWalletManager } from './edv/services/vaultWalletManager'; +import { AppAuthModule } from './app-auth/app-auth.module'; +import { DidModule } from './did/did.module'; +import { SchemaModule } from './schema/schema.module'; +import { PresentationModule } from './presentation/presentation.module'; +import { CredentialModule } from './credential/credential.module'; +import { AppOauthModule } from './app-oauth/app-oauth.module'; //import { Header } from '@nestjs/common'; async function bootstrap() { @@ -94,7 +100,21 @@ async function bootstrap() { console.log(e); } - const config = new DocumentBuilder() + const orgDocConfig = new DocumentBuilder() + .setTitle('Entity Studio SSI API Playground') + .setDescription('Open API Documentation of the Entity Studio') + .addBasicAuth( + { + type: 'http', + name: 'Basic Auth', + in: 'header', + }, + 'Basic Auth', + ) + .setVersion('1.0') + .build(); + + const tenantDocConfig = new DocumentBuilder() .setTitle('Entity Studio SSI API Playground') .setDescription('Open API Documentation of the Entity Studio') .addBearerAuth( @@ -105,12 +125,23 @@ async function bootstrap() { }, 'Authorization', ) - .setVersion('1.0') .build(); - const document = SwaggerModule.createDocument(app, config); - const options = { + const tenantDocuments = SwaggerModule.createDocument(app, tenantDocConfig, { + include: [ + AppOauthModule, + DidModule, + SchemaModule, + CredentialModule, + PresentationModule, + ], // don't include, say, BearsModule + }); + const orgDocuments = SwaggerModule.createDocument(app, orgDocConfig, { + include: [AppAuthModule], // don't include, say, BearsModule + }); + + const tenantOptions = { swaggerOptions: { defaultModelsExpandDepth: -1, }, @@ -119,7 +150,12 @@ async function bootstrap() { customCss: ` .topbar-wrapper img {content:url(\'./Entity_full.png\'); width:135px; height:auto;margin-left: -150px;} .swagger-ui .topbar { background-color: #fff; }`, }; - SwaggerModule.setup('api', app, document, options); + + const orgOptions = tenantOptions; + + SwaggerModule.setup('/ssi', app, tenantDocuments, tenantOptions); + SwaggerModule.setup('/', app, orgDocuments, orgOptions); + await app.listen(process.env.PORT || 3001); Logger.log( `Server running on http://localhost:${process.env.PORT}`,