From ce3db75133fd2ed5be1f0aa260beee701d6fe2c7 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:42:20 +0200 Subject: [PATCH] Create Signup page for users from Invite link (#2052) * refactor: user creation screen cleanup * feat: deprecation notice for google sso * fix: docs openid typo * user invite hook mock --- .../admin/auth/GoogleAuth/GoogleAuth.tsx | 38 ++-- .../component/user/NewUser/NewUser.styles.ts | 49 ----- .../src/component/user/NewUser/NewUser.tsx | 202 ++++++++++-------- .../NewUser/NewUserWrapper/NewUserWrapper.tsx | 53 +++++ .../user/ResetPassword/ResetPassword.tsx | 16 +- .../user/common/InvalidToken/InvalidToken.tsx | 60 ++++-- .../ResetPasswordDetails.tsx | 22 -- .../PasswordMatcher/PasswordMatcher.styles.ts | 1 + .../ResetPasswordForm/ResetPasswordForm.tsx | 2 +- .../useInviteUserToken/useInviteUserToken.ts | 10 + website/docs/advanced/sso-google.md | 4 +- website/docs/advanced/sso-open-id-connect.md | 2 +- website/docs/deploy/securing-unleash.md | 2 +- 13 files changed, 256 insertions(+), 205 deletions(-) delete mode 100644 frontend/src/component/user/NewUser/NewUser.styles.ts create mode 100644 frontend/src/component/user/NewUser/NewUserWrapper/NewUserWrapper.tsx delete mode 100644 frontend/src/component/user/common/ResetPasswordDetails/ResetPasswordDetails.tsx create mode 100644 frontend/src/hooks/api/getters/useInviteUserToken/useInviteUserToken.ts diff --git a/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx b/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx index fb23b99aa3..af87408c46 100644 --- a/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx +++ b/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx @@ -1,5 +1,6 @@ import React, { useContext, useEffect, useState } from 'react'; import { + Box, Button, FormControlLabel, Grid, @@ -75,23 +76,26 @@ export const GoogleAuth = () => { return ( - - - - Please read the{' '} - - documentation - {' '} - to learn how to integrate with Google OAuth 2.0.
- Callback URL:{' '} - {uiConfig.unleashUrl}/auth/google/callback -
-
-
+ + + This integration is deprecated and will be removed in next + major version. Please use OpenID Connect to + enable Google SSO. + + + Read the{' '} + + documentation + {' '} + to learn how to integrate with Google OAuth 2.0.
+ Callback URL:{' '} + {uiConfig.unleashUrl}/auth/google/callback +
+
diff --git a/frontend/src/component/user/NewUser/NewUser.styles.ts b/frontend/src/component/user/NewUser/NewUser.styles.ts deleted file mode 100644 index 54f20f2409..0000000000 --- a/frontend/src/component/user/NewUser/NewUser.styles.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { makeStyles } from 'tss-react/mui'; - -export const useStyles = makeStyles()(theme => ({ - newUser: { - width: '350px', - [theme.breakpoints.down('sm')]: { - width: '100%', - }, - }, - title: { - fontSize: theme.fontSizes.mainHeader, - marginBottom: '1.25rem', - textAlign: 'center', - }, - inviteText: { - marginBottom: '1rem', - textAlign: 'center', - }, - container: { - display: 'flex', - }, - roleContainer: { - marginTop: '2rem', - }, - innerContainer: { - width: '60%', - padding: '4rem 3rem', - }, - buttonContainer: { - display: 'flex', - marginTop: '1rem', - }, - primaryBtn: { - marginRight: '8px', - }, - subtitle: { - margin: '0.5rem 0', - }, - passwordHeader: { - marginTop: '2rem', - }, - emailField: { - minWidth: '300px', - width: '100%', - [theme.breakpoints.down('sm')]: { - minWidth: '100%', - }, - }, -})); diff --git a/frontend/src/component/user/NewUser/NewUser.tsx b/frontend/src/component/user/NewUser/NewUser.tsx index 57ddd3090b..6106d37a7b 100644 --- a/frontend/src/component/user/NewUser/NewUser.tsx +++ b/frontend/src/component/user/NewUser/NewUser.tsx @@ -1,113 +1,137 @@ -import useLoading from 'hooks/useLoading'; -import { TextField, Typography } from '@mui/material'; -import StandaloneBanner from '../StandaloneBanner/StandaloneBanner'; -import ResetPasswordDetails from '../common/ResetPasswordDetails/ResetPasswordDetails'; -import { useStyles } from './NewUser.styles'; +import { Box, TextField, Typography } from '@mui/material'; import useResetPassword from 'hooks/api/getters/useResetPassword/useResetPassword'; -import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import InvalidToken from '../common/InvalidToken/InvalidToken'; import AuthOptions from '../common/AuthOptions/AuthOptions'; import DividerText from 'component/common/DividerText/DividerText'; import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; +import { useInviteUserToken } from 'hooks/api/getters/useInviteUserToken/useInviteUserToken'; +import ResetPasswordForm from '../common/ResetPasswordForm/ResetPasswordForm'; +import InvalidToken from '../common/InvalidToken/InvalidToken'; +import { NewUserWrapper } from './NewUserWrapper/NewUserWrapper'; export const NewUser = () => { const { authDetails } = useAuthDetails(); - const { token, data, loading, setLoading, invalidToken } = - useResetPassword(); - const ref = useLoading(loading); - const { classes: styles } = useStyles(); + const { + token, + data, + loading: resetLoading, + setLoading, + invalidToken, + } = useResetPassword(); + const { invite, loading: inviteLoading } = useInviteUserToken(); + const passwordDisabled = authDetails?.defaultHidden === true; + + if (invalidToken && !invite) { + return ( + + + + ); + } return ( -
- } - > -
- } - elseShow={ - -

- Enter your personal details and start your - journey -

- - {data?.createdBy} -

has invited you to join - Unleash. - - } - /> - + + + {data?.createdBy} +
has invited you to join Unleash. + + } + /> + + We suggest using{' '} + + the email you use for work + + . + + + + + } + /> + + } + /> + + ( Your username is - + )} + /> + + ( -
- - - - - - - } - elseShow={ - - Set a password for your account. - - } - /> -
-
- } - /> -
-
-
+ )} + /> + + Set a password for your account. + + + + } + /> + ); }; diff --git a/frontend/src/component/user/NewUser/NewUserWrapper/NewUserWrapper.tsx b/frontend/src/component/user/NewUser/NewUserWrapper/NewUserWrapper.tsx new file mode 100644 index 0000000000..4990df66a8 --- /dev/null +++ b/frontend/src/component/user/NewUser/NewUserWrapper/NewUserWrapper.tsx @@ -0,0 +1,53 @@ +import { FC } from 'react'; +import { Box, Typography } from '@mui/material'; +import StandaloneLayout from 'component/user/common/StandaloneLayout/StandaloneLayout'; +import StandaloneBanner from 'component/user/StandaloneBanner/StandaloneBanner'; +import useLoading from 'hooks/useLoading'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; + +interface INewUserWrapperProps { + loading?: boolean; + title?: string; +} + +export const NewUserWrapper: FC = ({ + children, + loading, + title, +}) => { + const ref = useLoading(loading || false); + + return ( +
+ } + > + + + theme.fontSizes.mainHeader, + marginBottom: 2, + textAlign: 'center', + fontWeight: theme => theme.fontWeight.bold, + }} + > + {title} + + } + /> + {children} + + +
+ ); +}; diff --git a/frontend/src/component/user/ResetPassword/ResetPassword.tsx b/frontend/src/component/user/ResetPassword/ResetPassword.tsx index e43f8db40f..1abf33fca7 100644 --- a/frontend/src/component/user/ResetPassword/ResetPassword.tsx +++ b/frontend/src/component/user/ResetPassword/ResetPassword.tsx @@ -1,13 +1,11 @@ import useLoading from 'hooks/useLoading'; - -import ResetPasswordDetails from '../common/ResetPasswordDetails/ResetPasswordDetails'; - import { useStyles } from './ResetPassword.styles'; import { Typography } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import InvalidToken from '../common/InvalidToken/InvalidToken'; import useResetPassword from 'hooks/api/getters/useResetPassword/useResetPassword'; import StandaloneLayout from '../common/StandaloneLayout/StandaloneLayout'; +import ResetPasswordForm from '../common/ResetPasswordForm/ResetPasswordForm'; const ResetPassword = () => { const { classes: styles } = useStyles(); @@ -22,10 +20,7 @@ const ResetPassword = () => { condition={invalidToken} show={} elseShow={ - + <> { > Reset password - + + + } /> diff --git a/frontend/src/component/user/common/InvalidToken/InvalidToken.tsx b/frontend/src/component/user/common/InvalidToken/InvalidToken.tsx index 462f3e73dd..b14ca0e19a 100644 --- a/frontend/src/component/user/common/InvalidToken/InvalidToken.tsx +++ b/frontend/src/component/user/common/InvalidToken/InvalidToken.tsx @@ -1,11 +1,17 @@ +import { VFC } from 'react'; import { Button, Typography } from '@mui/material'; import { Link } from 'react-router-dom'; import { INVALID_TOKEN_BUTTON } from 'utils/testIds'; import { useThemeStyles } from 'themes/themeStyles'; import classnames from 'classnames'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; -const InvalidToken = () => { +const InvalidToken: VFC = () => { + const { authDetails } = useAuthDetails(); const { classes: themeStyles } = useThemeStyles(); + const passwordDisabled = authDetails?.defaultHidden === true; + return (
{ Invalid token - - Your token has either been used to reset your password, or it - has expired. Please request a new reset password URL in order to - reset your password. - - + + + Your instance does not support password + authentication. Use correct work email to access + your account. + + + + } + elseShow={ + <> + + Your token has either been used to reset your + password, or it has expired. Please request a new + reset password URL in order to reset your password. + + + + } + />
); }; diff --git a/frontend/src/component/user/common/ResetPasswordDetails/ResetPasswordDetails.tsx b/frontend/src/component/user/common/ResetPasswordDetails/ResetPasswordDetails.tsx deleted file mode 100644 index d05b2d57e0..0000000000 --- a/frontend/src/component/user/common/ResetPasswordDetails/ResetPasswordDetails.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { FC, Dispatch, SetStateAction } from 'react'; -import ResetPasswordForm from '../ResetPasswordForm/ResetPasswordForm'; - -interface IResetPasswordDetails { - token: string; - setLoading: Dispatch>; -} - -const ResetPasswordDetails: FC = ({ - children, - token, - setLoading, -}) => { - return ( -
- {children} - -
- ); -}; - -export default ResetPasswordDetails; diff --git a/frontend/src/component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher.styles.ts b/frontend/src/component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher.styles.ts index 2f0c1db36c..9f946a4a6e 100644 --- a/frontend/src/component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher.styles.ts +++ b/frontend/src/component/user/common/ResetPasswordForm/PasswordMatcher/PasswordMatcher.styles.ts @@ -3,6 +3,7 @@ import { makeStyles } from 'tss-react/mui'; export const useStyles = makeStyles()(theme => ({ matcherContainer: { position: 'relative', + paddingTop: theme.spacing(0.5), }, matcherIcon: { marginRight: '5px', diff --git a/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx b/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx index 5cc0527fc5..0a6b33c2e2 100644 --- a/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx +++ b/frontend/src/component/user/common/ResetPasswordForm/ResetPasswordForm.tsx @@ -74,7 +74,7 @@ const ResetPasswordForm = ({ token, setLoading }: IResetPasswordProps) => { const res = await makeResetPasswordReq(); setLoading(false); if (res.status === OK) { - navigate('login?reset=true'); + navigate('/login?reset=true'); setApiError(false); } else { setApiError(true); diff --git a/frontend/src/hooks/api/getters/useInviteUserToken/useInviteUserToken.ts b/frontend/src/hooks/api/getters/useInviteUserToken/useInviteUserToken.ts new file mode 100644 index 0000000000..20fcfb8434 --- /dev/null +++ b/frontend/src/hooks/api/getters/useInviteUserToken/useInviteUserToken.ts @@ -0,0 +1,10 @@ +import useQueryParams from 'hooks/useQueryParams'; + +export const useInviteUserToken = () => { + const query = useQueryParams(); + const invite = query.get('invite') || ''; + + // TODO: Invite token API + + return { invite, loading: false }; +}; diff --git a/website/docs/advanced/sso-google.md b/website/docs/advanced/sso-google.md index 4cfcf26931..6e43d831ee 100644 --- a/website/docs/advanced/sso-google.md +++ b/website/docs/advanced/sso-google.md @@ -1,9 +1,9 @@ --- id: sso-google -title: "[Deprecated] How to add SSO with Google" +title: '[Deprecated] How to add SSO with Google' --- -> Single Sign-on via the Google Authenticator provider is deprecated. We recommend using [OpenId Connect](./sso-open-id-connect.md) instead. +> Single Sign-on via the Google Authenticator provider is deprecated. We recommend using [OpenID Connect](./sso-open-id-connect.md) instead. ## Introduction {#introduction} diff --git a/website/docs/advanced/sso-open-id-connect.md b/website/docs/advanced/sso-open-id-connect.md index b2cd6a4ece..102c1b0518 100644 --- a/website/docs/advanced/sso-open-id-connect.md +++ b/website/docs/advanced/sso-open-id-connect.md @@ -1,6 +1,6 @@ --- id: sso-open-id-connect -title: How to add SSO with OpenId Connect +title: How to add SSO with OpenID Connect --- > The **Single-Sign-On capability** is only available for customers on the Enterprise subscription. Check out the [Unleash plans](https://www.getunleash.io/plans) for details. diff --git a/website/docs/deploy/securing-unleash.md b/website/docs/deploy/securing-unleash.md index 20c813903b..763ae937b3 100644 --- a/website/docs/deploy/securing-unleash.md +++ b/website/docs/deploy/securing-unleash.md @@ -5,7 +5,7 @@ title: Securing Unleash **If you are still using Unleash v3 you need to follow the [securing-unleash-v3](./securing-unleash-v3)** -> This guide is only relevant if you are using Unleash Open-Source. The Enterprise edition does already ship with multiple SSO options, such as SAML 2.0, OpenId Connect. +> This guide is only relevant if you are using Unleash Open-Source. The Enterprise edition does already ship with multiple SSO options, such as SAML 2.0, OpenID Connect. Unleash Open-Source v4 comes with username/password authentication out of the box. In addition Unleash v4 also comes with API token support, to make it easy to handle access tokens for Client SDKs and programmatic access to the Unleash APIs.