@sanvika/auth 2.5.8 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,14 @@
1
1
  "use client";
2
2
 
3
3
  // SanvikaAuthProvider.jsx
4
- import { createContext, useContext, useEffect, useState, useCallback } from "react";
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useEffect,
8
+ useState,
9
+ useCallback,
10
+ useMemo
11
+ } from "react";
5
12
 
6
13
  // constants.js
7
14
  var STORAGE_KEYS = {
@@ -14,29 +21,68 @@ var DEFAULT_AVATAR_SVG = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/200
14
21
  import { jsx } from "react/jsx-runtime";
15
22
  var S_AUTH_URL = "https://accounts.sanvikaproduction.com";
16
23
  var SanvikaAuthContext = createContext(null);
24
+ function randomDeviceId() {
25
+ try {
26
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
27
+ return crypto.randomUUID();
28
+ }
29
+ } catch {
30
+ }
31
+ return `device-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
32
+ }
33
+ function createDefaultWebPersistence() {
34
+ return {
35
+ getItem: async (key) => typeof localStorage !== "undefined" ? localStorage.getItem(key) : null,
36
+ setItem: async (key, value) => {
37
+ if (typeof localStorage !== "undefined") {
38
+ localStorage.setItem(key, value);
39
+ }
40
+ },
41
+ removeItem: async (key) => {
42
+ if (typeof localStorage !== "undefined") {
43
+ localStorage.removeItem(key);
44
+ }
45
+ }
46
+ };
47
+ }
17
48
  function SanvikaAuthProvider({
18
49
  children,
19
50
  clientId,
20
51
  redirectUri,
21
- dashboardPath
52
+ dashboardPath,
53
+ persistence: persistenceProp
22
54
  }) {
55
+ const persistence = useMemo(() => {
56
+ return persistenceProp || createDefaultWebPersistence();
57
+ }, [persistenceProp]);
23
58
  const [user, setUser] = useState(null);
24
59
  const [accessToken, setToken] = useState(null);
25
60
  const [loading, setLoading] = useState(true);
26
61
  useEffect(() => {
27
- try {
28
- const storedToken = localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
29
- const storedUser = localStorage.getItem(STORAGE_KEYS.USER);
30
- if (storedToken && storedUser) {
31
- setToken(storedToken);
32
- setUser(JSON.parse(storedUser));
62
+ let cancelled = false;
63
+ (async () => {
64
+ try {
65
+ const storedToken = await persistence.getItem(STORAGE_KEYS.ACCESS_TOKEN);
66
+ const storedUser = await persistence.getItem(STORAGE_KEYS.USER);
67
+ if (cancelled) {
68
+ return;
69
+ }
70
+ if (storedToken && storedUser) {
71
+ setToken(storedToken);
72
+ setUser(JSON.parse(storedUser));
73
+ }
74
+ } catch (e) {
75
+ console.error("[SanvikaAuth] Failed to load session:", e);
76
+ } finally {
77
+ if (!cancelled) {
78
+ setLoading(false);
79
+ }
33
80
  }
34
- } catch (e) {
35
- console.error("[SanvikaAuth] Failed to load from localStorage:", e);
36
- } finally {
37
- setLoading(false);
38
- }
39
- }, []);
81
+ })();
82
+ return () => {
83
+ cancelled = true;
84
+ };
85
+ }, [persistence]);
40
86
  const login = async ({ mobile, password, deviceId, deviceName }) => {
41
87
  const response = await fetch(`${S_AUTH_URL}/api/auth/login`, {
42
88
  method: "POST",
@@ -44,53 +90,61 @@ function SanvikaAuthProvider({
44
90
  body: JSON.stringify({
45
91
  mobile,
46
92
  password,
47
- deviceId: deviceId || crypto.randomUUID(),
93
+ deviceId: deviceId || randomDeviceId(),
48
94
  deviceName: deviceName || "Browser"
49
95
  })
50
96
  });
51
97
  const data = await response.json();
52
- if (!data.success) throw new Error(data.error);
53
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
54
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
98
+ if (!data.success) {
99
+ throw new Error(data.error || data.message || "Login failed");
100
+ }
101
+ await persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
102
+ await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
55
103
  setToken(data.accessToken);
56
104
  setUser(data.user);
57
105
  return data;
58
106
  };
59
- const setAuth = (token, userData) => {
60
- localStorage.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
61
- localStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
62
- setToken(token);
63
- setUser(userData);
64
- };
107
+ const setAuth = useCallback(
108
+ (token, userData) => {
109
+ setToken(token);
110
+ setUser(userData);
111
+ void persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, token).catch((e) => console.error("[SanvikaAuth] setAuth token persist:", e));
112
+ void persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(userData)).catch((e) => console.error("[SanvikaAuth] setAuth user persist:", e));
113
+ },
114
+ [persistence]
115
+ );
65
116
  const logout = async () => {
66
117
  try {
67
118
  await fetch(`${S_AUTH_URL}/api/auth/logout`, {
68
119
  method: "POST",
69
120
  headers: {
70
121
  "Content-Type": "application/json",
71
- Authorization: `Bearer ${accessToken}`
122
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
72
123
  }
73
124
  });
74
125
  } catch (e) {
75
126
  console.error("[SanvikaAuth] Logout API error:", e);
76
127
  } finally {
77
- localStorage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
78
- localStorage.removeItem(STORAGE_KEYS.USER);
128
+ await persistence.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
129
+ await persistence.removeItem(STORAGE_KEYS.USER);
79
130
  setToken(null);
80
131
  setUser(null);
81
132
  }
82
133
  };
83
- const authFetch = useCallback(async (url, opts = {}) => {
84
- const currentToken = typeof window !== "undefined" ? localStorage.getItem(STORAGE_KEYS.ACCESS_TOKEN) : null;
85
- const headers = {
86
- "Content-Type": "application/json",
87
- ...currentToken && { Authorization: `Bearer ${currentToken}` },
88
- ...opts.headers
89
- };
90
- const res = await fetch(url, { ...opts, headers });
91
- const data = await res.json();
92
- return { data, success: data.success };
93
- }, []);
134
+ const authFetch = useCallback(
135
+ async (url, opts = {}) => {
136
+ const currentToken = await persistence.getItem(STORAGE_KEYS.ACCESS_TOKEN);
137
+ const headers = {
138
+ "Content-Type": "application/json",
139
+ ...currentToken && { Authorization: `Bearer ${currentToken}` },
140
+ ...opts.headers
141
+ };
142
+ const res = await fetch(url, { ...opts, headers });
143
+ const data = await res.json();
144
+ return { data, success: data.success };
145
+ },
146
+ [persistence]
147
+ );
94
148
  const value = {
95
149
  user,
96
150
  accessToken,
@@ -109,8 +163,7 @@ function SanvikaAuthProvider({
109
163
  return /* @__PURE__ */ jsx(SanvikaAuthContext.Provider, { value, children });
110
164
  }
111
165
  function useSanvikaAuth() {
112
- const ctx = useContext(SanvikaAuthContext);
113
- return ctx;
166
+ return useContext(SanvikaAuthContext);
114
167
  }
115
168
 
116
169
  // SanvikaAccountButton.jsx
package/dist/server.js CHANGED
@@ -302,6 +302,17 @@ async function authenticateAdmin(request) {
302
302
  return _buildUnauthorized("Invalid or expired token.", "INVALID_TOKEN");
303
303
  }
304
304
  const uid = saPayload.sub;
305
+ const jwtRole = saPayload.role;
306
+ if (jwtRole === "superadmin" && uid) {
307
+ const adminData2 = {
308
+ uid,
309
+ adminId: null,
310
+ role: "superadmin",
311
+ mobile: saPayload.mobile || null
312
+ };
313
+ _adminCache.set(cacheKey, { adminData: adminData2, expires: Date.now() + _ADMIN_CACHE_TTL });
314
+ return { success: true, admin: adminData2 };
315
+ }
305
316
  const admin = await verifyAdminFromAuth(uid);
306
317
  if (!admin) {
307
318
  return _buildUnauthorized("Unauthorized or admin account not found.", "ADMIN_NOT_FOUND");
@@ -309,13 +320,14 @@ async function authenticateAdmin(request) {
309
320
  if (admin.isBlocked) {
310
321
  return _buildForbidden("Admin account is blocked.", "ADMIN_BLOCKED");
311
322
  }
312
- if (!["admin", "superadmin", "moderator"].includes(admin.role)) {
323
+ const effectiveRole = admin.role || (jwtRole && ["admin", "superadmin", "moderator"].includes(jwtRole) ? jwtRole : null);
324
+ if (!effectiveRole || !["admin", "superadmin", "moderator"].includes(effectiveRole)) {
313
325
  return _buildForbidden("Invalid admin role.", "INVALID_ADMIN_ROLE");
314
326
  }
315
327
  const adminData = {
316
328
  uid: admin.uid,
317
329
  adminId: admin.adminId,
318
- role: admin.role,
330
+ role: effectiveRole,
319
331
  mobile: admin.mobile
320
332
  };
321
333
  _adminCache.set(cacheKey, { adminData, expires: Date.now() + _ADMIN_CACHE_TTL });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanvika/auth",
3
- "version": "2.5.8",
3
+ "version": "2.6.0",
4
4
  "description": "Sanvika Auth SDK — React components/hooks + server-side token verification and user proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,11 @@
23
23
  "next": ">=16.2.1",
24
24
  "react": ">=18"
25
25
  },
26
+ "peerDependenciesMeta": {
27
+ "next": {
28
+ "optional": true
29
+ }
30
+ },
26
31
  "devDependencies": {
27
32
  "tsup": "^8.5.1"
28
33
  },