Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
56bf8a3
Add user to navbar and update version
noemifrisina Mar 9, 2026
881d0e4
Merge branch 'main' into i15-try-login
noemifrisina Mar 11, 2026
bcca05d
Update pnpm-lock
noemifrisina Mar 11, 2026
bac6ebd
Merge branch 'main' into i15-try-login
noemifrisina Mar 11, 2026
db14dd5
An attempt at oauth2 config
noemifrisina Mar 12, 2026
a262f85
Add config in the right place for oauth2
noemifrisina Mar 12, 2026
1aebfcd
Add context and provider for authentication
noemifrisina Mar 12, 2026
eec19a4
Add a useful hook
noemifrisina Mar 12, 2026
2d9234b
Wrap app in provider and set up user with hook in the navbar
noemifrisina Mar 12, 2026
016da17
Set up a starting mock - iwll need more work
noemifrisina Mar 12, 2026
a74e2f8
Disable blueapi buttons
noemifrisina Mar 12, 2026
3dff6c7
Tidy up a bit
noemifrisina Mar 12, 2026
37e4bcc
Tried and failed to add an error boundary fallback
noemifrisina Mar 12, 2026
12456b5
...and save the fallback too
noemifrisina Mar 12, 2026
70687c2
Fix typo
noemifrisina Mar 13, 2026
7a16598
Merge branch 'main' into i15-try-login
noemifrisina Mar 13, 2026
0e86791
Try to fix login
noemifrisina Mar 13, 2026
f3657bb
Try to fix login - again
noemifrisina Mar 13, 2026
4bfff68
See if this fixes the config map
noemifrisina Mar 13, 2026
5d671ba
Use the username and see if it helps
noemifrisina Mar 13, 2026
5c917c5
Try change to nginx
noemifrisina Mar 13, 2026
f7bcfed
Merge branch 'main' into i15-try-login
noemifrisina Mar 13, 2026
01593ad
And maybe fix the provider too
noemifrisina Mar 13, 2026
c68d7d8
Move the ErrorBoundary
noemifrisina Mar 13, 2026
0075c25
Putting nginx back
noemifrisina Mar 13, 2026
4b9ade6
Still not working as hoped
noemifrisina Mar 13, 2026
64cb1e9
Login info now working
noemifrisina Mar 17, 2026
0fb37ab
Test: enable ingress in oauth2-proxy
noemifrisina Mar 19, 2026
102d6e5
Add details to the ingress
noemifrisina Mar 19, 2026
2ac5827
Try changing the common yaml
noemifrisina Mar 19, 2026
e966291
Tidy up from latest attempts
noemifrisina Mar 19, 2026
bfb3a0a
Try skip_auth_preflight
noemifrisina Mar 19, 2026
7686074
Technically login works but still getting auth at start
noemifrisina Mar 19, 2026
17a3585
Maybe remove random words from mock
noemifrisina Mar 19, 2026
aeb2d3b
Try to redirect 302
noemifrisina Mar 25, 2026
6eea9e0
Tidy up mocks and see if still works without error boundary
noemifrisina Mar 25, 2026
d2a677d
Finish tidying up
noemifrisina Mar 25, 2026
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
6 changes: 4 additions & 2 deletions apps/i15-1/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
"dependencies": {
"@atlas/blueapi": "workspace:*",
"@atlas/blueapi-query": "workspace:*",
"@diamondlightsource/sci-react-ui": "^0.2.0",
"@diamondlightsource/sci-react-ui": "^0.4.0",
"@mui/icons-material": "^6.5.0",
"@mui/material": "<7.0.0",
"@tanstack/react-query": "^5.90.21"
"@tanstack/react-query": "^5.90.21",
"axios": "^1.13.4",
"react-error-boundary": "^6.0.0"
},
"devDependencies": {
"@atlas/vitest-conf": "workspace:*",
Expand Down
13 changes: 11 additions & 2 deletions apps/i15-1/src/components/RunPlanButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState } from "react";

import { useSetActiveTask, useSubmitTask } from "@atlas/blueapi-query";
import type { TaskRequest } from "@atlas/blueapi";
import { useUserAuth } from "../context/userAuth/useUserAuth";

type RunPlanButtonProps = {
name: string;
Expand All @@ -17,13 +18,14 @@ const RunPlanButton = ({
instrumentSession,
buttonText = "Run",
}: RunPlanButtonProps) => {

const user = useUserAuth();

const submitTask = useSubmitTask();
const startTask = useSetActiveTask();
const submitAndRunTask = async (task: TaskRequest) => {
await submitTask
.mutateAsync(task)
.then(response => startTask.mutateAsync(response.task_id));
.then((response) => startTask.mutateAsync(response.task_id));
};

const [loading, setLoading] = useState<boolean>(false);
Expand All @@ -38,12 +40,19 @@ const RunPlanButton = ({
setLoading(false);
};

const isButtonDisabled = () => {
const disable =
user.person == null || user.person == undefined ? true : false;
return disable;
};

return (
<Button
variant="contained"
loading={loading}
sx={{ width: "150px" }}
onClick={handleClick}
disabled={isButtonDisabled()}
>
{buttonText}
</Button>
Expand Down
17 changes: 17 additions & 0 deletions apps/i15-1/src/components/WaffleNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import {
Navbar,
NavLink,
NavLinks,
User,
} from "@diamondlightsource/sci-react-ui";
import { useUserAuth } from "../context/userAuth/useUserAuth";

function WaffleNavbar() {
const user = useUserAuth();

const handleLogIn = () => window.location.assign("/oauth2/sign_in");
const handleLogOut = () => window.location.assign("/oauth2/sign_out");

return (
<Navbar
logo="theme"
Expand All @@ -32,6 +39,16 @@ function WaffleNavbar() {
}
rightSlot={
<Box sx={{ marginLeft: 4 }}>
<User
onLogin={handleLogIn}
onLogout={handleLogOut}
user={
user.person == null || user.person == undefined
? null
: { fedid: user.person }
}
colour="white"
/>
<ColourSchemeButton />
</Box>
}
Expand Down
6 changes: 6 additions & 0 deletions apps/i15-1/src/context/userAuth/UserAuthContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createContext } from "react";
import { type UserAuthStatus } from "./authUtils";

const userDefault: UserAuthStatus = {person: null, person_status: "PENDING"};

export const UserAuthContext = createContext<UserAuthStatus>(userDefault);
58 changes: 58 additions & 0 deletions apps/i15-1/src/context/userAuth/UserAuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { type ReactNode } from "react";
import { UserAuthContext } from "./UserAuthContext";
import { getUser, type UserAuthStatus } from "./authUtils";
import { useQuery } from "@tanstack/react-query";
import type { AxiosError } from "axios";

export const UserAuthProvider = ({ children }: { children: ReactNode }) => {
const query = useQuery({
queryKey: ["user"],
queryFn: getUser,
retry: (failureCount, error: AxiosError) => {
if ("status" in error && (error.status == 401 || error.status == 403)) {
//dont retry 401/403
return false;
}

return failureCount < 2;
},
});

//undefined if pending
let response: UserAuthStatus = {
person: null,
person_status: "PENDING",
};

if (query.isError) {
if (query.error.status == 401) {
response = {
person: null,
person_status: "UNAUTHORIZED",
};
} else if (query.error.status == 403) {
response = {
person: null,
person_status: "FORBIDDEN",
};
} else {
response = {
person: null,
person_status: "ERROR",
};
}
} else {
if (query.data) {
response = {
person: query.data.preferredUsername,
person_status: "OK",
};
}
}

return (
<UserAuthContext.Provider value={response}>
{children}
</UserAuthContext.Provider>
);
};
22 changes: 22 additions & 0 deletions apps/i15-1/src/context/userAuth/authUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import axios, { type AxiosResponse } from "axios";

const userUrl = "oauth2/userinfo";

export type Person = {
identifier: string;
accepted_orca_eula: boolean;
};

export type UserName = {
preferredUsername: string;
};

export type UserAuthStatus = {
person: string | null | undefined;
person_status: "PENDING" | "UNAUTHORIZED" | "FORBIDDEN" | "OK" | "ERROR";
};

export const getUser = async () => {
const { data } = await axios.get<UserName, AxiosResponse<UserName>>(userUrl);
return data;
};
12 changes: 12 additions & 0 deletions apps/i15-1/src/context/userAuth/useUserAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { UserAuthContext } from "./UserAuthContext"
import { useContext } from "react"

export const useUserAuth = () => {
const context = useContext(UserAuthContext);
if (!context) {
throw new Error(
"No user context found, is your component without UserAuthProvider?"
);
}
return context;
}
9 changes: 6 additions & 3 deletions apps/i15-1/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ declare global {

import { createApi } from "@atlas/blueapi";
import { BlueapiProvider } from "@atlas/blueapi-query";
import { UserAuthProvider } from "./context/userAuth/UserAuthProvider.tsx";

async function enableMocking() {
if (import.meta.env.DEV) {
Expand Down Expand Up @@ -51,9 +52,11 @@ enableMocking().then(() => {
<StrictMode>
<ThemeProvider theme={DiamondTheme} defaultMode="light">
<QueryClientProvider client={queryClient}>
<BlueapiProvider api={api}>
<RouterProvider router={router} />
</BlueapiProvider>
<UserAuthProvider>
<BlueapiProvider api={api}>
<RouterProvider router={router} />
</BlueapiProvider>
</UserAuthProvider>
</QueryClientProvider>
</ThemeProvider>
</StrictMode>
Expand Down
6 changes: 5 additions & 1 deletion apps/i15-1/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { http, HttpResponse, graphql } from "msw";
import { http, HttpResponse } from "msw";

const fakeTaskId = "7304e8e0-81c6-4978-9a9d-9046ab79ce3c";

Expand All @@ -18,4 +18,8 @@ export const handlers = [
http.put("/api/worker/state", () => {
return HttpResponse.json("IDLE");
}),

http.get("/oauth2/userinfo", () => {
return HttpResponse.json({ preferredUsername: "abc123456" });
}),
];
4 changes: 4 additions & 0 deletions apps/i15-1/src/mocks/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { setupServer } from "msw/node";
import { handlers } from "./handlers.js";

export const server = setupServer(...handlers);
14 changes: 14 additions & 0 deletions apps/i15-1/src/routes/Fallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
import { Box, Typography } from "@mui/material";


export function AuthFallbackScreen() {
return (
<Box alignContent={"center"}>
<ErrorOutlineIcon color="error" fontSize="large" />
<Typography component="h1" variant="h4">
Something went wrong with authentication!
</Typography>
</Box>
);
}
5 changes: 4 additions & 1 deletion helm/templates/configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ data:
cookie_samesite = "lax"
cookie_expire = "168h"
cookie_refresh = "45s"
cookie_name = "{{ include "oauth2-proxy.fullname" . }}-cookie"
cookie_name = "{{ include "oauth2-proxy.fullname" . }}-cookie"
skip_auth_routes = ["GET=^/$","GET=^/assets",]
api_routes = ["^/oauth2", "^/api"]
skip_provider_button = true
6 changes: 3 additions & 3 deletions helm/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ oauth2-proxy:
config:
existingSecret: ui-keycloak-secret
existingConfig: oauth2-proxy-config

alphaConfig:
enabled: true
existingSecret: ui-oauth2-alpha-secret

service:
portNumber: 4180
portNumber: 4180
Loading
Loading