diff --git a/docusaurus/react/3-setup/2-entra-id.mdx b/docusaurus/react/3-setup/2-entra-id.mdx index bbb0d797..7803dc5b 100644 --- a/docusaurus/react/3-setup/2-entra-id.mdx +++ b/docusaurus/react/3-setup/2-entra-id.mdx @@ -20,7 +20,13 @@ Select a fitting name for your project, this name will be presented to the user Under `Supported account types`, choose either `Intility AS only - Single tenant` or `Any Microsoft Entra ID directory - Multitenant`. This can be changed later, so if you're unsure what to choose, select single tenant. -Under `Redirect URI`, select `Single-page application (SPA)` from the dropdown, and enter `http://localhost:3000`. +:::note Redirect Bridge +MSAL requires a separate redirect bridge page from version 5. +`@intility/vite-plugin-msal` will ensure you have the page in your application, +but you must add `/redirect` to your Redirect URIs in Entra ID. +::: + +Under `Redirect URI`, select `Single-page application (SPA)` from the dropdown, and enter `http://localhost:3000/redirect`. Hit the register button, and you will be taken to an overview of your newly created registration. @@ -40,7 +46,7 @@ For each deployment of your app, you'll need to register it. You can do that by The first we need to add is the URL the deploy step makes in OpenShift: ``` -https://{your-project-slug}-dev.apps.int.intility.no +https://{your-project-slug}-dev.apps.int.intility.no/redirect ``` You can also add more later if you create more environments. diff --git a/react/Dockerfile b/react/Dockerfile index b8d8a6d0..d4388e45 100644 --- a/react/Dockerfile +++ b/react/Dockerfile @@ -16,7 +16,7 @@ RUN --mount=type=secret,id=NODE_AUTH_TOKEN,env=NODE_AUTH_TOKEN \ COPY . . RUN npm run build -FROM ghcr.io/intility/nginx-unprivileged-react@sha256:ddfd3f040f3ec6342fdabcaa1299805d27cc08aba8914aac2766ba16fa6cff3c +FROM ghcr.io/intility/nginx-unprivileged-react:2.5.1 # Copy build files COPY --from=build /src/dist /usr/share/nginx/html diff --git a/react/Dockerfile.CI b/react/Dockerfile.CI index 6490baf4..51900210 100644 --- a/react/Dockerfile.CI +++ b/react/Dockerfile.CI @@ -1,5 +1,5 @@ # This Dockerfile requires running `npm run build` on the host first -FROM ghcr.io/intility/nginx-unprivileged-react@sha256:ddfd3f040f3ec6342fdabcaa1299805d27cc08aba8914aac2766ba16fa6cff3c +FROM ghcr.io/intility/nginx-unprivileged-react:2.5.1 # Copy build files COPY dist/ /usr/share/nginx/html diff --git a/react/package.json b/react/package.json index 020bac8f..0f8a8607 100644 --- a/react/package.json +++ b/react/package.json @@ -12,36 +12,38 @@ "test": "vitest" }, "dependencies": { - "@azure/msal-browser": "^4.27.0", - "@azure/msal-react": "^3.0.23", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-react": "^5.0.7", "@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/pro-duotone-svg-icons": "^7.2.0", "@fortawesome/pro-light-svg-icons": "^7.2.0", "@fortawesome/pro-regular-svg-icons": "^7.2.0", "@fortawesome/pro-solid-svg-icons": "^7.2.0", - "@intility/bifrost-react": "^6.13.4", - "@sentry/react": "^10.32.1", + "@intility/bifrost-react": "^6.15.2", + "@sentry/react": "^10.44.0", "react": "^19.2.4", "react-dom": "^19.2.4", - "react-router": "^7.13.0", + "react-router": "^7.13.1", "use-local-storage-state": "^19.5.0" }, "devDependencies": { - "@biomejs/biome": "2.4.4", + "@babel/core": "^7.29.0", + "@biomejs/biome": "2.4.8", "@intility/eslint-config-react-compiler": "^1.0.0", + "@intility/vite-plugin-msal": "^0.4.0", + "@rolldown/plugin-babel": "^0.2.2", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.2", + "@vitejs/plugin-react": "^6.0.1", "babel-plugin-react-compiler": "^1.0.0", - "eslint": "^9.39.2", - "happy-dom": "^20.7.0", + "eslint": "^9.39.4", + "happy-dom": "^20.8.4", "typescript": "~5.9.3", - "vite": "^7.3.1", + "vite": "^8.0.0", "vite-plugin-checker": "^0.12.0", - "vite-tsconfig-paths": "^6.0.5", - "vitest": "^4.0.18" + "vitest": "^4.1.0" } } diff --git a/react/src/auth/config.ts b/react/src/auth/config.ts index f899a577..f71506bb 100644 --- a/react/src/auth/config.ts +++ b/react/src/auth/config.ts @@ -1,5 +1,10 @@ import type { Configuration, RedirectRequest } from "@azure/msal-browser"; +/** + * LocalStorage key for loginHint + */ +export const LOGIN_HINT_KEY = "login_hint"; + /** * MSAL config for the PublicClientApplication * @see https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md @@ -8,7 +13,7 @@ export const msalConfig: Configuration = { auth: { clientId: import.meta.env.VITE_ENTRA_CLIENT_ID, authority: import.meta.env.VITE_ENTRA_AUTHORITY, - redirectUri: window.location.origin, + redirectUri: `${window.location.origin}/redirect`, }, }; @@ -17,4 +22,5 @@ export const msalConfig: Configuration = { */ export const loginRequest: RedirectRequest = { scopes: ["User.Read"], + loginHint: localStorage?.getItem(LOGIN_HINT_KEY) ?? undefined, }; diff --git a/react/src/auth/instance.ts b/react/src/auth/instance.ts index 635d2016..e109f505 100644 --- a/react/src/auth/instance.ts +++ b/react/src/auth/instance.ts @@ -4,7 +4,7 @@ import { PublicClientApplication, } from "@azure/msal-browser"; import * as Sentry from "@sentry/react"; -import { msalConfig } from "./config"; +import { LOGIN_HINT_KEY, msalConfig } from "./config"; /** * A PublicClientApplication instance @@ -18,15 +18,18 @@ export const msalInstance = new PublicClientApplication(msalConfig); * @see https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/events.md */ msalInstance.addEventCallback((message) => { - if ( - message.eventType === EventType.LOGIN_SUCCESS || - message.eventType === EventType.SSO_SILENT_SUCCESS - ) { + if (message.eventType === EventType.ACQUIRE_TOKEN_SUCCESS) { const result = message.payload as AuthenticationResult; if (!result.account) return; msalInstance.setActiveAccount(result.account); + // set according to order of preference from + // https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/login-user.md#with-user-hint + localStorage.setItem( + LOGIN_HINT_KEY, + result.account.loginHint ?? result.account.username, + ); Sentry.setUser({ id: result.account.homeAccountId, diff --git a/react/src/routes/Profile.tsx b/react/src/routes/Profile.tsx index 3be03926..c236b027 100644 --- a/react/src/routes/Profile.tsx +++ b/react/src/routes/Profile.tsx @@ -15,7 +15,7 @@ export default function Profile() {
Hello, {account?.name}!
- +You are not logged in.
diff --git a/react/vite.config.ts b/react/vite.config.ts index 219c92a5..9872436d 100644 --- a/react/vite.config.ts +++ b/react/vite.config.ts @@ -1,17 +1,19 @@ ///