mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 11:27:07 +00:00
added signup v3 endpoints and developed initial new signup flow
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
import * as secretsController from './secretsController';
|
import * as secretsController from './secretsController';
|
||||||
import * as workspacesController from './workspacesController';
|
import * as workspacesController from './workspacesController';
|
||||||
import * as authController from './authController';
|
import * as authController from './authController';
|
||||||
|
import * as signupController from './signupController';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
authController,
|
authController,
|
||||||
secretsController,
|
secretsController,
|
||||||
|
signupController,
|
||||||
workspacesController,
|
workspacesController,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import * as Sentry from '@sentry/node';
|
||||||
|
import { User, MembershipOrg } from '../../models';
|
||||||
|
import { completeAccount } from '../../helpers/user';
|
||||||
|
import {
|
||||||
|
initializeDefaultOrg
|
||||||
|
} from '../../helpers/signup';
|
||||||
|
import { issueAuthTokens, validateProviderAuthToken } from '../../helpers/auth';
|
||||||
|
import { INVITED, ACCEPTED } from '../../variables';
|
||||||
|
import request from '../../config/request';
|
||||||
|
import { getLoopsApiKey, getHttpsEnabled, getJwtSignupSecret } from '../../config';
|
||||||
|
import { BadRequestError } from '../../utils/errors';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete setting up user by adding their personal and auth information as part of the
|
||||||
|
* signup flow
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||||
|
let user, token, refreshToken;
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
email,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
protectedKey,
|
||||||
|
protectedKeyIV,
|
||||||
|
protectedKeyTag,
|
||||||
|
publicKey,
|
||||||
|
encryptedPrivateKey,
|
||||||
|
encryptedPrivateKeyIV,
|
||||||
|
encryptedPrivateKeyTag,
|
||||||
|
salt,
|
||||||
|
verifier,
|
||||||
|
organizationName,
|
||||||
|
providerAuthToken,
|
||||||
|
}: {
|
||||||
|
email: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
protectedKey: string;
|
||||||
|
protectedKeyIV: string;
|
||||||
|
protectedKeyTag: string;
|
||||||
|
publicKey: string;
|
||||||
|
encryptedPrivateKey: string;
|
||||||
|
encryptedPrivateKeyIV: string;
|
||||||
|
encryptedPrivateKeyTag: string;
|
||||||
|
salt: string;
|
||||||
|
verifier: string;
|
||||||
|
organizationName: string;
|
||||||
|
providerAuthToken?: string;
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
user = await User.findOne({ email });
|
||||||
|
|
||||||
|
if (!user || (user && user?.publicKey)) {
|
||||||
|
// case 1: user doesn't exist.
|
||||||
|
// case 2: user has already completed account
|
||||||
|
return res.status(403).send({
|
||||||
|
error: 'Failed to complete account for complete user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (providerAuthToken) {
|
||||||
|
await validateProviderAuthToken({
|
||||||
|
email,
|
||||||
|
providerAuthToken,
|
||||||
|
user,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const [ AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE ] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
|
||||||
|
if(AUTH_TOKEN_TYPE === null) {
|
||||||
|
throw BadRequestError({message: `Missing Authorization Header in the request header.`});
|
||||||
|
}
|
||||||
|
if(AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') {
|
||||||
|
throw BadRequestError({message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.`})
|
||||||
|
}
|
||||||
|
if(AUTH_TOKEN_VALUE === null) {
|
||||||
|
throw BadRequestError({
|
||||||
|
message: 'Missing Authorization Body in the request header',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
|
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (decodedToken.userId !== user.id) {
|
||||||
|
throw BadRequestError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete setting up user's account
|
||||||
|
user = await completeAccount({
|
||||||
|
userId: user._id.toString(),
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
encryptionVersion: 2,
|
||||||
|
protectedKey,
|
||||||
|
protectedKeyIV,
|
||||||
|
protectedKeyTag,
|
||||||
|
publicKey,
|
||||||
|
encryptedPrivateKey,
|
||||||
|
encryptedPrivateKeyIV,
|
||||||
|
encryptedPrivateKeyTag,
|
||||||
|
salt,
|
||||||
|
verifier
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
|
||||||
|
|
||||||
|
// initialize default organization and workspace
|
||||||
|
await initializeDefaultOrg({
|
||||||
|
organizationName,
|
||||||
|
user
|
||||||
|
});
|
||||||
|
|
||||||
|
// update organization membership statuses that are
|
||||||
|
// invited to completed with user attached
|
||||||
|
await MembershipOrg.updateMany(
|
||||||
|
{
|
||||||
|
inviteEmail: email,
|
||||||
|
status: INVITED
|
||||||
|
},
|
||||||
|
{
|
||||||
|
user,
|
||||||
|
status: ACCEPTED
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// issue tokens
|
||||||
|
const tokens = await issueAuthTokens({
|
||||||
|
userId: user._id.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
token = tokens.token;
|
||||||
|
|
||||||
|
// sending a welcome email to new users
|
||||||
|
if (await getLoopsApiKey()) {
|
||||||
|
await request.post("https://app.loops.so/api/v1/events/send", {
|
||||||
|
"email": email,
|
||||||
|
"eventName": "Sign Up",
|
||||||
|
"firstName": firstName,
|
||||||
|
"lastName": lastName
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Authorization": "Bearer " + (await getLoopsApiKey())
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// store (refresh) token in httpOnly cookie
|
||||||
|
res.cookie('jid', tokens.refreshToken, {
|
||||||
|
httpOnly: true,
|
||||||
|
path: '/',
|
||||||
|
sameSite: 'strict',
|
||||||
|
secure: await getHttpsEnabled()
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
Sentry.setUser(null);
|
||||||
|
Sentry.captureException(err);
|
||||||
|
return res.status(400).send({
|
||||||
|
message: 'Failed to complete account setup'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
message: 'Successfully set up account',
|
||||||
|
user,
|
||||||
|
token
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -65,7 +65,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
auth as v3AuthRouter,
|
auth as v3AuthRouter,
|
||||||
secrets as v3SecretsRouter,
|
secrets as v3SecretsRouter,
|
||||||
workspaces as v3WorkspacesRouter
|
signup as v3SignupRouter,
|
||||||
|
workspaces as v3WorkspacesRouter,
|
||||||
} from './routes/v3';
|
} from './routes/v3';
|
||||||
import { healthCheck } from './routes/status';
|
import { healthCheck } from './routes/status';
|
||||||
import { getLogger } from './utils/logger';
|
import { getLogger } from './utils/logger';
|
||||||
@@ -170,6 +171,7 @@ const main = async () => {
|
|||||||
app.use('/api/v3/auth', v3AuthRouter);
|
app.use('/api/v3/auth', v3AuthRouter);
|
||||||
app.use('/api/v3/secrets', v3SecretsRouter);
|
app.use('/api/v3/secrets', v3SecretsRouter);
|
||||||
app.use('/api/v3/workspaces', v3WorkspacesRouter);
|
app.use('/api/v3/workspaces', v3WorkspacesRouter);
|
||||||
|
app.use('/api/v3/signup', v3SignupRouter);
|
||||||
|
|
||||||
// api docs
|
// api docs
|
||||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile))
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile))
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const userSchema = new Schema<IUser>(
|
|||||||
email: {
|
email: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
unique: true,
|
||||||
},
|
},
|
||||||
firstName: {
|
firstName: {
|
||||||
type: String
|
type: String
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import auth from './auth';
|
import auth from './auth';
|
||||||
import secrets from './secrets';
|
import secrets from './secrets';
|
||||||
import workspaces from './workspaces';
|
import workspaces from './workspaces';
|
||||||
|
import signup from './signup';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
auth,
|
auth,
|
||||||
secrets,
|
secrets,
|
||||||
workspaces
|
signup,
|
||||||
|
workspaces,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import express from 'express';
|
||||||
|
const router = express.Router();
|
||||||
|
import { body } from 'express-validator';
|
||||||
|
import { signupController } from '../../controllers/v3';
|
||||||
|
import { authLimiter } from '../../helpers/rateLimiter';
|
||||||
|
import { validateRequest } from '../../middleware';
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/complete-account/signup',
|
||||||
|
authLimiter,
|
||||||
|
body('email').exists().isString().trim().notEmpty().isEmail(),
|
||||||
|
body('firstName').exists().isString().trim().notEmpty(),
|
||||||
|
body('lastName').exists().isString().trim().notEmpty(),
|
||||||
|
body('protectedKey').exists().isString().trim().notEmpty(),
|
||||||
|
body('protectedKeyIV').exists().isString().trim().notEmpty(),
|
||||||
|
body('protectedKeyTag').exists().isString().trim().notEmpty(),
|
||||||
|
body('publicKey').exists().isString().trim().notEmpty(),
|
||||||
|
body('encryptedPrivateKey').exists().isString().trim().notEmpty(),
|
||||||
|
body('encryptedPrivateKeyIV').exists().isString().trim().notEmpty(),
|
||||||
|
body('encryptedPrivateKeyTag').exists().isString().trim().notEmpty(),
|
||||||
|
body('salt').exists().isString().trim().notEmpty(),
|
||||||
|
body('verifier').exists().isString().trim().notEmpty(),
|
||||||
|
body('organizationName').exists().isString().trim().notEmpty(),
|
||||||
|
validateRequest,
|
||||||
|
signupController.completeAccountSignup,
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -12,7 +12,6 @@ import { getTranslatedStaticProps } from '@app/components/utilities/withTranslat
|
|||||||
import SecurityClient from '../utilities/SecurityClient';
|
import SecurityClient from '../utilities/SecurityClient';
|
||||||
|
|
||||||
export default function PasswordInputStep({
|
export default function PasswordInputStep({
|
||||||
userId,
|
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
setPassword,
|
setPassword,
|
||||||
@@ -20,7 +19,6 @@ export default function PasswordInputStep({
|
|||||||
setStep
|
setStep
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
userId: string;
|
|
||||||
password: string;
|
password: string;
|
||||||
setPassword: (password: string) => void;
|
setPassword: (password: string) => void;
|
||||||
setProviderAuthToken: (value: string) => void;
|
setProviderAuthToken: (value: string) => void;
|
||||||
@@ -34,13 +32,8 @@ export default function PasswordInputStep({
|
|||||||
|
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
try {
|
try {
|
||||||
if (!userId || !password) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const isLoginSuccessful = await attemptLogin({
|
const isLoginSuccessful = await attemptLogin({
|
||||||
userId,
|
|
||||||
email,
|
email,
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ interface UserInfoStepProps {
|
|||||||
setFirstName: (value: string) => void;
|
setFirstName: (value: string) => void;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
setLastName: (value: string) => void;
|
setLastName: (value: string) => void;
|
||||||
|
providerAuthToken?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +56,8 @@ export default function UserInfoStep({
|
|||||||
firstName,
|
firstName,
|
||||||
setFirstName,
|
setFirstName,
|
||||||
lastName,
|
lastName,
|
||||||
setLastName
|
setLastName,
|
||||||
|
providerAuthToken,
|
||||||
}: UserInfoStepProps): JSX.Element {
|
}: UserInfoStepProps): JSX.Element {
|
||||||
const [firstNameError, setFirstNameError] = useState(false);
|
const [firstNameError, setFirstNameError] = useState(false);
|
||||||
const [lastNameError, setLastNameError] = useState(false);
|
const [lastNameError, setLastNameError] = useState(false);
|
||||||
@@ -156,9 +158,10 @@ export default function UserInfoStep({
|
|||||||
encryptedPrivateKey,
|
encryptedPrivateKey,
|
||||||
encryptedPrivateKeyIV,
|
encryptedPrivateKeyIV,
|
||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
|
providerAuthToken,
|
||||||
salt: result.salt,
|
salt: result.salt,
|
||||||
verifier: result.verifier,
|
verifier: result.verifier,
|
||||||
organizationName: `${firstName}'s organization`
|
organizationName: `${firstName}'s organization`,
|
||||||
});
|
});
|
||||||
|
|
||||||
// unset signup JWT token and set JWT token
|
// unset signup JWT token and set JWT token
|
||||||
|
|||||||
@@ -29,20 +29,17 @@ const attemptLogin = async (
|
|||||||
{
|
{
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
userId,
|
|
||||||
}: {
|
}: {
|
||||||
email: string;
|
email: string;
|
||||||
userId?: string;
|
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
): Promise<IsLoginSuccessful> => {
|
): Promise<IsLoginSuccessful> => {
|
||||||
|
|
||||||
const username = userId ?? email;
|
|
||||||
const telemetry = new Telemetry().getInstance();
|
const telemetry = new Telemetry().getInstance();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.init(
|
client.init(
|
||||||
{
|
{
|
||||||
username,
|
username: email,
|
||||||
password
|
password
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
@@ -51,7 +48,6 @@ const attemptLogin = async (
|
|||||||
const { serverPublicKey, salt } = await login1({
|
const { serverPublicKey, salt } = await login1({
|
||||||
email,
|
email,
|
||||||
clientPublicKey,
|
clientPublicKey,
|
||||||
userId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
client.setSalt(salt);
|
client.setSalt(salt);
|
||||||
@@ -72,7 +68,6 @@ const attemptLogin = async (
|
|||||||
} = await login2(
|
} = await login2(
|
||||||
{
|
{
|
||||||
email,
|
email,
|
||||||
userId,
|
|
||||||
clientProof,
|
clientProof,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,10 +31,16 @@ export const useProviderAuth = () => {
|
|||||||
|
|
||||||
window.addEventListener('storage', handleStorageChange);
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
if (providerAuthToken) {
|
||||||
|
const { userId: resultUserId, email: resultEmail } = jwt_decode(providerAuthToken) as any;
|
||||||
|
setEmail(resultEmail);
|
||||||
|
setUserId(resultUserId);
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('storage', handleStorageChange);
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
};
|
};
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
email,
|
email,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ interface Props {
|
|||||||
protectedKey: string;
|
protectedKey: string;
|
||||||
protectedKeyIV: string;
|
protectedKeyIV: string;
|
||||||
protectedKeyTag: string;
|
protectedKeyTag: string;
|
||||||
|
providerAuthToken?: string;
|
||||||
publicKey: string;
|
publicKey: string;
|
||||||
encryptedPrivateKey: string;
|
encryptedPrivateKey: string;
|
||||||
encryptedPrivateKeyIV: string;
|
encryptedPrivateKeyIV: string;
|
||||||
@@ -49,9 +50,10 @@ const completeAccountInformationSignup = async ({
|
|||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier,
|
||||||
organizationName
|
organizationName,
|
||||||
|
providerAuthToken,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { data } = await apiRequest.post('/api/v2/signup/complete-account/signup', {
|
const { data } = await apiRequest.post('/api/v3/signup/complete-account/signup', {
|
||||||
email,
|
email,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -64,7 +66,8 @@ const completeAccountInformationSignup = async ({
|
|||||||
encryptedPrivateKeyTag,
|
encryptedPrivateKeyTag,
|
||||||
salt,
|
salt,
|
||||||
verifier,
|
verifier,
|
||||||
organizationName
|
organizationName,
|
||||||
|
providerAuthToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ interface Login1 {
|
|||||||
const login1 = async (loginDetails: {
|
const login1 = async (loginDetails: {
|
||||||
email: string;
|
email: string;
|
||||||
clientPublicKey: string;
|
clientPublicKey: string;
|
||||||
userId?: string;
|
|
||||||
}) => {
|
}) => {
|
||||||
const response = await fetch("/api/v2/auth/login1", {
|
const response = await fetch("/api/v2/auth/login1", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ interface Login2Response {
|
|||||||
const login2 = async (loginDetails: {
|
const login2 = async (loginDetails: {
|
||||||
email: string;
|
email: string;
|
||||||
clientProof: string;
|
clientProof: string;
|
||||||
userId?: string;
|
|
||||||
}) => {
|
}) => {
|
||||||
const response = await fetch('/api/v2/auth/login2', {
|
const response = await fetch('/api/v2/auth/login2', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export default function Login() {
|
|||||||
const [isLoginWithEmail, setIsLoginWithEmail] = useState(false);
|
const [isLoginWithEmail, setIsLoginWithEmail] = useState(false);
|
||||||
const {
|
const {
|
||||||
providerAuthToken,
|
providerAuthToken,
|
||||||
userId,
|
|
||||||
email: providerEmail,
|
email: providerEmail,
|
||||||
setProviderAuthToken
|
setProviderAuthToken
|
||||||
} = useProviderAuth();
|
} = useProviderAuth();
|
||||||
@@ -57,7 +56,6 @@ export default function Login() {
|
|||||||
if (providerAuthToken && step === 1) {
|
if (providerAuthToken && step === 1) {
|
||||||
return (
|
return (
|
||||||
<PasswordInputStep
|
<PasswordInputStep
|
||||||
userId={userId}
|
|
||||||
email={providerEmail}
|
email={providerEmail}
|
||||||
password={password}
|
password={password}
|
||||||
setPassword={setPassword}
|
setPassword={setPassword}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function SignUp() {
|
|||||||
const { data: serverDetails } = useFetchServerStatus();
|
const { data: serverDetails } = useFetchServerStatus();
|
||||||
const [isSignupWithEmail, setIsSignupWithEmail] = useState(false);
|
const [isSignupWithEmail, setIsSignupWithEmail] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { providerAuthToken } = useProviderAuth();
|
const { email: providerEmail, providerAuthToken } = useProviderAuth();
|
||||||
|
|
||||||
if (providerAuthToken && step < 3) {
|
if (providerAuthToken && step < 3) {
|
||||||
setStep(3);
|
setStep(3);
|
||||||
@@ -99,7 +99,7 @@ export default function SignUp() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button type='button' className='text-white' onClick={() => {
|
<button type='button' className='text-white' onClick={() => {
|
||||||
window.open('/api/v1/auth/login/google')
|
window.open('/api/v1/oauth/redirect/google')
|
||||||
}}>
|
}}>
|
||||||
Continue with Google
|
Continue with Google
|
||||||
</button>
|
</button>
|
||||||
@@ -127,13 +127,14 @@ export default function SignUp() {
|
|||||||
return (
|
return (
|
||||||
<UserInfoStep
|
<UserInfoStep
|
||||||
incrementStep={incrementStep}
|
incrementStep={incrementStep}
|
||||||
email={email}
|
email={email || providerEmail}
|
||||||
password={password}
|
password={password}
|
||||||
setPassword={setPassword}
|
setPassword={setPassword}
|
||||||
firstName={firstName}
|
firstName={firstName}
|
||||||
setFirstName={setFirstName}
|
setFirstName={setFirstName}
|
||||||
lastName={lastName}
|
lastName={lastName}
|
||||||
setLastName={setLastName}
|
setLastName={setLastName}
|
||||||
|
providerAuthToken={providerAuthToken}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -142,7 +143,7 @@ export default function SignUp() {
|
|||||||
return (
|
return (
|
||||||
<DownloadBackupPDF
|
<DownloadBackupPDF
|
||||||
incrementStep={incrementStep}
|
incrementStep={incrementStep}
|
||||||
email={email}
|
email={email || providerEmail}
|
||||||
password={password}
|
password={password}
|
||||||
name={`${firstName} ${lastName}`}
|
name={`${firstName} ${lastName}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user