Skip to content

feat(frontend): Add authentication debug window #9120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
660747f
https://github.com/inventree/InvenTree/pull/6293
matmair Feb 10, 2025
769e884
refactor to a shared component
matmair Feb 10, 2025
bb0c43e
refactoring container stuff to a wrapper
matmair Feb 10, 2025
7c0df24
move title to wrapper
matmair Feb 10, 2025
96fbfb9
move logoff and loader to wrapper
matmair Feb 10, 2025
07c51d2
mvoe functions to general auth
matmair Feb 10, 2025
ad67f53
seperate login and register into seperate pages
matmair Feb 10, 2025
cdc20fd
unify auth styling
matmair Feb 10, 2025
1fde3c9
rename component
matmair Feb 10, 2025
2f0d839
adapt to new look
matmair Feb 10, 2025
073f8d7
check if registration is enabled
matmair Feb 10, 2025
72cea32
Merge branch 'master' into pui-refactor-auth
matmair Feb 11, 2025
2404d57
feat(frontend):add authentication debug window
matmair Feb 17, 2025
bc00214
clear state on logout
matmair Feb 17, 2025
beaf47f
add reload button
matmair Feb 17, 2025
ddfd41d
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Feb 21, 2025
783ea76
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Feb 21, 2025
8fa84d4
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Feb 21, 2025
c6321c3
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Feb 24, 2025
1ca599e
reduce diff
matmair Feb 24, 2025
ecb3f4b
export helper
matmair Feb 24, 2025
a06a550
move hover out
matmair Feb 24, 2025
8f56e62
only show to superusers
matmair Feb 24, 2025
f64fafb
fix state args
matmair Feb 24, 2025
8ed6bf5
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Mar 8, 2025
5397775
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Mar 11, 2025
e854b15
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Mar 25, 2025
778289a
Merge branch 'feat(frontend)-add-authentication-debug-window' of http…
matmair Mar 25, 2025
18e7cba
fix merge
matmair Mar 25, 2025
21c8fa7
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Mar 26, 2025
5c1cded
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Apr 3, 2025
8341d95
Merge branch 'master' into feat(frontend)-add-authentication-debug-wi…
matmair Apr 8, 2025
59b6ef6
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Apr 24, 2025
fda57ae
fix merge
matmair Apr 24, 2025
1eb8aa2
clean up diff
matmair Apr 24, 2025
fc88cdc
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair May 4, 2025
8f7e648
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Jun 10, 2025
57ccf34
reduce diff
matmair Jun 10, 2025
d1cc753
re-diff
matmair Jun 10, 2025
8532697
fix shallow loading
matmair Jun 10, 2025
db6ce62
fix test
matmair Jun 10, 2025
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
11 changes: 11 additions & 0 deletions src/frontend/lib/types/Auth.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
export interface AuthContext {
status: number;
user?: {
id: number;
display: string;
has_usable_password: boolean;
username: string;
};
methods?: {
method: string;
at: number;
username: string;
}[];
data: { flows: Flow[] };
meta: { is_authenticated: boolean };
}
Expand Down
226 changes: 166 additions & 60 deletions src/frontend/src/components/nav/MainMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { Trans } from '@lingui/react/macro';
import {
Button,
Group,
HoverCard,
List,
Menu,
Skeleton,
Table,
Text,
UnstyledButton,
useMantineColorScheme
} from '@mantine/core';
import {
IconChevronDown,
IconInfoCircle,
IconLogout,
IconMoonStars,
IconSettings,
Expand All @@ -18,10 +23,15 @@ import {
} from '@tabler/icons-react';
import { Link, useNavigate } from 'react-router-dom';

import { ApiEndpoints, apiUrl } from '@lib/index';
import type { AuthContext } from '@lib/types/Auth';
import { useShallow } from 'zustand/react/shallow';
import { doLogout } from '../../functions/auth';
import { authApi, doLogout } from '../../functions/auth';
import * as classes from '../../main.css';
import { parseDate } from '../../pages/Index/Settings/AccountSettings/SecurityContent';
import { useServerApiState } from '../../states/ApiState';
import { useUserState } from '../../states/UserState';
import type { ServerAPIProps } from '../../states/states';
import { vars } from '../../theme';

export function MainMenu() {
Expand All @@ -30,72 +40,168 @@ export function MainMenu() {
useShallow((state) => [state.user, state.username])
);
const { colorScheme, toggleColorScheme } = useMantineColorScheme();

const [server, auth_context] = useServerApiState(
useShallow((state) => [state.server, state.auth_context])
);
return (
<Menu width={260} position='bottom-end'>
<Menu.Target>
<UnstyledButton className={classes.layoutHeaderUser}>
<Group gap={7}>
{username() ? (
<Text fw={500} size='sm' style={{ lineHeight: 1 }} mr={3}>
{username()}
</Text>
) : (
<Skeleton height={20} width={40} radius={vars.radiusDefault} />
)}
<IconChevronDown />
</Group>
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>
<Trans>Settings</Trans>
</Menu.Label>
<Menu.Item
leftSection={<IconUserCog />}
component={Link}
to='/settings/user'
>
<Trans>Account Settings</Trans>
</Menu.Item>
{user?.is_staff && (
<>
{user?.is_superuser && (
<HoverCard shadow='md' openDelay={500} closeDelay={500} withArrow>
<HoverCard.Target>
<IconInfoCircle />
</HoverCard.Target>
<HoverCard.Dropdown>
{
<AuthContextInformation
server={server}
auth_context={auth_context}
/>
}
</HoverCard.Dropdown>
</HoverCard>
)}
<Menu width={260} position='bottom-end'>
<Menu.Target>
<UnstyledButton className={classes.layoutHeaderUser}>
<Group gap={7}>
{username() ? (
<Text fw={500} size='sm' style={{ lineHeight: 1 }} mr={3}>
{username()}
</Text>
) : (
<Skeleton height={20} width={40} radius={vars.radiusDefault} />
)}
<IconChevronDown />
</Group>
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>
<Trans>Settings</Trans>
</Menu.Label>
<Menu.Item
leftSection={<IconSettings />}
leftSection={<IconUserCog />}
component={Link}
to='/settings/system'
to='/settings/user'
>
<Trans>System Settings</Trans>
<Trans>Account Settings</Trans>
</Menu.Item>
)}
{user?.is_staff && (
{user?.is_staff && (
<Menu.Item
leftSection={<IconSettings />}
component={Link}
to='/settings/system'
>
<Trans>System Settings</Trans>
</Menu.Item>
)}
{user?.is_staff && (
<Menu.Item
leftSection={<IconUserBolt />}
component={Link}
to='/settings/admin'
>
<Trans>Admin Center</Trans>
</Menu.Item>
)}
{user?.is_staff && <Menu.Divider />}
<Menu.Item
leftSection={<IconUserBolt />}
component={Link}
to='/settings/admin'
onClick={toggleColorScheme}
leftSection={
colorScheme === 'dark' ? <IconSun /> : <IconMoonStars />
}
c={
colorScheme === 'dark'
? vars.colors.yellow[4]
: vars.colors.blue[6]
}
>
<Trans>Admin Center</Trans>
<Trans>Change Color Mode</Trans>
</Menu.Item>
)}
{user?.is_staff && <Menu.Divider />}
<Menu.Item
onClick={toggleColorScheme}
leftSection={colorScheme === 'dark' ? <IconSun /> : <IconMoonStars />}
c={
colorScheme === 'dark' ? vars.colors.yellow[4] : vars.colors.blue[6]
}
>
<Trans>Change Color Mode</Trans>
</Menu.Item>
<Menu.Divider />
<Menu.Item
leftSection={<IconLogout />}
onClick={() => {
doLogout(navigate);
}}
>
<Trans>Logout</Trans>
</Menu.Item>
</Menu.Dropdown>
</Menu>
<Menu.Divider />
<Menu.Item
leftSection={<IconLogout />}
onClick={() => {
doLogout(navigate);
}}
>
<Trans>Logout</Trans>
</Menu.Item>
</Menu.Dropdown>
</Menu>
</>
);
}

function AuthContextInformation({
server,
auth_context
}: Readonly<{
server: ServerAPIProps;
auth_context: AuthContext | undefined;
}>) {
const [setAuthContext] = useServerApiState(
useShallow((state) => [state.setAuthContext])
);

function fetchAuthContext() {
authApi(apiUrl(ApiEndpoints.auth_session)).then((resp) => {
setAuthContext(resp.data.data);
});
}

const rows = [
{ name: 'Server version', value: server.version },
{ name: 'API version', value: server.apiVersion },
{ name: 'User ID', value: auth_context?.user?.id }
];
return (
<>
<Table>
<Table.Thead>
<Table.Tr>
<Table.Th>
<Trans>Name</Trans>
</Table.Th>
<Table.Th>
<Trans>Value</Trans>
</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{rows.map((element) => (
<Table.Tr key={element.name}>
<Table.Td>{element.name}</Table.Td>
<Table.Td>{element.value}</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
{auth_context?.methods && (
<>
<Text size='sm'>
<Trans>Methods</Trans>
</Text>

<List type='ordered'>
{auth_context?.methods.map((method: any) => (
<List.Item key={method.at}>
<strong>{parseDate(method.at)}</strong>: {method.method}
</List.Item>
))}
</List>
</>
)}
<Button
variant='light'
size='compact-md'
onClick={(e) => {
e.preventDefault();
fetchAuthContext();
}}
>
<Trans>Reload</Trans>
</Button>
</>
);
}
2 changes: 2 additions & 0 deletions src/frontend/src/functions/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export async function doBasicLogin(
*/
export const doLogout = async (navigate: NavigateFunction) => {
const { clearUserState, isLoggedIn } = useUserState.getState();
const { setAuthContext } = useServerApiState.getState();

// Logout from the server session
if (isLoggedIn() || !!getCsrfCookie()) {
Expand All @@ -183,6 +184,7 @@ export const doLogout = async (navigate: NavigateFunction) => {

clearUserState();
clearCsrfCookie();
setAuthContext(undefined);
navigate('/login');
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,6 @@ function MfaSection() {
);
};

const parseDate = (date: number) =>
date == null ? 'Never' : new Date(date * 1000).toLocaleString();

const rows = useMemo(() => {
if (isLoading || !data) return null;
return data.map((token: any) => (
Expand Down Expand Up @@ -713,3 +710,6 @@ async function runActionWithFallback(
});
}
}

export const parseDate = (date: number) =>
date == null ? 'Never' : new Date(date * 1000).toLocaleString();
2 changes: 1 addition & 1 deletion src/frontend/src/states/ApiState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface ServerApiStateProps {
fetchServerApiState: () => void;
auth_config?: AuthConfig;
auth_context?: AuthContext;
setAuthContext: (auth_context: AuthContext) => void;
setAuthContext: (auth_context: AuthContext | undefined) => void;
sso_enabled: () => boolean;
registration_enabled: () => boolean;
sso_registration_enabled: () => boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/tests/pages/pui_core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test('Core User/Group/Contact', async ({ browser }) => {

// users
await loadTab(page, 'Users');
await page.getByRole('cell', { name: 'admin' }).click();
await page.getByRole('cell', { name: 'admin', exact: true }).click();
await page.getByText('User: admin', { exact: true }).waitFor();
await page.getByLabel('User Details').waitFor();
await page.getByLabel('breadcrumb-1-users').click();
Expand Down
Loading