Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docusaurus/react/3-setup/2-entra-id.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion react/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion react/Dockerfile.CI
Original file line number Diff line number Diff line change
@@ -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
26 changes: 14 additions & 12 deletions react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
8 changes: 7 additions & 1 deletion react/src/auth/config.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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`,
},
};

Expand All @@ -17,4 +22,5 @@ export const msalConfig: Configuration = {
*/
export const loginRequest: RedirectRequest = {
scopes: ["User.Read"],
loginHint: localStorage?.getItem(LOGIN_HINT_KEY) ?? undefined,
};
13 changes: 8 additions & 5 deletions react/src/auth/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion react/src/routes/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function Profile() {
<div className="bf-content bfl-page-padding">
<AuthenticatedTemplate>
<p>Hello, {account?.name}!</p>
<Button onClick={() => instance.logout()}>Logout</Button>
<Button onClick={() => instance.logoutRedirect()}>Logout</Button>
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<p>You are not logged in.</p>
Expand Down
18 changes: 11 additions & 7 deletions react/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
/// <reference types="vitest/config" />
import react from "@vitejs/plugin-react";

import msal from "@intility/vite-plugin-msal";
import babel from "@rolldown/plugin-babel";
import react, { reactCompilerPreset } from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import checker from "vite-plugin-checker";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vite.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
plugins: ["babel-plugin-react-compiler"],
},
react(),
babel({
presets: [reactCompilerPreset()],
}),
msal(),
checker({
typescript: true,
biome: {
Expand All @@ -22,8 +24,10 @@ export default defineConfig({
useFlatConfig: true,
},
}),
tsconfigPaths(),
],
resolve: {
tsconfigPaths: true,
},
server: {
port: 3000,
open: true,
Expand Down
Loading