✨(app-desk) integrate keycloak
Integrate keycloak with the frontend. We use the keycloak-js library to handle the authentication and authorization. We installed Zustand to handle the states of the application. We store the token and auth process in authStore.
This commit is contained in:
3
src/frontend/app/desk/.env.development
Normal file
3
src/frontend/app/desk/.env.development
Normal file
@@ -0,0 +1,3 @@
|
||||
NEXT_PUBLIC_KEYCLOAK_URL=http://localhost:8080/
|
||||
NEXT_PUBLIC_KEYCLOAK_REALM=people
|
||||
NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=people-front
|
||||
@@ -17,7 +17,8 @@
|
||||
"@openfun/cunningham-react": "2.4.0",
|
||||
"next": "14.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react-dom": "18.2.0",
|
||||
"zustand": "4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "6.2.0",
|
||||
@@ -35,6 +36,7 @@
|
||||
"eslint-plugin-testing-library": "6.2.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"keycloak-js": "23.0.3",
|
||||
"prettier": "3.1.1",
|
||||
"stylelint": "16.1.0",
|
||||
"stylelint-config-standard": "36.0.0",
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { Button, Loader } from '@openfun/cunningham-react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import styles from './page.module.css';
|
||||
|
||||
import useAuthStore from '@/auth/useAuthStore';
|
||||
|
||||
export default function Home() {
|
||||
const { initAuth, authenticated } = useAuthStore();
|
||||
|
||||
useEffect(() => {
|
||||
initAuth();
|
||||
}, [initAuth]);
|
||||
|
||||
if (!authenticated) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<h2>Hello world!</h2>
|
||||
|
||||
23
src/frontend/app/desk/src/auth/keycloak.ts
Normal file
23
src/frontend/app/desk/src/auth/keycloak.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import Keycloak, { KeycloakConfig } from 'keycloak-js';
|
||||
|
||||
const keycloakConfig: KeycloakConfig = {
|
||||
url: process.env.NEXT_PUBLIC_KEYCLOAK_URL,
|
||||
realm: process.env.NEXT_PUBLIC_KEYCLOAK_REALM || '',
|
||||
clientId: process.env.NEXT_PUBLIC_KEYCLOAK_CLIENT_ID || '',
|
||||
};
|
||||
|
||||
export const initKeycloak = (onAuthSuccess: (_token?: string) => void) => {
|
||||
const keycloak = new Keycloak(keycloakConfig);
|
||||
keycloak
|
||||
.init({
|
||||
onLoad: 'login-required',
|
||||
})
|
||||
.then((authenticated) => {
|
||||
if (authenticated) {
|
||||
onAuthSuccess(keycloak.token);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
throw new Error('Failed to initialize Keycloak.');
|
||||
});
|
||||
};
|
||||
32
src/frontend/app/desk/src/auth/useAuthStore.tsx
Normal file
32
src/frontend/app/desk/src/auth/useAuthStore.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { initKeycloak } from './keycloak';
|
||||
|
||||
interface AuthStore {
|
||||
initialized: boolean;
|
||||
authenticated: boolean;
|
||||
token: string | null;
|
||||
initAuth: () => void;
|
||||
}
|
||||
|
||||
const useAuthStore = create<AuthStore>((set) => ({
|
||||
initialized: false,
|
||||
authenticated: false,
|
||||
token: null,
|
||||
|
||||
initAuth: () =>
|
||||
set((state) => {
|
||||
if (process.env.NODE_ENV === 'development' && !state.initialized) {
|
||||
initKeycloak((token) => set({ authenticated: true, token }));
|
||||
return { initialized: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Implement OIDC production authentication
|
||||
*/
|
||||
|
||||
return {};
|
||||
}),
|
||||
}));
|
||||
|
||||
export default useAuthStore;
|
||||
@@ -2657,6 +2657,11 @@ balanced-match@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
|
||||
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
|
||||
|
||||
base64-js@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -4752,6 +4757,11 @@ jest@29.7.0:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^29.7.0"
|
||||
|
||||
js-sha256@^0.10.1:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.10.1.tgz#b40104ba1368e823fdd5f41b66b104b15a0da60d"
|
||||
integrity sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -4856,6 +4866,20 @@ json5@^2.2.3:
|
||||
object.assign "^4.1.4"
|
||||
object.values "^1.1.6"
|
||||
|
||||
jwt-decode@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
|
||||
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
|
||||
|
||||
keycloak-js@23.0.3:
|
||||
version "23.0.3"
|
||||
resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-23.0.3.tgz#91c71ac5c8e3bbafe9c7e5b8ef81756ffdd023ac"
|
||||
integrity sha512-T28rdeRgGdOvIUMl6Wo9IiHEGcDxZhUZxXBwk3EwDFWrn8uhWBngDFxdsdsv7qGGKAMKrkZVL2JN/h81sllN7A==
|
||||
dependencies:
|
||||
base64-js "^1.5.1"
|
||||
js-sha256 "^0.10.1"
|
||||
jwt-decode "^4.0.0"
|
||||
|
||||
keyv@^4.5.3, keyv@^4.5.4:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
@@ -6282,6 +6306,11 @@ url-parse@^1.5.3:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
use-sync-external-store@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
@@ -6513,3 +6542,10 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zustand@4.4.7:
|
||||
version "4.4.7"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.4.7.tgz#355406be6b11ab335f59a66d2cf9815e8f24038c"
|
||||
integrity sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==
|
||||
dependencies:
|
||||
use-sync-external-store "1.2.0"
|
||||
|
||||
Reference in New Issue
Block a user