Update JWT secret scheme, replace many secrets with one secret

This commit is contained in:
Tuan Dang
2023-10-19 15:53:36 +01:00
parent a304228961
commit 058712e8ec
16 changed files with 101 additions and 116 deletions
+10 -16
View File
@@ -5,20 +5,7 @@ ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# JWT # JWT
# Required secrets to sign JWT tokens # Required secrets to sign JWT tokens
JWT_SIGNUP_SECRET=3679e04ca949f914c03332aaaeba805a AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=
JWT_REFRESH_SECRET=5f2f3c8f0159068dc2bbb3a652a716ff
JWT_AUTH_SECRET=4be6ba5602e0fa0ac6ac05c3cd4d247f
JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200
JWT_SERVICE_TOKEN_SECRET=f32f716d70a42c5703f4656015e76200
JWT_PROVIDER_AUTH_SECRET=f32f716d70a42c5703f4656015e76201
# JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string
# describing a time span (e.g. 60, "2 days", "10h", "7d")
JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME=
JWT_SIGNUP_LIFETIME=
JWT_PROVIDER_AUTH_LIFETIME=
# MongoDB # MongoDB
# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref # Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref
@@ -68,5 +55,12 @@ SENTRY_DSN=
POSTHOG_HOST= POSTHOG_HOST=
POSTHOG_PROJECT_API_KEY= POSTHOG_PROJECT_API_KEY=
CLIENT_ID_GOOGLE= # SSO-specific variables
CLIENT_SECRET_GOOGLE= CLIENT_ID_GOOGLE_LOGIN=
CLIENT_SECRET_GOOGLE_LOGIN=
CLIENT_ID_GITHUB_LOGIN=
CLIENT_SECRET_GITHUB_LOGIN=
CLIENT_ID_GITLAB_LOGIN=
CLIENT_SECRET_GITLAB_LOGIN=
+2 -6
View File
@@ -17,17 +17,13 @@ export const getRootEncryptionKey = async () => {
} }
export const getInviteOnlySignup = async () => (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true" export const getInviteOnlySignup = async () => (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true"
export const getSaltRounds = async () => parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10; export const getSaltRounds = async () => parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10;
export const getAuthSecret = async () => (await client.getSecret("AUTH_SECRET")).secretValue; // new
export const getJwtAuthLifetime = async () => (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d"; export const getJwtAuthLifetime = async () => (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d";
export const getJwtAuthSecret = async () => (await client.getSecret("JWT_AUTH_SECRET")).secretValue;
export const getJwtMfaLifetime = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m"; export const getJwtMfaLifetime = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
export const getJwtMfaSecret = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
export const getJwtRefreshLifetime = async () => (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d"; export const getJwtRefreshLifetime = async () => (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d";
export const getJwtRefreshSecret = async () => (await client.getSecret("JWT_REFRESH_SECRET")).secretValue; export const getJwtServiceSecret = async () => (await client.getSecret("JWT_SERVICE_SECRET")).secretValue; // TODO: deprecate (related to ST V1)
export const getJwtServiceSecret = async () => (await client.getSecret("JWT_SERVICE_SECRET")).secretValue;
export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m"; export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m";
export const getJwtProviderAuthSecret = async () => (await client.getSecret("JWT_PROVIDER_AUTH_SECRET")).secretValue;
export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m"; export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
export const getJwtServiceTokenSecret = async () => (await client.getSecret("JWT_SERVICE_TOKEN_SECRET")).secretValue; export const getJwtServiceTokenSecret = async () => (await client.getSecret("JWT_SERVICE_TOKEN_SECRET")).secretValue;
export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue; export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production"; export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
+13 -6
View File
@@ -6,15 +6,18 @@ const jsrp = require("jsrp");
import { LoginSRPDetail, TokenVersion, User } from "../../models"; import { LoginSRPDetail, TokenVersion, User } from "../../models";
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth"; import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
import { checkUserDevice } from "../../helpers/user"; import { checkUserDevice } from "../../helpers/user";
import { ACTION_LOGIN, ACTION_LOGOUT } from "../../variables"; import {
ACTION_LOGIN,
ACTION_LOGOUT,
AuthTokenType
} from "../../variables";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { EELogService } from "../../ee/services"; import { EELogService } from "../../ee/services";
import { getUserAgentType } from "../../utils/posthog"; import { getUserAgentType } from "../../utils/posthog";
import { import {
getAuthSecret,
getHttpsEnabled, getHttpsEnabled,
getJwtAuthLifetime, getJwtAuthLifetime
getJwtAuthSecret,
getJwtRefreshSecret
} from "../../config"; } from "../../config";
import { ActorType } from "../../ee/models"; import { ActorType } from "../../ee/models";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
@@ -238,6 +241,7 @@ export const checkAuth = async (req: Request, res: Response) => {
* @returns * @returns
*/ */
export const getNewToken = async (req: Request, res: Response) => { export const getNewToken = async (req: Request, res: Response) => {
const refreshToken = req.cookies.jid; const refreshToken = req.cookies.jid;
if (!refreshToken) if (!refreshToken)
@@ -245,7 +249,9 @@ export const getNewToken = async (req: Request, res: Response) => {
message: "Failed to find refresh token in request cookies" message: "Failed to find refresh token in request cookies"
}); });
const decodedToken = <jwt.UserIDJwtPayload>jwt.verify(refreshToken, await getJwtRefreshSecret()); const decodedToken = <jwt.UserIDJwtPayload>jwt.verify(refreshToken, await getAuthSecret());
if (decodedToken.authTokenType !== AuthTokenType.REFRESH_TOKEN) throw UnauthorizedRequestError();
const user = await User.findOne({ const user = await User.findOne({
_id: decodedToken.userId _id: decodedToken.userId
@@ -268,12 +274,13 @@ export const getNewToken = async (req: Request, res: Response) => {
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.ACCESS_TOKEN,
userId: decodedToken.userId, userId: decodedToken.userId,
tokenVersionId: tokenVersion._id.toString(), tokenVersionId: tokenVersion._id.toString(),
accessVersion: tokenVersion.refreshVersion accessVersion: tokenVersion.refreshVersion
}, },
expiresIn: await getJwtAuthLifetime(), expiresIn: await getJwtAuthLifetime(),
secret: await getJwtAuthSecret() secret: await getAuthSecret()
}); });
return res.status(200).send({ return res.status(200).send({
@@ -8,11 +8,11 @@ import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
import { sendMail } from "../../helpers/nodemailer"; import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services"; import { TokenService } from "../../services";
import { EELicenseService } from "../../ee/services"; import { EELicenseService } from "../../ee/services";
import { ACCEPTED, INVITED, MEMBER, TOKEN_EMAIL_ORG_INVITATION } from "../../variables"; import { ACCEPTED, AuthTokenType, INVITED, MEMBER, TOKEN_EMAIL_ORG_INVITATION } from "../../variables";
import * as reqValidator from "../../validation/membershipOrg"; import * as reqValidator from "../../validation/membershipOrg";
import { import {
getAuthSecret,
getJwtSignupLifetime, getJwtSignupLifetime,
getJwtSignupSecret,
getSiteURL, getSiteURL,
getSmtpConfigured getSmtpConfigured
} from "../../config"; } from "../../config";
@@ -272,10 +272,11 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
// generate temporary signup token // generate temporary signup token
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.SIGNUP_TOKEN,
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: await getJwtSignupLifetime(), expiresIn: await getJwtSignupLifetime(),
secret: await getJwtSignupSecret() secret: await getAuthSecret()
}); });
return res.status(200).send({ return res.status(200).send({
@@ -5,12 +5,12 @@ import * as bigintConversion from "bigint-conversion";
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models"; import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
import { clearTokens, createToken, sendMail } from "../../helpers"; import { clearTokens, createToken, sendMail } from "../../helpers";
import { TokenService } from "../../services"; import { TokenService } from "../../services";
import { TOKEN_EMAIL_PASSWORD_RESET } from "../../variables"; import { AuthTokenType, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
import { BadRequestError } from "../../utils/errors"; import { BadRequestError } from "../../utils/errors";
import { import {
getAuthSecret,
getHttpsEnabled, getHttpsEnabled,
getJwtSignupLifetime, getJwtSignupLifetime,
getJwtSignupSecret,
getSiteURL getSiteURL
} from "../../config"; } from "../../config";
import { ActorType } from "../../ee/models"; import { ActorType } from "../../ee/models";
@@ -88,10 +88,11 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
// generate temporary password-reset token // generate temporary password-reset token
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.SIGNUP_TOKEN,
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: await getJwtSignupLifetime(), expiresIn: await getJwtSignupLifetime(),
secret: await getJwtSignupSecret() secret: await getAuthSecret()
}); });
return res.status(200).send({ return res.status(200).send({
@@ -4,14 +4,15 @@ import { checkEmailVerification, sendEmailVerification } from "../../helpers/sig
import { createToken } from "../../helpers/auth"; import { createToken } from "../../helpers/auth";
import { BadRequestError } from "../../utils/errors"; import { BadRequestError } from "../../utils/errors";
import { import {
getAuthSecret,
getInviteOnlySignup, getInviteOnlySignup,
getJwtSignupLifetime, getJwtSignupLifetime,
getJwtSignupSecret,
getSmtpConfigured getSmtpConfigured
} from "../../config"; } from "../../config";
import { validateUserEmail } from "../../validation"; import { validateUserEmail } from "../../validation";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
import * as reqValidator from "../../validation/auth"; import * as reqValidator from "../../validation/auth";
import { AuthTokenType } from "../../variables";
/** /**
* Signup step 1: Initialize account for user under email [email] and send a verification code * Signup step 1: Initialize account for user under email [email] and send a verification code
@@ -95,10 +96,11 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
// generate temporary signup token // generate temporary signup token
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.SIGNUP_TOKEN,
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: await getJwtSignupLifetime(), expiresIn: await getJwtSignupLifetime(),
secret: await getJwtSignupSecret() secret: await getAuthSecret()
}); });
return res.status(200).send({ return res.status(200).send({
+4 -3
View File
@@ -10,9 +10,9 @@ import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services"; import { TokenService } from "../../services";
import { EELogService } from "../../ee/services"; import { EELogService } from "../../ee/services";
import { BadRequestError, InternalServerError } from "../../utils/errors"; import { BadRequestError, InternalServerError } from "../../utils/errors";
import { ACTION_LOGIN, TOKEN_EMAIL_MFA } from "../../variables"; import { ACTION_LOGIN, AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import { getHttpsEnabled, getJwtMfaLifetime, getJwtMfaSecret } from "../../config"; import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
import * as reqValidator from "../../validation/auth"; import * as reqValidator from "../../validation/auth";
@@ -109,10 +109,11 @@ export const login2 = async (req: Request, res: Response) => {
// generate temporary MFA token // generate temporary MFA token
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.MFA_TOKEN,
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: await getJwtMfaLifetime(), expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret() secret: await getAuthSecret()
}); });
const code = await TokenService.createToken({ const code = await TokenService.createToken({
+4 -3
View File
@@ -10,9 +10,9 @@ import { sendMail } from "../../helpers/nodemailer";
import { TokenService } from "../../services"; import { TokenService } from "../../services";
import { EELogService } from "../../ee/services"; import { EELogService } from "../../ee/services";
import { BadRequestError, InternalServerError } from "../../utils/errors"; import { BadRequestError, InternalServerError } from "../../utils/errors";
import { ACTION_LOGIN, TOKEN_EMAIL_MFA } from "../../variables"; import { ACTION_LOGIN, AuthTokenType, TOKEN_EMAIL_MFA } from "../../variables";
import { getUserAgentType } from "../../utils/posthog"; // TODO: move this import { getUserAgentType } from "../../utils/posthog"; // TODO: move this
import { getHttpsEnabled, getJwtMfaLifetime, getJwtMfaSecret } from "../../config"; import { getAuthSecret, getHttpsEnabled, getJwtMfaLifetime } from "../../config";
import { AuthMethod } from "../../models/user"; import { AuthMethod } from "../../models/user";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
import * as reqValidator from "../../validation/auth"; import * as reqValidator from "../../validation/auth";
@@ -134,10 +134,11 @@ export const login2 = async (req: Request, res: Response) => {
// generate temporary MFA token // generate temporary MFA token
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.MFA_TOKEN,
userId: user._id.toString() userId: user._id.toString()
}, },
expiresIn: await getJwtMfaLifetime(), expiresIn: await getJwtMfaLifetime(),
secret: await getJwtMfaSecret() secret: await getAuthSecret()
}); });
const code = await TokenService.createToken({ const code = await TokenService.createToken({
@@ -5,10 +5,10 @@ import { MembershipOrg, User } from "../../models";
import { completeAccount } from "../../helpers/user"; import { completeAccount } from "../../helpers/user";
import { initializeDefaultOrg } from "../../helpers/signup"; import { initializeDefaultOrg } from "../../helpers/signup";
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth"; import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
import { ACCEPTED, INVITED } from "../../variables"; import { ACCEPTED, AuthTokenType, INVITED } from "../../variables";
import { standardRequest } from "../../config/request"; import { standardRequest } from "../../config/request";
import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../config"; import { getAuthSecret, getHttpsEnabled, getLoopsApiKey } from "../../config";
import { BadRequestError } from "../../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { TelemetryService } from "../../services"; import { TelemetryService } from "../../services";
import { AuthMethod } from "../../models"; import { AuthMethod } from "../../models";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
@@ -78,12 +78,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
} }
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret()) jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret())
); );
if (decodedToken.userId !== user.id) { if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw UnauthorizedRequestError();
throw BadRequestError(); if (decodedToken.userId !== user.id) throw UnauthorizedRequestError();
}
} }
// complete setting up user's account // complete setting up user's account
+15 -16
View File
@@ -19,15 +19,14 @@ import {
UnauthorizedRequestError, UnauthorizedRequestError,
} from "../utils/errors"; } from "../utils/errors";
import { import {
getAuthSecret,
getJwtAuthLifetime, getJwtAuthLifetime,
getJwtAuthSecret,
getJwtProviderAuthSecret,
getJwtRefreshLifetime, getJwtRefreshLifetime,
getJwtRefreshSecret,
getJwtServiceTokenSecret getJwtServiceTokenSecret
} from "../config"; } from "../config";
import { import {
AuthMode AuthMode,
AuthTokenType
} from "../variables"; } from "../variables";
import { import {
ServiceTokenAuthData, ServiceTokenAuthData,
@@ -51,8 +50,6 @@ export const validateAuthMode = ({
acceptedAuthModes: AuthMode[] acceptedAuthModes: AuthMode[]
}) => { }) => {
// TODO: update this to accept service token v3
const apiKey = headers["x-api-key"]; const apiKey = headers["x-api-key"];
const authHeader = headers["authorization"]; const authHeader = headers["authorization"];
@@ -120,9 +117,11 @@ export const getAuthUserPayload = async ({
authTokenValue: string; authTokenValue: string;
}): Promise<UserAuthData> => { }): Promise<UserAuthData> => {
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(authTokenValue, await getJwtAuthSecret()) jwt.verify(authTokenValue, await getAuthSecret())
); );
if (decodedToken.authTokenType !== AuthTokenType.ACCESS_TOKEN) throw UnauthorizedRequestError();
const user = await User.findOne({ const user = await User.findOne({
_id: new Types.ObjectId(decodedToken.userId), _id: new Types.ObjectId(decodedToken.userId),
}).select("+publicKey +accessVersion"); }).select("+publicKey +accessVersion");
@@ -159,11 +158,6 @@ export const getAuthUserPayload = async ({
userAgent: req.headers["user-agent"] ?? "", userAgent: req.headers["user-agent"] ?? "",
userAgentType: getUserAgentType(req.headers["user-agent"]) userAgentType: getUserAgentType(req.headers["user-agent"])
} }
// return ({
// user,
// tokenVersionId: tokenVersion._id, // what to do with this? // move this out
// });
} }
/** /**
@@ -404,22 +398,24 @@ export const issueAuthTokens = async ({
// issue tokens // issue tokens
const token = createToken({ const token = createToken({
payload: { payload: {
authTokenType: AuthTokenType.ACCESS_TOKEN,
userId, userId,
tokenVersionId: tokenVersion._id.toString(), tokenVersionId: tokenVersion._id.toString(),
accessVersion: tokenVersion.accessVersion, accessVersion: tokenVersion.accessVersion,
}, },
expiresIn: await getJwtAuthLifetime(), expiresIn: await getJwtAuthLifetime(),
secret: await getJwtAuthSecret(), secret: await getAuthSecret(),
}); });
const refreshToken = createToken({ const refreshToken = createToken({
payload: { payload: {
authTokenType: AuthTokenType.REFRESH_TOKEN,
userId, userId,
tokenVersionId: tokenVersion._id.toString(), tokenVersionId: tokenVersion._id.toString(),
refreshVersion: tokenVersion.refreshVersion, refreshVersion: tokenVersion.refreshVersion,
}, },
expiresIn: await getJwtRefreshLifetime(), expiresIn: await getJwtRefreshLifetime(),
secret: await getJwtRefreshSecret(), secret: await getAuthSecret(),
}); });
return { return {
@@ -451,7 +447,7 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
* bearer/auth, refresh, and temporary signup tokens * bearer/auth, refresh, and temporary signup tokens
* @param {Object} obj * @param {Object} obj
* @param {Object} obj.payload - payload of (JWT) token * @param {Object} obj.payload - payload of (JWT) token
* @param {String} obj.secret - (JWT) secret such as [JWT_AUTH_SECRET] * @param {String} obj.secret - (JWT) secret such as [AUTH_SECRET]
* @param {String} obj.expiresIn - string describing time span such as '10h' or '7d' * @param {String} obj.expiresIn - string describing time span such as '10h' or '7d'
*/ */
export const createToken = ({ export const createToken = ({
@@ -479,13 +475,16 @@ export const validateProviderAuthToken = async ({
email: string; email: string;
providerAuthToken?: string; providerAuthToken?: string;
}) => { }) => {
if (!providerAuthToken) { if (!providerAuthToken) {
throw new Error("Invalid authentication request."); throw new Error("Invalid authentication request.");
} }
const decodedToken = <jwt.ProviderAuthJwtPayload>( const decodedToken = <jwt.ProviderAuthJwtPayload>(
jwt.verify(providerAuthToken, await getJwtProviderAuthSecret()) jwt.verify(providerAuthToken, await getAuthSecret())
); );
if (decodedToken.authTokenType !== AuthTokenType.PROVIDER_TOKEN) throw UnauthorizedRequestError();
if (decodedToken.email !== email) { if (decodedToken.email !== email) {
throw new Error("Invalid authentication credentials.") throw new Error("Invalid authentication credentials.")
+1 -1
View File
@@ -81,7 +81,7 @@ import {
getSecretScanningPrivateKey, getSecretScanningPrivateKey,
getSecretScanningWebhookProxy, getSecretScanningWebhookProxy,
getSecretScanningWebhookSecret, getSecretScanningWebhookSecret,
getSiteURL getSiteURL,
} from "./config"; } from "./config";
import { setup } from "./utils/setup"; import { setup } from "./utils/setup";
import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecretsToThirdPartyServices"; import { syncSecretsToThirdPartyServices } from "./queues/integrations/syncSecretsToThirdPartyServices";
+5 -2
View File
@@ -2,7 +2,8 @@ import jwt from "jsonwebtoken";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { User } from "../models"; import { User } from "../models";
import { BadRequestError, UnauthorizedRequestError } from "../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../utils/errors";
import { getJwtMfaSecret } from "../config"; import { getAuthSecret } from "../config";
import { AuthTokenType } from "../variables";
declare module "jsonwebtoken" { declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -26,8 +27,10 @@ const requireMfaAuth = async (
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"})) if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, await getJwtMfaSecret()) jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret())
); );
if (decodedToken.authTokenType !== AuthTokenType.MFA_TOKEN) throw UnauthorizedRequestError();
const user = await User.findOne({ const user = await User.findOne({
_id: decodedToken.userId, _id: decodedToken.userId,
+5 -2
View File
@@ -2,7 +2,8 @@ import jwt from "jsonwebtoken";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { User } from "../models"; import { User } from "../models";
import { BadRequestError, UnauthorizedRequestError } from "../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../utils/errors";
import { getJwtSignupSecret } from "../config"; import { getAuthSecret } from "../config";
import { AuthTokenType } from "../variables";
declare module "jsonwebtoken" { declare module "jsonwebtoken" {
export interface UserIDJwtPayload extends jwt.JwtPayload { export interface UserIDJwtPayload extends jwt.JwtPayload {
@@ -27,8 +28,10 @@ const requireSignupAuth = async (
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"})) if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: "Missing Authorization Body in the request header"}))
const decodedToken = <jwt.UserIDJwtPayload>( const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret()) jwt.verify(AUTH_TOKEN_VALUE, await getAuthSecret())
); );
if (decodedToken.authTokenType !== AuthTokenType.SIGNUP_TOKEN) throw UnauthorizedRequestError();
const user = await User.findOne({ const user = await User.findOne({
_id: decodedToken.userId, _id: decodedToken.userId,
+10 -6
View File
@@ -13,6 +13,7 @@ import {
} from "../models"; } from "../models";
import { createToken } from "../helpers/auth"; import { createToken } from "../helpers/auth";
import { import {
getAuthSecret,
getClientIdGitHubLogin, getClientIdGitHubLogin,
getClientIdGitLabLogin, getClientIdGitLabLogin,
getClientIdGoogleLogin, getClientIdGoogleLogin,
@@ -20,13 +21,12 @@ import {
getClientSecretGitLabLogin, getClientSecretGitLabLogin,
getClientSecretGoogleLogin, getClientSecretGoogleLogin,
getJwtProviderAuthLifetime, getJwtProviderAuthLifetime,
getJwtProviderAuthSecret,
getSiteURL, getSiteURL,
getUrlGitLabLogin getUrlGitLabLogin
} from "../config"; } from "../config";
import { getSSOConfigHelper } from "../ee/helpers/organizations"; import { getSSOConfigHelper } from "../ee/helpers/organizations";
import { InternalServerError, OrganizationNotFoundError } from "./errors"; import { InternalServerError, OrganizationNotFoundError } from "./errors";
import { ACCEPTED, INTEGRATION_GITHUB_API_URL, INVITED, MEMBER } from "../variables"; import { ACCEPTED, AuthTokenType, INTEGRATION_GITHUB_API_URL, INVITED, MEMBER } from "../variables";
import { standardRequest } from "../config/request"; import { standardRequest } from "../config/request";
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -131,6 +131,7 @@ const initializePassport = async () => {
const isUserCompleted = !!user.publicKey; const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({ const providerAuthToken = createToken({
payload: { payload: {
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName: user.firstName, firstName: user.firstName,
@@ -143,7 +144,7 @@ const initializePassport = async () => {
} : {}) } : {})
}, },
expiresIn: await getJwtProviderAuthLifetime(), expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(), secret: await getAuthSecret(),
}); });
req.isUserCompleted = isUserCompleted; req.isUserCompleted = isUserCompleted;
@@ -204,6 +205,7 @@ const initializePassport = async () => {
const isUserCompleted = !!user.publicKey; const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({ const providerAuthToken = createToken({
payload: { payload: {
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName: user.firstName, firstName: user.firstName,
@@ -216,7 +218,7 @@ const initializePassport = async () => {
} : {}) } : {})
}, },
expiresIn: await getJwtProviderAuthLifetime(), expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(), secret: await getAuthSecret(),
}); });
req.isUserCompleted = isUserCompleted; req.isUserCompleted = isUserCompleted;
@@ -258,6 +260,7 @@ const initializePassport = async () => {
const isUserCompleted = !!user.publicKey; const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({ const providerAuthToken = createToken({
payload: { payload: {
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName: user.firstName, firstName: user.firstName,
@@ -270,7 +273,7 @@ const initializePassport = async () => {
} : {}) } : {})
}, },
expiresIn: await getJwtProviderAuthLifetime(), expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(), secret: await getAuthSecret(),
}); });
req.isUserCompleted = isUserCompleted; req.isUserCompleted = isUserCompleted;
@@ -401,6 +404,7 @@ const initializePassport = async () => {
const isUserCompleted = !!user.publicKey; const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({ const providerAuthToken = createToken({
payload: { payload: {
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName, firstName,
@@ -413,7 +417,7 @@ const initializePassport = async () => {
} : {}) } : {})
}, },
expiresIn: await getJwtProviderAuthLifetime(), expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(), secret: await getAuthSecret(),
}); });
req.isUserCompleted = isUserCompleted; req.isUserCompleted = isUserCompleted;
+8
View File
@@ -1,3 +1,11 @@
export enum AuthTokenType {
ACCESS_TOKEN = "accessToken",
REFRESH_TOKEN = "refreshToken",
SIGNUP_TOKEN = "signupToken",
MFA_TOKEN = "mfaToken",
PROVIDER_TOKEN = "providerToken"
}
export enum AuthMode { export enum AuthMode {
JWT = "jwt", JWT = "jwt",
SERVICE_TOKEN = "serviceToken", SERVICE_TOKEN = "serviceToken",
+5 -39
View File
@@ -11,43 +11,12 @@ Other environment variables are listed below to increase the functionality of yo
<Tabs> <Tabs>
<Tab title="Required"> <Tab title="Required">
<ParamField query="ENCRYPTION_KEY" type="string" default="none" required> <ParamField query="ENCRYPTION_KEY" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex 16` Must be a random 16 byte hex string. Can be generated with `openssl rand -hex 16`
</ParamField> </ParamField>
{" "} <ParamField query="AUTH_SECRET" type="string" default="none" required>
Must be a random 32 byte base64 string. Can be generated with `openssl rand -base64 32`
<ParamField query="JWT_SIGNUP_SECRET" type="string" default="none" required> </ParamField>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex
16`
</ParamField>
{" "}
<ParamField query="JWT_REFRESH_SECRET" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex
16`
</ParamField>
{" "}
<ParamField query="JWT_AUTH_SECRET" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex
16`
</ParamField>
{" "}
<ParamField query="JWT_MFA_SECRET" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex
16`
</ParamField>
{" "}
<ParamField query="JWT_SERVICE_SECRET" type="string" default="none" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex
16`
</ParamField>
<ParamField query="MONGO_URL" type="string" default="none" required> <ParamField query="MONGO_URL" type="string" default="none" required>
*TLS based connection string is not yet supported *TLS based connection string is not yet supported
@@ -140,9 +109,6 @@ Other environment variables are listed below to increase the functionality of yo
</Tab> </Tab>
<Tab title="Auth Integrations"> <Tab title="Auth Integrations">
To integrate with external auth providers, provide value for the related keys To integrate with external auth providers, provide value for the related keys
<ParamField query="JWT_PROVIDER_AUTH_SECRET" type="string" required>
Must be a random 16 byte hex string. Can be generated with `openssl rand -hex 16`
</ParamField>
<ParamField query="CLIENT_ID_GOOGLE_LOGIN" type="string" default="none" optional> <ParamField query="CLIENT_ID_GOOGLE_LOGIN" type="string" default="none" optional>
OAuth2 client ID for Google login OAuth2 client ID for Google login
</ParamField> </ParamField>