diff --git a/src/frontend/packages/auth-provider/src/components/PodLoginPage.js b/src/frontend/packages/auth-provider/src/components/PodLoginPage.js new file mode 100644 index 000000000..08753e088 --- /dev/null +++ b/src/frontend/packages/auth-provider/src/components/PodLoginPage.js @@ -0,0 +1,128 @@ +import React, { useEffect } from 'react'; +import jwtDecode from 'jwt-decode'; +import { useNotify, useAuthProvider, Notification } from 'react-admin'; +import { ThemeProvider } from '@material-ui/styles'; +import { createTheme, makeStyles } from '@material-ui/core/styles'; +import { Avatar, Button, Card, CardActions, Typography } from '@material-ui/core'; +import LockIcon from '@material-ui/icons/Lock'; + +const useStyles = makeStyles(theme => ({ + main: { + display: 'flex', + flexDirection: 'column', + minHeight: '100vh', + alignItems: 'center', + justifyContent: 'flex-start', + backgroundColor: theme.palette.grey['300'] + }, + text: { + maxWidth: 300, + textAlign: 'center', + padding: '4px 8px 8px' + }, + card: { + minWidth: 300, + marginTop: '6em' + }, + lockIconAvatar: { + margin: '1em', + display: 'flex', + justifyContent: 'center' + }, + lockIcon: { + backgroundColor: theme.palette.grey['500'] + } +})); + +const PodLoginPage = ({ theme, history, location, podProviders, text }) => { + const classes = useStyles(); + const notify = useNotify(); + const authProvider = useAuthProvider(); + const searchParams = new URLSearchParams(location.search); + + useEffect(() => { + (async () => { + if (searchParams.has('login')) { + if (searchParams.has('error')) { + if (searchParams.get('error') === 'registration.not-allowed') { + notify('auth.message.user_email_not_found', 'error'); + } else { + notify('auth.message.bad_request', 'error', { error: searchParams.get('error') }); + } + } else if (searchParams.has('token')) { + const token = searchParams.get('token'); + const { webId } = jwtDecode(token); + const response = await fetch(webId, { + headers: { + Accept: 'application/json' + } + }); + if (!response.ok) { + notify('auth.message.unable_to_fetch_user_data', 'error'); + } else { + const data = await response.json(); + if (!authProvider.checkUser(data)) { + notify('auth.message.user_not_allowed_to_login', 'error'); + history.replace('/login'); + } else { + localStorage.setItem('token', token); + if (searchParams.has('new') && searchParams.get('new') === 'true') { + notify('auth.message.new_user_created', 'info'); + // TODO allow to attach profile to container ? + } else { + notify('auth.message.user_connected', 'info'); + } + history.push('/'); + } + } + } + } + + if (searchParams.has('logout')) { + localStorage.removeItem('token'); + notify('auth.message.user_disconnected', 'info'); + history.push('/'); + } + })(); + }, [searchParams]); + + return ( + +
+ +
+ + + +
+ {text && ( + + {text} + + )} + {podProviders && + podProviders.map((podProvider, i) => { + const url = new URL('/auth', podProvider); + if (searchParams.has('signup')) url.searchParams.set('signup', 'true'); + url.searchParams.set('redirect', window.location.href); + return ( + + + + ); + })} +
+
+ +
+ ); +}; + +export default PodLoginPage; diff --git a/src/frontend/packages/auth-provider/src/components/SignupForm.js b/src/frontend/packages/auth-provider/src/components/SignupForm.js index 4c9639389..1255f7e63 100644 --- a/src/frontend/packages/auth-provider/src/components/SignupForm.js +++ b/src/frontend/packages/auth-provider/src/components/SignupForm.js @@ -85,23 +85,10 @@ const SignupForm = ({ redirectTo, delayBeforeRedirect }) => {
(
-
- -
{ {identity && identity.id !== '' ? [ - , - , + , + , React.cloneElement(logout, { key: 'logout' }) ] : [ diff --git a/src/frontend/packages/auth-provider/src/index.js b/src/frontend/packages/auth-provider/src/index.js index 725569dd7..abd5a5766 100644 --- a/src/frontend/packages/auth-provider/src/index.js +++ b/src/frontend/packages/auth-provider/src/index.js @@ -20,6 +20,7 @@ export { default as ShowActionsWithPermissions } from './crud/show/ShowActionsWi export { default as PermissionsButton } from './components/PermissionsButton/PermissionsButton'; export { default as AuthDialog } from './components/AuthDialog'; export { default as SsoLoginPage, default as LoginPage } from './components/SsoLoginPage'; +export { default as PodLoginPage } from './components/PodLoginPage'; export { default as LocalLoginPage } from './components/LocalLoginPage'; export { default as LogoutButton } from './components/LogoutButton'; export { default as ResourceWithPermissions } from './components/ResourceWithPermissions'; diff --git a/src/middleware/packages/auth/services/auth.local.js b/src/middleware/packages/auth/services/auth.local.js index 4c7a08de7..949f778aa 100644 --- a/src/middleware/packages/auth/services/auth.local.js +++ b/src/middleware/packages/auth/services/auth.local.js @@ -14,6 +14,7 @@ const AuthLocalService = { reservedUsernames: [], webIdSelection: [], accountSelection: [], + formUrl: null, mail: { from: null, transport: { @@ -71,6 +72,18 @@ const AuthLocalService = { return { token, webId: accountData.webId, newUser: false }; }, + async redirectToForm(ctx) { + if (this.settings.formUrl) { + const formUrl = new URL(this.settings.formUrl); + for (let [key, value] of Object.entries(ctx.params)) { + formUrl.searchParams.set(key, value); + } + ctx.meta.$statusCode = 302; + ctx.meta.$location = formUrl.toString(); + } else { + throw new Error('No formUrl defined in auth.local settings') + } + }, async resetPassword(ctx) { const { email } = ctx.params; @@ -136,6 +149,13 @@ const AuthLocalService = { } }; + const formRoute = { + path: '/auth', + aliases: { + 'GET /': 'auth.redirectToForm' + } + }; + const resetPasswordRoute = { path: '/auth/reset_password', aliases: { @@ -158,7 +178,7 @@ const AuthLocalService = { authorization: true }; - const routes = [loginRoute, resetPasswordRoute, setNewPasswordRoute, accountSettingsRoute]; + const routes = [loginRoute, formRoute, resetPasswordRoute, setNewPasswordRoute, accountSettingsRoute]; if (this.settings.registrationAllowed) { return [...routes, signupRoute];