Integrated login with Gitlab

This commit is contained in:
Andrew Atimapre
2023-09-24 22:33:00 +01:00
parent 255705501f
commit 0d74752169
2 changed files with 146 additions and 67 deletions
+59
View File
@@ -13,8 +13,10 @@ import {
import { createToken } from "../helpers/auth"; import { createToken } from "../helpers/auth";
import { import {
getClientIdGitHubLogin, getClientIdGitHubLogin,
getClientIdGitLabLogin,
getClientIdGoogleLogin, getClientIdGoogleLogin,
getClientSecretGitHubLogin, getClientSecretGitHubLogin,
getClientSecretGitLabLogin,
getClientSecretGoogleLogin, getClientSecretGoogleLogin,
getJwtProviderAuthLifetime, getJwtProviderAuthLifetime,
getJwtProviderAuthSecret, getJwtProviderAuthSecret,
@@ -29,6 +31,8 @@ const GoogleStrategy = require("passport-google-oauth20").Strategy;
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const GitHubStrategy = require("passport-github").Strategy; const GitHubStrategy = require("passport-github").Strategy;
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const GitLabStrategy = require("passport-gitlab2").Strategy;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { MultiSamlStrategy } = require("@node-saml/passport-saml"); const { MultiSamlStrategy } = require("@node-saml/passport-saml");
/** /**
@@ -75,6 +79,8 @@ const initializePassport = async () => {
const clientSecretGoogleLogin = await getClientSecretGoogleLogin(); const clientSecretGoogleLogin = await getClientSecretGoogleLogin();
const clientIdGitHubLogin = await getClientIdGitHubLogin(); const clientIdGitHubLogin = await getClientIdGitHubLogin();
const clientSecretGitHubLogin = await getClientSecretGitHubLogin(); const clientSecretGitHubLogin = await getClientSecretGitHubLogin();
const clientIdGitLabLogin = await getClientIdGitLabLogin();
const clientSecretGitLabLogin = await getClientSecretGitLabLogin();
if (clientIdGoogleLogin && clientSecretGoogleLogin) { if (clientIdGoogleLogin && clientSecretGoogleLogin) {
passport.use(new GoogleStrategy({ passport.use(new GoogleStrategy({
@@ -190,6 +196,59 @@ const initializePassport = async () => {
} }
)); ));
} }
if (clientIdGitLabLogin && clientSecretGitLabLogin) {
passport.use(new GitLabStrategy({
passReqToCallback: true,
clientID: clientIdGitLabLogin,
clientSecret: clientSecretGitLabLogin,
callbackURL: "/api/v1/sso/gitlab"
},
async (req : express.Request, accessToken : any, refreshToken : any, profile : any, done : any) => {
const email = profile.emails[0].value;
let user = await User.findOne({
email
}).select("+publicKey");
if (!user) {
user = await new User({
email: email,
authMethods: [AuthMethod.GITLAB],
firstName: profile.displayName,
lastName: ""
}).save();
}
let isLinkingRequired = false;
if (!user.authMethods.includes(AuthMethod.GITLAB)) {
isLinkingRequired = true;
}
const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({
payload: {
userId: user._id.toString(),
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
authMethod: AuthMethod.GITLAB,
isUserCompleted,
isLinkingRequired,
...(req.query.state ? {
callbackPort: req.query.state as string
} : {})
},
expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(),
});
req.isUserCompleted = isUserCompleted;
req.providerAuthToken = providerAuthToken;
return done(null, profile);
}
));
}
passport.use("saml", new MultiSamlStrategy( passport.use("saml", new MultiSamlStrategy(
{ {
@@ -1,7 +1,7 @@
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 { faEnvelope } from "@fortawesome/free-regular-svg-icons"; import { faEnvelope } from "@fortawesome/free-regular-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";
@@ -9,74 +9,94 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "../v2"; import { Button } from "../v2";
export default function InitialSignupStep({ export default function InitialSignupStep({
setIsSignupWithEmail, setIsSignupWithEmail
}: { }: {
setIsSignupWithEmail: (value: boolean) => void setIsSignupWithEmail: (value: boolean) => void;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const router = useRouter(); const router = useRouter();
return <div className='flex flex-col mx-auto w-full justify-center items-center'> return (
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >{t("signup.initial-title")}</h1> <div className="mx-auto flex w-full flex-col items-center justify-center">
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'> <h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
<Button {t("signup.initial-title")}
colorSchema="primary" </h1>
variant="solid" <div className="w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
onClick={() => { <Button
window.open("/api/v1/sso/redirect/google"); colorSchema="primary"
window.close(); variant="solid"
}} onClick={() => {
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />} window.open("/api/v1/sso/redirect/google");
className="h-12 w-full mx-0" window.close();
> }}
{t("signup.continue-with-google")} leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
</Button> className="mx-0 h-12 w-full"
</div> >
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md mt-4'> {t("signup.continue-with-google")}
<Button </Button>
colorSchema="primary" </div>
variant="outline_bg" <div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
onClick={() => { <Button
window.open("/api/v1/sso/redirect/github"); colorSchema="primary"
window.close(); variant="outline_bg"
}} onClick={() => {
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />} window.open("/api/v1/sso/redirect/github");
className="h-12 w-full mx-0" window.close();
> }}
Continue with GitHub leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
</Button> className="mx-0 h-12 w-full"
</div> >
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'> Continue with GitHub
<Button </Button>
colorSchema="primary" </div>
variant="outline_bg" <div className="mt-4 w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
onClick={() => { <Button
setIsSignupWithEmail(true); colorSchema="primary"
}} variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />} onClick={() => {
className="h-12 w-full mx-0" window.open("/api/v1/sso/redirect/gitlab");
> window.close();
Continue with Email }}
</Button> leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
</div> className="mx-0 h-12 w-full"
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'> >
<Button Continue with GitLab
colorSchema="primary" </Button>
variant="outline_bg" </div>
onClick={() => router.push("/saml-sso")} <div className="mt-4 w-1/4 min-w-[20rem] rounded-md text-center lg:w-1/6">
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />} <Button
className="h-12 w-full mx-0" colorSchema="primary"
> variant="outline_bg"
Continue with SSO onClick={() => {
</Button> setIsSignupWithEmail(true);
</div> }}
<div className='lg:w-1/6 w-1/4 min-w-[20rem] px-8 text-center mt-6 text-xs text-bunker-400'> leftIcon={<FontAwesomeIcon icon={faEnvelope} className="mr-2" />}
{t("signup.create-policy")} className="mx-0 h-12 w-full"
</div> >
<div className="mt-2 text-bunker-400 text-xs flex flex-row"> Continue with Email
<Link href="/login"> </Button>
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>{t("signup.already-have-account")}</span> </div>
</Link> <div className="mt-4 w-1/4 min-w-[20rem] rounded-md text-center lg:w-1/6">
</div> <Button
colorSchema="primary"
variant="outline_bg"
onClick={() => router.push("/saml-sso")}
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
className="mx-0 h-12 w-full"
>
Continue with SSO
</Button>
</div>
<div className="mt-6 w-1/4 min-w-[20rem] px-8 text-center text-xs text-bunker-400 lg:w-1/6">
{t("signup.create-policy")}
</div>
<div className="mt-2 flex flex-row text-xs text-bunker-400">
<Link href="/login">
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
{t("signup.already-have-account")}
</span>
</Link>
</div>
</div> </div>
);
} }