feat: Refresh token on load and user information added

This commit is contained in:
مهرزاد قدرتی
2025-08-17 13:00:25 +03:30
parent dd6e2fdd0d
commit 3410c52656
13 changed files with 137 additions and 47 deletions

View File

@@ -1,8 +1,31 @@
// useAuth.tsx
import { AuthContext } from '@/contexts/AuthContext';
import { AuthContext, type UserInfo } from '@/contexts/AuthContext';
import type { GenerateTokenResponse } from '@/features/authorization/api/identityAPI';
import axios from 'axios';
import { useEffect, useState, type ReactNode } from 'react';
import { jwtDecode } from 'jwt-decode';
import type { GUID } from '@/types/commonTypes';
export interface AccessTokenJwtPayload {
iss: string;
nbf: number;
iat: number;
exp: number;
scope: string[];
amr: string[];
client_id: string;
sub: GUID;
auth_time: number;
idp: string;
picture: string;
given_name: string;
family_name: string;
email: string;
name: string;
phone_number: string;
session: GUID;
jti: string;
}
export const ACCESS_TOKEN_KEY: 'access_token' = 'access_token' as const;
export const REFRESH_TOKEN_KEY: 'refresh_token' = 'refresh_token' as const;
@@ -12,20 +35,31 @@ let inMemoryToken: string | null = null;
let expiresAt = 0;
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
const [authFinished, setAuthFinished] = useState<boolean>(false);
const [accessToken, setAccessToken] = useState<string | null>(
sessionStorage.getItem(ACCESS_TOKEN_KEY),
);
// Initialize from sessionStorage (page reload)
useEffect(() => {
const token = sessionStorage.getItem(ACCESS_TOKEN_KEY);
const exp = Number(sessionStorage.getItem(EXPIRES_IN_KEY) || 0);
const handleAndSetToken = async () => {
const token = sessionStorage.getItem(ACCESS_TOKEN_KEY);
if (token && Date.now() < exp) {
inMemoryToken = token;
expiresAt = exp;
setAccessToken(token);
}
if (token) {
setUserInfoFromToken(token);
await refreshAccessToken();
inMemoryToken = token;
expiresAt = Number(sessionStorage.getItem(EXPIRES_IN_KEY) || 0);
setAccessToken(token);
setAuthFinished(true);
}
setAuthFinished(true);
};
handleAndSetToken();
}, []);
// Background refresh
@@ -38,8 +72,6 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
setAccessToken(inMemoryToken);
};
handleRefreshTokenIfNeeded();
const interval = setInterval(async () => {
await handleRefreshTokenIfNeeded();
}, 30_000);
@@ -100,6 +132,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
expiresAt = Date.now() + result.data.expires_in * 1000;
sessionStorage.setItem(ACCESS_TOKEN_KEY, inMemoryToken as string);
sessionStorage.setItem(REFRESH_TOKEN_KEY, result.data.refresh_token);
sessionStorage.setItem(EXPIRES_IN_KEY, String(expiresAt));
setAccessToken(inMemoryToken);
@@ -111,8 +144,26 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
}
}
function setUserInfoFromToken(token: string) {
const accessTokenPayload = jwtDecode<AccessTokenJwtPayload>(token);
const userInfo: UserInfo = {
picture: accessTokenPayload.picture,
firstName: accessTokenPayload.given_name,
lastName: accessTokenPayload.family_name,
fullName: accessTokenPayload.name,
email: accessTokenPayload.email,
phoneNumber: accessTokenPayload.phone_number,
userID: accessTokenPayload.sub,
};
setUserInfo(userInfo);
}
return (
<AuthContext.Provider value={{ accessToken, getToken, login, logout }}>
<AuthContext.Provider
value={{ accessToken, getToken, login, logout, authFinished, userInfo }}
>
{children}
</AuthContext.Provider>
);