import { BadRequestException, Inject, Logger, UnauthorizedException } from '@nestjs/common'; import { ApplicationService } from '../application/application.service'; import { JwtService } from '@nestjs/jwt'; import { accessTokenJwtConstants } from './constants/access_token.constants'; import { IncomingAuthDto, IncomingAuthLoginDto } from './dto/incoming-auth.dto'; import { Injectable } from '@nestjs/common'; import { Application } from '../application/application.schema'; import { scopeMapper } from './minions/scopeMapper.minion'; import { UserService } from '../user/user.service'; import { User } from '../user/user.schema'; import { AccessUserDetailsDto } from './dto/access-user-details.dto'; import { AuthService } from '../auth/auth.service'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; @Injectable() export class AccountService { /** * Initialize account services */ constructor( private jwtService: JwtService, @Inject(ApplicationService) private readonly applicationService: ApplicationService, @Inject(UserService) private readonly userService: UserService, @Inject(AuthService) private readonly authService: AuthService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger = new Logger('accounts'), ) {} /** * To validate request from application whether to show signIn form or not */ async validateAccessRequest(incomingAuthDto: IncomingAuthDto): Promise<Application> { const { client_id, redirect_uri } = incomingAuthDto; /** check if given client exists, validate params */ const details = await this.applicationService.findOneById(client_id); if (details === null) { throw new BadRequestException('unknown application'); } /** check if redirect URI is one of the authorized URIs */ if (!details.authorizedRedirect.includes(redirect_uri)) { throw new BadRequestException('invalid redirected uri'); } details.scope = details.scope.map((givenScope) => scopeMapper(givenScope)); return details; } /** * To generate a new jwt containing access token */ async generateAccessToken(id: string) { /** now generate a new access token */ const tokenPayload = { token: String(id) }; const token = await this.jwtService.signAsync(tokenPayload, accessTokenJwtConstants); return token; } /** * Service to a user into servers */ async authenticateAndGenerateToken(incomingAuthDto: IncomingAuthLoginDto): Promise<{ token: string; user: User }> { /** check username and password */ const { email, password } = incomingAuthDto; const loggedInUser = await this.userService.login({ email, password }); this.logger.verbose(`Access granted by ${loggedInUser?.name}`); /** generate access token */ const accessToken = await this.generateAccessToken(loggedInUser?._id); return { token: accessToken, user: loggedInUser }; } /** * Function to provide response and data via oauth flow */ async provideUserDetailOnAccess(accessUserDetailsDto: AccessUserDetailsDto) { try { const { clientId, clientSecret, accessToken } = accessUserDetailsDto; /** ensure that application exists */ const applicationDetails = await this.applicationService.findOneByIdAndSecret(clientId, clientSecret); /** ensure that access_token is valid */ const { token } = await this.jwtService.verifyAsync(accessToken, accessTokenJwtConstants); /** get user details by id */ const userDetails = await this.userService.findOneById(token); const userDetailsJson = userDetails.toJSON(); const resultantSet = {}; /** map details as per application scope */ this.logger.verbose(`Application Scope: ${JSON.stringify(applicationDetails.scope)}`); Object.keys(userDetailsJson).forEach((key) => { if (applicationDetails.scope.includes(key)) { resultantSet[key] = userDetailsJson[key]; } }); this.logger.verbose(`Sharing details of ${userDetailsJson.name}, ${JSON.stringify(resultantSet)}`); return resultantSet; } catch (e) { throw new UnauthorizedException(e.message); } } }