Skip to content

Commit

Permalink
:feat remplacement d'inclusion connect par pro connect
Browse files Browse the repository at this point in the history
  • Loading branch information
dienamo committed Oct 2, 2024
1 parent 4f8db73 commit 879d1f1
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 102 deletions.
17 changes: 17 additions & 0 deletions create-htaccess.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const fs = require('fs');
const path = require('path');

const htaccessContent = `
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
RewriteRule ^ /index.html
`;

const distPath = path.join(__dirname, 'dist');
const htaccessPath = path.join(distPath, '.htaccess');

fs.writeFileSync(htaccessPath, htaccessContent.trim());
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"license": "AGPL-3.0",
"scripts": {
"build": "vite build",
"postbuild": "node create-htaccess.js",
"dev": "vite",
"eject": "react-scripts eject",
"lint": "eslint src --max-warnings=0 --cache --cache-location node_modules/.cache/eslint",
Expand Down
2 changes: 1 addition & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function App() {
const auth = useAuth();

useEffect(() => {
if (auth?.isAuthenticated && location.pathname !== '/login' && accessToken) {
if (location.pathname !== '/login' && accessToken) {
refreshToken(auth, dispatch, accessToken);
}
}, [location, auth, accessToken]);
Expand Down
30 changes: 30 additions & 0 deletions src/helpers/proConnectLogin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import axios from 'axios';

/**
* Gère le processus de connexion ProConnect
* @param {string} verificationToken - Le jeton de vérification pour le processus de connexion
* @param {function} setLoading - Fonction pour définir l'état de chargement
* @param {function} setNetworkError - Fonction pour définir l'état d'erreur réseau
* @returns {Promise<void>}
*/
export async function handleProConnectLogin(verificationToken, setLoading, setNetworkError) {
const nonce = Math.random().toString(36).substring(7);
const state = Math.random().toString(36).substring(7);
localStorage.setItem('nonce', nonce);
localStorage.setItem('state', state);
setLoading(true);
try {
const response = await axios.post(`${import.meta.env.VITE_APP_API_URL}/create-url`, {
verificationToken,
nonce,
state,
});
if (response.data) {
window.location.href = response.data.authorizationUrl;
}
} catch (error) {
setNetworkError(true);
} finally {
setLoading(false);
}
}
61 changes: 14 additions & 47 deletions src/services/auth/signInCallBack.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,23 @@
import axios from 'axios';
import { authenticationActions } from '../../actions';
import apiUrlRoot from '../../helpers/apiUrl';
import { UserManager, WebStorageStateStore } from 'oidc-client-ts';

const userManager = new UserManager({
authority: import.meta.env.VITE_APP_AUTH_OIDC_AUTHORITY,
client_id: import.meta.env.VITE_APP_AUTH_CLIENT_ID,
client_secret: import.meta.env.VITE_APP_AUTH_CLIENT_SECRET,
post_logout_redirect_uri: `${import.meta.env.VITE_APP_AUTH_REDIRECT_URI}/passerelle`,
userStore: new WebStorageStateStore({ store: window.localStorage }),
});


const getProfile = () =>
JSON.parse(
localStorage.getItem(
import.meta.env.VITE_APP_AUTH_OIDC_USER_KEY
)
);

const getVerificationToken = () => window.location.pathname.split('/').pop();

const signInCallBack = async store => {
const { dispatch } = store;
window.history.replaceState({}, document.title, window.location.pathname);
const profile = getProfile();
const verificationToken = getVerificationToken();
const token = profile?.access_token;
dispatch({ type: 'LOGIN_REQUEST' });
await axios
.get(`${apiUrlRoot}/login`, {
params: {
verificationToken
},
withCredentials: true,
headers: {
'Access-Control-Allow-Origin': `${apiUrlRoot}/login`,
'Authorization': `Bearer ${token}`,
},
})
.then(result => {
dispatch(authenticationActions.login(result?.data));
dispatch(authenticationActions.refreshToken(result?.data?.accessToken));
localStorage.setItem('user', JSON.stringify(result?.data));
localStorage.setItem('roleActivated', result?.data?.user?.roles[0]);
})
.catch(async error => {
const signInCallBack = async (dispatch, code, state, verificationToken) => {
try {
dispatch({ type: 'LOGIN_REQUEST' });
const reponse = await axios.post(import.meta.env.VITE_APP_API_URL + '/login', { code, state }, { params: { verificationToken } });
if (reponse?.data?.accessToken) {
dispatch(authenticationActions.login(reponse?.data));
dispatch(authenticationActions.refreshToken(reponse?.data?.accessToken));
localStorage.setItem('user', JSON.stringify(reponse?.data));
localStorage.setItem('roleActivated', reponse?.data?.user?.roles[0]);
return { success: true };
}
} catch (error) {
localStorage.setItem('loginError', JSON.stringify(error?.response?.data ?? 'Connexion refusée'));
userManager.signoutRedirect();
localStorage.removeItem('user');
dispatch({ type: 'LOGIN_FAILURE' });
});
return { success: false };
}
};

export default signInCallBack;
39 changes: 39 additions & 0 deletions src/views/anonymous/Login.css

Large diffs are not rendered by default.

41 changes: 15 additions & 26 deletions src/views/anonymous/Login.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useAuth } from 'react-oidc-context';
import { useParams, useNavigate } from 'react-router-dom';
import logoOneLineIC from '../../assets/brands/logo-inclusion-connect-one-line.svg';
import logoTwoLinesIC from '../../assets/brands/logo-inclusion-connect-two-lines.svg';
import { userActions } from '../../actions';
import './Login.css';
import Spinner from '../../components/Spinner';
import { handleProConnectLogin } from '../../helpers/proConnectLogin';

export default function Login() {

Expand All @@ -16,32 +16,21 @@ export default function Login() {
const error = useSelector(state => state.authentication?.error);
const tokenVerified = useSelector(state => state?.user?.tokenVerified);
const dispatch = useDispatch();
const auth = useAuth();
const [submitted, setSubmitted] = useState(false);
const [networkError, setNetworkError] = useState(false);
const [proConnectLoading, setProConnectLoading] = useState(false);
const { username, password } = inputs;
const user = useSelector(state => state.authentication?.user);

const navigate = useNavigate();


const login = async () => {
localStorage.setItem('user', JSON.stringify({}));
try {
await auth.signinRedirect();
} catch (e) {
setNetworkError(true);
}
};

const { verificationToken } = useParams();

useEffect(() => {
if (verificationToken) {
localStorage.removeItem('user');
dispatch(userActions.verifyToken(verificationToken));
}
// on remove suite à la déconnexion..
localStorage.removeItem('logoutAction');
}, []);

Expand All @@ -63,24 +52,24 @@ export default function Login() {
navigate('/accueil');
}
}, [user]);

return (
<div className="login">
<Spinner loading={!(localStorage.getItem('user') && localStorage.getItem('user') !== '{}') && proConnectLoading} />
<div className="fr-container fr-my-10w">
<div className="fr-grid-row fr-grid-row--center" style={{ textAlign: 'center' }}>
<div className="fr-col-xs-12 fr-col-md-6">
{(window.location.pathname === '/login' || tokenVerified) &&
<>
<div className="logo-inclusion-connect-one-line">
<button className="btn-inclusion-connect" onClick={login}>
<img src={logoOneLineIC} height="14" alt="Se connecter avec Inclusion Connect" />
</button>
</div>
<div className="logo-inclusion-connect-two-lines">
<button className="btn-inclusion-connect" onClick={login}>
<img src={logoTwoLinesIC} height="37" alt="Se connecter avec Inclusion Connect" />
</button>
</div>
<button
id="login"
className="agentconnect-button"
onClick={() =>
handleProConnectLogin(verificationToken, setProConnectLoading, setNetworkError)
}
>
<span className="agentconnect-sr-only">S’identifier avec AgentConnect</span>
</button>
{
networkError && <div className="fr-alert fr-alert--error fr-mt-1w fr-mb-4w">
<h3 className="fr-alert__title">&Eacute;chec de la connexion &agrave; Inclusion Connect</h3>
Expand Down
68 changes: 40 additions & 28 deletions src/views/anonymous/Passerelle.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useAuth } from 'react-oidc-context';
import { useNavigate } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate, useParams } from 'react-router-dom';
import Spinner from '../../components/Spinner';
import logoOneLineIC from '../../assets/brands/logo-inclusion-connect-one-line.svg';
import logoTwoLinesIC from '../../assets/brands/logo-inclusion-connect-two-lines.svg';
import signInCallBack from '../../services/auth/signInCallBack';
import { handleProConnectLogin } from '../../helpers/proConnectLogin';

export default function Passerelle() {

const user = useSelector(state => state.authentication?.user);
const isLoading = useSelector(state => state.authentication?.loading);
const [callbackLoading, setCallbackLoading] = useState(false);
const [loginLoading, setLoginLoading] = useState(false);

const [error, setError] = useState(null);
const auth = useAuth();
const navigate = useNavigate();
const dispatch = useDispatch();

const login = async () => {
localStorage.setItem('user', JSON.stringify({}));
auth.signinRedirect();
};
const { verificationToken } = useParams();

useEffect(() => {
if (user && localStorage.getItem('user') && localStorage.getItem('user') !== '{}') {
navigate('/accueil');
async function handleCallback() {
setCallbackLoading(true);
const searchParams = new URLSearchParams(location.search);
const code = searchParams.get('code');
const state = searchParams.get('state');
const storedState = localStorage.getItem('state');
const storedNonce = localStorage.getItem('nonce');
const stateWithToken = atob(state);
const { state: stateToken, nonce } = JSON.parse(stateWithToken);

if (stateToken === storedState && nonce === storedNonce) {
try {
const { state: decodedState, verificationToken } = JSON.parse(stateWithToken);
const result = await signInCallBack(dispatch, code, decodedState, verificationToken);
if (result.success) {
navigate('/accueil');
}
} catch (error) {
setError('Une erreur est survenue lors de la connexion');
}
} else {
setError('Paramètres manquants');
}
setCallbackLoading(false);
}
}, [user, error, isLoading, auth.isLoading]);
handleCallback();
}, [location, navigate]);

useEffect(() => {
const storedError = JSON.parse(localStorage.getItem('loginError'));
Expand All @@ -35,7 +56,7 @@ export default function Passerelle() {

return (
<div className="login">
<Spinner loading={!(localStorage.getItem('user') && localStorage.getItem('user') !== '{}') && (isLoading || auth.isLoading)} />
<Spinner loading={!(localStorage.getItem('user') && localStorage.getItem('user') !== '{}') && (isLoading || callbackLoading || loginLoading) } />
<div className="fr-container fr-my-10w">
{error === 'Connexion refusée' &&
<div className="fr-alert fr-alert--error fr-mt-1w fr-mb-4w">
Expand All @@ -55,19 +76,10 @@ export default function Passerelle() {
</div>
}
<div className="fr-grid-row fr-grid-row--center fr-mt-1-5v" style={{ textAlign: 'center' }}>
{ !isLoading && !auth.isLoading &&
<>
<div className="logo-inclusion-connect-one-line">
<button className="btn-inclusion-connect" onClick={login}>
<img src={logoOneLineIC} height="14" alt="Se connecter avec Inclusion Connect" />
</button>
</div>
<div className="logo-inclusion-connect-two-lines">
<button className="btn-inclusion-connect" onClick={login}>
<img src={logoTwoLinesIC} height="37" alt="Se connecter avec Inclusion Connect" />
</button>
</div>
</>
{ !isLoading && !callbackLoading && !loginLoading &&
<button id="login" className="agentconnect-button" onClick={() => handleProConnectLogin(verificationToken, setLoginLoading, setError)}>
<span className="agentconnect-sr-only">S’identifier avec AgentConnect</span>
</button>
}
</div>
</div>
Expand Down

0 comments on commit 879d1f1

Please sign in to comment.