diff --git a/deploy/helm-chart/templates/deployment.yaml b/deploy/helm-chart/templates/deployment.yaml index 11cbeea..013aa06 100644 --- a/deploy/helm-chart/templates/deployment.yaml +++ b/deploy/helm-chart/templates/deployment.yaml @@ -96,6 +96,8 @@ spec: value: {{ .Values.maestro.open_group_id }} - name: DEDICATED_PROXY value: {{ .Values.maestro.dedicated_proxy }} + - name: COOKIE_SECRET + value: {{ .Values.maestro.cookie_secret }} - name: REDIS_DATABASE value: "{{ .Values.maestro.redis_database }}" - name: REDIS_HOST diff --git a/deploy/helm-chart/templates/ingress.yaml b/deploy/helm-chart/templates/ingress.yaml index 1594baa..072b557 100644 --- a/deploy/helm-chart/templates/ingress.yaml +++ b/deploy/helm-chart/templates/ingress.yaml @@ -9,6 +9,9 @@ metadata: nginx.ingress.kubernetes.io/server-snippet: | underscores_in_headers on; ignore_invalid_headers on; + nginx.ingress.kubernetes.io/proxy-buffer-size: "16k" + nginx.ingress.kubernetes.io/proxy-buffers-number: "8" + nginx.ingress.kubernetes.io/proxy-busy-buffers-size: "64k" {{- if .Values.maestro.restricted_ip}} nginx.ingress.kubernetes.io/whitelist-source-range: {{ .Values.maestro.restricted_ip }} {{- end }} diff --git a/deploy/helm-chart/values.yaml b/deploy/helm-chart/values.yaml index 9ed722c..4844fbc 100644 --- a/deploy/helm-chart/values.yaml +++ b/deploy/helm-chart/values.yaml @@ -48,6 +48,7 @@ maestro: redis_host: "product-redis-prd.z4xvqj.0001.use1.cache.amazonaws.com" redis_port: "6379" redis_database: "0" + cookie_secret: "13cc5e136d3074bcc05bec8697092ec1f5f376bf" autoscaling: enabled: false minReplicas: 1 diff --git a/docsfera.json b/docsfera.json index 75a5699..a18dad5 100644 --- a/docsfera.json +++ b/docsfera.json @@ -34,14 +34,7 @@ }, "responses": { "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuthSignInRes" - } - } - } + "description": "" } }, "tags": [ @@ -675,6 +668,33 @@ ] } }, + "/auth/me": { + "get": { + "operationId": "AuthController_getMe", + "parameters": [ + { + "name": "dadosfera-lang", + "in": "header", + "required": false, + "schema": { + "enum": [ + "pt-br", + "en-us" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Auth" + ] + } + }, "/connections": { "post": { "operationId": "ConnectionController_createConnection", @@ -6791,181 +6811,6 @@ "password" ] }, - "AuthCustomer": { - "type": "object", - "properties": { - "modules": { - "type": "array", - "items": { - "type": "string" - } - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "tier": { - "type": "string" - }, - "scheduleLimit": { - "type": "string" - }, - "links": { - "type": "array", - "items": { - "type": "string" - } - }, - "themeEnabled": { - "type": "boolean" - }, - "enforceMfa": { - "type": "boolean" - } - }, - "required": [ - "modules", - "id", - "name", - "displayName", - "tier", - "scheduleLimit", - "links", - "themeEnabled", - "enforceMfa" - ] - }, - "AuthUser": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "username": { - "type": "string" - }, - "createdAt": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "username", - "createdAt" - ] - }, - "AuthTokens": { - "type": "object", - "properties": { - "accessToken": { - "type": "string" - }, - "refreshToken": { - "type": "string" - }, - "termsOfUseToken": { - "type": "string" - }, - "idToken": { - "type": "string", - "deprecated": true - } - }, - "required": [ - "accessToken", - "refreshToken", - "idToken" - ] - }, - "TermsOfUse": { - "type": "object", - "properties": { - "version": { - "type": "number" - }, - "publicUrl": { - "type": "string" - }, - "enforceDate": { - "type": "string" - }, - "createdAt": { - "type": "string" - } - }, - "required": [ - "version", - "publicUrl", - "enforceDate", - "createdAt" - ] - }, - "TermsOfUseStatus": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "pending", - "required", - "ok" - ] - }, - "lastSigned": { - "$ref": "#/components/schemas/TermsOfUse" - }, - "next": { - "$ref": "#/components/schemas/TermsOfUse" - } - }, - "required": [ - "status" - ] - }, - "AuthSignInRes": { - "type": "object", - "properties": { - "permissions": { - "type": "array", - "items": { - "type": "string" - } - }, - "mfaStatus": { - "type": "string", - "enum": [ - "pending", - "none", - "totp" - ] - }, - "customer": { - "$ref": "#/components/schemas/AuthCustomer" - }, - "user": { - "$ref": "#/components/schemas/AuthUser" - }, - "tokens": { - "$ref": "#/components/schemas/AuthTokens" - }, - "termsOfUse": { - "$ref": "#/components/schemas/TermsOfUseStatus" - } - }, - "required": [ - "permissions", - "mfaStatus" - ] - }, "AuthRefreshAccessTokenReq": { "type": "object", "properties": { diff --git a/environment.d.ts b/environment.d.ts index 3558f26..3b44250 100644 --- a/environment.d.ts +++ b/environment.d.ts @@ -15,6 +15,7 @@ declare global { OPEN_GROUP_ID: string; OPEN_CUSTOMER_ID: string; DEDICATED_PROXY: string; + COOKIE_SECRET: string; } } } diff --git a/package-lock.json b/package-lock.json index 4092f52..b491a76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "cache-manager-ioredis-yet": "^1.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "cookie-parser": "^1.4.7", "cron-parser": "^4.9.0", "csv": "^6.3.11", "dotenv": "^14.3.2", @@ -56,6 +57,7 @@ "swagger-ui-express": "^4.6.3" }, "devDependencies": { + "@types/cookie-parser": "^1.4.9", "@types/cache-manager": "^4.0.6", "@types/express": "^4.17.17", "@types/express-session": "^1.18.1", @@ -3742,6 +3744,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", @@ -5872,6 +5884,28 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index ac37a76..755c52f 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "cache-manager-ioredis-yet": "^1.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", + "cookie-parser": "^1.4.7", "cron-parser": "^4.9.0", "csv": "^6.3.11", "dotenv": "^14.3.2", @@ -77,6 +78,7 @@ "multer": "1.4.5-lts.1" }, "devDependencies": { + "@types/cookie-parser": "^1.4.9", "@types/cache-manager": "^4.0.6", "@types/express": "^4.17.17", "@types/express-session": "^1.18.1", diff --git a/src/main.ts b/src/main.ts index 4065312..ee5a1f2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { AppModule } from './app.module'; import { writeFileSync } from 'fs'; import { execSync } from 'child_process'; import { INestApplication } from '@nestjs/common'; +import cookieParser from 'cookie-parser'; async function bootstrap() { DadosferaLogger.setupLogger({ @@ -24,10 +25,11 @@ async function bootstrap() { methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, optionsSuccessStatus: 204, + credentials: true }, }); app.use(helmet()); - + app.use(cookieParser(process.env.COOKIE_SECRET)); if (process.env.ENV === 'prd') { app.use('/catalog/register-dataset', json({ limit: '10mb' })); app.use('/catalog/register-dataset', urlencoded({ extended: true, limit: '10mb' })); diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index e2f5e40..d32a2d7 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -12,6 +12,7 @@ import { Redirect, Req, Param, + Res, } from '@nestjs/common'; import { ApiHeaders, @@ -47,12 +48,19 @@ import { } from './dtos/login'; import { PackTheMetadata } from 'src/utils/PackTheMetadata'; import { AuthGuard } from '@nestjs/passport'; -import { Request } from 'express'; +import { Request, Response } from 'express'; import ErrorCodes, { OauthErrors } from 'src/utils/errorCodes'; -import jwt from 'jsonwebtoken'; +import jwt, { JwtPayload } from 'jsonwebtoken'; import { LanguageEnum } from 'src/utils/languages.enum'; import { Language } from 'src/decorators/language.decorator'; import { ApiInternalOnlyEndpoint } from 'src/decorators/swagger.decorator'; +import { Cookie } from 'express-session'; + +type CookiesValues = { + accessToken?: string; + refreshToken?: string; + userId?: string +} @ApiTags('Auth') @ApiHeaders([{ name: 'dadosfera-lang', enum: LanguageEnum, required: false }]) @@ -86,11 +94,65 @@ export class AuthController { async signIn( @Body() { username, password, totp }: AuthSignInReq, @Language() language: LanguageEnum, - ): Promise { - this.logger.info('/auth - SignIn'); - const metadata = PackTheMetadata({ language }); - this.logger.info('metadata: ' + JSON.stringify(metadata.toJSON())); - return this.authClient.signIn({ username, password, totp }, metadata); + @Res() res: Response, + ) { + try { + this.logger.info('/auth - SignIn'); + const metadata = PackTheMetadata({ language }); + this.logger.info('metadata: ' + JSON.stringify(metadata.toJSON())); + const data = await this.authClient.signIn({ username, password, totp }, metadata); + + if (data.tokens) { + this.addTokenInCookie(res, { + accessToken: data.tokens.accessToken, + refreshToken: data.tokens.refreshToken, + userId: data.user.id + }); + } + + + return res.send(data); + } catch (error) { + this.logger.error('/auth - SignIn - ERROR', error); + } + + } + + @Post('sign-out') + @HttpCode(HttpStatus.NO_CONTENT) + async signOut( + @Language() language: LanguageEnum, + @Res() res: Response, + ) { + try { + this.logger.info('/auth - SignOut'); + const exp = 1000 * 60 * 3; + + res.cookie('ddf-auth', '', { + domain: 'dadosfera.local', + maxAge: Date.now() - exp, + expires: new Date(), + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + + res.cookie('ddf-refresh-auth', '', { + domain: 'dadosfera.local', + maxAge: Date.now() - exp, + expires: new Date(), + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + + this.logger.info('Clean cookie sessions'); + + return res.send(); + } catch (error) { + this.logger.error('/auth - SignIn - ERROR', error); + } + } @Post('refresh-access-token') @@ -100,16 +162,25 @@ export class AuthController { @Body() body: AuthRefreshAccessTokenReq, @Language() language: LanguageEnum, @Headers('origin') origin: string, + @Res() res: Response, ) { this.logger.info('/auth - RefreshAccessToken'); - const { refreshToken, userId } = body; const frontHost = origin.replace(/^https?:\/\//, ''); + const { refreshToken, userId } = body; + const metadata = PackTheMetadata({ language, custom_host: frontHost, }); - return this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); + const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); + + this.addTokenInCookie(res, { + accessToken: data.accessToken, + userId + }); + + return res.send(data); } @ApiInternalOnlyEndpoint() @@ -418,4 +489,117 @@ export class AuthController { return this.authClient.resetUsers(body.users, metadata); } + @Get('me') + async getMe(@Req() req: Request, @Res() res: Response) { + this.logger.info('GET /auth/me ') + // Lê cookies + const accessToken = req.cookies['ddf-auth']; + const userId = req.cookies['ddf-user-id']; + + this.logger.info('Has cookie: ' + Boolean(accessToken)) + let payload: any; + let userInfo: any = {}; + try { + // Decodifica e valida o JWT de acesso + const decoded: any = accessToken && jwt.decode(accessToken, { complete: true }); + if (!decoded) throw new Error('Invalid token') + const { kid } = decoded.header; + // Busca a chave pública + const { keys } = await this.authClient.getPublicKeys(); + const pemValue = keys.find((k) => k.kid === kid)?.pem; + if (!pemValue) throw new Error('Public key not found'); + jwt.verify(accessToken, pemValue); + payload = decoded.payload; + userInfo = { + id: payload.user_id, + name: payload.username, + customer: { + id: payload.customer_id, + name: payload.customer_name, + tier: payload.customer_tier, + } + }; + return res.status(200).json(userInfo); + } catch (err) { + this.logger.error(err.message); + const refreshToken = req.cookies['ddf-refresh-auth']; + + this.logger.info('Token is invalid') + this.logger.info('Has Refresh Token: '+ Boolean(refreshToken)) + // Se access token inválido, tenta refresh + if (!refreshToken || !userId) { + this.logger.error('Invalid refresh token or customer name'); + return res.status(401).json({ error: 'Not authenticated' }); + } + try { + // Chama refreshAccessToken + const metadata = PackTheMetadata({ + }); + this.logger.info('Call Refresh Token') + const data = await this.authClient.refreshAccessToken({ refreshToken, userId }, metadata); + this.logger.info('Finish Refresh Token') + // Retorna novo access token e dados mínimos + this.addTokenInCookie(res, { + accessToken: data.accessToken, + userId + }); + // Decodifica novo token + const decoded: any = jwt.decode(data.accessToken, { complete: true }); + const payload = decoded.payload; + userInfo = { + id: payload.user_id, + name: payload.username, + customer: { + id: payload.customer_id, + name: payload.customer_name, + tier: payload.customer_tier, + } + }; + return res.status(200).json(userInfo); + } catch (refreshErr) { + this.logger.error(refreshErr) + return res.status(401).json({ error: 'Not authenticated' }); + } + } + } + + private addTokenInCookie(res: Response, data: CookiesValues) { + let exp = 1000 * 60 * 5; // 5 minutes + + if (data.accessToken) { + const { exp: expiration } = jwt.decode(data.accessToken) as JwtPayload; + exp = (expiration - 30) * 1000; // exp em segundos, maxAge em ms + + this.logger.info('Set Cookie ddf-auth') + res.cookie('ddf-auth', data.accessToken, { + domain: 'stg.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + } + + if (data.refreshToken) { + this.logger.info('Set Cookie ddf-refresh-auth') + res.cookie('ddf-refresh-auth', data.refreshToken, { + domain: 'stg.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + } + + if (data.userId) { + this.logger.info('Set Cookie ddf-refresh-auth') + res.cookie('ddf-user-id', data.userId, { + domain: 'stg.dadosfera.ai', + maxAge: exp, + httpOnly: true, + secure: true, + sameSite: 'none', // Necessário para cookies em requisições cross-site + }); + } + } } diff --git a/src/modules/identity-provider/identity-provider.controller.ts b/src/modules/identity-provider/identity-provider.controller.ts index de22e1c..c612f4e 100644 --- a/src/modules/identity-provider/identity-provider.controller.ts +++ b/src/modules/identity-provider/identity-provider.controller.ts @@ -153,12 +153,27 @@ export class IdentityProviderController { throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE); } - const origin = req.headers['origin'] as string; + try { + const origin = req.headers['origin'] as string; + this.logger.info('Header Origin: ' + origin); - const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); - const callbackUrl = process.env.ENV !== "prd" ? `${origin}/auth/callback` : `${origin}/${lang}/auth/callback`; + const lang = + language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = + process.env.ENV !== 'prd' + ? `${origin}/auth/callback` + : `${origin}/${lang}/auth/callback`; - return await this.identityProviderService.getTokenByIdp(code, state, callbackUrl); + this.logger.info('Callback URL: ' + callbackUrl); + return await this.identityProviderService.getTokenByIdp( + code, + state, + callbackUrl, + ); + } catch (error) { + this.logger.error(error); + throw error; + } } @Get('/links') @@ -166,41 +181,66 @@ export class IdentityProviderController { async providerLinks(@Req() req: Request) { this.logger.info('GET /identity-providers/links'); - const frontDomain = req.headers['origin'] as string; + try { + const frontDomain = req.headers['origin'] as string; + this.logger.info('Header Origin: ' + frontDomain); - if (!frontDomain) { - throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); + if (!frontDomain) { + this.logger.info('Not found front domain'); + throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); + } + + const result = + await this.identityProviderService.identityProvidersLinksPerDomain( + frontDomain, + ); + return result; + } catch (error) { + this.logger.error(error); + throw error; } - - const result = - await this.identityProviderService.identityProvidersLinksPerDomain( - frontDomain, - ); - return result; } @Get(':id') @HttpCode(HttpStatus.OK) @Redirect() - async loginIdp(@Param('id') id: string, @Req() req: Request, @Language() language: LanguageEnum) { + async loginIdp( + @Param('id') id: string, + @Req() req: Request, + @Language() language: LanguageEnum, + ) { this.logger.info('GET /identity-providers/:id'); + try { + const frontDomain = + (req.headers['origin'] as string) || (req.headers['referer'] as string); + this.logger.info(`Front domain: ${frontDomain}`); + const host = + frontDomain.lastIndexOf('/') !== -1 + ? frontDomain.substring(0, frontDomain.lastIndexOf('/')) + : frontDomain; - const frontDomain = req.headers['origin'] as string || req.headers['referer'] as string; - this.logger.info(`Front domain: ${frontDomain}`); - const host = frontDomain.lastIndexOf('/') !== -1 - ? frontDomain.substring(0, frontDomain.lastIndexOf('/')) - : frontDomain; + const lang = + language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = + process.env.ENV !== 'prd' + ? `${host}/auth/callback` + : `${host}/${lang}/auth/callback`; - const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); - const callbackUrl = process.env.ENV !== "prd" ? `${host}/auth/callback` : `${host}/${lang}/auth/callback`; + this.logger.info('Callback URL: ' + callbackUrl); + const redirectUrl = + await this.identityProviderService.loginIdentityProvider( + id, + callbackUrl, + ); - const redirectUrl = - await this.identityProviderService.loginIdentityProvider(id, callbackUrl); - - this.logger.info(`Redirecting to: ${redirectUrl}`); - return { - url: redirectUrl, - }; + this.logger.info(`Redirecting to: ${redirectUrl}`); + return { + url: redirectUrl, + }; + } catch (error) { + this.logger.error(error); + throw error; + } } } diff --git a/src/modules/identity-provider/identity-provider.service.ts b/src/modules/identity-provider/identity-provider.service.ts index 4cca0fe..a2e7084 100644 --- a/src/modules/identity-provider/identity-provider.service.ts +++ b/src/modules/identity-provider/identity-provider.service.ts @@ -14,16 +14,21 @@ import { Metadata } from '@grpc/grpc-js'; import { Issuer, generators } from 'openid-client'; import { SsoSignInDto } from './dto/sso-signin.dto'; import { CacheService } from 'src/services/cache.service'; -import { Request } from 'express'; import { CreateIdentityProvider } from './dto/identity-provider.dto'; +import DadosferaLogger from '@dadosfera/dadosfera-logs'; @Injectable() export class IdentityProviderService implements OnModuleInit { + private logger: DadosferaLogger; private identityProviderService: IdentityProviderProtoService; constructor( @Inject(DucClient.name) private readonly grpcClient: ClientGrpc, private readonly cacheService: CacheService, - ) {} + @Inject(DadosferaLogger) + private dadosferaLoggger: DadosferaLogger + ) { + this.logger = dadosferaLoggger.logger; + } onModuleInit() { this.identityProviderService = @@ -33,22 +38,26 @@ export class IdentityProviderService implements OnModuleInit { } async create(body: IdentityProviderRequest, metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC Create") return await lastValueFrom( this.identityProviderService.Create(body, metadata), ); } async getList(metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC GetList") return await lastValueFrom( this.identityProviderService.GetList({}, metadata), ); } async loginIdentityProvider(id: string, callbackUrl: string) { + this.logger.info("Call IdentityProvider GRPC FindIdentityProvider with: " + id); const idp = await lastValueFrom( this.identityProviderService.FindIdentityProvider({ id }), ); + this.logger.info("Discovery issueURL: " + idp.issuerUrl) const issuer = await Issuer.discover(idp.issuerUrl); const client = new issuer.Client({ client_id: idp.clientId, @@ -57,14 +66,19 @@ export class IdentityProviderService implements OnModuleInit { response_types: ['code'], }); + this.logger.info("Generate Challenge") const code_verifier: string = generators.codeVerifier(); const code_challenge: string = generators.codeChallenge(code_verifier); + this.logger.info("Generate State") const state = generators.state(); + + this.logger.info("Generate Nonce") const nonce = generators.nonce(); // Using state because it is returned in the callback // and we can use it to retrieve the code_verifier and nonce + this.logger.info("Save Login parameters in redis") await this.cacheService.set(state, { codeVerifier: code_verifier, nonce, @@ -76,6 +90,7 @@ export class IdentityProviderService implements OnModuleInit { redirectUrls: idp.redirectUrls, }); + this.logger.info("Generate Authorization URL") const url = client.authorizationUrl({ scope: 'openid email', response_type: 'code', @@ -86,16 +101,20 @@ export class IdentityProviderService implements OnModuleInit { redirect_uri: callbackUrl, }); const idpUrl = url + '&identity_provider=' + idp.name; + this.logger.info(idpUrl) return idpUrl; } async getTokenByIdp(code: string, state: string, callbackUrl: string) { + this.logger.info("Get login parameters in redis") const ssoSign = await this.cacheService.get(state); if (!ssoSign) { + this.logger.info("Login Parameters Not Found") throw new BadRequestException('SSO sign-in is expired or not found'); } + this.logger.info("Discovery Issue URL: " + ssoSign.issuerUrl) const issuer = await Issuer.discover(ssoSign.issuerUrl); const client = new issuer.Client({ client_id: ssoSign.clientId, @@ -103,22 +122,22 @@ export class IdentityProviderService implements OnModuleInit { redirect_uris: ssoSign.redirectUrls, }); - if (ssoSign === null) { - throw new BadRequestException('SSO sign-in is expired or not found'); - } - const params = client.callbackParams( `${callbackUrl}?code=${code}&state=${state}`, ); try { + + this.logger.info("Get Token Set"); const tokenSet = await client.callback(callbackUrl, params, { nonce: ssoSign.nonce, code_verifier: ssoSign.codeVerifier, state: ssoSign.state }); + this.logger.info("Delete parameters in redis"); await this.cacheService.delete(ssoSign.state); + this.logger.info("Call IdentityProvider GRPC SignInUser"); return await lastValueFrom( this.identityProviderService.SignInUser({ accessToken: tokenSet.access_token, @@ -128,12 +147,13 @@ export class IdentityProviderService implements OnModuleInit { }), ); } catch (error) { - console.log(error); + this.logger.error(error); throw error; } } async deleteIdentityProvider(id: string, metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC Delete with: " + id) return await lastValueFrom( this.identityProviderService.DeleteIdentityProvider({ id }, metadata), ); @@ -144,6 +164,7 @@ export class IdentityProviderService implements OnModuleInit { body: CreateIdentityProvider, metadata: Metadata, ) { + this.logger.info("Call IdentityProvider GRPC Update with: " + id) return await lastValueFrom( this.identityProviderService.UpdateIdentityProvider( { @@ -156,6 +177,7 @@ export class IdentityProviderService implements OnModuleInit { } async identityProvidersLinksPerDomain(frontDomain: string) { + this.logger.info("Call IdentityProvider GRPC LinksPerDomain with: " + frontDomain) return await lastValueFrom( this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }), ); diff --git a/src/services/service.module.ts b/src/services/service.module.ts index 8152519..e78846e 100644 --- a/src/services/service.module.ts +++ b/src/services/service.module.ts @@ -7,7 +7,7 @@ import { redisStore } from 'cache-manager-ioredis-yet'; CacheModule.registerAsync({ useFactory: async () => ({ store: await redisStore({ - ttl: 1000 * 60, //1 minute + ttl: 5 * 1000 * 60, // 5 minute host: process.env.REDIS_HOST, port: process.env.REDIS_PORT && Number(process.env.REDIS_PORT), db: process.env.REDIS_DATABASE && Number(process.env.REDIS_DATABASE),