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 (
+