refactor(web,server): use feature flags for oauth (#3928)

* refactor: oauth to use feature flags

* chore: open api

* chore: e2e test for authorize endpoint
This commit is contained in:
Jason Rasmussen 2023-09-01 07:08:42 -04:00 committed by GitHub
parent c7d53a5006
commit a26ed3d1a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 660 additions and 110 deletions

View file

@ -1,5 +1,12 @@
import { SystemConfig, UserEntity } from '@app/infra/entities';
import { BadRequestException, Inject, Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import {
BadRequestException,
Inject,
Injectable,
InternalServerErrorException,
Logger,
UnauthorizedException,
} from '@nestjs/common';
import cookieParser from 'cookie';
import { IncomingHttpHeaders } from 'http';
import { DateTime } from 'luxon';
@ -27,6 +34,7 @@ import {
mapAdminSignupResponse,
mapLoginResponse,
mapUserToken,
OAuthAuthorizeResponseDto,
OAuthConfigResponseDto,
} from './response-dto';
import { IUserTokenRepository } from './user-token.repository';
@ -201,6 +209,22 @@ export class AuthService {
return { ...response, buttonText, url, autoLaunch };
}
async authorize(dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
const config = await this.configCore.getConfig();
if (!config.oauth.enabled) {
throw new BadRequestException('OAuth is not enabled');
}
const client = await this.getOAuthClient(config);
const url = await client.authorizationUrl({
redirect_uri: this.normalize(config, dto.redirectUri),
scope: config.oauth.scope,
state: generators.state(),
});
return { url };
}
async callback(
dto: OAuthCallbackDto,
loginDetails: LoginDetails,
@ -280,8 +304,13 @@ export class AuthService {
const redirectUri = this.normalize(config, url.split('?')[0]);
const client = await this.getOAuthClient(config);
const params = client.callbackParams(url);
const tokens = await client.callback(redirectUri, params, { state: params.state });
return client.userinfo<OAuthProfile>(tokens.access_token || '');
try {
const tokens = await client.callback(redirectUri, params, { state: params.state });
return client.userinfo<OAuthProfile>(tokens.access_token || '');
} catch (error: Error | any) {
this.logger.error(`Unable to complete OAuth login: ${error}`, error?.stack);
throw new InternalServerErrorException(`Unable to complete OAuth login: ${error}`, { cause: error });
}
}
private async getOAuthClient(config: SystemConfig) {

View file

@ -1,9 +1,7 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class OAuthConfigDto {
@IsNotEmpty()
@IsString()
@ApiProperty()
redirectUri!: string;
}

View file

@ -5,3 +5,7 @@ export class OAuthConfigResponseDto {
buttonText?: string;
autoLaunch?: boolean;
}
export class OAuthAuthorizeResponseDto {
url!: string;
}

View file

@ -3,6 +3,7 @@ import {
AuthUserDto,
LoginDetails,
LoginResponseDto,
OAuthAuthorizeResponseDto,
OAuthCallbackDto,
OAuthConfigDto,
OAuthConfigResponseDto,
@ -31,12 +32,19 @@ export class OAuthController {
};
}
/** @deprecated use feature flags and /oauth/authorize */
@PublicRoute()
@Post('config')
generateConfig(@Body() dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
return this.service.generateConfig(dto);
}
@PublicRoute()
@Post('authorize')
authorizeOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
return this.service.authorize(dto);
}
@PublicRoute()
@Post('callback')
async callback(