Skip to content

Commit 4ed1321

Browse files
committed
force reroute to profile if we dont know their first and last name
1 parent a09f816 commit 4ed1321

File tree

7 files changed

+252
-48
lines changed

7 files changed

+252
-48
lines changed

src/common/types/msGraphApi.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
export interface UserProfileData {
2-
id?: string;
1+
export interface UserProfileDataBase {
32
userPrincipalName: string;
43
displayName?: string;
54
givenName?: string;
65
surname?: string;
76
mail?: string;
8-
jobTitle?: string;
9-
mobilePhone?: string;
10-
officeLocation?: string;
11-
preferredLanguage?: string;
12-
businessPhones?: string[];
7+
otherMails?: string[]
8+
}
9+
10+
export interface UserProfileData extends UserProfileDataBase {
11+
discordUsername?: string;
1312
}

src/ui/Router.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,28 @@ import { ViewTicketsPage } from './pages/tickets/ViewTickets.page';
1919
import { ManageIamPage } from './pages/iam/ManageIam.page';
2020
import { ManageProfilePage } from './pages/profile/ManageProfile.page';
2121

22+
const ProfileRediect: React.FC = () => {
23+
const location = useLocation();
24+
25+
// Don't store login-related paths and ALLOW the callback path
26+
const excludedPaths = [
27+
'/login',
28+
'/logout',
29+
'/force_login',
30+
'/a',
31+
'/auth/callback', // Add this to excluded paths
32+
];
33+
34+
if (excludedPaths.includes(location.pathname)) {
35+
return <Navigate to="/login" replace />;
36+
}
37+
38+
// Include search params and hash in the return URL if they exist
39+
const returnPath = location.pathname + location.search + location.hash;
40+
const loginUrl = `/profile?returnTo=${encodeURIComponent(returnPath)}&firstTime=true`;
41+
return <Navigate to={loginUrl} replace />;
42+
};
43+
2244
// Component to handle redirects to login with return path
2345
const LoginRedirect: React.FC = () => {
2446
const location = useLocation();
@@ -57,6 +79,18 @@ const commonRoutes = [
5779
},
5880
];
5981

82+
const profileRouter = createBrowserRouter([
83+
...commonRoutes,
84+
{
85+
path: '/profile',
86+
element: <ManageProfilePage />,
87+
},
88+
{
89+
path: '*',
90+
element: <ProfileRediect />,
91+
},
92+
]);
93+
6094
const unauthenticatedRouter = createBrowserRouter([
6195
...commonRoutes,
6296
{
@@ -67,7 +101,6 @@ const unauthenticatedRouter = createBrowserRouter([
67101
path: '/login',
68102
element: <LoginPage />,
69103
},
70-
// Catch-all route that preserves the attempted path
71104
{
72105
path: '*',
73106
element: <LoginRedirect />,
@@ -168,7 +201,12 @@ const ErrorBoundary: React.FC<ErrorBoundaryProps> = ({ children }) => {
168201

169202
export const Router: React.FC = () => {
170203
const { isLoggedIn } = useAuth();
171-
const router = isLoggedIn ? authenticatedRouter : unauthenticatedRouter;
204+
console.log(isLoggedIn);
205+
const router = isLoggedIn
206+
? authenticatedRouter
207+
: isLoggedIn === null
208+
? profileRouter
209+
: unauthenticatedRouter;
172210

173211
return (
174212
<ErrorBoundary>

src/ui/components/AuthContext/index.tsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { CACHE_KEY_PREFIX } from '../AuthGuard/index.js';
1919
import FullScreenLoader from './LoadingScreen.js';
2020

2121
import { getRunEnvironmentConfig, ValidServices } from '@ui/config.js';
22+
import { transformCommaSeperatedName } from '@common/utils.js';
23+
import { useApi } from '@ui/util/api.js';
2224

2325
interface AuthContextDataWrapper {
2426
isLoggedIn: boolean;
@@ -28,6 +30,7 @@ interface AuthContextDataWrapper {
2830
getToken: CallableFunction;
2931
logoutCallback: CallableFunction;
3032
getApiToken: CallableFunction;
33+
setLoginStatus: CallableFunction;
3134
}
3235

3336
export type AuthContextData = {
@@ -53,7 +56,6 @@ export const clearAuthCache = () => {
5356

5457
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
5558
const { instance, inProgress, accounts } = useMsal();
56-
5759
const [userData, setUserData] = useState<AuthContextData | null>(null);
5860
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(false);
5961

@@ -67,11 +69,9 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
6769
if (response) {
6870
handleMsalResponse(response);
6971
} else if (accounts.length > 0) {
70-
// User is already logged in, set the state
71-
const [lastName, firstName] = accounts[0].name?.split(',') || [];
7272
setUserData({
7373
email: accounts[0].username,
74-
name: `${firstName} ${lastName}`,
74+
name: transformCommaSeperatedName(accounts[0].name || ''),
7575
});
7676
setIsLoggedIn(true);
7777
}
@@ -94,29 +94,26 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
9494
})
9595
.then((silentResponse) => {
9696
if (silentResponse?.account?.name) {
97-
const [lastName, firstName] = silentResponse.account.name.split(',');
9897
setUserData({
99-
email: silentResponse.account.username,
100-
name: `${firstName} ${lastName}`,
98+
email: accounts[0].username,
99+
name: transformCommaSeperatedName(accounts[0].name || ''),
101100
});
102101
setIsLoggedIn(true);
103102
}
104103
})
105104
.catch(console.error);
106105
return;
107106
}
108-
109-
// Use response.account instead of accounts[0]
110-
const [lastName, firstName] = response.account.name?.split(',') || [];
111107
setUserData({
112-
email: response.account.username,
113-
name: `${firstName} ${lastName}`,
108+
email: accounts[0].username,
109+
name: transformCommaSeperatedName(accounts[0].name || ''),
114110
});
115111
setIsLoggedIn(true);
116112
}
117113
},
118114
[accounts, instance]
119115
);
116+
120117
const getApiToken = useCallback(
121118
async (service: ValidServices) => {
122119
if (!userData) {
@@ -194,6 +191,9 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
194191
},
195192
[instance]
196193
);
194+
const setLoginStatus = useCallback((val: boolean) => {
195+
setIsLoggedIn(val);
196+
}, []);
197197

198198
const logout = useCallback(async () => {
199199
try {
@@ -209,7 +209,16 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
209209
};
210210
return (
211211
<AuthContext.Provider
212-
value={{ isLoggedIn, userData, loginMsal, logout, getToken, logoutCallback, getApiToken }}
212+
value={{
213+
isLoggedIn,
214+
userData,
215+
setLoginStatus,
216+
loginMsal,
217+
logout,
218+
getToken,
219+
logoutCallback,
220+
getApiToken,
221+
}}
213222
>
214223
{inProgress !== InteractionStatus.None ? (
215224
<MantineProvider>

src/ui/components/AuthGuard/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export const AuthGuard: React.FC<
8282
setIsAuthenticated(true);
8383
return;
8484
}
85+
if (validRoles.length === 0) {
86+
setIsAuthenticated(true);
87+
return;
88+
}
8589

8690
// Check for cached response first
8791
const cachedData = getCachedResponse(service, authCheckRoute);

src/ui/pages/Login.page.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,33 @@ import { Center, Alert } from '@mantine/core';
55
import { IconAlertCircle, IconAlertTriangle } from '@tabler/icons-react';
66
import { useEffect } from 'react';
77
import { useNavigate, useSearchParams } from 'react-router-dom';
8+
import { useApi } from '@ui/util/api';
89

910
export function LoginPage() {
1011
const navigate = useNavigate();
11-
const { isLoggedIn } = useAuth();
12+
const graphApi = useApi('msGraphApi');
13+
const { isLoggedIn, setLoginStatus } = useAuth();
1214
const [searchParams] = useSearchParams();
1315
const showLogoutMessage = searchParams.get('lc') === 'true';
1416
const showLoginMessage = !showLogoutMessage && searchParams.get('li') === 'true';
1517

1618
useEffect(() => {
17-
if (isLoggedIn) {
18-
const returnTo = searchParams.get('returnTo');
19-
navigate(returnTo || '/home');
20-
}
19+
const evalState = async () => {
20+
if (isLoggedIn) {
21+
const returnTo = searchParams.get('returnTo');
22+
const me = (await graphApi.get('/v1.0/me?$select=givenName,surname')).data as {
23+
givenName?: string;
24+
surname?: string;
25+
};
26+
if (!me.givenName || !me.surname) {
27+
setLoginStatus(null);
28+
navigate(`/profile?firstTime=true${returnTo ? `&returnTo=${returnTo}` : ''}`);
29+
} else {
30+
navigate(returnTo || '/home');
31+
}
32+
}
33+
};
34+
evalState();
2135
}, [navigate, isLoggedIn, searchParams]);
2236

2337
return (
Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,52 @@
11
import React from 'react';
2-
import { Title } from '@mantine/core';
2+
import { Container, Title } from '@mantine/core';
33
import { AuthGuard } from '@ui/components/AuthGuard';
44
import { useApi } from '@ui/util/api';
5-
import { UserProfileData } from '@common/types/msGraphApi';
5+
import { UserProfileData, UserProfileDataBase } from '@common/types/msGraphApi';
66
import { ManageProfileComponent } from './ManageProfileComponent';
7+
import { useSearchParams } from 'react-router-dom';
78

89
export const ManageProfilePage: React.FC = () => {
910
const api = useApi('msGraphApi');
10-
11+
const [searchParams] = useSearchParams();
12+
const returnTo = searchParams.get('returnTo') || undefined;
13+
const firstTime = searchParams.get('firstTime') === 'true' || false;
1114
const getProfile = async () => {
12-
return (await api.get('/v1.0/me')).data as UserProfileData;
15+
const raw = (
16+
await api.get(
17+
'/v1.0/me?$select=userPrincipalName,givenName,surname,displayName,otherMails,mail'
18+
)
19+
).data as UserProfileDataBase;
20+
const discordUsername = raw.otherMails?.filter((x) => x.endsWith('@discord'));
21+
const enhanced = raw as UserProfileData;
22+
if (discordUsername?.length === 1) {
23+
enhanced.discordUsername = discordUsername[0].replace('@discord', '');
24+
enhanced.otherMails = enhanced.otherMails?.filter((x) => !x.endsWith('@discord'));
25+
}
26+
return enhanced;
1327
};
1428

1529
const setProfile = async (data: UserProfileData) => {
30+
const newOtherEmails = [data.mail || data.userPrincipalName];
31+
if (data.discordUsername && data.discordUsername !== '') {
32+
newOtherEmails.push(`${data.discordUsername}@discord`);
33+
}
34+
data.otherMails = newOtherEmails;
35+
delete data.discordUsername;
1636
return (await api.patch('/v1.0/me', data)).data;
1737
};
1838

1939
return (
20-
<AuthGuard resourceDef={{ service: 'msGraphApi', validRoles: [] }} showSidebar={true}>
21-
<Title>Edit Profile</Title>
22-
<ManageProfileComponent getProfile={getProfile} setProfile={setProfile} />
40+
<AuthGuard resourceDef={{ service: 'core', validRoles: [] }} showSidebar={true}>
41+
<Container fluid>
42+
<Title>Edit Profile</Title>
43+
<ManageProfileComponent
44+
getProfile={getProfile}
45+
setProfile={setProfile}
46+
firstTime={firstTime}
47+
returnTo={returnTo}
48+
/>
49+
</Container>
2350
</AuthGuard>
2451
);
2552
};

0 commit comments

Comments
 (0)