@sanvika/auth 2.8.0 → 2.9.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.
Files changed (2) hide show
  1. package/dist/index.js +184 -23
  2. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -17,10 +17,9 @@ var STORAGE_KEYS = {
17
17
  };
18
18
  var DEFAULT_AVATAR_SVG = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Ccircle cx='20' cy='20' r='20' fill='%23e5e7eb'/%3E%3Ccircle cx='20' cy='15' r='7' fill='%23adb5bd'/%3E%3Cellipse cx='20' cy='35' rx='12' ry='8' fill='%23adb5bd'/%3E%3C/svg%3E`;
19
19
 
20
- // SanvikaAuthProvider.jsx
21
- import { jsx } from "react/jsx-runtime";
22
- var S_AUTH_URL = "https://auth.sanvikaproduction.com";
23
- var SanvikaAuthContext = createContext(null);
20
+ // authFlow.js
21
+ var DEFAULT_AUTH_URL = "https://auth.sanvikaproduction.com";
22
+ var DEVICE_ID_STORAGE_KEY = "sanvika_deviceId";
24
23
  function randomDeviceId() {
25
24
  try {
26
25
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -30,6 +29,121 @@ function randomDeviceId() {
30
29
  }
31
30
  return `device-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
32
31
  }
32
+ function getOrCreateWebDeviceId() {
33
+ var _a, _b;
34
+ if (typeof window === "undefined") return randomDeviceId();
35
+ let deviceId = localStorage.getItem(DEVICE_ID_STORAGE_KEY);
36
+ if (!deviceId) {
37
+ const raw = `${navigator.userAgent}|${((_a = window.screen) == null ? void 0 : _a.width) ?? 0}x${((_b = window.screen) == null ? void 0 : _b.height) ?? 0}|${navigator.language}`;
38
+ let hash = 0;
39
+ for (let i = 0; i < raw.length; i++) {
40
+ hash = Math.imul(31, hash) + raw.charCodeAt(i) | 0;
41
+ }
42
+ deviceId = `d_${Math.abs(hash).toString(36)}_${Date.now().toString(36)}`;
43
+ localStorage.setItem(DEVICE_ID_STORAGE_KEY, deviceId);
44
+ }
45
+ return deviceId;
46
+ }
47
+ async function checkMobile({
48
+ authBaseUrl = DEFAULT_AUTH_URL,
49
+ mobile,
50
+ deviceId,
51
+ clientId,
52
+ callbackUrl
53
+ }) {
54
+ const res = await fetch(`${authBaseUrl}/api/auth/check-mobile`, {
55
+ method: "POST",
56
+ headers: { "Content-Type": "application/json" },
57
+ body: JSON.stringify({
58
+ mobile: String(mobile).trim(),
59
+ deviceId,
60
+ clientId,
61
+ ...callbackUrl ? { callbackUrl } : {}
62
+ })
63
+ });
64
+ const data = await res.json();
65
+ if (!data.success) {
66
+ const err = new Error(data.message || data.error || "Check mobile failed");
67
+ err.code = data.error;
68
+ throw err;
69
+ }
70
+ return data;
71
+ }
72
+ async function postLogin({
73
+ authBaseUrl = DEFAULT_AUTH_URL,
74
+ mobile,
75
+ password,
76
+ deviceId,
77
+ userAgent,
78
+ clientId,
79
+ deviceName
80
+ }) {
81
+ const body = {
82
+ mobile: String(mobile).trim(),
83
+ deviceId,
84
+ clientId,
85
+ userAgent: userAgent || deviceName || "Sanvika App"
86
+ };
87
+ if (password) {
88
+ body.password = password;
89
+ }
90
+ const res = await fetch(`${authBaseUrl}/api/auth/login`, {
91
+ method: "POST",
92
+ headers: { "Content-Type": "application/json" },
93
+ body: JSON.stringify(body)
94
+ });
95
+ const data = await res.json();
96
+ if (!data.success) {
97
+ const err = new Error(data.error || data.message || "Login failed");
98
+ err.code = data.error;
99
+ throw err;
100
+ }
101
+ return data;
102
+ }
103
+ async function deviceAwareLogin({
104
+ authBaseUrl = DEFAULT_AUTH_URL,
105
+ mobile,
106
+ password,
107
+ deviceId,
108
+ userAgent,
109
+ deviceName,
110
+ clientId,
111
+ callbackUrl,
112
+ resolveDeviceId
113
+ }) {
114
+ const resolvedDeviceId = deviceId || (typeof resolveDeviceId === "function" ? await resolveDeviceId() : getOrCreateWebDeviceId());
115
+ const check = await checkMobile({
116
+ authBaseUrl,
117
+ mobile,
118
+ deviceId: resolvedDeviceId,
119
+ clientId,
120
+ callbackUrl
121
+ });
122
+ if (!check.exists) {
123
+ const err = new Error("Mobile number not registered.");
124
+ err.code = "USER_NOT_REGISTERED";
125
+ throw err;
126
+ }
127
+ if (!check.isKnownDevice && !password) {
128
+ const err = new Error("Password is required for this device.");
129
+ err.code = "MISSING_PASSWORD";
130
+ err.requiresPassword = true;
131
+ throw err;
132
+ }
133
+ return postLogin({
134
+ authBaseUrl,
135
+ mobile,
136
+ password: check.isKnownDevice ? void 0 : password,
137
+ deviceId: resolvedDeviceId,
138
+ userAgent,
139
+ deviceName,
140
+ clientId
141
+ });
142
+ }
143
+
144
+ // SanvikaAuthProvider.jsx
145
+ import { jsx } from "react/jsx-runtime";
146
+ var SanvikaAuthContext = createContext(null);
33
147
  function createDefaultWebPersistence() {
34
148
  return {
35
149
  getItem: async (key) => typeof localStorage !== "undefined" ? localStorage.getItem(key) : null,
@@ -50,6 +164,7 @@ function SanvikaAuthProvider({
50
164
  clientId,
51
165
  redirectUri,
52
166
  dashboardPath,
167
+ authBaseUrl = DEFAULT_AUTH_URL,
53
168
  persistence: persistenceProp
54
169
  }) {
55
170
  const persistence = useMemo(() => {
@@ -83,25 +198,62 @@ function SanvikaAuthProvider({
83
198
  cancelled = true;
84
199
  };
85
200
  }, [persistence]);
86
- const login = async ({ mobile, password, deviceId, deviceName }) => {
87
- const response = await fetch(`${S_AUTH_URL}/api/auth/login`, {
88
- method: "POST",
89
- headers: { "Content-Type": "application/json" },
90
- body: JSON.stringify({
201
+ const persistSession = useCallback(
202
+ async (token, userData) => {
203
+ await persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
204
+ await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
205
+ setToken(token);
206
+ setUser(userData);
207
+ },
208
+ [persistence]
209
+ );
210
+ const checkMobile2 = useCallback(
211
+ async ({ mobile, deviceId }) => {
212
+ const resolvedDeviceId = deviceId || getOrCreateWebDeviceId();
213
+ return checkMobile({
214
+ authBaseUrl,
215
+ mobile,
216
+ deviceId: resolvedDeviceId,
217
+ clientId,
218
+ callbackUrl: redirectUri
219
+ });
220
+ },
221
+ [authBaseUrl, clientId, redirectUri]
222
+ );
223
+ const login = async ({
224
+ mobile,
225
+ password,
226
+ deviceId,
227
+ deviceName,
228
+ userAgent,
229
+ skipDeviceCheck = false
230
+ }) => {
231
+ const resolveDeviceId = async () => deviceId || getOrCreateWebDeviceId();
232
+ let data;
233
+ if (skipDeviceCheck) {
234
+ data = await postLogin({
235
+ authBaseUrl,
91
236
  mobile,
92
237
  password,
93
238
  deviceId: deviceId || randomDeviceId(),
94
- deviceName: deviceName || "Browser"
95
- })
96
- });
97
- const data = await response.json();
98
- if (!data.success) {
99
- throw new Error(data.error || data.message || "Login failed");
239
+ userAgent: userAgent || deviceName,
240
+ clientId,
241
+ deviceName
242
+ });
243
+ } else {
244
+ data = await deviceAwareLogin({
245
+ authBaseUrl,
246
+ mobile,
247
+ password,
248
+ deviceId,
249
+ userAgent: userAgent || (typeof navigator !== "undefined" ? navigator.userAgent : void 0),
250
+ deviceName,
251
+ clientId,
252
+ callbackUrl: redirectUri,
253
+ resolveDeviceId
254
+ });
100
255
  }
101
- await persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, data.accessToken);
102
- await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
103
- setToken(data.accessToken);
104
- setUser(data.user);
256
+ await persistSession(data.accessToken, data.user);
105
257
  return data;
106
258
  };
107
259
  const setAuth = useCallback(
@@ -115,7 +267,7 @@ function SanvikaAuthProvider({
115
267
  );
116
268
  const logout = async () => {
117
269
  try {
118
- await fetch(`${S_AUTH_URL}/api/auth/logout`, {
270
+ await fetch(`${authBaseUrl}/api/auth/logout`, {
119
271
  method: "POST",
120
272
  headers: {
121
273
  "Content-Type": "application/json",
@@ -153,12 +305,14 @@ function SanvikaAuthProvider({
153
305
  isAuthenticated: !!user,
154
306
  isLoggedIn: !!user,
155
307
  login,
308
+ checkMobile: checkMobile2,
156
309
  logout,
157
310
  setAuth,
158
311
  authFetch,
159
312
  clientId,
160
313
  redirectUri,
161
- dashboardPath
314
+ dashboardPath,
315
+ authBaseUrl
162
316
  };
163
317
  return /* @__PURE__ */ jsx(SanvikaAuthContext.Provider, { value, children });
164
318
  }
@@ -196,7 +350,7 @@ styleInject("@keyframes snvk-shimmer {\n 0% {\n background-position: 200% 0;
196
350
 
197
351
  // SanvikaAccountButton.jsx
198
352
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
199
- var S_AUTH_URL2 = "https://auth.sanvikaproduction.com";
353
+ var S_AUTH_URL = "https://auth.sanvikaproduction.com";
200
354
  var SanvikaAccountButtonErrorBoundary = class extends Component {
201
355
  constructor(props) {
202
356
  super(props);
@@ -329,7 +483,7 @@ function SanvikaAccountButtonContent({
329
483
  if (!isAuthenticated || loading) {
330
484
  const { clientId } = auth;
331
485
  const redirectUri = auth.redirectUri || (typeof window !== "undefined" && window.location ? window.location.origin + "/auth/callback" : "");
332
- const authorizeUrl = clientId && redirectUri ? `${S_AUTH_URL2}/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}` : `${S_AUTH_URL2}/authorize`;
486
+ const authorizeUrl = clientId && redirectUri ? `${S_AUTH_URL}/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}` : `${S_AUTH_URL}/authorize`;
333
487
  return /* @__PURE__ */ jsxs(
334
488
  "button",
335
489
  {
@@ -687,11 +841,18 @@ function SanvikaAdminLogin({
687
841
  ] }) });
688
842
  }
689
843
  export {
844
+ DEFAULT_AUTH_URL,
690
845
  DEFAULT_AVATAR_SVG,
846
+ DEVICE_ID_STORAGE_KEY,
691
847
  STORAGE_KEYS,
692
848
  SanvikaAccountButton,
693
849
  SanvikaAdminLogin,
694
850
  SanvikaAuthContext,
695
851
  SanvikaAuthProvider,
852
+ checkMobile,
853
+ deviceAwareLogin,
854
+ getOrCreateWebDeviceId,
855
+ postLogin,
856
+ randomDeviceId,
696
857
  useSanvikaAuth
697
858
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanvika/auth",
3
- "version": "2.8.0",
3
+ "version": "2.9.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",
@@ -12,6 +12,10 @@
12
12
  "files": [
13
13
  "dist"
14
14
  ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "prepublishOnly": "npm run build"
18
+ },
15
19
  "publishConfig": {
16
20
  "access": "public"
17
21
  },
@@ -35,8 +39,5 @@
35
39
  "nextjs"
36
40
  ],
37
41
  "author": "sanvika",
38
- "license": "MIT",
39
- "scripts": {
40
- "build": "tsup"
41
- }
42
- }
42
+ "license": "MIT"
43
+ }