Skip to content

Commit fe5fbad

Browse files
authored
Merge pull request #1320 from credebl/feat/database-strategy
feat: SSO changes for fetching, update and delete session
2 parents c023f07 + d616a85 commit fe5fbad

File tree

28 files changed

+1604
-834
lines changed

28 files changed

+1604
-834
lines changed

.env.demo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ OTEL_LOGS_OTLP_ENDPOINT='http://localhost:4318/v1/logs'
156156
OTEL_HEADERS_KEY=88ca6b1XXXXXXXXXXXXXXXXXXXXXXXXXXX
157157
OTEL_LOGGER_NAME='credebl-platform-logger'
158158
HOSTNAME='localhost'
159-
159+
SESSIONS_LIMIT=10
160160
# SSO
161161
# To add more clients, simply add comma separated values of client names
162162
SUPPORTED_SSO_CLIENTS=CREDEBL

.env.sample

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ SCHEMA_FILE_SERVER_URL= // Please provide schema URL
153153
SCHEMA_FILE_SERVER_TOKEN=xxxxxxxx // Please provide schema file server token for polygon
154154

155155
FILEUPLOAD_CACHE_TTL= //Provide file upload cache ttl
156-
156+
SESSIONS_LIMIT= //Provide limits of sessions
157157
FIELD_UPLOAD_SIZE= //Provide field upload size
158158

159159
IS_ECOSYSTEM_ENABLE= //Set this flag to `true` to enable the ecosystem, or `false` to disable it.
@@ -180,6 +180,7 @@ HOSTNAME='localhost' # Hostname or unique identifier
180180
# SSO
181181
# To add more clients, simply add comma separated values of client names
182182
SUPPORTED_SSO_CLIENTS=CREDEBL
183+
NEXTAUTH_PROTOCOL=
183184

184185
# Key for agent base wallet
185186
AGENT_API_KEY='supersecret-that-too-16chars'

apps/api-gateway/src/authz/authz.controller.ts

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ import {
77
Param,
88
Post,
99
Query,
10+
Req,
1011
Res,
1112
UnauthorizedException,
12-
UseFilters
13+
UseFilters,
14+
UseGuards
1315
} from '@nestjs/common';
1416
import { AuthzService } from './authz.service';
1517
import { CommonService } from '../../../../libs/common/src/common.service';
16-
import { ApiBody, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
18+
import { ApiBearerAuth, ApiBody, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
1719
import { ApiResponseDto } from '../dtos/apiResponse.dto';
1820
import { UserEmailVerificationDto } from '../user/dto/create-user.dto';
1921
import IResponseType from '@credebl/common/interfaces/response.interface';
2022
import { ResponseMessages } from '@credebl/common/response-messages';
21-
import { Response } from 'express';
23+
import { Response, Request } from 'express';
2224
import { EmailVerificationDto } from '../user/dto/email-verify.dto';
2325
import { AuthTokenResponse } from './dtos/auth-token-res.dto';
2426
import { LoginUserDto } from '../user/dto/login-user.dto';
@@ -30,7 +32,10 @@ import { ResetTokenPasswordDto } from './dtos/reset-token-password';
3032
import { RefreshTokenDto } from './dtos/refresh-token.dto';
3133
import { getDefaultClient } from '../user/utils';
3234
import { ClientAliasValidationPipe } from './decorators/user-auth-client';
33-
35+
import { SessionGuard } from './guards/session.guard';
36+
import { UserLogoutDto } from './dtos/user-logout.dto';
37+
import { AuthGuard } from '@nestjs/passport';
38+
import { ISessionData } from 'apps/user/interfaces/user.interface';
3439
@Controller('auth')
3540
@ApiTags('auth')
3641
@UseFilters(CustomExceptionFilter)
@@ -139,6 +144,7 @@ export class AuthzController {
139144
};
140145
return res.status(HttpStatus.CREATED).json(finalResponse);
141146
}
147+
142148
/**
143149
* Authenticates a user and returns an access token.
144150
*
@@ -168,6 +174,42 @@ export class AuthzController {
168174
}
169175
}
170176

177+
/**
178+
* Fetch session details
179+
*
180+
* @returns User's access token details
181+
*/
182+
@Get('/sessionDetails')
183+
@UseGuards(SessionGuard)
184+
@ApiOperation({
185+
summary: 'Fetch session details',
186+
description: 'Fetch session details against logged in user'
187+
})
188+
@ApiQuery({
189+
name: 'sessionId',
190+
required: false
191+
})
192+
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: AuthTokenResponse })
193+
async sessionDetails(@Res() res: Response, @Req() req: Request, @Query() sessionId: ISessionData): Promise<Response> {
194+
this.logger.debug(`in authz controller`);
195+
196+
let sessionDetails;
197+
if (0 < Object.keys(sessionId).length) {
198+
sessionDetails = await this.authzService.getSession(sessionId);
199+
}
200+
if (req.user) {
201+
sessionDetails = req.user;
202+
}
203+
204+
const finalResponse: IResponseType = {
205+
statusCode: HttpStatus.OK,
206+
message: ResponseMessages.user.success.fetchSession,
207+
data: sessionDetails
208+
};
209+
210+
return res.status(HttpStatus.OK).json(finalResponse);
211+
}
212+
171213
/**
172214
* Resets user's password.
173215
*
@@ -263,4 +305,30 @@ export class AuthzController {
263305

264306
return res.status(HttpStatus.OK).json(finalResponse);
265307
}
308+
309+
/**
310+
* Log out user.
311+
*
312+
* @body LogoutUserDto
313+
* @returns Logged out user from current session
314+
*/
315+
@Post('/signout')
316+
@ApiOperation({
317+
summary: 'Logout user',
318+
description: 'Logout user from current session.'
319+
})
320+
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
321+
@UseGuards(AuthGuard('jwt'))
322+
@ApiBearerAuth()
323+
@ApiBody({ type: UserLogoutDto })
324+
async logout(@Body() logoutUserDto: UserLogoutDto, @Res() res: Response): Promise<Response> {
325+
await this.authzService.logout(logoutUserDto);
326+
327+
const finalResponse: IResponseType = {
328+
statusCode: HttpStatus.OK,
329+
message: ResponseMessages.user.success.logout
330+
};
331+
332+
return res.status(HttpStatus.OK).json(finalResponse);
333+
}
266334
}

apps/api-gateway/src/authz/authz.module.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import { ClientsModule, Transport } from '@nestjs/microservices';
2+
import { Logger, Module } from '@nestjs/common';
23

34
import { AgentService } from '../agent/agent.service';
45
import { AuthzController } from './authz.controller';
56
import { AuthzService } from './authz.service';
7+
import { CommonConstants } from '@credebl/common/common.constant';
68
import { CommonModule } from '../../../../libs/common/src/common.module';
79
import { CommonService } from '../../../../libs/common/src/common.service';
810
import { ConnectionService } from '../connection/connection.service';
911
import { HttpModule } from '@nestjs/axios';
1012
import { JwtStrategy } from './jwt.strategy';
1113
import { MobileJwtStrategy } from './mobile-jwt.strategy';
12-
import { Module } from '@nestjs/common';
14+
import { NATSClient } from '@credebl/common/NATSClient';
15+
import { OrganizationService } from '../organization/organization.service';
1316
import { PassportModule } from '@nestjs/passport';
17+
import { PrismaServiceModule } from '@credebl/prisma-service';
1418
import { SocketGateway } from './socket.gateway';
1519
import { SupabaseService } from '@credebl/supabase';
1620
import { UserModule } from '../user/user.module';
21+
import { UserRepository } from 'apps/user/repositories/user.repository';
1722
import { UserService } from '../user/user.service';
1823
import { VerificationService } from '../verification/verification.service';
1924
import { getNatsOptions } from '@credebl/common/nats.config';
20-
import { OrganizationService } from '../organization/organization.service';
21-
import { CommonConstants } from '@credebl/common/common.constant';
22-
import { NATSClient } from '@credebl/common/NATSClient';
2325

2426
@Module({
2527
imports: [
@@ -36,7 +38,8 @@ import { NATSClient } from '@credebl/common/NATSClient';
3638
},
3739
CommonModule
3840
]),
39-
UserModule
41+
UserModule,
42+
PrismaServiceModule
4043
],
4144
providers: [
4245
JwtStrategy,
@@ -50,12 +53,11 @@ import { NATSClient } from '@credebl/common/NATSClient';
5053
CommonService,
5154
UserService,
5255
SupabaseService,
53-
OrganizationService
54-
],
55-
exports: [
56-
PassportModule,
57-
AuthzService
56+
OrganizationService,
57+
UserRepository,
58+
Logger
5859
],
60+
exports: [PassportModule, AuthzService],
5961
controllers: [AuthzController]
6062
})
61-
export class AuthzModule { }
63+
export class AuthzModule {}

apps/api-gateway/src/authz/authz.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { ForgotPasswordDto } from './dtos/forgot-password.dto';
1717
import { ResetTokenPasswordDto } from './dtos/reset-token-password';
1818
import { NATSClient } from '@credebl/common/NATSClient';
1919
import { user } from '@prisma/client';
20+
import { ISessionDetails } from 'apps/user/interfaces/user.interface';
21+
import { UserLogoutDto } from './dtos/user-logout.dto';
2022
@Injectable()
2123
@WebSocketGateway()
2224
export class AuthzService extends BaseService {
@@ -53,6 +55,11 @@ export class AuthzService extends BaseService {
5355
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'user-holder-login', payload);
5456
}
5557

58+
async getSession(sessionId): Promise<ISessionDetails> {
59+
const payload = { ...sessionId };
60+
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'fetch-session-details', payload);
61+
}
62+
5663
async resetPassword(resetPasswordDto: ResetPasswordDto): Promise<IResetPasswordResponse> {
5764
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'user-reset-password', resetPasswordDto);
5865
}
@@ -73,4 +80,8 @@ export class AuthzService extends BaseService {
7380
const payload = { userInfo };
7481
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'add-user', payload);
7582
}
83+
84+
async logout(logoutUserDto: UserLogoutDto): Promise<string> {
85+
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'user-logout', logoutUserDto);
86+
}
7687
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';
2+
3+
import { ApiPropertyOptional } from '@nestjs/swagger';
4+
5+
export class UserLogoutDto {
6+
@ApiPropertyOptional({
7+
description: 'List of session IDs to log out',
8+
type: [String]
9+
})
10+
@IsOptional()
11+
@IsArray({ message: 'sessions must be an array' })
12+
@IsString({ each: true, message: 'each session Id must be a string' })
13+
@IsNotEmpty({ each: true, message: 'session Id must not be empty' })
14+
sessions?: string[];
15+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
2+
3+
import { Request } from 'express';
4+
import { UserRepository } from 'apps/user/repositories/user.repository';
5+
6+
@Injectable()
7+
export class SessionGuard implements CanActivate {
8+
constructor(private userRepository: UserRepository) {}
9+
10+
async canActivate(context: ExecutionContext): Promise<boolean> {
11+
const request = context.switchToHttp().getRequest<Request>();
12+
const sessionId = request.cookies['session_id'];
13+
14+
// if (!sessionId) {
15+
// throw new UnauthorizedException('Missing session cookie');
16+
// }
17+
if (sessionId) {
18+
const user = await this.userRepository.validateSession(sessionId);
19+
request.user = user;
20+
}
21+
22+
// if (!user) {
23+
// throw new UnauthorizedException('Invalid session');
24+
// }
25+
return true;
26+
}
27+
}

apps/api-gateway/src/main.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as express from 'express';
44

55
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
66
import { Logger, VERSION_NEUTRAL, VersioningType } from '@nestjs/common';
7-
7+
import * as cookieParser from 'cookie-parser';
88
import { AppModule } from './app.module';
99
import { HttpAdapterHost, NestFactory, Reflector } from '@nestjs/core';
1010
import { AllExceptionsFilter } from '@credebl/common/exception-handler';
@@ -45,6 +45,7 @@ async function bootstrap(): Promise<void> {
4545
expressApp.set('x-powered-by', false);
4646
app.use(express.json({ limit: '100mb' }));
4747
app.use(express.urlencoded({ limit: '100mb', extended: true }));
48+
app.use(cookieParser());
4849

4950
app.use((req, res, next) => {
5051
let err = null;

apps/api-gateway/src/organization/organization.controller.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,18 @@ export class OrganizationController {
550550
}
551551

552552
const orgCredentials = await this.organizationService.clientLoginCredentials(clientCredentialsDto);
553+
553554
const finalResponse: IResponse = {
554555
statusCode: HttpStatus.OK,
555556
message: ResponseMessages.organisation.success.clientCredentials,
556557
data: orgCredentials
557558
};
559+
res.cookie('session_id', orgCredentials.sessionId, {
560+
httpOnly: true,
561+
sameSite: 'none',
562+
secure: 'http' !== process.env.NEXTAUTH_PROTOCOL
563+
});
564+
558565
return res.status(HttpStatus.OK).json(finalResponse);
559566
}
560567
/**

apps/organization/repositories/organization.repository.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,29 @@ export class OrganizationRepository {
760760
throw error;
761761
}
762762
}
763-
763+
async getOrgAndOwnerUser(orgId: string): Promise<user_org_roles> {
764+
try {
765+
return this.prisma.user_org_roles.findFirst({
766+
where: {
767+
orgId,
768+
orgRole: {
769+
name: 'owner'
770+
}
771+
},
772+
include: {
773+
user: {
774+
select: {
775+
id: true,
776+
keycloakUserId: true
777+
}
778+
}
779+
}
780+
});
781+
} catch (error) {
782+
this.logger.error(`Error in fetch in organization with admin details`);
783+
throw error;
784+
}
785+
}
764786
async getCredDefByOrg(orgId: string): Promise<
765787
{
766788
tag: string;

0 commit comments

Comments
 (0)