@kawaiininja/fetch 1.0.1 → 1.0.4
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/hooks/useCsrf.d.ts +1 -1
- package/dist/hooks/useCsrf.js +46 -3
- package/dist/hooks/useFetch.js +55 -5
- package/package.json +46 -40
package/dist/hooks/useCsrf.d.ts
CHANGED
package/dist/hooks/useCsrf.js
CHANGED
|
@@ -1,27 +1,59 @@
|
|
|
1
|
+
import { SecureStoragePlugin } from "capacitor-secure-storage-plugin";
|
|
1
2
|
import { useCallback, useRef } from "react";
|
|
2
3
|
import { INTERNAL_HEADER } from "../context/ApiContext";
|
|
3
4
|
import { useApiConfig } from "./useApiConfig";
|
|
4
5
|
export function useCsrf() {
|
|
5
6
|
const { apiUrl } = useApiConfig();
|
|
6
7
|
const csrfRef = useRef(null);
|
|
7
|
-
const clearCsrf = useCallback(() => {
|
|
8
|
+
const clearCsrf = useCallback(async () => {
|
|
9
|
+
console.log("[useCsrf] Clearing CSRF token...");
|
|
8
10
|
csrfRef.current = null;
|
|
9
11
|
if (typeof window !== "undefined") {
|
|
12
|
+
const platform = window.Capacitor?.getPlatform() || "web";
|
|
13
|
+
const isNative = platform === "ios" || platform === "android";
|
|
14
|
+
if (isNative) {
|
|
15
|
+
try {
|
|
16
|
+
await SecureStoragePlugin.remove({ key: "csrf_token" });
|
|
17
|
+
console.log("[useCsrf] Cleared from SecureStorage.");
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.warn("[useCsrf] Failed to clear from SecureStorage, trying localStorage:", e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
10
23
|
localStorage.removeItem("csrf_token");
|
|
11
24
|
}
|
|
12
25
|
}, []);
|
|
13
26
|
const fetchCSRF = useCallback(async () => {
|
|
14
27
|
if (csrfRef.current)
|
|
15
28
|
return csrfRef.current;
|
|
16
|
-
// Try to get from
|
|
29
|
+
// Try to get from storage first (for Capacitor/mobile)
|
|
17
30
|
if (typeof window !== "undefined") {
|
|
31
|
+
const platform = window.Capacitor?.getPlatform() || "web";
|
|
32
|
+
const isNative = platform === "ios" || platform === "android";
|
|
33
|
+
if (isNative) {
|
|
34
|
+
try {
|
|
35
|
+
const { value } = await SecureStoragePlugin.get({
|
|
36
|
+
key: "csrf_token",
|
|
37
|
+
});
|
|
38
|
+
if (value) {
|
|
39
|
+
console.log("[useCsrf] Retrieved from SecureStorage.");
|
|
40
|
+
csrfRef.current = value;
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
console.warn("[useCsrf] SecureStorage check failed:", e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
18
48
|
const storedCsrf = localStorage.getItem("csrf_token");
|
|
19
49
|
if (storedCsrf) {
|
|
50
|
+
console.log("[useCsrf] Retrieved from localStorage.");
|
|
20
51
|
csrfRef.current = storedCsrf;
|
|
21
52
|
return storedCsrf;
|
|
22
53
|
}
|
|
23
54
|
}
|
|
24
55
|
// Construct CSRF URL using the context's baseUrl
|
|
56
|
+
console.log("[useCsrf] Fetching new CSRF token from server...");
|
|
25
57
|
const csrfUrl = apiUrl("auth/csrf-token");
|
|
26
58
|
const res = await fetch(csrfUrl, {
|
|
27
59
|
method: "GET",
|
|
@@ -33,8 +65,19 @@ export function useCsrf() {
|
|
|
33
65
|
if (!token)
|
|
34
66
|
throw new Error("Missing CSRF token");
|
|
35
67
|
csrfRef.current = token;
|
|
36
|
-
// Store in
|
|
68
|
+
// Store in storage
|
|
37
69
|
if (typeof window !== "undefined") {
|
|
70
|
+
const platform = window.Capacitor?.getPlatform() || "web";
|
|
71
|
+
const isNative = platform === "ios" || platform === "android";
|
|
72
|
+
if (isNative) {
|
|
73
|
+
try {
|
|
74
|
+
await SecureStoragePlugin.set({ key: "csrf_token", value: token });
|
|
75
|
+
console.log("[useCsrf] Stored in SecureStorage.");
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
console.warn("[useCsrf] SecureStorage set failed, falling back to localStorage:", e);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
38
81
|
localStorage.setItem("csrf_token", token);
|
|
39
82
|
}
|
|
40
83
|
return token;
|
package/dist/hooks/useFetch.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SecureStoragePlugin } from "capacitor-secure-storage-plugin";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
3
|
import { INTERNAL_HEADER } from "../context/ApiContext";
|
|
3
4
|
import { useApiConfig } from "./useApiConfig";
|
|
@@ -31,21 +32,70 @@ export const useFetch = (endpoint, baseOptions = {}) => {
|
|
|
31
32
|
}
|
|
32
33
|
}, []);
|
|
33
34
|
const performFetch = async (url, method, token, body, headers, rest) => {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// 🌍 PLATFORM DETECTION
|
|
36
|
+
// We check if we are running natively (iOS/Android) or on the Web
|
|
37
|
+
const platform = typeof window !== "undefined"
|
|
38
|
+
? window.Capacitor?.getPlatform() || "web"
|
|
39
|
+
: "web";
|
|
40
|
+
const isNative = platform === "ios" || platform === "android";
|
|
41
|
+
// 🎯 TARGET CHECK (Prevent Credential Leakage)
|
|
42
|
+
// Only attach sensitive headers if sending to our own API
|
|
43
|
+
const isInternal = url.startsWith("/") ||
|
|
44
|
+
(apiUrl("") && url.startsWith(apiUrl(""))) ||
|
|
45
|
+
false;
|
|
36
46
|
const headersConfig = {
|
|
37
47
|
...(optionsRef.current.headers || {}),
|
|
38
48
|
...(headers || {}),
|
|
39
49
|
...INTERNAL_HEADER,
|
|
40
|
-
"X-CSRF-Token": token,
|
|
41
|
-
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
42
|
-
...(sessionId ? { "X-Session-ID": sessionId } : {}),
|
|
43
50
|
};
|
|
51
|
+
// 🔒 SECURITY STRATEGY: NATIVE (MOBILE)
|
|
52
|
+
if (isNative && isInternal) {
|
|
53
|
+
// Mobile relies on manual headers ("Active Courier")
|
|
54
|
+
// 🛡️ S-RANK UPGRADE: Use Secure Storage (Async) instead of LocalStorage
|
|
55
|
+
console.log(`[useFetch] [Native] Strategy: Active Courier for ${url}`);
|
|
56
|
+
let authToken = null;
|
|
57
|
+
let sessionId = null;
|
|
58
|
+
try {
|
|
59
|
+
const { value: secureAuthToken } = await SecureStoragePlugin.get({
|
|
60
|
+
key: "token",
|
|
61
|
+
});
|
|
62
|
+
const { value: secureSessionId } = await SecureStoragePlugin.get({
|
|
63
|
+
key: "session_id",
|
|
64
|
+
});
|
|
65
|
+
authToken = secureAuthToken;
|
|
66
|
+
sessionId = secureSessionId;
|
|
67
|
+
if (authToken)
|
|
68
|
+
console.log("[useFetch] [Native] Token retrieved from SecureStorage.");
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
console.warn("[useFetch] [Native] SecureStorage failed or not present, trying localStorage:", err);
|
|
72
|
+
authToken = localStorage.getItem("token");
|
|
73
|
+
sessionId = localStorage.getItem("session_id");
|
|
74
|
+
if (authToken)
|
|
75
|
+
console.log("[useFetch] [Native] Token retrieved from localStorage fallback.");
|
|
76
|
+
}
|
|
77
|
+
if (authToken)
|
|
78
|
+
headersConfig["Authorization"] = `Bearer ${authToken}`;
|
|
79
|
+
if (sessionId)
|
|
80
|
+
headersConfig["X-Session-ID"] = sessionId;
|
|
81
|
+
}
|
|
82
|
+
// 🔒 SECURITY STRATEGY: WEB (BROWSER)
|
|
83
|
+
if (!isNative && isInternal) {
|
|
84
|
+
// Web relies on Cookies ("Passive Courier") for Auth
|
|
85
|
+
// BUT we MUST attach the CSRF Token to prevent attacks
|
|
86
|
+
if (token) {
|
|
87
|
+
headersConfig["X-CSRF-Token"] = token;
|
|
88
|
+
// console.log("[useFetch] [Web] CSRF token attached.");
|
|
89
|
+
}
|
|
90
|
+
// On Web, we DO NOT attach 'Authorization' or 'Session-ID' from localStorage
|
|
91
|
+
// This minimizes XSS risk. The HttpOnly cookie handles it.
|
|
92
|
+
}
|
|
44
93
|
return fetch(url, {
|
|
45
94
|
...optionsRef.current,
|
|
46
95
|
...rest,
|
|
47
96
|
method,
|
|
48
97
|
signal: abortRef.current.signal,
|
|
98
|
+
// 🍪 Web: "include" sends cookies. Mobile: ignored/useless but harmless.
|
|
49
99
|
credentials: "include",
|
|
50
100
|
headers: headersConfig,
|
|
51
101
|
body,
|
package/package.json
CHANGED
|
@@ -1,40 +1,46 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@kawaiininja/fetch",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Core fetch utility for Onyx Framework",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"module": "dist/index.js",
|
|
8
|
-
"type": "module",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": "./dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"dist",
|
|
14
|
-
"README.md"
|
|
15
|
-
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"react",
|
|
21
|
-
"fetch",
|
|
22
|
-
"onyx",
|
|
23
|
-
"kawaiininja",
|
|
24
|
-
"http"
|
|
25
|
-
],
|
|
26
|
-
"author": "Vinay (4kawaiininja)",
|
|
27
|
-
"license": "MIT",
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
30
|
-
"react-dom": "^18.0.0 || ^19.0.0"
|
|
31
|
-
},
|
|
32
|
-
"publishConfig": {
|
|
33
|
-
"access": "public"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@types/react": "^19.2.7",
|
|
37
|
-
"@types/react-dom": "^19.2.3",
|
|
38
|
-
"typescript": "^5.7.0"
|
|
39
|
-
}
|
|
40
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@kawaiininja/fetch",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "Core fetch utility for Onyx Framework",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"react",
|
|
21
|
+
"fetch",
|
|
22
|
+
"onyx",
|
|
23
|
+
"kawaiininja",
|
|
24
|
+
"http"
|
|
25
|
+
],
|
|
26
|
+
"author": "Vinay (4kawaiininja)",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
30
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/react": "^19.2.7",
|
|
37
|
+
"@types/react-dom": "^19.2.3",
|
|
38
|
+
"typescript": "^5.7.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@capacitor-community/security-provider": "^7.0.0",
|
|
42
|
+
"@capacitor-community/text-to-speech": "^6.1.0",
|
|
43
|
+
"@capacitor/core": "^8.0.1",
|
|
44
|
+
"capacitor-secure-storage-plugin": "^0.13.0"
|
|
45
|
+
}
|
|
46
|
+
}
|