Compare commits
15 Commits
feat/migra
...
feat-cherr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb067feaf | ||
|
|
0b5f57436f | ||
|
|
0c0ed5610e | ||
|
|
dce032a275 | ||
|
|
2599b1d326 | ||
|
|
419e376f46 | ||
|
|
c79fcbeceb | ||
|
|
092cb4c3a5 | ||
|
|
d3f25361f7 | ||
|
|
09c13e86b2 | ||
|
|
04bb219c12 | ||
|
|
ca79cf40b1 | ||
|
|
454c82975e | ||
|
|
c38488dfc4 | ||
|
|
2d0ebedbbb |
@@ -13,6 +13,7 @@ SESSION_SECRET='add some secret here'
|
|||||||
# Hoppscotch App Domain Config
|
# Hoppscotch App Domain Config
|
||||||
REDIRECT_URL="http://localhost:3000"
|
REDIRECT_URL="http://localhost:3000"
|
||||||
WHITELISTED_ORIGINS = "http://localhost:3170,http://localhost:3000,http://localhost:3100"
|
WHITELISTED_ORIGINS = "http://localhost:3170,http://localhost:3000,http://localhost:3100"
|
||||||
|
ALLOWED_AUTH_PROVIDERS = GOOGLE,GITHUB,MICROSOFT,EMAIL
|
||||||
|
|
||||||
# Google Auth Config
|
# Google Auth Config
|
||||||
GOOGLE_CLIENT_ID="************************************************"
|
GOOGLE_CLIENT_ID="************************************************"
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import {
|
|||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
|
InternalServerErrorException,
|
||||||
Post,
|
Post,
|
||||||
Query,
|
Query,
|
||||||
Req,
|
|
||||||
Request,
|
Request,
|
||||||
Res,
|
Res,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
@@ -19,12 +19,18 @@ import { JwtAuthGuard } from './guards/jwt-auth.guard';
|
|||||||
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
import { GqlUser } from 'src/decorators/gql-user.decorator';
|
||||||
import { AuthUser } from 'src/types/AuthUser';
|
import { AuthUser } from 'src/types/AuthUser';
|
||||||
import { RTCookie } from 'src/decorators/rt-cookie.decorator';
|
import { RTCookie } from 'src/decorators/rt-cookie.decorator';
|
||||||
import { authCookieHandler, throwHTTPErr } from './helper';
|
import {
|
||||||
|
AuthProvider,
|
||||||
|
authCookieHandler,
|
||||||
|
authProviderCheck,
|
||||||
|
throwHTTPErr,
|
||||||
|
} from './helper';
|
||||||
import { GoogleSSOGuard } from './guards/google-sso.guard';
|
import { GoogleSSOGuard } from './guards/google-sso.guard';
|
||||||
import { GithubSSOGuard } from './guards/github-sso.guard';
|
import { GithubSSOGuard } from './guards/github-sso.guard';
|
||||||
import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard';
|
import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard';
|
||||||
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
|
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
|
||||||
import { SkipThrottle } from '@nestjs/throttler';
|
import { SkipThrottle } from '@nestjs/throttler';
|
||||||
|
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||||
|
|
||||||
@UseGuards(ThrottlerBehindProxyGuard)
|
@UseGuards(ThrottlerBehindProxyGuard)
|
||||||
@Controller({ path: 'auth', version: '1' })
|
@Controller({ path: 'auth', version: '1' })
|
||||||
@@ -39,6 +45,9 @@ export class AuthController {
|
|||||||
@Body() authData: SignInMagicDto,
|
@Body() authData: SignInMagicDto,
|
||||||
@Query('origin') origin: string,
|
@Query('origin') origin: string,
|
||||||
) {
|
) {
|
||||||
|
if (!authProviderCheck(AuthProvider.EMAIL))
|
||||||
|
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
|
||||||
|
|
||||||
const deviceIdToken = await this.authService.signInMagicLink(
|
const deviceIdToken = await this.authService.signInMagicLink(
|
||||||
authData.email,
|
authData.email,
|
||||||
origin,
|
origin,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { RTJwtStrategy } from './strategies/rt-jwt.strategy';
|
|||||||
import { GoogleStrategy } from './strategies/google.strategy';
|
import { GoogleStrategy } from './strategies/google.strategy';
|
||||||
import { GithubStrategy } from './strategies/github.strategy';
|
import { GithubStrategy } from './strategies/github.strategy';
|
||||||
import { MicrosoftStrategy } from './strategies/microsoft.strategy';
|
import { MicrosoftStrategy } from './strategies/microsoft.strategy';
|
||||||
|
import { AuthProvider, authProviderCheck } from './helper';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -26,9 +27,9 @@ import { MicrosoftStrategy } from './strategies/microsoft.strategy';
|
|||||||
AuthService,
|
AuthService,
|
||||||
JwtStrategy,
|
JwtStrategy,
|
||||||
RTJwtStrategy,
|
RTJwtStrategy,
|
||||||
GoogleStrategy,
|
...(authProviderCheck(AuthProvider.GOOGLE) ? [GoogleStrategy] : []),
|
||||||
GithubStrategy,
|
...(authProviderCheck(AuthProvider.GITHUB) ? [GithubStrategy] : []),
|
||||||
MicrosoftStrategy,
|
...(authProviderCheck(AuthProvider.MICROSOFT) ? [MicrosoftStrategy] : []),
|
||||||
],
|
],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GithubSSOGuard extends AuthGuard('github') {
|
export class GithubSSOGuard extends AuthGuard('github') implements CanActivate {
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
if (!authProviderCheck(AuthProvider.GITHUB))
|
||||||
|
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
|
||||||
|
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
|
||||||
getAuthenticateOptions(context: ExecutionContext) {
|
getAuthenticateOptions(context: ExecutionContext) {
|
||||||
const req = context.switchToHttp().getRequest();
|
const req = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GoogleSSOGuard extends AuthGuard('google') {
|
export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate {
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
if (!authProviderCheck(AuthProvider.GOOGLE))
|
||||||
|
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
|
||||||
|
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
|
||||||
getAuthenticateOptions(context: ExecutionContext) {
|
getAuthenticateOptions(context: ExecutionContext) {
|
||||||
const req = context.switchToHttp().getRequest();
|
const req = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
import { ExecutionContext, Injectable } from '@nestjs/common';
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MicrosoftSSOGuard extends AuthGuard('microsoft') {
|
export class MicrosoftSSOGuard
|
||||||
|
extends AuthGuard('microsoft')
|
||||||
|
implements CanActivate
|
||||||
|
{
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
if (!authProviderCheck(AuthProvider.MICROSOFT))
|
||||||
|
throwHTTPErr({
|
||||||
|
message: AUTH_PROVIDER_NOT_SPECIFIED,
|
||||||
|
statusCode: 404,
|
||||||
|
});
|
||||||
|
|
||||||
|
return super.canActivate(context);
|
||||||
|
}
|
||||||
|
|
||||||
getAuthenticateOptions(context: ExecutionContext) {
|
getAuthenticateOptions(context: ExecutionContext) {
|
||||||
const req = context.switchToHttp().getRequest();
|
const req = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { ForbiddenException, HttpException, HttpStatus } from '@nestjs/common';
|
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { AuthError } from 'src/types/AuthError';
|
import { AuthError } from 'src/types/AuthError';
|
||||||
import { AuthTokens } from 'src/types/AuthTokens';
|
import { AuthTokens } from 'src/types/AuthTokens';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import * as cookie from 'cookie';
|
import * as cookie from 'cookie';
|
||||||
import { COOKIES_NOT_FOUND } from 'src/errors';
|
import { AUTH_PROVIDER_NOT_SPECIFIED, COOKIES_NOT_FOUND } from 'src/errors';
|
||||||
|
import { throwErr } from 'src/utils';
|
||||||
|
|
||||||
enum AuthTokenType {
|
enum AuthTokenType {
|
||||||
ACCESS_TOKEN = 'access_token',
|
ACCESS_TOKEN = 'access_token',
|
||||||
@@ -16,6 +17,13 @@ export enum Origin {
|
|||||||
APP = 'app',
|
APP = 'app',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AuthProvider {
|
||||||
|
GOOGLE = 'GOOGLE',
|
||||||
|
GITHUB = 'GITHUB',
|
||||||
|
MICROSOFT = 'MICROSOFT',
|
||||||
|
EMAIL = 'EMAIL',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function allows throw to be used as an expression
|
* This function allows throw to be used as an expression
|
||||||
* @param errMessage Message present in the error message
|
* @param errMessage Message present in the error message
|
||||||
@@ -97,3 +105,25 @@ export const subscriptionContextCookieParser = (rawCookies: string) => {
|
|||||||
refresh_token: cookies[AuthTokenType.REFRESH_TOKEN],
|
refresh_token: cookies[AuthTokenType.REFRESH_TOKEN],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if given auth provider is present in the ALLOWED_AUTH_PROVIDERS env variable
|
||||||
|
*
|
||||||
|
* @param provider Provider we want to check the presence of
|
||||||
|
* @returns Boolean if provider specified is present or not
|
||||||
|
*/
|
||||||
|
export function authProviderCheck(provider: string) {
|
||||||
|
if (!provider) {
|
||||||
|
throwErr(AUTH_PROVIDER_NOT_SPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const envVariables = process.env.ALLOWED_AUTH_PROVIDERS
|
||||||
|
? process.env.ALLOWED_AUTH_PROVIDERS.split(',').map((provider) =>
|
||||||
|
provider.trim().toUpperCase(),
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!envVariables.includes(provider.toUpperCase())) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,6 +22,30 @@ export const AUTH_FAIL = 'auth/fail';
|
|||||||
*/
|
*/
|
||||||
export const JSON_INVALID = 'json_invalid';
|
export const JSON_INVALID = 'json_invalid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth Provider not specified
|
||||||
|
* (Auth)
|
||||||
|
*/
|
||||||
|
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment variable "ALLOWED_AUTH_PROVIDERS" is not present in .env file
|
||||||
|
*/
|
||||||
|
export const ENV_NOT_FOUND_KEY_AUTH_PROVIDERS =
|
||||||
|
'"ALLOWED_AUTH_PROVIDERS" is not present in .env file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment variable "ALLOWED_AUTH_PROVIDERS" is empty in .env file
|
||||||
|
*/
|
||||||
|
export const ENV_EMPTY_AUTH_PROVIDERS =
|
||||||
|
'"ALLOWED_AUTH_PROVIDERS" is empty in .env file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment variable "ALLOWED_AUTH_PROVIDERS" contains unsupported provider in .env file
|
||||||
|
*/
|
||||||
|
export const ENV_NOT_SUPPORT_AUTH_PROVIDERS =
|
||||||
|
'"ALLOWED_AUTH_PROVIDERS" contains an unsupported auth provider in .env file';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tried to delete a user data document from fb firestore but failed.
|
* Tried to delete a user data document from fb firestore but failed.
|
||||||
* (FirebaseService)
|
* (FirebaseService)
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import * as cookieParser from 'cookie-parser';
|
|||||||
import { VersioningType } from '@nestjs/common';
|
import { VersioningType } from '@nestjs/common';
|
||||||
import * as session from 'express-session';
|
import * as session from 'express-session';
|
||||||
import { emitGQLSchemaFile } from './gql-schema';
|
import { emitGQLSchemaFile } from './gql-schema';
|
||||||
|
import { checkEnvironmentAuthProvider } from './utils';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
console.log(`Running in production: ${process.env.PRODUCTION}`);
|
console.log(`Running in production: ${process.env.PRODUCTION}`);
|
||||||
console.log(`Port: ${process.env.PORT}`);
|
console.log(`Port: ${process.env.PORT}`);
|
||||||
|
|
||||||
|
checkEnvironmentAuthProvider();
|
||||||
|
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import * as E from 'fp-ts/Either';
|
|||||||
import * as A from 'fp-ts/Array';
|
import * as A from 'fp-ts/Array';
|
||||||
import { TeamMemberRole } from './team/team.model';
|
import { TeamMemberRole } from './team/team.model';
|
||||||
import { User } from './user/user.model';
|
import { User } from './user/user.model';
|
||||||
import { JSON_INVALID } from './errors';
|
import { ENV_EMPTY_AUTH_PROVIDERS, ENV_NOT_FOUND_KEY_AUTH_PROVIDERS, ENV_NOT_SUPPORT_AUTH_PROVIDERS, JSON_INVALID } from './errors';
|
||||||
|
import { AuthProvider } from './auth/helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A workaround to throw an exception in an expression.
|
* A workaround to throw an exception in an expression.
|
||||||
@@ -152,3 +153,31 @@ export function isValidLength(title: string, length: number) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called by bootstrap() in main.ts
|
||||||
|
* It checks if the "ALLOWED_AUTH_PROVIDERS" environment variable is properly set or not.
|
||||||
|
* If not, it throws an error.
|
||||||
|
*/
|
||||||
|
export function checkEnvironmentAuthProvider() {
|
||||||
|
if (!process.env.hasOwnProperty('ALLOWED_AUTH_PROVIDERS')) {
|
||||||
|
throw new Error(ENV_NOT_FOUND_KEY_AUTH_PROVIDERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.ALLOWED_AUTH_PROVIDERS === '') {
|
||||||
|
throw new Error(ENV_EMPTY_AUTH_PROVIDERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
const givenAuthProviders = process.env.ALLOWED_AUTH_PROVIDERS.split(',').map(
|
||||||
|
(provider) => provider.toLocaleUpperCase(),
|
||||||
|
);
|
||||||
|
const supportedAuthProviders = Object.values(AuthProvider).map(
|
||||||
|
(provider: string) => provider.toLocaleUpperCase(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const givenAuthProvider of givenAuthProviders) {
|
||||||
|
if (!supportedAuthProviders.includes(givenAuthProvider)) {
|
||||||
|
throw new Error(ENV_NOT_SUPPORT_AUTH_PROVIDERS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
620
pnpm-lock.yaml
generated
620
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user