Test 2 reoslutions
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
} from '../services/userSyncService';
|
||||
import { AccountActivationModal } from '../components/auth/AccountActivationModal';
|
||||
import { SessionExpiredModal } from '../components/auth/SessionExpiredModal';
|
||||
import { encodeHashIdSync } from '../services/toolsService';
|
||||
import {
|
||||
onSessionExpired,
|
||||
triggerSessionExpired,
|
||||
@@ -30,6 +31,7 @@ export interface AuthUserProfile {
|
||||
role: string;
|
||||
organisationName?: string;
|
||||
tenant?: string;
|
||||
tenant_id?: number;
|
||||
avatarUrl?: string;
|
||||
}
|
||||
|
||||
@@ -39,6 +41,8 @@ interface AuthContextType {
|
||||
user: AuthUserProfile | null;
|
||||
sub: string;
|
||||
claims: UserClaims;
|
||||
tenantId: number | null;
|
||||
hashedTenantId: string | null;
|
||||
accessToken: string | null;
|
||||
isAuthenticated: boolean;
|
||||
isLoading: boolean;
|
||||
@@ -60,6 +64,75 @@ export const LOCAL_STORAGE_USER_KEY = 'pfm_auth_user';
|
||||
export const LOCAL_STORAGE_SUB_KEY = 'pfm_user_sub';
|
||||
export const LOCAL_STORAGE_CLAIMS_KEY = 'pfm_user_claims';
|
||||
|
||||
export function extractTenantId(claims?: UserClaims | null, token?: string | null): number | null {
|
||||
if (claims) {
|
||||
const raw =
|
||||
claims.tenant_id ??
|
||||
(claims as any).tenantId ??
|
||||
claims['tenant_id'] ??
|
||||
claims['http://schemas.microsoft.com/ws/2008/06/identity/claims/tenantid'];
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = Number(raw);
|
||||
if (!isNaN(parsed) && isFinite(parsed)) return parsed;
|
||||
}
|
||||
}
|
||||
if (token) {
|
||||
try {
|
||||
const tokenClaims = parseJwtClaims(token);
|
||||
const raw = tokenClaims.tenant_id ?? tokenClaims.tenantId ?? tokenClaims['tenant_id'];
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = Number(raw);
|
||||
if (!isNaN(parsed) && isFinite(parsed)) return parsed;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
const storedClaimsRaw = localStorage.getItem(LOCAL_STORAGE_CLAIMS_KEY);
|
||||
if (storedClaimsRaw) {
|
||||
const parsedClaims = JSON.parse(storedClaimsRaw);
|
||||
const raw = parsedClaims.tenant_id ?? parsedClaims.tenantId ?? parsedClaims['tenant_id'];
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = Number(raw);
|
||||
if (!isNaN(parsed) && isFinite(parsed)) return parsed;
|
||||
}
|
||||
}
|
||||
const storedToken = localStorage.getItem(LOCAL_STORAGE_TOKEN_KEY);
|
||||
if (storedToken) {
|
||||
const tokenClaims = parseJwtClaims(storedToken);
|
||||
const raw = tokenClaims.tenant_id ?? tokenClaims.tenantId ?? tokenClaims['tenant_id'];
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = Number(raw);
|
||||
if (!isNaN(parsed) && isFinite(parsed)) return parsed;
|
||||
}
|
||||
}
|
||||
const storedUser = localStorage.getItem(LOCAL_STORAGE_USER_KEY);
|
||||
if (storedUser) {
|
||||
const parsedUser = JSON.parse(storedUser);
|
||||
const raw = parsedUser.tenant_id ?? parsedUser.tenantId;
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = Number(raw);
|
||||
if (!isNaN(parsed) && isFinite(parsed)) return parsed;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getCurrentUserTenantId(): number | null {
|
||||
return extractTenantId(
|
||||
getCurrentUserClaims(),
|
||||
typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_TOKEN_KEY) : null
|
||||
);
|
||||
}
|
||||
|
||||
export function getCurrentUserHashedTenantId(): string | null {
|
||||
const tid = getCurrentUserTenantId();
|
||||
if (tid === null || tid === undefined) return null;
|
||||
return encodeHashIdSync(tid);
|
||||
}
|
||||
|
||||
export function getCurrentUserSub(): string {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem(LOCAL_STORAGE_SUB_KEY) || '';
|
||||
@@ -119,6 +192,12 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
const [user, setUser] = useState<AuthUserProfile | null>(null);
|
||||
const [sub, setSub] = useState<string>('');
|
||||
const [claims, setClaims] = useState<UserClaims>({});
|
||||
const [tenantId, setTenantId] = useState<number | null>(() => {
|
||||
return extractTenantId(
|
||||
getCurrentUserClaims(),
|
||||
typeof window !== 'undefined' ? localStorage.getItem(LOCAL_STORAGE_TOKEN_KEY) : null
|
||||
);
|
||||
});
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isSynced, setIsSynced] = useState<boolean>(() => isUserSyncedInBrowser());
|
||||
@@ -128,6 +207,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
const [isSessionExpired, setIsSessionExpired] = useState<boolean>(false);
|
||||
const [sessionExpiredReason, setSessionExpiredReason] = useState<string | null>(null);
|
||||
|
||||
const hashedTenantId = tenantId !== null && tenantId !== undefined ? encodeHashIdSync(tenantId) : null;
|
||||
|
||||
// Subscribe to global HTTP 401 session expiration events
|
||||
useEffect(() => {
|
||||
const unsubscribe = onSessionExpired((reason) => {
|
||||
@@ -203,6 +284,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
localStorage.setItem(LOCAL_STORAGE_CLAIMS_KEY, JSON.stringify(mergedClaims));
|
||||
|
||||
const tenantClaim = (mergedClaims.tenant as string) || (oidcUser.profile?.tenant as string) || undefined;
|
||||
const parsedTenantId = extractTenantId(mergedClaims, token);
|
||||
setTenantId(parsedTenantId);
|
||||
|
||||
const profile: AuthUserProfile = {
|
||||
id: userSub || 'user-1',
|
||||
@@ -212,6 +295,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
role: (oidcUser.profile.role as string) || (mergedClaims.role as string) || (Array.isArray(mergedClaims.roles) ? mergedClaims.roles[0] : 'Programme Sponsor'),
|
||||
organisationName: (mergedClaims.organisation as string) || (mergedClaims.org as string) || undefined,
|
||||
tenant: tenantClaim,
|
||||
tenant_id: parsedTenantId ?? undefined,
|
||||
};
|
||||
|
||||
setUser(profile);
|
||||
@@ -281,6 +365,9 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
setSub(storedSub);
|
||||
}
|
||||
|
||||
const storedTenantId = extractTenantId(parsedClaims, storedToken);
|
||||
setTenantId(storedTenantId);
|
||||
|
||||
let resolvedUser: AuthUserProfile | null = null;
|
||||
if (storedUser) {
|
||||
try {
|
||||
@@ -288,6 +375,9 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
if (!parsedUser.tenant && parsedClaims.tenant) {
|
||||
parsedUser.tenant = parsedClaims.tenant;
|
||||
}
|
||||
if (parsedUser.tenant_id === undefined && storedTenantId !== null) {
|
||||
parsedUser.tenant_id = storedTenantId;
|
||||
}
|
||||
setUser(parsedUser);
|
||||
resolvedUser = parsedUser;
|
||||
} catch {}
|
||||
@@ -402,6 +492,7 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
setUser(null);
|
||||
setSub('');
|
||||
setClaims({});
|
||||
setTenantId(null);
|
||||
setAccessToken(null);
|
||||
setIsSynced(false);
|
||||
setIsPendingActivation(false);
|
||||
@@ -424,6 +515,8 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
user,
|
||||
sub,
|
||||
claims,
|
||||
tenantId,
|
||||
hashedTenantId,
|
||||
accessToken,
|
||||
isAuthenticated: !!accessToken,
|
||||
isLoading,
|
||||
|
||||
Reference in New Issue
Block a user