source: src/lib/auth.ts@ 50f2fb4

Last change on this file since 50f2fb4 was 298f9b3, checked in by Stefan-Saveski <stefansaveski19@…>, 8 months ago

feat: Implement professor portal with student management, grade handling, and profile display

  • Property mode set to 100644
File size: 3.5 KB
RevLine 
[b67f274]1export type AuthTokens = {
2 accessToken: string;
3 refreshToken: string;
4};
5
[298f9b3]6export type UserRole = 'Professor' | 'Student' | string;
7
8export type AuthSession = AuthTokens & {
9 role: UserRole;
10};
11
[b67f274]12type StoredToken = {
13 value: string;
14 expiresAt: number; // epoch ms
15};
16
17const STORAGE_KEYS = {
18 access: 'iknow.auth.access',
19 refresh: 'iknow.auth.refresh',
[298f9b3]20 role: 'iknow.auth.role',
[b67f274]21} as const;
22
23const ACCESS_TOKEN_TTL_MS = 60 * 60 * 1000; // 1 hour
24const REFRESH_TOKEN_TTL_MS = 30 * 24 * 60 * 60 * 1000; // ~1 month (30 days)
25
26function isBrowser() {
27 return typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';
28}
29
30function readStoredToken(key: string): StoredToken | null {
31 if (!isBrowser()) return null;
32 const raw = window.localStorage.getItem(key);
33 if (!raw) return null;
34
35 try {
36 const parsed = JSON.parse(raw) as StoredToken;
37 if (!parsed?.value || typeof parsed.expiresAt !== 'number') {
38 window.localStorage.removeItem(key);
39 return null;
40 }
41
42 if (Date.now() >= parsed.expiresAt) {
43 window.localStorage.removeItem(key);
44 return null;
45 }
46
47 return parsed;
48 } catch {
49 window.localStorage.removeItem(key);
50 return null;
51 }
52}
53
54function writeStoredToken(key: string, value: string, ttlMs: number) {
55 if (!isBrowser()) return;
56 const payload: StoredToken = {
57 value,
58 expiresAt: Date.now() + ttlMs,
59 };
60 window.localStorage.setItem(key, JSON.stringify(payload));
61}
62
63export function setAuthTokens(tokens: AuthTokens) {
64 writeStoredToken(STORAGE_KEYS.access, tokens.accessToken, ACCESS_TOKEN_TTL_MS);
65 writeStoredToken(STORAGE_KEYS.refresh, tokens.refreshToken, REFRESH_TOKEN_TTL_MS);
66}
67
[298f9b3]68export function setUserRole(role: UserRole) {
69 // Keep role TTL aligned with access token TTL.
70 writeStoredToken(STORAGE_KEYS.role, role, ACCESS_TOKEN_TTL_MS);
71}
72
[b67f274]73export function getAccessToken(): string | null {
74 return readStoredToken(STORAGE_KEYS.access)?.value ?? null;
75}
76
77export function getRefreshToken(): string | null {
78 return readStoredToken(STORAGE_KEYS.refresh)?.value ?? null;
79}
80
[298f9b3]81export function getUserRole(): UserRole | null {
82 return readStoredToken(STORAGE_KEYS.role)?.value ?? null;
83}
84
[b67f274]85export function clearAuthTokens() {
86 if (!isBrowser()) return;
87 window.localStorage.removeItem(STORAGE_KEYS.access);
88 window.localStorage.removeItem(STORAGE_KEYS.refresh);
[298f9b3]89 window.localStorage.removeItem(STORAGE_KEYS.role);
[b67f274]90}
91
[298f9b3]92export async function login(params: { email: string; password: string }): Promise<AuthSession> {
[90f7842]93 const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? 'https://iknow-api.onrender.com';
[b67f274]94
95 const response = await fetch(`${baseUrl}/api/auth/login`, {
96 method: 'POST',
97 headers: {
98 'Content-Type': 'application/json',
99 },
100 body: JSON.stringify({
101 email: params.email,
102 password: params.password,
103 GenerateRefreshToken: true,
104 }),
105 });
106
107 if (!response.ok) {
108 let message = `Login failed (${response.status})`;
109 try {
110 const text = await response.text();
111 if (text) message = text;
112 } catch {
113 // ignore
114 }
115 throw new Error(message);
116 }
117
118 const data = (await response.json()) as {
[298f9b3]119 token?: string;
120 refreshToken?: string;
121 role?: string;
[b67f274]122 };
123
[298f9b3]124 const accessToken = data?.token;
125 const refreshToken = data?.refreshToken;
126 const role = data?.role;
[b67f274]127
128 if (!accessToken || !refreshToken) {
129 throw new Error('Login response did not contain tokens.');
130 }
131
[298f9b3]132 const session: AuthSession = {
133 accessToken,
134 refreshToken,
135 role: role ?? 'Student',
136 };
137
138 setAuthTokens(session);
139 setUserRole(session.role);
140 return session;
[b67f274]141}
Note: See TracBrowser for help on using the repository browser.