mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 01:27:41 +00:00
Refactoring functions into services, helper functions, hooks, patch bugs
This commit is contained in:
@@ -269,19 +269,36 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
secure: NODE_ENV === 'production' ? true : false
|
secure: NODE_ENV === 'production' ? true : false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
interface VerifyMfaTokenRes {
|
||||||
|
encryptionVersion: number;
|
||||||
|
protectedKey?: string;
|
||||||
|
protectedKeyIV?: string;
|
||||||
|
protectedKeyTag?: string;
|
||||||
|
token: string;
|
||||||
|
publicKey: string;
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resObj: VerifyMfaTokenRes = {
|
||||||
|
encryptionVersion: user.encryptionVersion,
|
||||||
|
token: tokens.token,
|
||||||
|
publicKey: user.publicKey as string,
|
||||||
|
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
||||||
|
iv: user.iv as string,
|
||||||
|
tag: user.tag as string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||||
|
resObj.protectedKey = user.protectedKey;
|
||||||
|
resObj.protectedKeyIV = user.protectedKeyIV;
|
||||||
|
resObj.protectedKeyTag = user.protectedKeyTag;
|
||||||
|
}
|
||||||
|
|
||||||
// case: user does not have MFA enabled
|
// case: user does not have MFA enabled
|
||||||
// return (access) token in response
|
// return (access) token in response
|
||||||
return res.status(200).send({
|
return res.status(200).send(resObj);
|
||||||
encryptionVersion: user.encryptionVersion,
|
|
||||||
protectedKey: user.protectedKey ?? null,
|
|
||||||
protectedKeyIV: user.protectedKeyIV ?? null,
|
|
||||||
protectedKeyTag: user.protectedKeyTag ?? null,
|
|
||||||
token: tokens.token,
|
|
||||||
publicKey: user.publicKey,
|
|
||||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
|
||||||
iv: user.iv,
|
|
||||||
tag: user.tag
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from '../../helpers/signup';
|
} from '../../helpers/signup';
|
||||||
import { issueAuthTokens } from '../../helpers/auth';
|
import { issueAuthTokens } from '../../helpers/auth';
|
||||||
import { INVITED, ACCEPTED } from '../../variables';
|
import { INVITED, ACCEPTED } from '../../variables';
|
||||||
|
import { NODE_ENV } from '../../config';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,7 +106,6 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
refreshToken = tokens.refreshToken;
|
|
||||||
|
|
||||||
// sending a welcome email to new users
|
// sending a welcome email to new users
|
||||||
if (process.env.LOOPS_API_KEY) {
|
if (process.env.LOOPS_API_KEY) {
|
||||||
@@ -121,6 +121,14 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store (refresh) token in httpOnly cookie
|
||||||
|
res.cookie('jid', tokens.refreshToken, {
|
||||||
|
httpOnly: true,
|
||||||
|
path: '/',
|
||||||
|
sameSite: 'strict',
|
||||||
|
secure: NODE_ENV === 'production' ? true : false
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
@@ -132,8 +140,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully set up account',
|
message: 'Successfully set up account',
|
||||||
user,
|
user,
|
||||||
token,
|
token
|
||||||
refreshToken
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -219,7 +226,14 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
refreshToken = tokens.refreshToken;
|
|
||||||
|
// store (refresh) token in httpOnly cookie
|
||||||
|
res.cookie('jid', tokens.refreshToken, {
|
||||||
|
httpOnly: true,
|
||||||
|
path: '/',
|
||||||
|
sameSite: 'strict',
|
||||||
|
secure: NODE_ENV === 'production' ? true : false
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
@@ -227,11 +241,10 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
message: 'Failed to complete account setup'
|
message: 'Failed to complete account setup'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
message: 'Successfully set up account',
|
message: 'Successfully set up account',
|
||||||
user,
|
user,
|
||||||
token,
|
token
|
||||||
refreshToken
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
SALT_ROUNDS
|
SALT_ROUNDS
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { UnauthorizedRequestError } from '../utils/errors';
|
import { ForbiddenRequestError } from '../utils/errors';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and store a token in the database for purpose [type]
|
* Create and store a token in the database for purpose [type]
|
||||||
@@ -65,29 +65,45 @@ const createTokenHelper = async ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Query {
|
interface TokenDataQuery {
|
||||||
type: string;
|
type: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
organization?: Types.ObjectId;
|
organization?: Types.ObjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TokenDataUpdate {
|
||||||
|
type: string;
|
||||||
|
email?: string;
|
||||||
|
phoneNumber?: string;
|
||||||
|
organization?: Types.ObjectId;
|
||||||
|
tokenHash: string;
|
||||||
|
expiresAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
const query: Query = { type };
|
const query: TokenDataQuery = { type };
|
||||||
|
const update: TokenDataUpdate = {
|
||||||
|
type,
|
||||||
|
tokenHash: await bcrypt.hash(token, SALT_ROUNDS),
|
||||||
|
expiresAt
|
||||||
|
}
|
||||||
|
|
||||||
if (email) { query.email = email; }
|
if (email) {
|
||||||
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
query.email = email;
|
||||||
if (organizationId) { query.organization = organizationId }
|
update.email = email;
|
||||||
|
}
|
||||||
|
if (phoneNumber) {
|
||||||
|
query.phoneNumber = phoneNumber;
|
||||||
|
update.phoneNumber = phoneNumber;
|
||||||
|
}
|
||||||
|
if (organizationId) {
|
||||||
|
query.organization = organizationId
|
||||||
|
update.organization = organizationId
|
||||||
|
}
|
||||||
|
|
||||||
await TokenData.findOneAndUpdate(
|
await TokenData.findOneAndUpdate(
|
||||||
query,
|
query,
|
||||||
{
|
update,
|
||||||
type,
|
|
||||||
email,
|
|
||||||
phoneNumber,
|
|
||||||
organization: organizationId,
|
|
||||||
tokenHash: await bcrypt.hash(token, SALT_ROUNDS),
|
|
||||||
expiresAt
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
new: true,
|
new: true,
|
||||||
upsert: true
|
upsert: true
|
||||||
@@ -123,37 +139,38 @@ const validateTokenHelper = async ({
|
|||||||
organizationId?: Types.ObjectId;
|
organizationId?: Types.ObjectId;
|
||||||
token: string;
|
token: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
interface Query {
|
||||||
interface Query {
|
type: string;
|
||||||
type: string;
|
email?: string;
|
||||||
email?: string;
|
phoneNumber?: string;
|
||||||
phoneNumber?: string;
|
organization?: Types.ObjectId;
|
||||||
organization?: Types.ObjectId;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const query: Query = { type };
|
const query: Query = { type };
|
||||||
|
|
||||||
if (email) { query.email = email; }
|
if (email) { query.email = email; }
|
||||||
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
||||||
if (organizationId) { query.organization = organizationId; }
|
if (organizationId) { query.organization = organizationId; }
|
||||||
|
|
||||||
const tokenData = await TokenData.findOneAndDelete(query);
|
const tokenData = await TokenData.findOne(query).select('+tokenHash');
|
||||||
|
|
||||||
if (!tokenData) throw new Error('Failed to find token to validate');
|
if (!tokenData) throw new Error('Failed to find token to validate');
|
||||||
|
|
||||||
if (tokenData.expiresAt < new Date()) throw new Error('Token has expired');
|
if (tokenData.expiresAt < new Date()) {
|
||||||
|
await TokenData.findByIdAndDelete(tokenData._id);
|
||||||
const isValid = await bcrypt.compare(token, tokenData.tokenHash);
|
throw ForbiddenRequestError({
|
||||||
if (!isValid) throw UnauthorizedRequestError({
|
message: 'Failed token data validation due to token is no longer valid'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await bcrypt.compare(token, tokenData.tokenHash);
|
||||||
|
if (!isValid) {
|
||||||
|
throw ForbiddenRequestError({
|
||||||
message: 'Failed token data validation due to incorrect token'
|
message: 'Failed token data validation due to incorrect token'
|
||||||
});
|
});
|
||||||
} catch (err) {
|
|
||||||
Sentry.setUser(null);
|
|
||||||
Sentry.captureException(err);
|
|
||||||
throw new Error(
|
|
||||||
"Failed to validate token data"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await TokenData.findByIdAndDelete(tokenData._id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { ErrorRequestHandler } from "express";
|
import { ErrorRequestHandler } from "express";
|
||||||
|
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import { InternalServerError } from "../utils/errors";
|
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
import { getLogger } from "../utils/logger";
|
import { getLogger } from "../utils/logger";
|
||||||
import RequestError, { LogLevel } from "../utils/requestError";
|
import RequestError, { LogLevel } from "../utils/requestError";
|
||||||
import { NODE_ENV } from "../config";
|
import { NODE_ENV } from "../config";
|
||||||
|
|
||||||
|
import { TokenExpiredError } from 'jsonwebtoken';
|
||||||
|
|
||||||
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => {
|
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => {
|
||||||
if (res.headersSent) return next();
|
if (res.headersSent) return next();
|
||||||
@@ -16,7 +17,9 @@ export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | E
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
||||||
if (!(error instanceof RequestError)) {
|
if (error instanceof TokenExpiredError) {
|
||||||
|
error = UnauthorizedRequestError({ stack: error.stack, message: 'Token expired' });
|
||||||
|
} else if (!(error instanceof RequestError)) {
|
||||||
error = InternalServerError({ context: { exception: error.message }, stack: error.stack })
|
error = InternalServerError({ context: { exception: error.message }, stack: error.stack })
|
||||||
getLogger('backend-main').log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
|
getLogger('backend-main').log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,37 @@
|
|||||||
---
|
---
|
||||||
title: "Circle CI"
|
title: "Circle CI"
|
||||||
|
description: "How to automatically sync secrets from Infisical into your CircleCI project."
|
||||||
---
|
---
|
||||||
|
|
||||||
Coming soon.
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
|
||||||
|
## Navigate to your project's integrations tab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Authorize Infisical for CircleCI
|
||||||
|
|
||||||
|
Obtain a Fly.io access token in Access Tokens
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
Press on the Fly.io tile and input your Fly.io access token to grant Infisical access to your Fly.io account.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<Info>
|
||||||
|
If this is your project's first cloud integration, then you'll have to grant
|
||||||
|
Infisical access to your project's environment variables. Although this step
|
||||||
|
breaks E2EE, it's necessary for Infisical to sync the environment variables to
|
||||||
|
the cloud platform.
|
||||||
|
</Info>
|
||||||
|
|
||||||
|
## Start integration
|
||||||
|
|
||||||
|
Select which Infisical environment secrets you want to sync to which Fly.io app and press create integration to start syncing secrets to Fly.io.
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|||||||
@@ -21,6 +21,8 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
|||||||
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
||||||
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
||||||
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
||||||
|
| [GitLab Pipeline](/integrations/cicd/gitlab) | CI/CD | Available |
|
||||||
|
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
|
||||||
| [React](/integrations/frameworks/react) | Framework | Available |
|
| [React](/integrations/frameworks/react) | Framework | Available |
|
||||||
| [Vue](/integrations/frameworks/vue) | Framework | Available |
|
| [Vue](/integrations/frameworks/vue) | Framework | Available |
|
||||||
| [Express](/integrations/frameworks/express) | Framework | Available |
|
| [Express](/integrations/frameworks/express) | Framework | Available |
|
||||||
@@ -39,8 +41,6 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
|
|||||||
| GCP | Cloud | Coming soon |
|
| GCP | Cloud | Coming soon |
|
||||||
| Azure | Cloud | Coming soon |
|
| Azure | Cloud | Coming soon |
|
||||||
| DigitalOcean | Cloud | Coming soon |
|
| DigitalOcean | Cloud | Coming soon |
|
||||||
| [GitLab Pipeline](/integrations/cicd/gitlab) | CI/CD | Available |
|
|
||||||
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Coming soon |
|
|
||||||
| TravisCI | CI/CD | Coming soon |
|
| TravisCI | CI/CD | Coming soon |
|
||||||
| GitHub Actions | CI/CD | Coming soon |
|
| GitHub Actions | CI/CD | Coming soon |
|
||||||
| Jenkins | CI/CD | Coming soon |
|
| Jenkins | CI/CD | Coming soon |
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
"step1-start": "Let's get started",
|
"step1-start": "Let's get started",
|
||||||
"step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.",
|
"step1-privacy": "By creating an account, you agree to our Terms and have read and acknowledged the Privacy Policy.",
|
||||||
"step1-submit": "Get Started",
|
"step1-submit": "Get Started",
|
||||||
"step2-message": "We've sent a verification email to",
|
"step2-message": "We've sent a verification code to",
|
||||||
"step2-code-error": "Oops. Your code is wrong. Please try again.",
|
"step2-code-error": "Oops. Your code is wrong. Please try again.",
|
||||||
"step2-resend-alert": "Don't see the email?",
|
"step2-resend-alert": "Don't see the code?",
|
||||||
"step2-resend-submit": "Resend",
|
"step2-resend-submit": "Resend",
|
||||||
"step2-resend-progress": "Resending...",
|
"step2-resend-progress": "Resending...",
|
||||||
"step2-spam-alert": "Make sure to check your spam inbox.",
|
"step2-spam-alert": "Make sure to check your spam inbox.",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"step1-submit": "C'est parti",
|
"step1-submit": "C'est parti",
|
||||||
"step2-message": "Nous avons envoyé un email de vérification à",
|
"step2-message": "Nous avons envoyé un email de vérification à",
|
||||||
"step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.",
|
"step2-code-error": "Oops. Votre code est faux. Veuillez réessayer.",
|
||||||
"step2-resend-alert": "Vous ne voyez pas l'email?",
|
"step2-resend-alert": "Vous ne voyez pas le code?",
|
||||||
"step2-resend-submit": "Renvoyer",
|
"step2-resend-submit": "Renvoyer",
|
||||||
"step2-resend-progress": "Envoie en cours...",
|
"step2-resend-progress": "Envoie en cours...",
|
||||||
"step2-spam-alert": "Assurez-vous de vérifier vos spams.",
|
"step2-spam-alert": "Assurez-vous de vérifier vos spams.",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"step1-submit": "Başla",
|
"step1-submit": "Başla",
|
||||||
"step2-message": "Şu adrese bir doğrulama maili yolladık",
|
"step2-message": "Şu adrese bir doğrulama maili yolladık",
|
||||||
"step2-code-error": "Tüh. Kodun hatalı. Lütfen tekrar dene.",
|
"step2-code-error": "Tüh. Kodun hatalı. Lütfen tekrar dene.",
|
||||||
"step2-resend-alert": "Mail ulaşamadı mı?",
|
"step2-resend-alert": "Kodu ulaşamadı mı?",
|
||||||
"step2-resend-submit": "Tekrar Yolla",
|
"step2-resend-submit": "Tekrar Yolla",
|
||||||
"step2-resend-progress": "Tekrar yollanıyor...",
|
"step2-resend-progress": "Tekrar yollanıyor...",
|
||||||
"step2-spam-alert": "Spam kutunuzu kontrol ettiğinizden emin olun.",
|
"step2-spam-alert": "Spam kutunuzu kontrol ettiğinizden emin olun.",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useRouter } from 'next/router';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import attemptLoginMfa from '@app/components/utilities/attemptLoginMfa';
|
import attemptLoginMfa from '@app/components/utilities/attemptLoginMfa';
|
||||||
import resendMfaToken from '@app/pages/api/auth/resendMfaToken';
|
import { useSendMfaToken } from '@app/hooks/api/auth';
|
||||||
|
|
||||||
import Button from '../basic/buttons/Button';
|
import Button from '../basic/buttons/Button';
|
||||||
import Error from '../basic/Error';
|
import Error from '../basic/Error';
|
||||||
@@ -50,6 +50,8 @@ export default function MFAStep({
|
|||||||
const [mfaCode, setMfaCode] = useState('');
|
const [mfaCode, setMfaCode] = useState('');
|
||||||
const [codeError, setCodeError] = useState(false);
|
const [codeError, setCodeError] = useState(false);
|
||||||
|
|
||||||
|
const sendMfaToken = useSendMfaToken();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleLoginMfa = async () => {
|
const handleLoginMfa = async () => {
|
||||||
@@ -72,15 +74,14 @@ export default function MFAStep({
|
|||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
setIsLoading(false);
|
||||||
setCodeError(true);
|
setCodeError(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResendMfaCode = async () => {
|
const handleResendMfaCode = async () => {
|
||||||
try {
|
try {
|
||||||
await resendMfaToken({
|
await sendMfaToken.mutateAsync({ email });
|
||||||
email
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ export default function Navbar() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const closeApp = async () => {
|
const closeApp = async () => {
|
||||||
console.log('Logging out...');
|
|
||||||
await logout();
|
await logout();
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ export default function UserInfoStep({
|
|||||||
organizationName: `${firstName}'s organization`
|
organizationName: `${firstName}'s organization`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// unset signup JWT token and set JWT token
|
||||||
|
SecurityClient.setSignupToken('');
|
||||||
SecurityClient.setToken(response.token);
|
SecurityClient.setToken(response.token);
|
||||||
|
|
||||||
saveTokenToLocalStorage({
|
saveTokenToLocalStorage({
|
||||||
@@ -174,14 +176,18 @@ export default function UserInfoStep({
|
|||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
incrementStep();
|
|
||||||
|
|
||||||
const userOrgs = await getOrganizations();
|
const userOrgs = await getOrganizations();
|
||||||
await ProjectService.initProject({
|
const orgId = userOrgs[0]?._id;
|
||||||
organizationId: userOrgs[0]?._id,
|
const project = await ProjectService.initProject({
|
||||||
|
organizationId: orgId,
|
||||||
projectName: 'Example Project'
|
projectName: 'Example Project'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
localStorage.setItem('orgData.id', orgId);
|
||||||
|
localStorage.setItem('projectData.id', project._id);
|
||||||
|
|
||||||
|
incrementStep();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { getAuthToken, setAuthToken , setMfaTempToken } from '@app/reactQuery';
|
import {
|
||||||
|
getAuthToken,
|
||||||
|
setAuthToken,
|
||||||
|
setMfaTempToken,
|
||||||
|
setSignupTempToken} from '@app/reactQuery';
|
||||||
|
|
||||||
// depreciated: go for apiRequest module in config/api
|
// depreciated: go for apiRequest module in config/api
|
||||||
export default class SecurityClient {
|
export default class SecurityClient {
|
||||||
|
static setSignupToken(tokenStr: string) {
|
||||||
|
setSignupTempToken(tokenStr);
|
||||||
|
}
|
||||||
|
|
||||||
static setMfaToken(tokenStr: string) {
|
static setMfaToken(tokenStr: string) {
|
||||||
setMfaTempToken(tokenStr);
|
setMfaTempToken(tokenStr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const attemptLogin = async (
|
|||||||
client.setSalt(salt);
|
client.setSalt(salt);
|
||||||
client.setServerPublicKey(serverPublicKey);
|
client.setServerPublicKey(serverPublicKey);
|
||||||
const clientProof = client.getProof(); // called M1
|
const clientProof = client.getProof(); // called M1
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mfaEnabled,
|
mfaEnabled,
|
||||||
encryptionVersion,
|
encryptionVersion,
|
||||||
@@ -65,7 +65,7 @@ const attemptLogin = async (
|
|||||||
// case: MFA is enabled
|
// case: MFA is enabled
|
||||||
|
|
||||||
// set temporary (MFA) JWT token
|
// set temporary (MFA) JWT token
|
||||||
SecurityClient.setToken(token);
|
SecurityClient.setMfaToken(token);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
mfaEnabled,
|
mfaEnabled,
|
||||||
@@ -113,40 +113,14 @@ const attemptLogin = async (
|
|||||||
const userOrgs = await getOrganizations();
|
const userOrgs = await getOrganizations();
|
||||||
const orgId = userOrgs[0]._id;
|
const orgId = userOrgs[0]._id;
|
||||||
localStorage.setItem('orgData.id', orgId);
|
localStorage.setItem('orgData.id', orgId);
|
||||||
|
|
||||||
const orgUserProjects = await getOrganizationUserProjects({
|
const orgUserProjects = await getOrganizationUserProjects({
|
||||||
orgId
|
orgId
|
||||||
});
|
});
|
||||||
localStorage.setItem('projectData.id', orgUserProjects[0]._id);
|
|
||||||
|
|
||||||
// // TODO: this part definitely needs to be refactored
|
if (orgUserProjects.length > 0) {
|
||||||
// const userOrgs = await getOrganizations();
|
localStorage.setItem('projectData.id', orgUserProjects[0]._id);
|
||||||
// const userOrgsData = userOrgs.map((org: { _id: string }) => org._id);
|
}
|
||||||
|
|
||||||
// let orgToLogin;
|
|
||||||
// if (userOrgsData.includes(localStorage.getItem('orgData.id'))) {
|
|
||||||
// orgToLogin = localStorage.getItem('orgData.id');
|
|
||||||
// } else {
|
|
||||||
// orgToLogin = userOrgsData[0];
|
|
||||||
// localStorage.setItem('orgData.id', orgToLogin);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let orgUserProjects = await getOrganizationUserProjects({
|
|
||||||
// orgId: orgToLogin
|
|
||||||
// });
|
|
||||||
|
|
||||||
// orgUserProjects = orgUserProjects?.map((project: { _id: string }) => project._id);
|
|
||||||
// let projectToLogin;
|
|
||||||
// if (orgUserProjects.includes(localStorage.getItem('projectData.id'))) {
|
|
||||||
// projectToLogin = localStorage.getItem('projectData.id');
|
|
||||||
// } else {
|
|
||||||
// try {
|
|
||||||
// projectToLogin = orgUserProjects[0];
|
|
||||||
// localStorage.setItem('projectData.id', projectToLogin);
|
|
||||||
// } catch (error) {
|
|
||||||
// console.log('ERROR: User likely has no projects. ', error);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
telemetry.identify(email);
|
telemetry.identify(email);
|
||||||
@@ -166,100 +140,4 @@ const attemptLogin = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default attemptLogin;
|
export default attemptLogin;
|
||||||
|
|
||||||
// should be function: init first project
|
|
||||||
|
|
||||||
// if (isSignUp) {
|
|
||||||
// const randomBytes = crypto.randomBytes(16).toString('hex');
|
|
||||||
// const PRIVATE_KEY = String(localStorage.getItem('PRIVATE_KEY'));
|
|
||||||
|
|
||||||
// const myUser = await getUser();
|
|
||||||
|
|
||||||
// const { ciphertext, nonce } = encryptAssymmetric({
|
|
||||||
// plaintext: randomBytes,
|
|
||||||
// publicKey: myUser.publicKey,
|
|
||||||
// privateKey: PRIVATE_KEY
|
|
||||||
// }) as { ciphertext: string; nonce: string };
|
|
||||||
|
|
||||||
// await uploadKeys(projectToLogin, myUser._id, ciphertext, nonce);
|
|
||||||
|
|
||||||
// const secretsToBeAdded: SecretDataProps[] = [
|
|
||||||
// {
|
|
||||||
// pos: 0,
|
|
||||||
// key: 'DATABASE_URL',
|
|
||||||
// // eslint-disable-next-line no-template-curly-in-string
|
|
||||||
// value: 'mongodb+srv://${DB_USERNAME}:${DB_PASSWORD}@mongodb.net',
|
|
||||||
// valueOverride: undefined,
|
|
||||||
// comment: 'Secret referencing example',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 1,
|
|
||||||
// key: 'DB_USERNAME',
|
|
||||||
// value: 'OVERRIDE_THIS',
|
|
||||||
// valueOverride: undefined,
|
|
||||||
// comment:
|
|
||||||
// 'Override secrets with personal value',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 2,
|
|
||||||
// key: 'DB_PASSWORD',
|
|
||||||
// value: 'OVERRIDE_THIS',
|
|
||||||
// valueOverride: undefined,
|
|
||||||
// comment:
|
|
||||||
// 'Another secret override',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 3,
|
|
||||||
// key: 'DB_USERNAME',
|
|
||||||
// value: 'user1234',
|
|
||||||
// valueOverride: 'user1234',
|
|
||||||
// comment: '',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 4,
|
|
||||||
// key: 'DB_PASSWORD',
|
|
||||||
// value: 'example_password',
|
|
||||||
// valueOverride: 'example_password',
|
|
||||||
// comment: '',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 5,
|
|
||||||
// key: 'TWILIO_AUTH_TOKEN',
|
|
||||||
// value: 'example_twillio_token',
|
|
||||||
// valueOverride: undefined,
|
|
||||||
// comment: '',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// pos: 6,
|
|
||||||
// key: 'WEBSITE_URL',
|
|
||||||
// value: 'http://localhost:3000',
|
|
||||||
// valueOverride: undefined,
|
|
||||||
// comment: '',
|
|
||||||
// id: '',
|
|
||||||
// tags: []
|
|
||||||
// }
|
|
||||||
// ];
|
|
||||||
// const secrets = await encryptSecrets({
|
|
||||||
// secretsToEncrypt: secretsToBeAdded,
|
|
||||||
// workspaceId: String(localStorage.getItem('projectData.id')),
|
|
||||||
// env: 'dev'
|
|
||||||
// });
|
|
||||||
// await addSecrets({
|
|
||||||
// secrets: secrets ?? [],
|
|
||||||
// env: 'dev',
|
|
||||||
// workspaceId: String(localStorage.getItem('projectData.id'))
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
@@ -3,6 +3,8 @@ import jsrp from 'jsrp';
|
|||||||
|
|
||||||
import login1 from '@app/pages/api/auth/Login1';
|
import login1 from '@app/pages/api/auth/Login1';
|
||||||
import verifyMfaToken from '@app/pages/api/auth/verifyMfaToken';
|
import verifyMfaToken from '@app/pages/api/auth/verifyMfaToken';
|
||||||
|
import getOrganizations from '@app/pages/api/organization/getOrgs';
|
||||||
|
import getOrganizationUserProjects from '@app/pages/api/organization/GetOrgUserProjects';
|
||||||
import KeyService from '@app/services/KeyService';
|
import KeyService from '@app/services/KeyService';
|
||||||
|
|
||||||
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||||
@@ -51,7 +53,8 @@ const attemptLoginMfa = async ({
|
|||||||
mfaToken
|
mfaToken
|
||||||
});
|
});
|
||||||
|
|
||||||
// set JWT token
|
// unset temporary (MFA) JWT token and set JWT token
|
||||||
|
SecurityClient.setMfaToken('');
|
||||||
SecurityClient.setToken(token);
|
SecurityClient.setToken(token);
|
||||||
|
|
||||||
const privateKey = await KeyService.decryptPrivateKey({
|
const privateKey = await KeyService.decryptPrivateKey({
|
||||||
@@ -77,6 +80,18 @@ const attemptLoginMfa = async ({
|
|||||||
privateKey
|
privateKey
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: in the future - move this logic elsewhere
|
||||||
|
// because this function is about logging the user in
|
||||||
|
// and not initializing the login details
|
||||||
|
const userOrgs = await getOrganizations();
|
||||||
|
const orgId = userOrgs[0]._id;
|
||||||
|
localStorage.setItem('orgData.id', orgId);
|
||||||
|
|
||||||
|
const orgUserProjects = await getOrganizationUserProjects({
|
||||||
|
orgId
|
||||||
|
});
|
||||||
|
localStorage.setItem('projectData.id', orgUserProjects[0]._id);
|
||||||
|
|
||||||
resolve(true);
|
resolve(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
import { getAuthToken } from '@app/reactQuery';
|
import {
|
||||||
|
getAuthToken,
|
||||||
|
getMfaTempToken,
|
||||||
|
getSignupTempToken} from '@app/reactQuery';
|
||||||
|
|
||||||
export const apiRequest = axios.create({
|
export const apiRequest = axios.create({
|
||||||
baseURL: '/',
|
baseURL: '/',
|
||||||
@@ -10,11 +13,17 @@ export const apiRequest = axios.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
apiRequest.interceptors.request.use((config) => {
|
apiRequest.interceptors.request.use((config) => {
|
||||||
|
const signupTempToken = getSignupTempToken();
|
||||||
|
const mfaTempToken = getMfaTempToken();
|
||||||
const token = getAuthToken();
|
const token = getAuthToken();
|
||||||
console.log('interceptors');
|
|
||||||
console.log('token', token);
|
if (signupTempToken && config.headers) {
|
||||||
console.log('config.headers', config.headers);
|
// eslint-disable-next-line no-param-reassign
|
||||||
if (token && config.headers) {
|
config.headers.Authorization = `Bearer ${signupTempToken}`;
|
||||||
|
} else if (mfaTempToken && config.headers) {
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
config.headers.Authorization = `Bearer ${mfaTempToken}`;
|
||||||
|
} else if (token && config.headers) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
export { useGetAuthToken } from './queries';
|
export {
|
||||||
|
useGetAuthToken,
|
||||||
|
useSendMfaToken,
|
||||||
|
useVerifyMfaToken} from './queries'
|
||||||
|
|||||||
@@ -1,14 +1,39 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
import { apiRequest } from '@app/config/request';
|
import { apiRequest } from '@app/config/request';
|
||||||
import { setAuthToken } from '@app/reactQuery';
|
import { setAuthToken } from '@app/reactQuery';
|
||||||
|
|
||||||
import { GetAuthTokenAPI } from './types';
|
import {
|
||||||
|
GetAuthTokenAPI,
|
||||||
|
SendMfaTokenDTO,
|
||||||
|
VerifyMfaTokenDTO,
|
||||||
|
VerifyMfaTokenRes} from './types';
|
||||||
|
|
||||||
const authKeys = {
|
const authKeys = {
|
||||||
getAuthToken: ['token'] as const
|
getAuthToken: ['token'] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSendMfaToken = () => {
|
||||||
|
return useMutation<{}, {}, SendMfaTokenDTO>({
|
||||||
|
mutationFn: async ({ email }) => {
|
||||||
|
const { data } = await apiRequest.post('/api/v2/auth/mfa/send', { email });
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useVerifyMfaToken = () => {
|
||||||
|
return useMutation<VerifyMfaTokenRes, {}, VerifyMfaTokenDTO>({
|
||||||
|
mutationFn: async ({ email, mfaCode }) => {
|
||||||
|
const { data } = await apiRequest.post('/api/v2/auth/mfa/verify', {
|
||||||
|
email,
|
||||||
|
mfaToken: mfaCode
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh token is set as cookie when logged in
|
// Refresh token is set as cookie when logged in
|
||||||
// Using that we fetch the auth bearer token needed for auth calls
|
// Using that we fetch the auth bearer token needed for auth calls
|
||||||
const fetchAuthToken = async () => {
|
const fetchAuthToken = async () => {
|
||||||
|
|||||||
@@ -1,3 +1,24 @@
|
|||||||
export type GetAuthTokenAPI = {
|
export type GetAuthTokenAPI = {
|
||||||
token: string;
|
token: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SendMfaTokenDTO = {
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VerifyMfaTokenDTO = {
|
||||||
|
email: string;
|
||||||
|
mfaCode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type VerifyMfaTokenRes = {
|
||||||
|
encryptionVersion: number;
|
||||||
|
protectedKey?: string;
|
||||||
|
protectedKeyIV?: string;
|
||||||
|
protectedKeyTag?: string;
|
||||||
|
token: string;
|
||||||
|
publicKey: string;
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
iv: string;
|
||||||
|
tag: string;
|
||||||
|
}
|
||||||
@@ -93,6 +93,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
// Placing the localstorage as much as possible
|
// Placing the localstorage as much as possible
|
||||||
// Wait till tony integrates the azure and its launched
|
// Wait till tony integrates the azure and its launched
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
// Put a user in a workspace if they're not in one yet
|
// Put a user in a workspace if they're not in one yet
|
||||||
const putUserInWorkSpace = async () => {
|
const putUserInWorkSpace = async () => {
|
||||||
if (tempLocalStorage('orgData.id') === '') {
|
if (tempLocalStorage('orgData.id') === '') {
|
||||||
|
|||||||
@@ -75,8 +75,18 @@ export const Navbar = () => {
|
|||||||
|
|
||||||
const closeApp = async () => {
|
const closeApp = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('Logging out...');
|
console.log('Logging out...')
|
||||||
await logout.mutateAsync();
|
await logout.mutateAsync();
|
||||||
|
localStorage.removeItem('protectedKey');
|
||||||
|
localStorage.removeItem('protectedKeyIV');
|
||||||
|
localStorage.removeItem('protectedKeyTag');
|
||||||
|
localStorage.removeItem('publicKey');
|
||||||
|
localStorage.removeItem('encryptedPrivateKey');
|
||||||
|
localStorage.removeItem('iv');
|
||||||
|
localStorage.removeItem('tag');
|
||||||
|
localStorage.removeItem('PRIVATE_KEY');
|
||||||
|
localStorage.removeItem('orgData.id');
|
||||||
|
localStorage.removeItem('projectData.id');
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import SecurityClient from '@app/components/utilities/SecurityClient';
|
|
||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
email: string;
|
email: string;
|
||||||
@@ -35,7 +36,7 @@ interface Props {
|
|||||||
* @param {string} obj.verifier
|
* @param {string} obj.verifier
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const completeAccountInformationSignup = ({
|
const completeAccountInformationSignup = async ({
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -49,32 +50,24 @@ const completeAccountInformationSignup = ({
|
|||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier,
|
||||||
organizationName
|
organizationName
|
||||||
}: Props) => SecurityClient.fetchCall('/api/v2/signup/complete-account/signup', {
|
}: Props) => {
|
||||||
method: 'POST',
|
const { data } = await apiRequest.post('/api/v2/signup/complete-account/signup', {
|
||||||
headers: {
|
email,
|
||||||
'Content-Type': 'application/json'
|
firstName,
|
||||||
},
|
lastName,
|
||||||
body: JSON.stringify({
|
protectedKey,
|
||||||
email,
|
protectedKeyIV,
|
||||||
firstName,
|
protectedKeyTag,
|
||||||
lastName,
|
publicKey,
|
||||||
protectedKey,
|
encryptedPrivateKey,
|
||||||
protectedKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
protectedKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
publicKey,
|
salt,
|
||||||
encryptedPrivateKey,
|
verifier,
|
||||||
encryptedPrivateKeyIV,
|
organizationName
|
||||||
encryptedPrivateKeyTag,
|
|
||||||
salt,
|
|
||||||
verifier,
|
|
||||||
organizationName
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res?.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log('Failed to verify MFA code');
|
|
||||||
throw new Error('Something went wrong during MFA code verification');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export default completeAccountInformationSignup;
|
export default completeAccountInformationSignup;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
email: string;
|
email: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
@@ -11,9 +13,12 @@ interface Props {
|
|||||||
encryptedPrivateKeyTag: string;
|
encryptedPrivateKeyTag: string;
|
||||||
salt: string;
|
salt: string;
|
||||||
verifier: string;
|
verifier: string;
|
||||||
token: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// missing token?
|
||||||
|
// TODO: add to SecurityClient
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is called in the end of the signup process.
|
* This function is called in the end of the signup process.
|
||||||
* It sends all the necessary nformation to the server.
|
* It sends all the necessary nformation to the server.
|
||||||
@@ -30,7 +35,7 @@ interface Props {
|
|||||||
* @param {string} obj.token - token that confirms a user's identity
|
* @param {string} obj.token - token that confirms a user's identity
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const completeAccountInformationSignupInvite = ({
|
const completeAccountInformationSignupInvite = async ({
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -42,28 +47,24 @@ const completeAccountInformationSignupInvite = ({
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier
|
||||||
token
|
}: Props) => {
|
||||||
}: Props) => fetch('/api/v2/signup/complete-account/invite', {
|
const { data } = await apiRequest.post('/api/v2/signup/complete-account/invite', {
|
||||||
method: 'POST',
|
email,
|
||||||
headers: {
|
firstName,
|
||||||
'Content-Type': 'application/json',
|
lastName,
|
||||||
Authorization: `Bearer ${token}`
|
protectedKey,
|
||||||
},
|
protectedKeyIV,
|
||||||
body: JSON.stringify({
|
protectedKeyTag,
|
||||||
email,
|
publicKey,
|
||||||
firstName,
|
encryptedPrivateKey,
|
||||||
lastName,
|
encryptedPrivateKeyIV,
|
||||||
protectedKey,
|
encryptedPrivateKeyTag,
|
||||||
protectedKeyIV,
|
salt,
|
||||||
protectedKeyTag,
|
verifier
|
||||||
publicKey,
|
|
||||||
encryptedPrivateKey,
|
|
||||||
encryptedPrivateKeyIV,
|
|
||||||
encryptedPrivateKeyTag,
|
|
||||||
salt,
|
|
||||||
verifier
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
export default completeAccountInformationSignupInvite;
|
export default completeAccountInformationSignupInvite;
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import SecurityClient from '@app/components/utilities/SecurityClient';
|
|||||||
* This route logs the user out. Note: the user should authorized to do this.
|
* This route logs the user out. Note: the user should authorized to do this.
|
||||||
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
|
* We first try to log out - if the authorization fails (response.status = 401), we refetch the new token, and then retry
|
||||||
*/
|
*/
|
||||||
const logout = async () =>
|
const logout = async () => {
|
||||||
SecurityClient.fetchCall('/api/v1/auth/logout', {
|
try {
|
||||||
method: 'POST',
|
const res = await SecurityClient.fetchCall('/api/v1/auth/logout', {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'Content-Type': 'application/json'
|
headers: {
|
||||||
},
|
'Content-Type': 'application/json'
|
||||||
credentials: 'include'
|
},
|
||||||
}).then((res) => {
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
if (res?.status === 200) {
|
if (res?.status === 200) {
|
||||||
SecurityClient.setToken('');
|
SecurityClient.setToken('');
|
||||||
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
// Delete the cookie by not setting a value; Alternatively clear the local storage
|
||||||
@@ -23,12 +25,17 @@ const logout = async () =>
|
|||||||
localStorage.removeItem('iv');
|
localStorage.removeItem('iv');
|
||||||
localStorage.removeItem('tag');
|
localStorage.removeItem('tag');
|
||||||
localStorage.removeItem('PRIVATE_KEY');
|
localStorage.removeItem('PRIVATE_KEY');
|
||||||
|
localStorage.removeItem('orgData.id');
|
||||||
|
localStorage.removeItem('projectData.id');
|
||||||
|
|
||||||
console.log('User logged out', res);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
console.log('Failed to log out');
|
|
||||||
return undefined;
|
} catch (error) {
|
||||||
});
|
console.log('Error logging out', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
export default logout;
|
export default logout;
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send new MFA token to user with email [email]
|
|
||||||
* @param {object} obj
|
|
||||||
* @param {string} obj.email - email of user
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const resendMfaToken = async ({
|
|
||||||
email,
|
|
||||||
}: {
|
|
||||||
email: string;
|
|
||||||
}) => SecurityClient.fetchCall('/api/v2/auth/mfa/send', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email
|
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res?.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log('Failed to send new MFA code');
|
|
||||||
throw new Error('Something went wrong while sending new MFA code');
|
|
||||||
});
|
|
||||||
|
|
||||||
export default resendMfaToken;
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify MFA token [mfaToken] for user with email [email]
|
* Verify MFA token [mfaToken] for user with email [email]
|
||||||
@@ -13,23 +13,13 @@ const verifyMfaToken = async ({
|
|||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
mfaToken: string;
|
mfaToken: string;
|
||||||
}) => SecurityClient.fetchCall('/api/v2/auth/mfa/verify', {
|
}) => {
|
||||||
method: 'POST',
|
const { data } = await apiRequest.post('/api/v2/auth/mfa/verify', {
|
||||||
headers: {
|
email,
|
||||||
'Content-Type': 'application/json'
|
mfaToken
|
||||||
},
|
});
|
||||||
body: JSON.stringify({
|
|
||||||
email,
|
return data;
|
||||||
mfaToken
|
}
|
||||||
})
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res && res?.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log('Failed to verify MFA code');
|
|
||||||
throw new Error('Something went wrong during MFA code verification');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default verifyMfaToken;
|
export default verifyMfaToken;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export default function Login() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [isAlreadyLoggedIn, setIsAlreadyLoggedIn] = useState(false);
|
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const lang = router.locale ?? 'en';
|
const lang = router.locale ?? 'en';
|
||||||
@@ -41,7 +40,6 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (isLoggedIn()) {
|
if (isLoggedIn()) {
|
||||||
setIsAlreadyLoggedIn(true);
|
|
||||||
redirectToDashboard();
|
redirectToDashboard();
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@@ -72,10 +70,6 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAlreadyLoggedIn) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bunker-800 h-screen flex flex-col justify-start px-6">
|
<div className="bg-bunker-800 h-screen flex flex-col justify-start px-6">
|
||||||
<Head>
|
<Head>
|
||||||
@@ -106,4 +100,4 @@ export default function Login() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStaticProps = getTranslatedStaticProps(['auth', 'login']);
|
export const getStaticProps = getTranslatedStaticProps(['auth', 'login', 'signup']);
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ export default function SignUp() {
|
|||||||
// Checking if the code matches the email.
|
// Checking if the code matches the email.
|
||||||
const response = await checkEmailVerificationCode({ email, code });
|
const response = await checkEmailVerificationCode({ email, code });
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
SecurityClient.setToken((await response.json()).token);
|
const {token} = await response.json();
|
||||||
|
SecurityClient.setSignupToken(token);
|
||||||
setStep(3);
|
setStep(3);
|
||||||
} else {
|
} else {
|
||||||
setCodeError(true);
|
setCodeError(true);
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import passwordCheck from '@app/components/utilities/checks/PasswordCheck';
|
|||||||
import Aes256Gcm from '@app/components/utilities/cryptography/aes-256-gcm';
|
import Aes256Gcm from '@app/components/utilities/cryptography/aes-256-gcm';
|
||||||
import { deriveArgonKey } from '@app/components/utilities/cryptography/crypto';
|
import { deriveArgonKey } from '@app/components/utilities/cryptography/crypto';
|
||||||
import issueBackupKey from '@app/components/utilities/cryptography/issueBackupKey';
|
import issueBackupKey from '@app/components/utilities/cryptography/issueBackupKey';
|
||||||
|
import { saveTokenToLocalStorage } from '@app/components/utilities/saveTokenToLocalStorage';
|
||||||
|
import SecurityClient from '@app/components/utilities/SecurityClient';
|
||||||
|
import getOrganizations from '@app/pages/api/organization/getOrgs';
|
||||||
|
import getOrganizationUserProjects from '@app/pages/api/organization/GetOrgUserProjects';
|
||||||
|
|
||||||
import completeAccountInformationSignupInvite from './api/auth/CompleteAccountInformationSignupInvite';
|
import completeAccountInformationSignupInvite from './api/auth/CompleteAccountInformationSignupInvite';
|
||||||
import verifySignupInvite from './api/auth/VerifySignupInvite';
|
import verifySignupInvite from './api/auth/VerifySignupInvite';
|
||||||
@@ -29,7 +33,6 @@ import verifySignupInvite from './api/auth/VerifySignupInvite';
|
|||||||
const client = new jsrp.client();
|
const client = new jsrp.client();
|
||||||
|
|
||||||
export default function SignupInvite() {
|
export default function SignupInvite() {
|
||||||
console.log('SignupInvite');
|
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [firstName, setFirstName] = useState('');
|
const [firstName, setFirstName] = useState('');
|
||||||
const [lastName, setLastName] = useState('');
|
const [lastName, setLastName] = useState('');
|
||||||
@@ -82,13 +85,6 @@ export default function SignupInvite() {
|
|||||||
const privateKey = encodeBase64(secretKeyUint8Array);
|
const privateKey = encodeBase64(secretKeyUint8Array);
|
||||||
const publicKey = encodeBase64(publicKeyUint8Array);
|
const publicKey = encodeBase64(publicKeyUint8Array);
|
||||||
|
|
||||||
// const { ciphertext, iv, tag } = Aes256Gcm.encrypt({
|
|
||||||
// text: PRIVATE_KEY,
|
|
||||||
// secret: password
|
|
||||||
// .slice(0, 32)
|
|
||||||
// .padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), '0')
|
|
||||||
// });
|
|
||||||
|
|
||||||
localStorage.setItem('PRIVATE_KEY', privateKey);
|
localStorage.setItem('PRIVATE_KEY', privateKey);
|
||||||
|
|
||||||
client.init(
|
client.init(
|
||||||
@@ -134,8 +130,9 @@ export default function SignupInvite() {
|
|||||||
secret: Buffer.from(derivedKey.hash)
|
secret: Buffer.from(derivedKey.hash)
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('SignupInvite A');
|
const {
|
||||||
let response = await completeAccountInformationSignupInvite({
|
token: jwtToken
|
||||||
|
} = await completeAccountInformationSignupInvite({
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -147,24 +144,30 @@ export default function SignupInvite() {
|
|||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt: result.salt,
|
salt: result.salt,
|
||||||
verifier: result.verifier,
|
verifier: result.verifier
|
||||||
token: verificationToken
|
|
||||||
});
|
});
|
||||||
console.log('SignupInvite B');
|
|
||||||
|
// unset temporary signup JWT token and set JWT token
|
||||||
|
SecurityClient.setSignupToken('');
|
||||||
|
SecurityClient.setToken(jwtToken);
|
||||||
|
|
||||||
// if everything works, go the main dashboard page.
|
saveTokenToLocalStorage({
|
||||||
if (!errorCheck && response.status === 200) {
|
protectedKey,
|
||||||
response = await response.json();
|
protectedKeyIV,
|
||||||
|
protectedKeyTag,
|
||||||
|
publicKey,
|
||||||
|
encryptedPrivateKey,
|
||||||
|
iv: encryptedPrivateKeyIV,
|
||||||
|
tag: encryptedPrivateKeyTag,
|
||||||
|
privateKey
|
||||||
|
});
|
||||||
|
|
||||||
console.log('SignupInvite C');
|
const userOrgs = await getOrganizations();
|
||||||
localStorage.setItem('publicKey', publicKey);
|
|
||||||
localStorage.setItem('encryptedPrivateKey', encryptedPrivateKey);
|
|
||||||
localStorage.setItem('iv', encryptedPrivateKeyIV);
|
|
||||||
localStorage.setItem('tag', encryptedPrivateKeyTag);
|
|
||||||
console.log('SignupInvite D');
|
|
||||||
|
|
||||||
setStep(3);
|
const orgId = userOrgs[0]._id;
|
||||||
}
|
localStorage.setItem('orgData.id', orgId);
|
||||||
|
|
||||||
|
setStep(3);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -197,7 +200,7 @@ export default function SignupInvite() {
|
|||||||
// user will have temp token if doesn't have an account
|
// user will have temp token if doesn't have an account
|
||||||
// then continue with account setup workflow
|
// then continue with account setup workflow
|
||||||
if (res?.token) {
|
if (res?.token) {
|
||||||
setVerificationToken(res.token);
|
SecurityClient.setSignupToken(res.token);
|
||||||
setStep(2);
|
setStep(2);
|
||||||
} else {
|
} else {
|
||||||
// user will be redirected to dashboard
|
// user will be redirected to dashboard
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { QueryClient } from '@tanstack/react-query';
|
import { QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
// this is saved in react-query cache
|
// this is saved in react-query cache
|
||||||
|
export const SIGNUP_TEMP_TOKEN_CACHE_KEY = ['infisical__signup-temp-token'];
|
||||||
export const MFA_TEMP_TOKEN_CACHE_KEY = ['infisical__mfa-temp-token'];
|
export const MFA_TEMP_TOKEN_CACHE_KEY = ['infisical__mfa-temp-token'];
|
||||||
export const AUTH_TOKEN_CACHE_KEY = ['infisical__auth-token'];
|
export const AUTH_TOKEN_CACHE_KEY = ['infisical__auth-token'];
|
||||||
|
|
||||||
@@ -14,12 +15,16 @@ export const queryClient = new QueryClient({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// set token in memory cache
|
// set token in memory cache
|
||||||
|
export const setSignupTempToken = (token: string) =>
|
||||||
|
queryClient.setQueryData(SIGNUP_TEMP_TOKEN_CACHE_KEY, token);
|
||||||
|
|
||||||
export const setMfaTempToken = (token: string) =>
|
export const setMfaTempToken = (token: string) =>
|
||||||
queryClient.setQueryData(MFA_TEMP_TOKEN_CACHE_KEY, token);
|
queryClient.setQueryData(MFA_TEMP_TOKEN_CACHE_KEY, token);
|
||||||
|
|
||||||
export const setAuthToken = (token: string) =>
|
export const setAuthToken = (token: string) =>
|
||||||
queryClient.setQueryData(AUTH_TOKEN_CACHE_KEY, token);
|
queryClient.setQueryData(AUTH_TOKEN_CACHE_KEY, token);
|
||||||
|
|
||||||
|
export const getSignupTempToken = () => queryClient.getQueryData(SIGNUP_TEMP_TOKEN_CACHE_KEY) as string;
|
||||||
export const getMfaTempToken = () => queryClient.getQueryData(MFA_TEMP_TOKEN_CACHE_KEY) as string;
|
export const getMfaTempToken = () => queryClient.getQueryData(MFA_TEMP_TOKEN_CACHE_KEY) as string;
|
||||||
export const getAuthToken = () => queryClient.getQueryData(AUTH_TOKEN_CACHE_KEY) as string;
|
export const getAuthToken = () => queryClient.getQueryData(AUTH_TOKEN_CACHE_KEY) as string;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user