@sanvika/auth 2.8.0 → 2.9.1
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 +188 -24
- 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
|
-
//
|
|
21
|
-
|
|
22
|
-
var
|
|
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,
|
|
@@ -49,9 +163,13 @@ function SanvikaAuthProvider({
|
|
|
49
163
|
children,
|
|
50
164
|
clientId,
|
|
51
165
|
redirectUri,
|
|
166
|
+
/** RN embedded LoginScreen: skip callbackUrl on check-mobile/login APIs (OAuth URI still used for /authorize links). */
|
|
167
|
+
embeddedLogin = false,
|
|
52
168
|
dashboardPath,
|
|
169
|
+
authBaseUrl = DEFAULT_AUTH_URL,
|
|
53
170
|
persistence: persistenceProp
|
|
54
171
|
}) {
|
|
172
|
+
const apiCallbackUrl = embeddedLogin ? void 0 : redirectUri;
|
|
55
173
|
const persistence = useMemo(() => {
|
|
56
174
|
return persistenceProp || createDefaultWebPersistence();
|
|
57
175
|
}, [persistenceProp]);
|
|
@@ -83,25 +201,62 @@ function SanvikaAuthProvider({
|
|
|
83
201
|
cancelled = true;
|
|
84
202
|
};
|
|
85
203
|
}, [persistence]);
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
204
|
+
const persistSession = useCallback(
|
|
205
|
+
async (token, userData) => {
|
|
206
|
+
await persistence.setItem(STORAGE_KEYS.ACCESS_TOKEN, token);
|
|
207
|
+
await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(userData));
|
|
208
|
+
setToken(token);
|
|
209
|
+
setUser(userData);
|
|
210
|
+
},
|
|
211
|
+
[persistence]
|
|
212
|
+
);
|
|
213
|
+
const checkMobile2 = useCallback(
|
|
214
|
+
async ({ mobile, deviceId }) => {
|
|
215
|
+
const resolvedDeviceId = deviceId || getOrCreateWebDeviceId();
|
|
216
|
+
return checkMobile({
|
|
217
|
+
authBaseUrl,
|
|
218
|
+
mobile,
|
|
219
|
+
deviceId: resolvedDeviceId,
|
|
220
|
+
clientId,
|
|
221
|
+
callbackUrl: apiCallbackUrl
|
|
222
|
+
});
|
|
223
|
+
},
|
|
224
|
+
[authBaseUrl, clientId, apiCallbackUrl]
|
|
225
|
+
);
|
|
226
|
+
const login = async ({
|
|
227
|
+
mobile,
|
|
228
|
+
password,
|
|
229
|
+
deviceId,
|
|
230
|
+
deviceName,
|
|
231
|
+
userAgent,
|
|
232
|
+
skipDeviceCheck = false
|
|
233
|
+
}) => {
|
|
234
|
+
const resolveDeviceId = async () => deviceId || getOrCreateWebDeviceId();
|
|
235
|
+
let data;
|
|
236
|
+
if (skipDeviceCheck) {
|
|
237
|
+
data = await postLogin({
|
|
238
|
+
authBaseUrl,
|
|
91
239
|
mobile,
|
|
92
240
|
password,
|
|
93
241
|
deviceId: deviceId || randomDeviceId(),
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
242
|
+
userAgent: userAgent || deviceName,
|
|
243
|
+
clientId,
|
|
244
|
+
deviceName
|
|
245
|
+
});
|
|
246
|
+
} else {
|
|
247
|
+
data = await deviceAwareLogin({
|
|
248
|
+
authBaseUrl,
|
|
249
|
+
mobile,
|
|
250
|
+
password,
|
|
251
|
+
deviceId,
|
|
252
|
+
userAgent: userAgent || (typeof navigator !== "undefined" ? navigator.userAgent : void 0),
|
|
253
|
+
deviceName,
|
|
254
|
+
clientId,
|
|
255
|
+
callbackUrl: apiCallbackUrl,
|
|
256
|
+
resolveDeviceId
|
|
257
|
+
});
|
|
100
258
|
}
|
|
101
|
-
await
|
|
102
|
-
await persistence.setItem(STORAGE_KEYS.USER, JSON.stringify(data.user));
|
|
103
|
-
setToken(data.accessToken);
|
|
104
|
-
setUser(data.user);
|
|
259
|
+
await persistSession(data.accessToken, data.user);
|
|
105
260
|
return data;
|
|
106
261
|
};
|
|
107
262
|
const setAuth = useCallback(
|
|
@@ -115,7 +270,7 @@ function SanvikaAuthProvider({
|
|
|
115
270
|
);
|
|
116
271
|
const logout = async () => {
|
|
117
272
|
try {
|
|
118
|
-
await fetch(`${
|
|
273
|
+
await fetch(`${authBaseUrl}/api/auth/logout`, {
|
|
119
274
|
method: "POST",
|
|
120
275
|
headers: {
|
|
121
276
|
"Content-Type": "application/json",
|
|
@@ -153,12 +308,14 @@ function SanvikaAuthProvider({
|
|
|
153
308
|
isAuthenticated: !!user,
|
|
154
309
|
isLoggedIn: !!user,
|
|
155
310
|
login,
|
|
311
|
+
checkMobile: checkMobile2,
|
|
156
312
|
logout,
|
|
157
313
|
setAuth,
|
|
158
314
|
authFetch,
|
|
159
315
|
clientId,
|
|
160
316
|
redirectUri,
|
|
161
|
-
dashboardPath
|
|
317
|
+
dashboardPath,
|
|
318
|
+
authBaseUrl
|
|
162
319
|
};
|
|
163
320
|
return /* @__PURE__ */ jsx(SanvikaAuthContext.Provider, { value, children });
|
|
164
321
|
}
|
|
@@ -196,7 +353,7 @@ styleInject("@keyframes snvk-shimmer {\n 0% {\n background-position: 200% 0;
|
|
|
196
353
|
|
|
197
354
|
// SanvikaAccountButton.jsx
|
|
198
355
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
199
|
-
var
|
|
356
|
+
var S_AUTH_URL = "https://auth.sanvikaproduction.com";
|
|
200
357
|
var SanvikaAccountButtonErrorBoundary = class extends Component {
|
|
201
358
|
constructor(props) {
|
|
202
359
|
super(props);
|
|
@@ -329,7 +486,7 @@ function SanvikaAccountButtonContent({
|
|
|
329
486
|
if (!isAuthenticated || loading) {
|
|
330
487
|
const { clientId } = auth;
|
|
331
488
|
const redirectUri = auth.redirectUri || (typeof window !== "undefined" && window.location ? window.location.origin + "/auth/callback" : "");
|
|
332
|
-
const authorizeUrl = clientId && redirectUri ? `${
|
|
489
|
+
const authorizeUrl = clientId && redirectUri ? `${S_AUTH_URL}/authorize?client_id=${encodeURIComponent(clientId)}&redirect_uri=${encodeURIComponent(redirectUri)}` : `${S_AUTH_URL}/authorize`;
|
|
333
490
|
return /* @__PURE__ */ jsxs(
|
|
334
491
|
"button",
|
|
335
492
|
{
|
|
@@ -497,7 +654,7 @@ function friendlyError(code) {
|
|
|
497
654
|
case "INVALID_PASSWORD":
|
|
498
655
|
return "Incorrect password.";
|
|
499
656
|
case "RATE_LIMIT_EXCEEDED":
|
|
500
|
-
return "Too many attempts. Please try again
|
|
657
|
+
return "Too many attempts. Please wait and try again.";
|
|
501
658
|
case "SERVER_ERROR":
|
|
502
659
|
return "Auth service error. Please try again.";
|
|
503
660
|
default:
|
|
@@ -687,11 +844,18 @@ function SanvikaAdminLogin({
|
|
|
687
844
|
] }) });
|
|
688
845
|
}
|
|
689
846
|
export {
|
|
847
|
+
DEFAULT_AUTH_URL,
|
|
690
848
|
DEFAULT_AVATAR_SVG,
|
|
849
|
+
DEVICE_ID_STORAGE_KEY,
|
|
691
850
|
STORAGE_KEYS,
|
|
692
851
|
SanvikaAccountButton,
|
|
693
852
|
SanvikaAdminLogin,
|
|
694
853
|
SanvikaAuthContext,
|
|
695
854
|
SanvikaAuthProvider,
|
|
855
|
+
checkMobile,
|
|
856
|
+
deviceAwareLogin,
|
|
857
|
+
getOrCreateWebDeviceId,
|
|
858
|
+
postLogin,
|
|
859
|
+
randomDeviceId,
|
|
696
860
|
useSanvikaAuth
|
|
697
861
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanvika/auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
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
|
-
|
|
40
|
-
"build": "tsup"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
}
|