Compare commits

...
3 changed files with 98 additions and 36 deletions
@@ -153,12 +153,27 @@ export class IdentityProviderController {
throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE); 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 lang =
const callbackUrl = process.env.ENV !== "prd" ? `${origin}/auth/callback` : `${origin}/${lang}/auth/callback`; 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') @Get('/links')
@@ -166,41 +181,66 @@ export class IdentityProviderController {
async providerLinks(@Req() req: Request) { async providerLinks(@Req() req: Request) {
this.logger.info('GET /identity-providers/links'); 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) { if (!frontDomain) {
throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); 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') @Get(':id')
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@Redirect() @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'); 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; const lang =
this.logger.info(`Front domain: ${frontDomain}`); language.substring(0, 2) + language.substring(2).toUpperCase();
const host = frontDomain.lastIndexOf('/') !== -1 const callbackUrl =
? frontDomain.substring(0, frontDomain.lastIndexOf('/')) process.env.ENV !== 'prd'
: frontDomain; ? `${host}/auth/callback`
: `${host}/${lang}/auth/callback`;
const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); this.logger.info('Callback URL: ' + callbackUrl);
const callbackUrl = process.env.ENV !== "prd" ? `${host}/auth/callback` : `${host}/${lang}/auth/callback`; const redirectUrl =
await this.identityProviderService.loginIdentityProvider(
id,
callbackUrl,
);
const redirectUrl = this.logger.info(`Redirecting to: ${redirectUrl}`);
await this.identityProviderService.loginIdentityProvider(id, callbackUrl); return {
url: redirectUrl,
this.logger.info(`Redirecting to: ${redirectUrl}`); };
return { } catch (error) {
url: redirectUrl, this.logger.error(error);
}; throw error;
}
} }
} }
@@ -14,16 +14,21 @@ import { Metadata } from '@grpc/grpc-js';
import { Issuer, generators } from 'openid-client'; import { Issuer, generators } from 'openid-client';
import { SsoSignInDto } from './dto/sso-signin.dto'; import { SsoSignInDto } from './dto/sso-signin.dto';
import { CacheService } from 'src/services/cache.service'; import { CacheService } from 'src/services/cache.service';
import { Request } from 'express';
import { CreateIdentityProvider } from './dto/identity-provider.dto'; import { CreateIdentityProvider } from './dto/identity-provider.dto';
import DadosferaLogger from '@dadosfera/dadosfera-logs';
@Injectable() @Injectable()
export class IdentityProviderService implements OnModuleInit { export class IdentityProviderService implements OnModuleInit {
private logger: DadosferaLogger;
private identityProviderService: IdentityProviderProtoService; private identityProviderService: IdentityProviderProtoService;
constructor( constructor(
@Inject(DucClient.name) private readonly grpcClient: ClientGrpc, @Inject(DucClient.name) private readonly grpcClient: ClientGrpc,
private readonly cacheService: CacheService<SsoSignInDto>, private readonly cacheService: CacheService<SsoSignInDto>,
) {} @Inject(DadosferaLogger)
private dadosferaLoggger: DadosferaLogger
) {
this.logger = dadosferaLoggger.logger;
}
onModuleInit() { onModuleInit() {
this.identityProviderService = this.identityProviderService =
@@ -33,22 +38,26 @@ export class IdentityProviderService implements OnModuleInit {
} }
async create(body: IdentityProviderRequest, metadata: Metadata) { async create(body: IdentityProviderRequest, metadata: Metadata) {
this.logger.info("Call IdentityProvider GRPC Create")
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.Create(body, metadata), this.identityProviderService.Create(body, metadata),
); );
} }
async getList(metadata: Metadata) { async getList(metadata: Metadata) {
this.logger.info("Call IdentityProvider GRPC GetList")
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.GetList({}, metadata), this.identityProviderService.GetList({}, metadata),
); );
} }
async loginIdentityProvider(id: string, callbackUrl: string) { async loginIdentityProvider(id: string, callbackUrl: string) {
this.logger.info("Call IdentityProvider GRPC FindIdentityProvider with: " + id);
const idp = await lastValueFrom( const idp = await lastValueFrom(
this.identityProviderService.FindIdentityProvider({ id }), this.identityProviderService.FindIdentityProvider({ id }),
); );
this.logger.info("Discovery issueURL: " + idp.issuerUrl)
const issuer = await Issuer.discover(idp.issuerUrl); const issuer = await Issuer.discover(idp.issuerUrl);
const client = new issuer.Client({ const client = new issuer.Client({
client_id: idp.clientId, client_id: idp.clientId,
@@ -57,14 +66,19 @@ export class IdentityProviderService implements OnModuleInit {
response_types: ['code'], response_types: ['code'],
}); });
this.logger.info("Generate Challenge")
const code_verifier: string = generators.codeVerifier(); const code_verifier: string = generators.codeVerifier();
const code_challenge: string = generators.codeChallenge(code_verifier); const code_challenge: string = generators.codeChallenge(code_verifier);
this.logger.info("Generate State")
const state = generators.state(); const state = generators.state();
this.logger.info("Generate Nonce")
const nonce = generators.nonce(); const nonce = generators.nonce();
// Using state because it is returned in the callback // Using state because it is returned in the callback
// and we can use it to retrieve the code_verifier and nonce // 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, { await this.cacheService.set(state, {
codeVerifier: code_verifier, codeVerifier: code_verifier,
nonce, nonce,
@@ -76,6 +90,7 @@ export class IdentityProviderService implements OnModuleInit {
redirectUrls: idp.redirectUrls, redirectUrls: idp.redirectUrls,
}); });
this.logger.info("Generate Authorization URL")
const url = client.authorizationUrl({ const url = client.authorizationUrl({
scope: 'openid email', scope: 'openid email',
response_type: 'code', response_type: 'code',
@@ -86,16 +101,20 @@ export class IdentityProviderService implements OnModuleInit {
redirect_uri: callbackUrl, redirect_uri: callbackUrl,
}); });
const idpUrl = url + '&identity_provider=' + idp.name; const idpUrl = url + '&identity_provider=' + idp.name;
this.logger.info(idpUrl)
return idpUrl; return idpUrl;
} }
async getTokenByIdp(code: string, state: string, callbackUrl: string) { async getTokenByIdp(code: string, state: string, callbackUrl: string) {
this.logger.info("Get login parameters in redis")
const ssoSign = await this.cacheService.get(state); const ssoSign = await this.cacheService.get(state);
if (!ssoSign) { if (!ssoSign) {
this.logger.info("Login Parameters Not Found")
throw new BadRequestException('SSO sign-in is expired or 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 issuer = await Issuer.discover(ssoSign.issuerUrl);
const client = new issuer.Client({ const client = new issuer.Client({
client_id: ssoSign.clientId, client_id: ssoSign.clientId,
@@ -103,22 +122,22 @@ export class IdentityProviderService implements OnModuleInit {
redirect_uris: ssoSign.redirectUrls, redirect_uris: ssoSign.redirectUrls,
}); });
if (ssoSign === null) {
throw new BadRequestException('SSO sign-in is expired or not found');
}
const params = client.callbackParams( const params = client.callbackParams(
`${callbackUrl}?code=${code}&state=${state}`, `${callbackUrl}?code=${code}&state=${state}`,
); );
try { try {
this.logger.info("Get Token Set");
const tokenSet = await client.callback(callbackUrl, params, { const tokenSet = await client.callback(callbackUrl, params, {
nonce: ssoSign.nonce, nonce: ssoSign.nonce,
code_verifier: ssoSign.codeVerifier, code_verifier: ssoSign.codeVerifier,
state: ssoSign.state state: ssoSign.state
}); });
this.logger.info("Delete parameters in redis");
await this.cacheService.delete(ssoSign.state); await this.cacheService.delete(ssoSign.state);
this.logger.info("Call IdentityProvider GRPC SignInUser");
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.SignInUser({ this.identityProviderService.SignInUser({
accessToken: tokenSet.access_token, accessToken: tokenSet.access_token,
@@ -128,12 +147,13 @@ export class IdentityProviderService implements OnModuleInit {
}), }),
); );
} catch (error) { } catch (error) {
console.log(error); this.logger.error(error);
throw error; throw error;
} }
} }
async deleteIdentityProvider(id: string, metadata: Metadata) { async deleteIdentityProvider(id: string, metadata: Metadata) {
this.logger.info("Call IdentityProvider GRPC Delete with: " + id)
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.DeleteIdentityProvider({ id }, metadata), this.identityProviderService.DeleteIdentityProvider({ id }, metadata),
); );
@@ -144,6 +164,7 @@ export class IdentityProviderService implements OnModuleInit {
body: CreateIdentityProvider, body: CreateIdentityProvider,
metadata: Metadata, metadata: Metadata,
) { ) {
this.logger.info("Call IdentityProvider GRPC Update with: " + id)
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.UpdateIdentityProvider( this.identityProviderService.UpdateIdentityProvider(
{ {
@@ -156,6 +177,7 @@ export class IdentityProviderService implements OnModuleInit {
} }
async identityProvidersLinksPerDomain(frontDomain: string) { async identityProvidersLinksPerDomain(frontDomain: string) {
this.logger.info("Call IdentityProvider GRPC LinksPerDomain with: " + frontDomain)
return await lastValueFrom( return await lastValueFrom(
this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }), this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }),
); );
+1 -1
View File
@@ -7,7 +7,7 @@ import { redisStore } from 'cache-manager-ioredis-yet';
CacheModule.registerAsync({ CacheModule.registerAsync({
useFactory: async () => ({ useFactory: async () => ({
store: await redisStore({ store: await redisStore({
ttl: 1000 * 60, //1 minute ttl: 5 * 1000 * 60, // 5 minute
host: process.env.REDIS_HOST, host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT && Number(process.env.REDIS_PORT), port: process.env.REDIS_PORT && Number(process.env.REDIS_PORT),
db: process.env.REDIS_DATABASE && Number(process.env.REDIS_DATABASE), db: process.env.REDIS_DATABASE && Number(process.env.REDIS_DATABASE),