@entropy-softworks/ui 2026.8.30 → 2026.8.32
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/lib/components/password-generator.d.ts.map +1 -1
- package/lib/components/password-generator.jsx +4 -157
- package/lib/components/password-generator.jsx.map +1 -1
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/index.js +1 -0
- package/lib/hooks/index.js.map +1 -1
- package/lib/hooks/usePasswordGenerator.d.ts +29 -0
- package/lib/hooks/usePasswordGenerator.d.ts.map +1 -0
- package/lib/hooks/usePasswordGenerator.js +187 -0
- package/lib/hooks/usePasswordGenerator.js.map +1 -0
- package/lib/utils/api-fetch.d.ts.map +1 -1
- package/lib/utils/api-fetch.js +19 -1
- package/lib/utils/api-fetch.js.map +1 -1
- package/package.json +1 -1
- package/src/components/password-generator.tsx +18 -161
- package/src/hooks/index.ts +8 -0
- package/src/hooks/usePasswordGenerator.ts +221 -0
- package/src/utils/api-fetch.ts +19 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-generator.d.ts","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"password-generator.d.ts","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,oDAgM5B,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,140 +1,19 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { ActivityIndicator,
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ActivityIndicator, Pressable, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
4
|
import { useColorScheme } from "nativewind";
|
|
5
|
-
import * as Haptics from "expo-haptics";
|
|
6
5
|
import Slider from "@react-native-community/slider";
|
|
7
|
-
import Toast from "react-native-toast-message";
|
|
8
6
|
import { Text } from "./text";
|
|
9
|
-
import {
|
|
10
|
-
import { generatePassword } from "../utils/password";
|
|
11
|
-
import { checkPasswordPwned } from "../services/hibp";
|
|
12
|
-
// A randomly drawn password being in a breach corpus is astronomically
|
|
13
|
-
// unlikely, but if it ever hits, regenerate until clean — capped so a
|
|
14
|
-
// misbehaving API can't loop forever.
|
|
15
|
-
const MAX_BREACH_REGEN = 5;
|
|
16
|
-
const isWeb = Platform.OS === "web";
|
|
7
|
+
import { usePasswordGenerator } from "../hooks/usePasswordGenerator";
|
|
17
8
|
/**
|
|
18
9
|
* UI shell over `utils/password.generatePassword`. Owns length slider,
|
|
19
10
|
* character-set toggles, copy-to-clipboard (via shared util with auto-
|
|
20
11
|
* clear), and a coarse strength indicator.
|
|
21
12
|
*/
|
|
22
13
|
export const PasswordGenerator = React.memo(function PasswordGenerator({ onSelect, hideCopy, }) {
|
|
23
|
-
const
|
|
24
|
-
const [length, setLength] = useState(16);
|
|
25
|
-
const [includeUppercase, setIncludeUppercase] = useState(true);
|
|
26
|
-
const [includeLowercase, setIncludeLowercase] = useState(true);
|
|
27
|
-
const [includeNumbers, setIncludeNumbers] = useState(true);
|
|
28
|
-
const [includeSymbols, setIncludeSymbols] = useState(true);
|
|
29
|
-
const [excludeSimilar, setExcludeSimilar] = useState(false);
|
|
30
|
-
const [breach, setBreach] = useState("idle");
|
|
14
|
+
const { password, length, setLength, includeUppercase, setIncludeUppercase, includeLowercase, setIncludeLowercase, includeNumbers, setIncludeNumbers, includeSymbols, setIncludeSymbols, excludeSimilar, setExcludeSimilar, breach, strength: passwordStrength, handleGenerate, handleCopy, } = usePasswordGenerator();
|
|
31
15
|
const { colorScheme } = useColorScheme();
|
|
32
16
|
const isDark = colorScheme === "dark";
|
|
33
|
-
// Latest options, read by the breach-regen path without re-subscribing the
|
|
34
|
-
// check effect on every option change.
|
|
35
|
-
const optsRef = useRef({
|
|
36
|
-
length,
|
|
37
|
-
includeUppercase,
|
|
38
|
-
includeLowercase,
|
|
39
|
-
includeNumbers,
|
|
40
|
-
includeSymbols,
|
|
41
|
-
excludeSimilar,
|
|
42
|
-
});
|
|
43
|
-
optsRef.current = {
|
|
44
|
-
length,
|
|
45
|
-
includeUppercase,
|
|
46
|
-
includeLowercase,
|
|
47
|
-
includeNumbers,
|
|
48
|
-
includeSymbols,
|
|
49
|
-
excludeSimilar,
|
|
50
|
-
};
|
|
51
|
-
// Ignore stale async HIBP results once a newer password supersedes them.
|
|
52
|
-
const breachTokenRef = useRef(0);
|
|
53
|
-
const breachAttemptRef = useRef(0);
|
|
54
|
-
// 150 ms throttle on Generate. The CSPRNG draw itself is cheap
|
|
55
|
-
// (synchronous bytes), but a held-down Enter key or a stuck
|
|
56
|
-
// accessibility-keyboard repeat could fire many calls per second
|
|
57
|
-
// and the rejection-sampling loop inside `generatePassword` does
|
|
58
|
-
// a small amount of work per call. The throttle also gates the
|
|
59
|
-
// haptic so the user isn't getting buzzed at keypress rate.
|
|
60
|
-
const lastGenerateRef = useRef(0);
|
|
61
|
-
const handleGenerate = useCallback(() => {
|
|
62
|
-
const now = Date.now();
|
|
63
|
-
if (now - lastGenerateRef.current < 150) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
lastGenerateRef.current = now;
|
|
67
|
-
try {
|
|
68
|
-
const next = generatePassword({
|
|
69
|
-
length,
|
|
70
|
-
includeUppercase,
|
|
71
|
-
includeLowercase,
|
|
72
|
-
includeNumbers,
|
|
73
|
-
includeSymbols,
|
|
74
|
-
excludeSimilar,
|
|
75
|
-
});
|
|
76
|
-
setPassword(next);
|
|
77
|
-
if (!isWeb) {
|
|
78
|
-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch (e) {
|
|
82
|
-
Toast.show({
|
|
83
|
-
type: "error",
|
|
84
|
-
text1: e instanceof Error ? e.message : "Please select at least one character type",
|
|
85
|
-
visibilityTime: 3000,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
|
|
89
|
-
const handleCopy = useCallback(async () => {
|
|
90
|
-
if (!password) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const ok = await copyToClipboard(password);
|
|
94
|
-
if (!ok) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
if (!isWeb) {
|
|
98
|
-
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
|
99
|
-
}
|
|
100
|
-
Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
|
|
101
|
-
}, [password]);
|
|
102
|
-
// Generate on mount and when character-class options change.
|
|
103
|
-
useEffect(() => {
|
|
104
|
-
handleGenerate();
|
|
105
|
-
}, [handleGenerate]);
|
|
106
|
-
// Verify each generated password against HIBP (k-anonymity — only the SHA-1
|
|
107
|
-
// prefix leaves the device). Debounced so spamming Generate only checks the
|
|
108
|
-
// final password. On the (near-impossible) breach hit, silently regenerate;
|
|
109
|
-
// a failed/offline check surfaces as "unverified" rather than blocking.
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (!password) {
|
|
112
|
-
setBreach("idle");
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const token = ++breachTokenRef.current;
|
|
116
|
-
setBreach("checking");
|
|
117
|
-
const timer = setTimeout(async () => {
|
|
118
|
-
const res = await checkPasswordPwned(password);
|
|
119
|
-
if (token !== breachTokenRef.current) {
|
|
120
|
-
return;
|
|
121
|
-
} // superseded
|
|
122
|
-
if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
|
|
123
|
-
breachAttemptRef.current += 1;
|
|
124
|
-
try {
|
|
125
|
-
setPassword(generatePassword(optsRef.current));
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
setBreach("unverified");
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
breachAttemptRef.current = 0;
|
|
133
|
-
setBreach(res.isPwned === null ? "unverified" : "safe");
|
|
134
|
-
}, 400);
|
|
135
|
-
return () => clearTimeout(timer);
|
|
136
|
-
}, [password]);
|
|
137
|
-
const passwordStrength = useStrength(password);
|
|
138
17
|
return (<>
|
|
139
18
|
<View className="mb-4 rounded-lg border border-border dark:border-neutral-800 bg-card p-4 dark:bg-neutral-900/50">
|
|
140
19
|
<View className="mb-3 flex-row items-center justify-between">
|
|
@@ -221,38 +100,6 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({ onSelec
|
|
|
221
100
|
</>);
|
|
222
101
|
});
|
|
223
102
|
export default PasswordGenerator;
|
|
224
|
-
/** Coarse strength indicator — purely UX, server doesn't see it. */
|
|
225
|
-
function useStrength(password) {
|
|
226
|
-
if (!password) {
|
|
227
|
-
return { label: "Generate a password", color: "gray" };
|
|
228
|
-
}
|
|
229
|
-
let strength = 0;
|
|
230
|
-
if (password.length >= 12) {
|
|
231
|
-
strength++;
|
|
232
|
-
}
|
|
233
|
-
if (password.length >= 16) {
|
|
234
|
-
strength++;
|
|
235
|
-
}
|
|
236
|
-
if (/[a-z]/.test(password)) {
|
|
237
|
-
strength++;
|
|
238
|
-
}
|
|
239
|
-
if (/[A-Z]/.test(password)) {
|
|
240
|
-
strength++;
|
|
241
|
-
}
|
|
242
|
-
if (/[0-9]/.test(password)) {
|
|
243
|
-
strength++;
|
|
244
|
-
}
|
|
245
|
-
if (/[^A-Za-z0-9]/.test(password)) {
|
|
246
|
-
strength++;
|
|
247
|
-
}
|
|
248
|
-
if (strength <= 2) {
|
|
249
|
-
return { label: "Weak", color: "#ef4444" };
|
|
250
|
-
}
|
|
251
|
-
if (strength <= 4) {
|
|
252
|
-
return { label: "Medium", color: "#f59e0b" };
|
|
253
|
-
}
|
|
254
|
-
return { label: "Strong", color: "#22c55e" };
|
|
255
|
-
}
|
|
256
103
|
function CharsetCheckbox({ label, checked, onToggle, isDark }) {
|
|
257
104
|
return (<Pressable onPress={onToggle} className="flex-row items-center gap-3" accessibilityRole="checkbox" accessibilityLabel={label} accessibilityState={{ checked }}>
|
|
258
105
|
<View style={{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-generator.jsx","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"password-generator.jsx","sourceRoot":"","sources":["../../src/components/password-generator.tsx"],"names":[],"mappings":"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"}
|
package/lib/hooks/index.d.ts
CHANGED
|
@@ -3,4 +3,5 @@ export { useShakeAnimation } from "./useShakeAnimation";
|
|
|
3
3
|
export { useDebouncedValue } from "./useDebouncedValue";
|
|
4
4
|
export { useAutoLock, type UseAutoLockOptions } from "./useAutoLock";
|
|
5
5
|
export { useUploadBlobUrl, type UploadBlobUrlResult } from "./useUploadBlobUrl";
|
|
6
|
+
export { usePasswordGenerator, getPasswordStrength, type UsePasswordGeneratorResult, type PasswordStrength, type BreachState, } from "./usePasswordGenerator";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/hooks/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC"}
|
package/lib/hooks/index.js
CHANGED
|
@@ -3,4 +3,5 @@ export { useShakeAnimation } from "./useShakeAnimation";
|
|
|
3
3
|
export { useDebouncedValue } from "./useDebouncedValue";
|
|
4
4
|
export { useAutoLock } from "./useAutoLock";
|
|
5
5
|
export { useUploadBlobUrl } from "./useUploadBlobUrl";
|
|
6
|
+
export { usePasswordGenerator, getPasswordStrength, } from "./usePasswordGenerator";
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/lib/hooks/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,cAAc,EACd,UAAU,GAKX,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAC;AAErE,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAIpB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Dispatch, type SetStateAction } from "react";
|
|
2
|
+
export type BreachState = "idle" | "checking" | "safe" | "unverified";
|
|
3
|
+
export interface PasswordStrength {
|
|
4
|
+
label: string;
|
|
5
|
+
color: string;
|
|
6
|
+
}
|
|
7
|
+
export interface UsePasswordGeneratorResult {
|
|
8
|
+
password: string;
|
|
9
|
+
length: number;
|
|
10
|
+
setLength: Dispatch<SetStateAction<number>>;
|
|
11
|
+
includeUppercase: boolean;
|
|
12
|
+
setIncludeUppercase: Dispatch<SetStateAction<boolean>>;
|
|
13
|
+
includeLowercase: boolean;
|
|
14
|
+
setIncludeLowercase: Dispatch<SetStateAction<boolean>>;
|
|
15
|
+
includeNumbers: boolean;
|
|
16
|
+
setIncludeNumbers: Dispatch<SetStateAction<boolean>>;
|
|
17
|
+
includeSymbols: boolean;
|
|
18
|
+
setIncludeSymbols: Dispatch<SetStateAction<boolean>>;
|
|
19
|
+
excludeSimilar: boolean;
|
|
20
|
+
setExcludeSimilar: Dispatch<SetStateAction<boolean>>;
|
|
21
|
+
breach: BreachState;
|
|
22
|
+
strength: PasswordStrength;
|
|
23
|
+
handleGenerate: () => void;
|
|
24
|
+
handleCopy: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export declare function usePasswordGenerator(): UsePasswordGeneratorResult;
|
|
27
|
+
/** Coarse strength indicator — purely UX, server doesn't see it. */
|
|
28
|
+
export declare function getPasswordStrength(password: string): PasswordStrength;
|
|
29
|
+
//# sourceMappingURL=usePasswordGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePasswordGenerator.d.ts","sourceRoot":"","sources":["../../src/hooks/usePasswordGenerator.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,cAAc,EAA4C,MAAM,OAAO,CAAC;AAYrG,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,YAAY,CAAC;AAItE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,wBAAgB,oBAAoB,IAAI,0BAA0B,CA0IjE;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CA8BtE"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Stateful core of PasswordGenerator (components/password-generator.tsx),
|
|
2
|
+
// extracted so a platform-specific presentational layer can consume the
|
|
3
|
+
// same state/logic without re-implementing generate/copy/breach-check.
|
|
4
|
+
// Native's own UI (the vault mobile app's own screen) needed a genuinely
|
|
5
|
+
// different, more native-feeling layout rather than the web card/checkbox
|
|
6
|
+
// layout — see components/password-generator.tsx's own doc comment for
|
|
7
|
+
// why that stayed a separate render rather than one component branching
|
|
8
|
+
// on Platform.OS internally. User-requested, 2026-08-17.
|
|
9
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
|
+
import { Platform } from "react-native";
|
|
11
|
+
import * as Haptics from "expo-haptics";
|
|
12
|
+
import Toast from "react-native-toast-message";
|
|
13
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
14
|
+
import { generatePassword } from "../utils/password";
|
|
15
|
+
import { checkPasswordPwned } from "../services/hibp";
|
|
16
|
+
// A randomly drawn password being in a breach corpus is astronomically
|
|
17
|
+
// unlikely, but if it ever hits, regenerate until clean — capped so a
|
|
18
|
+
// misbehaving API can't loop forever.
|
|
19
|
+
const MAX_BREACH_REGEN = 5;
|
|
20
|
+
const isWeb = Platform.OS === "web";
|
|
21
|
+
export function usePasswordGenerator() {
|
|
22
|
+
const [password, setPassword] = useState("");
|
|
23
|
+
const [length, setLength] = useState(16);
|
|
24
|
+
const [includeUppercase, setIncludeUppercase] = useState(true);
|
|
25
|
+
const [includeLowercase, setIncludeLowercase] = useState(true);
|
|
26
|
+
const [includeNumbers, setIncludeNumbers] = useState(true);
|
|
27
|
+
const [includeSymbols, setIncludeSymbols] = useState(true);
|
|
28
|
+
const [excludeSimilar, setExcludeSimilar] = useState(false);
|
|
29
|
+
const [breach, setBreach] = useState("idle");
|
|
30
|
+
// Latest options, read by the breach-regen path without re-subscribing
|
|
31
|
+
// the check effect on every option change.
|
|
32
|
+
const optsRef = useRef({
|
|
33
|
+
length,
|
|
34
|
+
includeUppercase,
|
|
35
|
+
includeLowercase,
|
|
36
|
+
includeNumbers,
|
|
37
|
+
includeSymbols,
|
|
38
|
+
excludeSimilar,
|
|
39
|
+
});
|
|
40
|
+
optsRef.current = {
|
|
41
|
+
length,
|
|
42
|
+
includeUppercase,
|
|
43
|
+
includeLowercase,
|
|
44
|
+
includeNumbers,
|
|
45
|
+
includeSymbols,
|
|
46
|
+
excludeSimilar,
|
|
47
|
+
};
|
|
48
|
+
// Ignore stale async HIBP results once a newer password supersedes them.
|
|
49
|
+
const breachTokenRef = useRef(0);
|
|
50
|
+
const breachAttemptRef = useRef(0);
|
|
51
|
+
// 150 ms throttle on Generate. The CSPRNG draw itself is cheap
|
|
52
|
+
// (synchronous bytes), but a held-down Enter key or a stuck
|
|
53
|
+
// accessibility-keyboard repeat could fire many calls per second
|
|
54
|
+
// and the rejection-sampling loop inside `generatePassword` does
|
|
55
|
+
// a small amount of work per call. The throttle also gates the
|
|
56
|
+
// haptic so the user isn't getting buzzed at keypress rate.
|
|
57
|
+
const lastGenerateRef = useRef(0);
|
|
58
|
+
const handleGenerate = useCallback(() => {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
if (now - lastGenerateRef.current < 150) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
lastGenerateRef.current = now;
|
|
64
|
+
try {
|
|
65
|
+
const next = generatePassword({
|
|
66
|
+
length,
|
|
67
|
+
includeUppercase,
|
|
68
|
+
includeLowercase,
|
|
69
|
+
includeNumbers,
|
|
70
|
+
includeSymbols,
|
|
71
|
+
excludeSimilar,
|
|
72
|
+
});
|
|
73
|
+
setPassword(next);
|
|
74
|
+
if (!isWeb) {
|
|
75
|
+
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
Toast.show({
|
|
80
|
+
type: "error",
|
|
81
|
+
text1: e instanceof Error ? e.message : "Please select at least one character type",
|
|
82
|
+
visibilityTime: 3000,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
|
|
86
|
+
const handleCopy = useCallback(async () => {
|
|
87
|
+
if (!password) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const ok = await copyToClipboard(password);
|
|
91
|
+
if (!ok) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!isWeb) {
|
|
95
|
+
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
|
96
|
+
}
|
|
97
|
+
Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
|
|
98
|
+
}, [password]);
|
|
99
|
+
// Generate on mount and when character-class options change.
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
handleGenerate();
|
|
102
|
+
}, [handleGenerate]);
|
|
103
|
+
// Verify each generated password against HIBP (k-anonymity — only the SHA-1
|
|
104
|
+
// prefix leaves the device). Debounced so spamming Generate only checks the
|
|
105
|
+
// final password. On the (near-impossible) breach hit, silently regenerate;
|
|
106
|
+
// a failed/offline check surfaces as "unverified" rather than blocking.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!password) {
|
|
109
|
+
setBreach("idle");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const token = ++breachTokenRef.current;
|
|
113
|
+
setBreach("checking");
|
|
114
|
+
const timer = setTimeout(async () => {
|
|
115
|
+
const res = await checkPasswordPwned(password);
|
|
116
|
+
if (token !== breachTokenRef.current) {
|
|
117
|
+
return;
|
|
118
|
+
} // superseded
|
|
119
|
+
if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
|
|
120
|
+
breachAttemptRef.current += 1;
|
|
121
|
+
try {
|
|
122
|
+
setPassword(generatePassword(optsRef.current));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
setBreach("unverified");
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
breachAttemptRef.current = 0;
|
|
130
|
+
setBreach(res.isPwned === null ? "unverified" : "safe");
|
|
131
|
+
}, 400);
|
|
132
|
+
return () => clearTimeout(timer);
|
|
133
|
+
}, [password]);
|
|
134
|
+
const strength = getPasswordStrength(password);
|
|
135
|
+
return {
|
|
136
|
+
password,
|
|
137
|
+
length,
|
|
138
|
+
setLength,
|
|
139
|
+
includeUppercase,
|
|
140
|
+
setIncludeUppercase,
|
|
141
|
+
includeLowercase,
|
|
142
|
+
setIncludeLowercase,
|
|
143
|
+
includeNumbers,
|
|
144
|
+
setIncludeNumbers,
|
|
145
|
+
includeSymbols,
|
|
146
|
+
setIncludeSymbols,
|
|
147
|
+
excludeSimilar,
|
|
148
|
+
setExcludeSimilar,
|
|
149
|
+
breach,
|
|
150
|
+
strength,
|
|
151
|
+
handleGenerate,
|
|
152
|
+
handleCopy,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/** Coarse strength indicator — purely UX, server doesn't see it. */
|
|
156
|
+
export function getPasswordStrength(password) {
|
|
157
|
+
if (!password) {
|
|
158
|
+
return { label: "Generate a password", color: "gray" };
|
|
159
|
+
}
|
|
160
|
+
let strength = 0;
|
|
161
|
+
if (password.length >= 12) {
|
|
162
|
+
strength++;
|
|
163
|
+
}
|
|
164
|
+
if (password.length >= 16) {
|
|
165
|
+
strength++;
|
|
166
|
+
}
|
|
167
|
+
if (/[a-z]/.test(password)) {
|
|
168
|
+
strength++;
|
|
169
|
+
}
|
|
170
|
+
if (/[A-Z]/.test(password)) {
|
|
171
|
+
strength++;
|
|
172
|
+
}
|
|
173
|
+
if (/[0-9]/.test(password)) {
|
|
174
|
+
strength++;
|
|
175
|
+
}
|
|
176
|
+
if (/[^A-Za-z0-9]/.test(password)) {
|
|
177
|
+
strength++;
|
|
178
|
+
}
|
|
179
|
+
if (strength <= 2) {
|
|
180
|
+
return { label: "Weak", color: "#ef4444" };
|
|
181
|
+
}
|
|
182
|
+
if (strength <= 4) {
|
|
183
|
+
return { label: "Medium", color: "#f59e0b" };
|
|
184
|
+
}
|
|
185
|
+
return { label: "Strong", color: "#22c55e" };
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=usePasswordGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePasswordGenerator.js","sourceRoot":"","sources":["../../src/hooks/usePasswordGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"api-fetch.d.ts","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAuBA;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CA0CnB"}
|
package/lib/utils/api-fetch.js
CHANGED
|
@@ -47,6 +47,24 @@ export async function apiFetch(path, init = {}, timeoutMs) {
|
|
|
47
47
|
if (token) {
|
|
48
48
|
headers.set("Authorization", `Bearer ${token}`);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
// Explicit `credentials: "omit"` — native auth is Bearer-only, and the
|
|
51
|
+
// server's CSRF middleware branches purely on whether a session cookie
|
|
52
|
+
// is PRESENT on the request (see server/src/auth/middleware.rs's
|
|
53
|
+
// csrf_protect: any request carrying the session cookie is treated as
|
|
54
|
+
// cookie-auth and required to also carry a matching X-CSRF-Token +
|
|
55
|
+
// Origin, neither of which this native branch ever sends). React
|
|
56
|
+
// Native's Android fetch persists Set-Cookie responses in a shared
|
|
57
|
+
// CookieManager and reattaches them to matching-origin requests
|
|
58
|
+
// regardless of the web `credentials` semantics being followed
|
|
59
|
+
// elsewhere — so if the server ever sets a session cookie as a side
|
|
60
|
+
// effect for a native client (e.g. during passkey/WebAuthn login,
|
|
61
|
+
// which can involve a system browser or Credential Manager sheet),
|
|
62
|
+
// every subsequent native mutation silently starts requiring CSRF
|
|
63
|
+
// headers it can never provide, failing with "Missing CSRF header".
|
|
64
|
+
// Reported as folder-creation failing this way, 2026-08-17 — but the
|
|
65
|
+
// same stray cookie would affect ANY mutating request once present,
|
|
66
|
+
// not just this one. Omitting credentials here is the actual fix:
|
|
67
|
+
// native should never send or rely on cookies at all.
|
|
68
|
+
return fetchWithTimeout(url, { ...init, headers, credentials: "omit" }, timeoutMs);
|
|
51
69
|
}
|
|
52
70
|
//# sourceMappingURL=api-fetch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-fetch.js","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAEzD,MAAM,gBAAgB,GAAG,YAAY,CAAC;AACtC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;IAC1B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,OAAoB,EAAE,EACtB,SAAkB;IAElB,MAAM,GAAG,GAAG,GAAG,UAAU,EAAE,GAAG,IAAI,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE1C,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;YAC1C,IAAI,IAAI,EAAE,CAAC;gBACT,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC;QACzC,IAAI,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,QAAQ,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,gBAAgB,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,SAAS,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,eAAe,EAAE,CAAC;IACtC,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,gBAAgB,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"api-fetch.js","sourceRoot":"","sources":["../../src/utils/api-fetch.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { ActivityIndicator,
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ActivityIndicator, Pressable, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
4
|
import { useColorScheme } from "nativewind";
|
|
5
|
-
import * as Haptics from "expo-haptics";
|
|
6
5
|
import Slider from "@react-native-community/slider";
|
|
7
|
-
import Toast from "react-native-toast-message";
|
|
8
6
|
import { Text } from "./text";
|
|
9
|
-
import {
|
|
10
|
-
import { generatePassword } from "../utils/password";
|
|
11
|
-
import { checkPasswordPwned } from "../services/hibp";
|
|
12
|
-
|
|
13
|
-
// A randomly drawn password being in a breach corpus is astronomically
|
|
14
|
-
// unlikely, but if it ever hits, regenerate until clean — capped so a
|
|
15
|
-
// misbehaving API can't loop forever.
|
|
16
|
-
const MAX_BREACH_REGEN = 5;
|
|
17
|
-
type BreachState = "idle" | "checking" | "safe" | "unverified";
|
|
18
|
-
|
|
19
|
-
const isWeb = Platform.OS === "web";
|
|
7
|
+
import { usePasswordGenerator } from "../hooks/usePasswordGenerator";
|
|
20
8
|
|
|
21
9
|
export interface PasswordGeneratorProps {
|
|
22
10
|
/** Called when the user taps "Use Password" (only shown when `onSelect` is provided). */
|
|
@@ -34,126 +22,28 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
34
22
|
onSelect,
|
|
35
23
|
hideCopy,
|
|
36
24
|
}: PasswordGeneratorProps) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const [includeUppercase, setIncludeUppercase] = useState(true);
|
|
40
|
-
const [includeLowercase, setIncludeLowercase] = useState(true);
|
|
41
|
-
const [includeNumbers, setIncludeNumbers] = useState(true);
|
|
42
|
-
const [includeSymbols, setIncludeSymbols] = useState(true);
|
|
43
|
-
const [excludeSimilar, setExcludeSimilar] = useState(false);
|
|
44
|
-
const [breach, setBreach] = useState<BreachState>("idle");
|
|
45
|
-
|
|
46
|
-
const { colorScheme } = useColorScheme();
|
|
47
|
-
const isDark = colorScheme === "dark";
|
|
48
|
-
|
|
49
|
-
// Latest options, read by the breach-regen path without re-subscribing the
|
|
50
|
-
// check effect on every option change.
|
|
51
|
-
const optsRef = useRef({
|
|
52
|
-
length,
|
|
53
|
-
includeUppercase,
|
|
54
|
-
includeLowercase,
|
|
55
|
-
includeNumbers,
|
|
56
|
-
includeSymbols,
|
|
57
|
-
excludeSimilar,
|
|
58
|
-
});
|
|
59
|
-
optsRef.current = {
|
|
25
|
+
const {
|
|
26
|
+
password,
|
|
60
27
|
length,
|
|
28
|
+
setLength,
|
|
61
29
|
includeUppercase,
|
|
30
|
+
setIncludeUppercase,
|
|
62
31
|
includeLowercase,
|
|
32
|
+
setIncludeLowercase,
|
|
63
33
|
includeNumbers,
|
|
34
|
+
setIncludeNumbers,
|
|
64
35
|
includeSymbols,
|
|
36
|
+
setIncludeSymbols,
|
|
65
37
|
excludeSimilar,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// (synchronous bytes), but a held-down Enter key or a stuck
|
|
73
|
-
// accessibility-keyboard repeat could fire many calls per second
|
|
74
|
-
// and the rejection-sampling loop inside `generatePassword` does
|
|
75
|
-
// a small amount of work per call. The throttle also gates the
|
|
76
|
-
// haptic so the user isn't getting buzzed at keypress rate.
|
|
77
|
-
const lastGenerateRef = useRef(0);
|
|
78
|
-
const handleGenerate = useCallback(() => {
|
|
79
|
-
const now = Date.now();
|
|
80
|
-
if (now - lastGenerateRef.current < 150) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
lastGenerateRef.current = now;
|
|
84
|
-
try {
|
|
85
|
-
const next = generatePassword({
|
|
86
|
-
length,
|
|
87
|
-
includeUppercase,
|
|
88
|
-
includeLowercase,
|
|
89
|
-
includeNumbers,
|
|
90
|
-
includeSymbols,
|
|
91
|
-
excludeSimilar,
|
|
92
|
-
});
|
|
93
|
-
setPassword(next);
|
|
94
|
-
if (!isWeb) {
|
|
95
|
-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
96
|
-
}
|
|
97
|
-
} catch (e) {
|
|
98
|
-
Toast.show({
|
|
99
|
-
type: "error",
|
|
100
|
-
text1: e instanceof Error ? e.message : "Please select at least one character type",
|
|
101
|
-
visibilityTime: 3000,
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
|
|
105
|
-
|
|
106
|
-
const handleCopy = useCallback(async () => {
|
|
107
|
-
if (!password) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const ok = await copyToClipboard(password);
|
|
111
|
-
if (!ok) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (!isWeb) {
|
|
115
|
-
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
|
116
|
-
}
|
|
117
|
-
Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
|
|
118
|
-
}, [password]);
|
|
119
|
-
|
|
120
|
-
// Generate on mount and when character-class options change.
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
handleGenerate();
|
|
123
|
-
}, [handleGenerate]);
|
|
38
|
+
setExcludeSimilar,
|
|
39
|
+
breach,
|
|
40
|
+
strength: passwordStrength,
|
|
41
|
+
handleGenerate,
|
|
42
|
+
handleCopy,
|
|
43
|
+
} = usePasswordGenerator();
|
|
124
44
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
// final password. On the (near-impossible) breach hit, silently regenerate;
|
|
128
|
-
// a failed/offline check surfaces as "unverified" rather than blocking.
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (!password) {
|
|
131
|
-
setBreach("idle");
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
const token = ++breachTokenRef.current;
|
|
135
|
-
setBreach("checking");
|
|
136
|
-
const timer = setTimeout(async () => {
|
|
137
|
-
const res = await checkPasswordPwned(password);
|
|
138
|
-
if (token !== breachTokenRef.current) {
|
|
139
|
-
return;
|
|
140
|
-
} // superseded
|
|
141
|
-
if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
|
|
142
|
-
breachAttemptRef.current += 1;
|
|
143
|
-
try {
|
|
144
|
-
setPassword(generatePassword(optsRef.current));
|
|
145
|
-
} catch {
|
|
146
|
-
setBreach("unverified");
|
|
147
|
-
}
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
breachAttemptRef.current = 0;
|
|
151
|
-
setBreach(res.isPwned === null ? "unverified" : "safe");
|
|
152
|
-
}, 400);
|
|
153
|
-
return () => clearTimeout(timer);
|
|
154
|
-
}, [password]);
|
|
155
|
-
|
|
156
|
-
const passwordStrength = useStrength(password);
|
|
45
|
+
const { colorScheme } = useColorScheme();
|
|
46
|
+
const isDark = colorScheme === "dark";
|
|
157
47
|
|
|
158
48
|
return (
|
|
159
49
|
<>
|
|
@@ -324,39 +214,6 @@ export const PasswordGenerator = React.memo(function PasswordGenerator({
|
|
|
324
214
|
|
|
325
215
|
export default PasswordGenerator;
|
|
326
216
|
|
|
327
|
-
/** Coarse strength indicator — purely UX, server doesn't see it. */
|
|
328
|
-
function useStrength(password: string): { label: string; color: string } {
|
|
329
|
-
if (!password) {
|
|
330
|
-
return { label: "Generate a password", color: "gray" };
|
|
331
|
-
}
|
|
332
|
-
let strength = 0;
|
|
333
|
-
if (password.length >= 12) {
|
|
334
|
-
strength++;
|
|
335
|
-
}
|
|
336
|
-
if (password.length >= 16) {
|
|
337
|
-
strength++;
|
|
338
|
-
}
|
|
339
|
-
if (/[a-z]/.test(password)) {
|
|
340
|
-
strength++;
|
|
341
|
-
}
|
|
342
|
-
if (/[A-Z]/.test(password)) {
|
|
343
|
-
strength++;
|
|
344
|
-
}
|
|
345
|
-
if (/[0-9]/.test(password)) {
|
|
346
|
-
strength++;
|
|
347
|
-
}
|
|
348
|
-
if (/[^A-Za-z0-9]/.test(password)) {
|
|
349
|
-
strength++;
|
|
350
|
-
}
|
|
351
|
-
if (strength <= 2) {
|
|
352
|
-
return { label: "Weak", color: "#ef4444" };
|
|
353
|
-
}
|
|
354
|
-
if (strength <= 4) {
|
|
355
|
-
return { label: "Medium", color: "#f59e0b" };
|
|
356
|
-
}
|
|
357
|
-
return { label: "Strong", color: "#22c55e" };
|
|
358
|
-
}
|
|
359
|
-
|
|
360
217
|
interface CharsetCheckboxProps {
|
|
361
218
|
label: string;
|
|
362
219
|
checked: boolean;
|
package/src/hooks/index.ts
CHANGED
|
@@ -17,3 +17,11 @@ export { useDebouncedValue } from "./useDebouncedValue";
|
|
|
17
17
|
export { useAutoLock, type UseAutoLockOptions } from "./useAutoLock";
|
|
18
18
|
|
|
19
19
|
export { useUploadBlobUrl, type UploadBlobUrlResult } from "./useUploadBlobUrl";
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
usePasswordGenerator,
|
|
23
|
+
getPasswordStrength,
|
|
24
|
+
type UsePasswordGeneratorResult,
|
|
25
|
+
type PasswordStrength,
|
|
26
|
+
type BreachState,
|
|
27
|
+
} from "./usePasswordGenerator";
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// Stateful core of PasswordGenerator (components/password-generator.tsx),
|
|
2
|
+
// extracted so a platform-specific presentational layer can consume the
|
|
3
|
+
// same state/logic without re-implementing generate/copy/breach-check.
|
|
4
|
+
// Native's own UI (the vault mobile app's own screen) needed a genuinely
|
|
5
|
+
// different, more native-feeling layout rather than the web card/checkbox
|
|
6
|
+
// layout — see components/password-generator.tsx's own doc comment for
|
|
7
|
+
// why that stayed a separate render rather than one component branching
|
|
8
|
+
// on Platform.OS internally. User-requested, 2026-08-17.
|
|
9
|
+
import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from "react";
|
|
10
|
+
import { Platform } from "react-native";
|
|
11
|
+
import * as Haptics from "expo-haptics";
|
|
12
|
+
import Toast from "react-native-toast-message";
|
|
13
|
+
import { copyToClipboard } from "../utils/clipboard";
|
|
14
|
+
import { generatePassword } from "../utils/password";
|
|
15
|
+
import { checkPasswordPwned } from "../services/hibp";
|
|
16
|
+
|
|
17
|
+
// A randomly drawn password being in a breach corpus is astronomically
|
|
18
|
+
// unlikely, but if it ever hits, regenerate until clean — capped so a
|
|
19
|
+
// misbehaving API can't loop forever.
|
|
20
|
+
const MAX_BREACH_REGEN = 5;
|
|
21
|
+
export type BreachState = "idle" | "checking" | "safe" | "unverified";
|
|
22
|
+
|
|
23
|
+
const isWeb = Platform.OS === "web";
|
|
24
|
+
|
|
25
|
+
export interface PasswordStrength {
|
|
26
|
+
label: string;
|
|
27
|
+
color: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface UsePasswordGeneratorResult {
|
|
31
|
+
password: string;
|
|
32
|
+
length: number;
|
|
33
|
+
setLength: Dispatch<SetStateAction<number>>;
|
|
34
|
+
includeUppercase: boolean;
|
|
35
|
+
setIncludeUppercase: Dispatch<SetStateAction<boolean>>;
|
|
36
|
+
includeLowercase: boolean;
|
|
37
|
+
setIncludeLowercase: Dispatch<SetStateAction<boolean>>;
|
|
38
|
+
includeNumbers: boolean;
|
|
39
|
+
setIncludeNumbers: Dispatch<SetStateAction<boolean>>;
|
|
40
|
+
includeSymbols: boolean;
|
|
41
|
+
setIncludeSymbols: Dispatch<SetStateAction<boolean>>;
|
|
42
|
+
excludeSimilar: boolean;
|
|
43
|
+
setExcludeSimilar: Dispatch<SetStateAction<boolean>>;
|
|
44
|
+
breach: BreachState;
|
|
45
|
+
strength: PasswordStrength;
|
|
46
|
+
handleGenerate: () => void;
|
|
47
|
+
handleCopy: () => Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function usePasswordGenerator(): UsePasswordGeneratorResult {
|
|
51
|
+
const [password, setPassword] = useState("");
|
|
52
|
+
const [length, setLength] = useState(16);
|
|
53
|
+
const [includeUppercase, setIncludeUppercase] = useState(true);
|
|
54
|
+
const [includeLowercase, setIncludeLowercase] = useState(true);
|
|
55
|
+
const [includeNumbers, setIncludeNumbers] = useState(true);
|
|
56
|
+
const [includeSymbols, setIncludeSymbols] = useState(true);
|
|
57
|
+
const [excludeSimilar, setExcludeSimilar] = useState(false);
|
|
58
|
+
const [breach, setBreach] = useState<BreachState>("idle");
|
|
59
|
+
|
|
60
|
+
// Latest options, read by the breach-regen path without re-subscribing
|
|
61
|
+
// the check effect on every option change.
|
|
62
|
+
const optsRef = useRef({
|
|
63
|
+
length,
|
|
64
|
+
includeUppercase,
|
|
65
|
+
includeLowercase,
|
|
66
|
+
includeNumbers,
|
|
67
|
+
includeSymbols,
|
|
68
|
+
excludeSimilar,
|
|
69
|
+
});
|
|
70
|
+
optsRef.current = {
|
|
71
|
+
length,
|
|
72
|
+
includeUppercase,
|
|
73
|
+
includeLowercase,
|
|
74
|
+
includeNumbers,
|
|
75
|
+
includeSymbols,
|
|
76
|
+
excludeSimilar,
|
|
77
|
+
};
|
|
78
|
+
// Ignore stale async HIBP results once a newer password supersedes them.
|
|
79
|
+
const breachTokenRef = useRef(0);
|
|
80
|
+
const breachAttemptRef = useRef(0);
|
|
81
|
+
|
|
82
|
+
// 150 ms throttle on Generate. The CSPRNG draw itself is cheap
|
|
83
|
+
// (synchronous bytes), but a held-down Enter key or a stuck
|
|
84
|
+
// accessibility-keyboard repeat could fire many calls per second
|
|
85
|
+
// and the rejection-sampling loop inside `generatePassword` does
|
|
86
|
+
// a small amount of work per call. The throttle also gates the
|
|
87
|
+
// haptic so the user isn't getting buzzed at keypress rate.
|
|
88
|
+
const lastGenerateRef = useRef(0);
|
|
89
|
+
const handleGenerate = useCallback(() => {
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
if (now - lastGenerateRef.current < 150) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
lastGenerateRef.current = now;
|
|
95
|
+
try {
|
|
96
|
+
const next = generatePassword({
|
|
97
|
+
length,
|
|
98
|
+
includeUppercase,
|
|
99
|
+
includeLowercase,
|
|
100
|
+
includeNumbers,
|
|
101
|
+
includeSymbols,
|
|
102
|
+
excludeSimilar,
|
|
103
|
+
});
|
|
104
|
+
setPassword(next);
|
|
105
|
+
if (!isWeb) {
|
|
106
|
+
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {
|
|
109
|
+
Toast.show({
|
|
110
|
+
type: "error",
|
|
111
|
+
text1: e instanceof Error ? e.message : "Please select at least one character type",
|
|
112
|
+
visibilityTime: 3000,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}, [length, includeUppercase, includeLowercase, includeNumbers, includeSymbols, excludeSimilar]);
|
|
116
|
+
|
|
117
|
+
const handleCopy = useCallback(async () => {
|
|
118
|
+
if (!password) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const ok = await copyToClipboard(password);
|
|
122
|
+
if (!ok) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!isWeb) {
|
|
126
|
+
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
|
|
127
|
+
}
|
|
128
|
+
Toast.show({ type: "success", text1: "Copied to clipboard", visibilityTime: 2000 });
|
|
129
|
+
}, [password]);
|
|
130
|
+
|
|
131
|
+
// Generate on mount and when character-class options change.
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
handleGenerate();
|
|
134
|
+
}, [handleGenerate]);
|
|
135
|
+
|
|
136
|
+
// Verify each generated password against HIBP (k-anonymity — only the SHA-1
|
|
137
|
+
// prefix leaves the device). Debounced so spamming Generate only checks the
|
|
138
|
+
// final password. On the (near-impossible) breach hit, silently regenerate;
|
|
139
|
+
// a failed/offline check surfaces as "unverified" rather than blocking.
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
if (!password) {
|
|
142
|
+
setBreach("idle");
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const token = ++breachTokenRef.current;
|
|
146
|
+
setBreach("checking");
|
|
147
|
+
const timer = setTimeout(async () => {
|
|
148
|
+
const res = await checkPasswordPwned(password);
|
|
149
|
+
if (token !== breachTokenRef.current) {
|
|
150
|
+
return;
|
|
151
|
+
} // superseded
|
|
152
|
+
if (res.isPwned === true && breachAttemptRef.current < MAX_BREACH_REGEN) {
|
|
153
|
+
breachAttemptRef.current += 1;
|
|
154
|
+
try {
|
|
155
|
+
setPassword(generatePassword(optsRef.current));
|
|
156
|
+
} catch {
|
|
157
|
+
setBreach("unverified");
|
|
158
|
+
}
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
breachAttemptRef.current = 0;
|
|
162
|
+
setBreach(res.isPwned === null ? "unverified" : "safe");
|
|
163
|
+
}, 400);
|
|
164
|
+
return () => clearTimeout(timer);
|
|
165
|
+
}, [password]);
|
|
166
|
+
|
|
167
|
+
const strength = getPasswordStrength(password);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
password,
|
|
171
|
+
length,
|
|
172
|
+
setLength,
|
|
173
|
+
includeUppercase,
|
|
174
|
+
setIncludeUppercase,
|
|
175
|
+
includeLowercase,
|
|
176
|
+
setIncludeLowercase,
|
|
177
|
+
includeNumbers,
|
|
178
|
+
setIncludeNumbers,
|
|
179
|
+
includeSymbols,
|
|
180
|
+
setIncludeSymbols,
|
|
181
|
+
excludeSimilar,
|
|
182
|
+
setExcludeSimilar,
|
|
183
|
+
breach,
|
|
184
|
+
strength,
|
|
185
|
+
handleGenerate,
|
|
186
|
+
handleCopy,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Coarse strength indicator — purely UX, server doesn't see it. */
|
|
191
|
+
export function getPasswordStrength(password: string): PasswordStrength {
|
|
192
|
+
if (!password) {
|
|
193
|
+
return { label: "Generate a password", color: "gray" };
|
|
194
|
+
}
|
|
195
|
+
let strength = 0;
|
|
196
|
+
if (password.length >= 12) {
|
|
197
|
+
strength++;
|
|
198
|
+
}
|
|
199
|
+
if (password.length >= 16) {
|
|
200
|
+
strength++;
|
|
201
|
+
}
|
|
202
|
+
if (/[a-z]/.test(password)) {
|
|
203
|
+
strength++;
|
|
204
|
+
}
|
|
205
|
+
if (/[A-Z]/.test(password)) {
|
|
206
|
+
strength++;
|
|
207
|
+
}
|
|
208
|
+
if (/[0-9]/.test(password)) {
|
|
209
|
+
strength++;
|
|
210
|
+
}
|
|
211
|
+
if (/[^A-Za-z0-9]/.test(password)) {
|
|
212
|
+
strength++;
|
|
213
|
+
}
|
|
214
|
+
if (strength <= 2) {
|
|
215
|
+
return { label: "Weak", color: "#ef4444" };
|
|
216
|
+
}
|
|
217
|
+
if (strength <= 4) {
|
|
218
|
+
return { label: "Medium", color: "#f59e0b" };
|
|
219
|
+
}
|
|
220
|
+
return { label: "Strong", color: "#22c55e" };
|
|
221
|
+
}
|
package/src/utils/api-fetch.ts
CHANGED
|
@@ -57,5 +57,23 @@ export async function apiFetch(
|
|
|
57
57
|
if (token) {
|
|
58
58
|
headers.set("Authorization", `Bearer ${token}`);
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
// Explicit `credentials: "omit"` — native auth is Bearer-only, and the
|
|
61
|
+
// server's CSRF middleware branches purely on whether a session cookie
|
|
62
|
+
// is PRESENT on the request (see server/src/auth/middleware.rs's
|
|
63
|
+
// csrf_protect: any request carrying the session cookie is treated as
|
|
64
|
+
// cookie-auth and required to also carry a matching X-CSRF-Token +
|
|
65
|
+
// Origin, neither of which this native branch ever sends). React
|
|
66
|
+
// Native's Android fetch persists Set-Cookie responses in a shared
|
|
67
|
+
// CookieManager and reattaches them to matching-origin requests
|
|
68
|
+
// regardless of the web `credentials` semantics being followed
|
|
69
|
+
// elsewhere — so if the server ever sets a session cookie as a side
|
|
70
|
+
// effect for a native client (e.g. during passkey/WebAuthn login,
|
|
71
|
+
// which can involve a system browser or Credential Manager sheet),
|
|
72
|
+
// every subsequent native mutation silently starts requiring CSRF
|
|
73
|
+
// headers it can never provide, failing with "Missing CSRF header".
|
|
74
|
+
// Reported as folder-creation failing this way, 2026-08-17 — but the
|
|
75
|
+
// same stray cookie would affect ANY mutating request once present,
|
|
76
|
+
// not just this one. Omitting credentials here is the actual fix:
|
|
77
|
+
// native should never send or rely on cookies at all.
|
|
78
|
+
return fetchWithTimeout(url, { ...init, headers, credentials: "omit" }, timeoutMs);
|
|
61
79
|
}
|