mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 07:28:21 +00:00
Integrated login with Gitlab
This commit is contained in:
Generated
+20
@@ -46,6 +46,7 @@
|
|||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-github": "^1.1.0",
|
"passport-github": "^1.1.0",
|
||||||
|
"passport-gitlab2": "^5.0.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
"posthog-node": "^2.6.0",
|
"posthog-node": "^2.6.0",
|
||||||
"probot": "^12.3.1",
|
"probot": "^12.3.1",
|
||||||
@@ -13698,6 +13699,17 @@
|
|||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/passport-gitlab2": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-gitlab2/-/passport-gitlab2-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-cXQMgM6JQx9wHVh7JLH30D8fplfwjsDwRz+zS0pqC8JS+4bNmc1J04NGp5g2M4yfwylH9kQRrMN98GxMw7q7cg==",
|
||||||
|
"dependencies": {
|
||||||
|
"passport-oauth2": "^1.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/passport-google-oauth20": {
|
"node_modules/passport-google-oauth20": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
|
||||||
@@ -27074,6 +27086,14 @@
|
|||||||
"passport-oauth2": "1.x.x"
|
"passport-oauth2": "1.x.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"passport-gitlab2": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/passport-gitlab2/-/passport-gitlab2-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-cXQMgM6JQx9wHVh7JLH30D8fplfwjsDwRz+zS0pqC8JS+4bNmc1J04NGp5g2M4yfwylH9kQRrMN98GxMw7q7cg==",
|
||||||
|
"requires": {
|
||||||
|
"passport-oauth2": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"passport-google-oauth20": {
|
"passport-google-oauth20": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-github": "^1.1.0",
|
"passport-github": "^1.1.0",
|
||||||
|
"passport-gitlab2": "^5.0.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
"posthog-node": "^2.6.0",
|
"posthog-node": "^2.6.0",
|
||||||
"probot": "^12.3.1",
|
"probot": "^12.3.1",
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ export const getClientIdGoogleLogin = async () => (await client.getSecret("CLIEN
|
|||||||
export const getClientSecretGoogleLogin = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE_LOGIN")).secretValue;
|
export const getClientSecretGoogleLogin = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE_LOGIN")).secretValue;
|
||||||
export const getClientIdGitHubLogin = async () => (await client.getSecret("CLIENT_ID_GITHUB_LOGIN")).secretValue;
|
export const getClientIdGitHubLogin = async () => (await client.getSecret("CLIENT_ID_GITHUB_LOGIN")).secretValue;
|
||||||
export const getClientSecretGitHubLogin = async () => (await client.getSecret("CLIENT_SECRET_GITHUB_LOGIN")).secretValue;
|
export const getClientSecretGitHubLogin = async () => (await client.getSecret("CLIENT_SECRET_GITHUB_LOGIN")).secretValue;
|
||||||
|
export const getClientIdGitLabLogin = async () => (await client.getSecret("CLIENT_ID_GITLAB_LOGIN")).secretValue;
|
||||||
|
export const getClientSecretGitLabLogin = async () => (await client.getSecret("CLIENT_SECRET_GITLAB_LOGIN")).secretValue;
|
||||||
|
|
||||||
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
||||||
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
||||||
|
|||||||
@@ -65,6 +65,29 @@ router.get(
|
|||||||
ssoController.redirectSSO
|
ssoController.redirectSSO
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/redirect/gitlab",
|
||||||
|
authLimiter,
|
||||||
|
(req, res, next) => {
|
||||||
|
passport.authenticate("gitlab", {
|
||||||
|
session: false,
|
||||||
|
...(req.query.callback_port ? {
|
||||||
|
state: req.query.callback_port as string
|
||||||
|
} : {})
|
||||||
|
})(req, res, next);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/gitlab",
|
||||||
|
authLimiter,
|
||||||
|
passport.authenticate("gitlab", {
|
||||||
|
failureRedirect: "/login/provider/error",
|
||||||
|
session: false
|
||||||
|
}),
|
||||||
|
ssoController.redirectSSO
|
||||||
|
)
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
"/redirect/saml2/:ssoIdentifier",
|
"/redirect/saml2/:ssoIdentifier",
|
||||||
authLimiter,
|
authLimiter,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export enum AuthMethod {
|
|||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
GITHUB = "github",
|
GITHUB = "github",
|
||||||
|
GITLAB = "gitlab",
|
||||||
OKTA_SAML = "okta-saml",
|
OKTA_SAML = "okta-saml",
|
||||||
AZURE_SAML = "azure-saml",
|
AZURE_SAML = "azure-saml",
|
||||||
JUMPCLOUD_SAML = "jumpcloud-saml",
|
JUMPCLOUD_SAML = "jumpcloud-saml",
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ router.put(
|
|||||||
return authMethods.every(provider => [
|
return authMethods.every(provider => [
|
||||||
AuthMethod.EMAIL,
|
AuthMethod.EMAIL,
|
||||||
AuthMethod.GOOGLE,
|
AuthMethod.GOOGLE,
|
||||||
AuthMethod.GITHUB
|
AuthMethod.GITHUB,
|
||||||
|
AuthMethod.GITLAB
|
||||||
].includes(provider))
|
].includes(provider))
|
||||||
}),
|
}),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
|
|||||||
@@ -155,6 +155,12 @@ Other environment variables are listed below to increase the functionality of yo
|
|||||||
<ParamField query="CLIENT_SECRET_GITHUB_LOGIN" type="string" default="none" optional>
|
<ParamField query="CLIENT_SECRET_GITHUB_LOGIN" type="string" default="none" optional>
|
||||||
OAuth2 client secret for GitHub login
|
OAuth2 client secret for GitHub login
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
<ParamField query="CLIENT_ID_GITLAB_LOGIN" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for GitLab login
|
||||||
|
</ParamField>
|
||||||
|
<ParamField query="CLIENT_SECRET_GITLAB_LOGIN" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for GitLab login
|
||||||
|
</ParamField>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Others">
|
<Tab title="Others">
|
||||||
#### JWT
|
#### JWT
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export enum AuthMethod {
|
|||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
GITHUB = "github",
|
GITHUB = "github",
|
||||||
|
GITLAB = "gitlab",
|
||||||
OKTA_SAML = "okta-saml",
|
OKTA_SAML = "okta-saml",
|
||||||
AZURE_SAML = "azure-saml",
|
AZURE_SAML = "azure-saml",
|
||||||
JUMPCLOUD_SAML = "jumpcloud-saml"
|
JUMPCLOUD_SAML = "jumpcloud-saml"
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import { FormEvent, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faGithub,faGoogle } from "@fortawesome/free-brands-svg-icons";
|
import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faLock } from "@fortawesome/free-solid-svg-icons";
|
import { faLock } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import axios from "axios"
|
import axios from "axios";
|
||||||
|
|
||||||
import Error from "@app/components/basic/Error";
|
import Error from "@app/components/basic/Error";
|
||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
@@ -21,15 +21,9 @@ type Props = {
|
|||||||
setEmail: (email: string) => void;
|
setEmail: (email: string) => void;
|
||||||
password: string;
|
password: string;
|
||||||
setPassword: (email: string) => void;
|
setPassword: (email: string) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const InitialStep = ({
|
export const InitialStep = ({ setStep, email, setEmail, password, setPassword }: Props) => {
|
||||||
setStep,
|
|
||||||
email,
|
|
||||||
setEmail,
|
|
||||||
password,
|
|
||||||
setPassword
|
|
||||||
}: Props) => {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -39,7 +33,7 @@ export const InitialStep = ({
|
|||||||
const queryParams = new URLSearchParams(window.location.search);
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
|
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
return;
|
return;
|
||||||
@@ -47,16 +41,15 @@ export const InitialStep = ({
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
if (queryParams && queryParams.get("callback_port")) {
|
if (queryParams && queryParams.get("callback_port")) {
|
||||||
const callbackPort = queryParams.get("callback_port")
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
// attemptCliLogin
|
// attemptCliLogin
|
||||||
const isCliLoginSuccessful = await attemptCliLogin({
|
const isCliLoginSuccessful = await attemptCliLogin({
|
||||||
email: email.toLowerCase(),
|
email: email.toLowerCase(),
|
||||||
password,
|
password
|
||||||
})
|
});
|
||||||
|
|
||||||
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
||||||
|
|
||||||
if (isCliLoginSuccessful.mfaEnabled) {
|
if (isCliLoginSuccessful.mfaEnabled) {
|
||||||
// case: login requires MFA step
|
// case: login requires MFA step
|
||||||
setStep(1);
|
setStep(1);
|
||||||
@@ -64,22 +57,21 @@ export const InitialStep = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// case: login was successful
|
// case: login was successful
|
||||||
const cliUrl = `http://localhost:${callbackPort}`
|
const cliUrl = `http://localhost:${callbackPort}`;
|
||||||
|
|
||||||
// send request to server endpoint
|
// send request to server endpoint
|
||||||
const instance = axios.create()
|
const instance = axios.create();
|
||||||
await instance.post(cliUrl, { ...isCliLoginSuccessful.loginResponse })
|
await instance.post(cliUrl, { ...isCliLoginSuccessful.loginResponse });
|
||||||
|
|
||||||
// cli page
|
// cli page
|
||||||
router.push("/cli-redirect");
|
router.push("/cli-redirect");
|
||||||
|
|
||||||
// on success, router.push to cli Login Successful page
|
// on success, router.push to cli Login Successful page
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const isLoginSuccessful = await attemptLogin({
|
const isLoginSuccessful = await attemptLogin({
|
||||||
email: email.toLowerCase(),
|
email: email.toLowerCase(),
|
||||||
password,
|
password
|
||||||
});
|
});
|
||||||
if (isLoginSuccessful && isLoginSuccessful.success) {
|
if (isLoginSuccessful && isLoginSuccessful.success) {
|
||||||
// case: login was successful
|
// case: login was successful
|
||||||
@@ -101,8 +93,6 @@ export const InitialStep = ({
|
|||||||
router.push(`/org/${userOrg}/overview`);
|
router.push(`/org/${userOrg}/overview`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoginError(true);
|
setLoginError(true);
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -112,45 +102,73 @@ export const InitialStep = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'>
|
<form
|
||||||
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1>
|
onSubmit={handleLogin}
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
|
className="mx-auto flex w-full flex-col items-center justify-center"
|
||||||
|
>
|
||||||
|
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
||||||
|
Login to Infisical
|
||||||
|
</h1>
|
||||||
|
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const callbackPort = queryParams.get("callback_port");
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
window.open(`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
|
window.open(
|
||||||
|
`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
|
);
|
||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
||||||
className="h-11 w-full mx-0"
|
className="mx-0 h-11 w-full"
|
||||||
>
|
>
|
||||||
{t("login.continue-with-google")}
|
{t("login.continue-with-google")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
|
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const callbackPort = queryParams.get("callback_port");
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
window.open(`/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
|
window.open(
|
||||||
|
`/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
|
);
|
||||||
|
|
||||||
window.close();
|
window.close();
|
||||||
}}
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
|
||||||
className="h-11 w-full mx-0"
|
className="mx-0 h-11 w-full"
|
||||||
>
|
>
|
||||||
Continue with GitHub
|
Continue with GitHub
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
|
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
|
<Button
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
const callbackPort = queryParams.get("callback_port");
|
||||||
|
|
||||||
|
window.open(
|
||||||
|
`/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
|
||||||
|
);
|
||||||
|
|
||||||
|
window.close();
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
|
||||||
|
className="mx-0 h-11 w-full"
|
||||||
|
>
|
||||||
|
Continue with GitLab
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
@@ -158,17 +176,17 @@ export const InitialStep = ({
|
|||||||
setStep(2);
|
setStep(2);
|
||||||
}}
|
}}
|
||||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||||
className="h-11 w-full mx-0"
|
className="mx-0 h-11 w-full"
|
||||||
>
|
>
|
||||||
Continue with SSO
|
Continue with SSO
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
|
<div className="my-4 flex w-1/4 min-w-[20rem] flex-row items-center py-2 lg:w-1/6">
|
||||||
<div className='w-full border-t border-mineshaft-400/60' />
|
<div className="w-full border-t border-mineshaft-400/60" />
|
||||||
<span className="mx-2 text-mineshaft-200 text-xs">or</span>
|
<span className="mx-2 text-xs text-mineshaft-200">or</span>
|
||||||
<div className='w-full border-t border-mineshaft-400/60' />
|
<div className="w-full border-t border-mineshaft-400/60" />
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md'>
|
<div className="w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Input
|
<Input
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
@@ -179,7 +197,7 @@ export const InitialStep = ({
|
|||||||
className="h-11"
|
className="h-11"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
|
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Input
|
<Input
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
@@ -188,36 +206,44 @@ export const InitialStep = ({
|
|||||||
isRequired
|
isRequired
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
id="current-password"
|
id="current-password"
|
||||||
className="h-11 select:-webkit-autofill:focus"
|
className="select:-webkit-autofill:focus h-11"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-5'>
|
<div className="mt-5 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
size="sm"
|
size="sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
className='h-11'
|
className="h-11"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
> Continue with Email </Button>
|
>
|
||||||
|
{" "}
|
||||||
|
Continue with Email{" "}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
|
{!isLoading && loginError && <Error text={t("login.error-login") ?? ""} />}
|
||||||
{
|
{!serverDetails?.inviteOnlySignup ? (
|
||||||
!serverDetails?.inviteOnlySignup ?
|
<div className="mt-6 flex flex-row text-sm text-bunker-400">
|
||||||
<div className="mt-6 text-bunker-400 text-sm flex flex-row">
|
|
||||||
<span className="mr-1">Don't have an acount yet?</span>
|
<span className="mr-1">Don't have an acount yet?</span>
|
||||||
<Link href="/signup">
|
<Link href="/signup">
|
||||||
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>{t("login.create-account")}</span>
|
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
|
{t("login.create-account")}
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div> : <div />
|
</div>
|
||||||
}
|
) : (
|
||||||
<div className="text-bunker-400 text-sm flex flex-row">
|
<div />
|
||||||
|
)}
|
||||||
|
<div className="flex flex-row text-sm text-bunker-400">
|
||||||
<span className="mr-1">Forgot password?</span>
|
<span className="mr-1">Forgot password?</span>
|
||||||
<Link href="/verify-email">
|
<Link href="/verify-email">
|
||||||
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>Recover your account</span>
|
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
|
Recover your account
|
||||||
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
+24
-26
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { faGithub, faGoogle, IconDefinition } from "@fortawesome/free-brands-svg-icons";
|
import { faGithub, faGitlab, faGoogle, IconDefinition } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
|
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
@@ -10,20 +10,19 @@ import { useNotificationContext } from "@app/components/context/Notifications/No
|
|||||||
import { Switch } from "@app/components/v2";
|
import { Switch } from "@app/components/v2";
|
||||||
import { useUser } from "@app/context";
|
import { useUser } from "@app/context";
|
||||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||||
import {
|
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||||
AuthMethod
|
|
||||||
} from "@app/hooks/api/users/types";
|
|
||||||
|
|
||||||
interface AuthMethodOption {
|
interface AuthMethodOption {
|
||||||
label: string,
|
label: string;
|
||||||
value: AuthMethod,
|
value: AuthMethod;
|
||||||
icon: IconDefinition;
|
icon: IconDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
const authMethodOpts: AuthMethodOption[] = [
|
const authMethodOpts: AuthMethodOption[] = [
|
||||||
{ label: "Email", value: AuthMethod.EMAIL, icon: faEnvelope },
|
{ label: "Email", value: AuthMethod.EMAIL, icon: faEnvelope },
|
||||||
{ label: "Google", value: AuthMethod.GOOGLE, icon: faGoogle },
|
{ label: "Google", value: AuthMethod.GOOGLE, icon: faGoogle },
|
||||||
{ label: "GitHub", value: AuthMethod.GITHUB, icon: faGithub }
|
{ label: "GitHub", value: AuthMethod.GITHUB, icon: faGithub },
|
||||||
|
{ label: "GitLab", value: AuthMethod.GITLAB, icon: faGitlab }
|
||||||
];
|
];
|
||||||
|
|
||||||
const samlProviders = [AuthMethod.OKTA_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.AZURE_SAML];
|
const samlProviders = [AuthMethod.OKTA_SAML, AuthMethod.JUMPCLOUD_SAML, AuthMethod.AZURE_SAML];
|
||||||
@@ -39,13 +38,9 @@ export const AuthMethodSection = () => {
|
|||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
const { mutateAsync } = useUpdateUserAuthMethods();
|
const { mutateAsync } = useUpdateUserAuthMethods();
|
||||||
|
|
||||||
const {
|
const { reset, setValue, watch } = useForm<FormData>({
|
||||||
reset,
|
|
||||||
setValue,
|
|
||||||
watch,
|
|
||||||
} = useForm<FormData>({
|
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
authMethods: user.authMethods,
|
authMethods: user.authMethods
|
||||||
},
|
},
|
||||||
resolver: yupResolver(schema)
|
resolver: yupResolver(schema)
|
||||||
});
|
});
|
||||||
@@ -55,14 +50,15 @@ export const AuthMethodSection = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
reset({
|
reset({
|
||||||
authMethods: user.authMethods,
|
authMethods: user.authMethods
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const onAuthMethodToggle = async (value: boolean, authMethodOpt: AuthMethodOption) => {
|
const onAuthMethodToggle = async (value: boolean, authMethodOpt: AuthMethodOption) => {
|
||||||
const hasSamlEnabled = user.authMethods
|
const hasSamlEnabled = user.authMethods.some((authMethod: AuthMethod) =>
|
||||||
.some((authMethod: AuthMethod) => samlProviders.includes(authMethod));
|
samlProviders.includes(authMethod)
|
||||||
|
);
|
||||||
|
|
||||||
if (hasSamlEnabled) {
|
if (hasSamlEnabled) {
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -73,7 +69,7 @@ export const AuthMethodSection = () => {
|
|||||||
|
|
||||||
const newAuthMethods = value
|
const newAuthMethods = value
|
||||||
? [...authMethods, authMethodOpt.value]
|
? [...authMethods, authMethodOpt.value]
|
||||||
: authMethods.filter(auth => auth !== authMethodOpt.value);
|
: authMethods.filter((auth) => auth !== authMethodOpt.value);
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
const newUser = await mutateAsync({
|
const newUser = await mutateAsync({
|
||||||
@@ -105,20 +101,22 @@ export const AuthMethodSection = () => {
|
|||||||
text: "Successfully disabled authentication method",
|
text: "Successfully disabled authentication method",
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<h2 className="text-xl font-semibold flex-1 text-mineshaft-100 mb-8">
|
<h2 className="mb-8 flex-1 text-xl font-semibold text-mineshaft-100">
|
||||||
Authentication methods
|
Authentication methods
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-400 mb-4">
|
<p className="mb-4 text-gray-400">
|
||||||
By enabling a SSO provider, you are allowing an account with that provider which uses the same email address as your existing Infisical account to be able to log in to Infisical.
|
By enabling a SSO provider, you are allowing an account with that provider which uses the
|
||||||
|
same email address as your existing Infisical account to be able to log in to Infisical.
|
||||||
</p>
|
</p>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
{user && authMethodOpts.map((authMethodOpt) => {
|
{user &&
|
||||||
|
authMethodOpts.map((authMethodOpt) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex p-4 items-center" key={`auth-method-${authMethodOpt.value}`}>
|
<div className="flex items-center p-4" key={`auth-method-${authMethodOpt.value}`}>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<FontAwesomeIcon icon={authMethodOpt.icon} className="mr-4" />
|
<FontAwesomeIcon icon={authMethodOpt.icon} className="mr-4" />
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +125,7 @@ export const AuthMethodSection = () => {
|
|||||||
onCheckedChange={(value) => onAuthMethodToggle(value, authMethodOpt)}
|
onCheckedChange={(value) => onAuthMethodToggle(value, authMethodOpt)}
|
||||||
isChecked={authMethods?.includes(authMethodOpt.value) ?? false}
|
isChecked={authMethods?.includes(authMethodOpt.value) ?? false}
|
||||||
>
|
>
|
||||||
<p className="w-12 mr-4">{authMethodOpt.label}</p>
|
<p className="mr-4 w-12">{authMethodOpt.label}</p>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -135,4 +133,4 @@ export const AuthMethodSection = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user