Skip to content

Commit ed1b003

Browse files
authored
Merge pull request #494 from mCodex/refactor/appExample
Refactor/app example
2 parents c495d01 + 69876ac commit ed1b003

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1781
-1918
lines changed

CODE_OF_CONDUCT.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
2+
# Contributor Covenant Code of Conduct
3+
4+
## Our Pledge
5+
6+
We as members, contributors, and leaders pledge to make participation in our
7+
community a harassment-free experience for everyone, regardless of age, body
8+
size, visible or invisible disability, ethnicity, sex characteristics, gender
9+
identity and expression, level of experience, education, socio-economic status,
10+
nationality, personal appearance, race, caste, color, religion, or sexual
11+
identity and orientation.
12+
13+
We pledge to act and interact in ways that contribute to an open, welcoming,
14+
diverse, inclusive, and healthy community.
15+
16+
## Our Standards
17+
18+
Examples of behavior that contributes to a positive environment for our
19+
community include:
20+
21+
* Demonstrating empathy and kindness toward other people
22+
* Being respectful of differing opinions, viewpoints, and experiences
23+
* Giving and gracefully accepting constructive feedback
24+
* Accepting responsibility and apologizing to those affected by our mistakes,
25+
and learning from the experience
26+
* Focusing on what is best not just for us as individuals, but for the overall
27+
community
28+
29+
Examples of unacceptable behavior include:
30+
31+
* The use of sexualized language or imagery, and sexual attention or advances of
32+
any kind
33+
* Trolling, insulting or derogatory comments, and personal or political attacks
34+
* Public or private harassment
35+
* Publishing others' private information, such as a physical or email address,
36+
without their explicit permission
37+
* Other conduct which could reasonably be considered inappropriate in a
38+
professional setting
39+
40+
## Enforcement Responsibilities
41+
42+
Community leaders are responsible for clarifying and enforcing our standards of
43+
acceptable behavior and will take appropriate and fair corrective action in
44+
response to any behavior that they deem inappropriate, threatening, offensive,
45+
or harmful.
46+
47+
Community leaders have the right and responsibility to remove, edit, or reject
48+
comments, commits, code, wiki edits, issues, and other contributions that are
49+
not aligned to this Code of Conduct, and will communicate reasons for moderation
50+
decisions when appropriate.
51+
52+
## Scope
53+
54+
This Code of Conduct applies within all community spaces, and also applies when
55+
an individual is officially representing the community in public spaces.
56+
Examples of representing our community include using an official e-mail address,
57+
posting via an official social media account, or acting as an appointed
58+
representative at an online or offline event.
59+
60+
## Enforcement
61+
62+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
63+
reported to the community leaders responsible for enforcement at
64+
[INSERT CONTACT METHOD].
65+
All complaints will be reviewed and investigated promptly and fairly.
66+
67+
All community leaders are obligated to respect the privacy and security of the
68+
reporter of any incident.
69+
70+
## Enforcement Guidelines
71+
72+
Community leaders will follow these Community Impact Guidelines in determining
73+
the consequences for any action they deem in violation of this Code of Conduct:
74+
75+
### 1. Correction
76+
77+
**Community Impact**: Use of inappropriate language or other behavior deemed
78+
unprofessional or unwelcome in the community.
79+
80+
**Consequence**: A private, written warning from community leaders, providing
81+
clarity around the nature of the violation and an explanation of why the
82+
behavior was inappropriate. A public apology may be requested.
83+
84+
### 2. Warning
85+
86+
**Community Impact**: A violation through a single incident or series of
87+
actions.
88+
89+
**Consequence**: A warning with consequences for continued behavior. No
90+
interaction with the people involved, including unsolicited interaction with
91+
those enforcing the Code of Conduct, for a specified period of time. This
92+
includes avoiding interactions in community spaces as well as external channels
93+
like social media. Violating these terms may lead to a temporary or permanent
94+
ban.
95+
96+
### 3. Temporary Ban
97+
98+
**Community Impact**: A serious violation of community standards, including
99+
sustained inappropriate behavior.
100+
101+
**Consequence**: A temporary ban from any sort of interaction or public
102+
communication with the community for a specified period of time. No public or
103+
private interaction with the people involved, including unsolicited interaction
104+
with those enforcing the Code of Conduct, is allowed during this period.
105+
Violating these terms may lead to a permanent ban.
106+
107+
### 4. Permanent Ban
108+
109+
**Community Impact**: Demonstrating a pattern of violation of community
110+
standards, including sustained inappropriate behavior, harassment of an
111+
individual, or aggression toward or disparagement of classes of individuals.
112+
113+
**Consequence**: A permanent ban from any sort of public interaction within the
114+
community.
115+
116+
## Attribution
117+
118+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119+
version 2.1, available at
120+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121+
122+
Community Impact Guidelines were inspired by
123+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124+
125+
For answers to common questions about this code of conduct, see the FAQ at
126+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
127+
[https://www.contributor-covenant.org/translations][translations].
128+
129+
[homepage]: https://www.contributor-covenant.org
130+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131+
[Mozilla CoC]: https://github.com/mozilla/diversity
132+
[FAQ]: https://www.contributor-covenant.org/faq
133+
[translations]: https://www.contributor-covenant.org/translations

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Mateus Andrade
3+
Copyright (c) 2016-2025 Mateus Andrade
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ Modern secure storage for React Native, powered by Nitro Modules. Version 6 ship
1717
> This README tracks the in-progress v6 work on `master`. For the stable legacy release, switch to the `v5.x` branch.
1818
1919
> [!NOTE]
20-
> **Choosing between 5.6.0 and 6.x**
20+
> **Choosing between 5.6.x and 6.x**
2121
>
22-
> - **Need bridge stability?** `5.6.0` is the last pre-Nitro release with the latest biometric fixes, docs, and Android namespace cleanups. It’s drop-in for any `5.5.x` app already running on React Native’s Fabric architecture, but you keep the legacy JS bridge overhead—Paper is no longer supported.
22+
> - **Need bridge stability?** `5.6.x` is the last pre-Nitro release with the latest biometric fixes, docs, and Android namespace cleanups. It’s drop-in for any `5.5.x` app already running on React Native’s Fabric architecture, but you keep the legacy JS bridge overhead—Paper is no longer supported.
2323
> - **Ready for Nitro speed?** `6.x` swaps in the Nitro hybrid core, auto-enforces Class 3/StrongBox biometrics, and ships the refreshed sample app plus richer metadata. Upgrade when you can adopt the Nitro toolchain (RN 0.76+, Node 18+, `react-native-nitro-modules`).
24-
> - **Staying back on 5.5.x?** You remain on the legacy (Paper) architecture and miss the Android 13 prompt fixes, the manual credential fallback restoration, and the new docs—migrate to `5.6.0` at minimum before planning the Nitro jump.
24+
> - **Staying back on 5.5.x?** You remain on the legacy (Paper) architecture and miss the Android 13 prompt fixes, the manual credential fallback restoration, and the new docs—migrate to `5.6.x` at minimum before planning the Nitro jump.
2525
2626
## Table of contents
2727

@@ -31,6 +31,7 @@ Modern secure storage for React Native, powered by Nitro Modules. Version 6 ship
3131
- [⚡️ Quick start](#-quick-start)
3232
- [📚 API reference](#-api-reference)
3333
- [🔐 Access control & metadata](#-access-control--metadata)
34+
- [❗ Error handling](#-error-handling)
3435
- [🧪 Simulators and emulators](#-simulators-and-emulators)
3536
- [📈 Performance benchmarks](#-performance-benchmarks)
3637
- [🎮 Example application](#-example-application)
@@ -219,6 +220,47 @@ function YourComponent() {
219220

220221
For comprehensive examples and advanced patterns, see [`HOOKS.md`](./HOOKS.md).
221222

223+
## ❗ Error handling
224+
225+
Every public hook returns failures as `HookError` instances. Besides `message`, each error carries:
226+
227+
- `operation` – the hook action that failed (for example, `useSecureStorage.saveSecret`).
228+
- `cause` – the original native error for additional diagnostics.
229+
- `hint` – a short suggestion shown in the example app and useful for toast copy.
230+
231+
Biometric or device-credential prompts cancelled by the user now surface as a friendly message (`Authentication prompt canceled by the user.`) and *do not* poison hook state. Imperative calls still reject with the raw error so you can decide how to react.
232+
233+
```tsx
234+
import { Text } from 'react-native'
235+
import { useSecureStorage } from 'react-native-sensitive-info'
236+
237+
function SecretsList() {
238+
const { items, error } = useSecureStorage({ service: 'auth', includeValues: true })
239+
240+
if (error) {
241+
if (error.message.includes('Authentication prompt canceled')) {
242+
return <Text>The user dismissed biometric authentication.</Text>
243+
}
244+
245+
return (
246+
<Text testID="secure-error">
247+
{error.message}
248+
{'\n'}Hint: {error.hint ?? 'Check your secure storage configuration.'}
249+
</Text>
250+
)
251+
}
252+
253+
return items.length === 0 ? (
254+
<Text>No secrets stored yet.</Text>
255+
) : (
256+
<Text>{items.map((item) => item.key).join(', ')}</Text>
257+
)
258+
}
259+
```
260+
261+
> [!TIP]
262+
> When using the imperative API, look for the `[E_AUTH_CANCELED]` marker in the thrown error message to detect cancellations.
263+
222264
## Imperative API
223265

224266
```tsx

SECURITY.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
| Version | Supported |
6+
| --- | --- |
7+
| 6.x | ✅ Supported
8+
| 5.6.x | ✅ Supported
9+
| < 5.6.0 | ❌ Not supported
10+
11+
We ship security fixes for the current v6 line and the latest v5 maintenance branch (≥ 5.6.0). Releases prior to 5.6.0 no longer receive patches—upgrade as soon as possible to stay protected.
12+
13+
## Reporting a Vulnerability
14+
15+
1. **Contact**: Email security reports to <[email protected]>.
16+
2. **Disclosure Window**: We aim to acknowledge reports within 3 business days and provide a remediation plan within 10 business days.
17+
3. **Coordinated Disclosure**: Please refrain from publicly disclosing the issue until a fix is available or 30 days have passed since acknowledgement.
18+
19+
## Patch Process
20+
21+
- Critical fixes ship in a point release for the supported branches (6.x and ≥ 5.6.0).
22+
- Vulnerability advisories are published on the GitHub release page and npm once patches are available.
23+
- We credit reporters who follow coordinated disclosure and wish to be acknowledged.
24+
25+
## Hardening Recommendations
26+
27+
- Stay on the latest minor release within your major version to receive defense-in-depth updates.
28+
- Review the [Access control & metadata](README.md#-access-control--metadata) section for guidance on choosing the strongest policies.
29+
- Test secure storage flows on physical hardware before shipping; emulators often omit secure elements.
30+
31+
Thank you for helping us keep `react-native-sensitive-info` secure.

android/src/main/java/com/sensitiveinfo/internal/auth/BiometricAuthenticator.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
1010
import kotlinx.coroutines.suspendCancellableCoroutine
1111
import kotlinx.coroutines.withContext
1212
import com.sensitiveinfo.internal.util.ReactContextHolder
13+
import com.sensitiveinfo.internal.util.SensitiveInfoException
1314
import javax.crypto.Cipher
1415
import kotlin.coroutines.cancellation.CancellationException
1516
import kotlin.coroutines.resume
@@ -42,11 +43,8 @@ internal class BiometricAuthenticator {
4243

4344
return withContext(Dispatchers.Main) {
4445
if (cipher == null && allowLegacyDeviceCredential && !canUseBiometric()) {
45-
if (DeviceCredentialPromptFragment.authenticate(activity, effectivePrompt)) {
46-
cipher
47-
} else {
48-
throw IllegalStateException("Device credential authentication canceled.")
49-
}
46+
DeviceCredentialPromptFragment.authenticate(activity, effectivePrompt)
47+
cipher
5048
} else {
5149
try {
5250
authenticateWithBiometricPrompt(
@@ -59,9 +57,8 @@ internal class BiometricAuthenticator {
5957
} catch (error: Throwable) {
6058
if (error is CancellationException) throw error
6159
if (allowLegacyDeviceCredential) {
62-
if (DeviceCredentialPromptFragment.authenticate(activity, effectivePrompt)) {
63-
return@withContext cipher
64-
}
60+
DeviceCredentialPromptFragment.authenticate(activity, effectivePrompt)
61+
return@withContext cipher
6562
}
6663
throw error
6764
}
@@ -86,11 +83,12 @@ internal class BiometricAuthenticator {
8683
}
8784

8885
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
89-
if (errorCode == BiometricPrompt.ERROR_CANCELED ||
86+
if (
87+
errorCode == BiometricPrompt.ERROR_CANCELED ||
9088
errorCode == BiometricPrompt.ERROR_USER_CANCELED ||
9189
errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON
9290
) {
93-
continuation.cancel()
91+
continuation.resumeWithException(SensitiveInfoException.AuthenticationCanceled())
9492
} else {
9593
continuation.resumeWithException(IllegalStateException(errString.toString()))
9694
}

android/src/main/java/com/sensitiveinfo/internal/auth/DeviceCredentialPromptFragment.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.os.Build
88
import androidx.fragment.app.Fragment
99
import androidx.fragment.app.FragmentActivity
1010
import com.margelo.nitro.sensitiveinfo.AuthenticationPrompt
11+
import com.sensitiveinfo.internal.util.SensitiveInfoException
1112
import kotlin.coroutines.resume
1213
import kotlin.coroutines.resumeWithException
1314
import kotlinx.coroutines.CancellableContinuation
@@ -58,7 +59,7 @@ internal class DeviceCredentialPromptFragment : Fragment() {
5859
if (resultCode == Activity.RESULT_OK) {
5960
cont.resume(true)
6061
} else {
61-
cont.cancel()
62+
cont.resumeWithException(SensitiveInfoException.AuthenticationCanceled())
6263
}
6364
cleanup()
6465
}

android/src/main/java/com/sensitiveinfo/internal/util/SensitiveInfoExceptions.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@ sealed class SensitiveInfoException(
1212
code = "E_NOT_FOUND",
1313
message = "[E_NOT_FOUND] No secret found for key \"$key\" in service \"$service\"."
1414
)
15+
16+
class AuthenticationCanceled : SensitiveInfoException(
17+
code = "E_AUTH_CANCELED",
18+
message = "[E_AUTH_CANCELED] Authentication prompt canceled by the user."
19+
)
1520
}
1621

eslint.config.mts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import { fixupPluginRules } from '@eslint/compat'
2-
import { FlatCompat } from '@eslint/eslintrc'
3-
import js from '@eslint/js'
4-
import typescriptEslint from '@typescript-eslint/eslint-plugin'
5-
import tsParser from '@typescript-eslint/parser'
6-
import importHelpers from 'eslint-plugin-import-helpers'
7-
import prettier from 'eslint-plugin-prettier'
8-
import react from 'eslint-plugin-react'
9-
import reactHooks from 'eslint-plugin-react-hooks'
10-
import globals from 'globals'
11-
import path from 'node:path'
12-
import { fileURLToPath } from 'node:url'
1+
import { fixupPluginRules } from '@eslint/compat';
2+
import { FlatCompat } from '@eslint/eslintrc';
3+
import js from '@eslint/js';
4+
import typescriptEslint from '@typescript-eslint/eslint-plugin';
5+
import tsParser from '@typescript-eslint/parser';
6+
import importHelpers from 'eslint-plugin-import-helpers';
7+
import prettier from 'eslint-plugin-prettier';
8+
import react from 'eslint-plugin-react';
9+
import reactHooks from 'eslint-plugin-react-hooks';
10+
import globals from 'globals';
11+
import path from 'node:path';
12+
import { fileURLToPath } from 'node:url';
1313

14-
const __filename = fileURLToPath(import.meta.url)
15-
const __dirname = path.dirname(__filename)
14+
const __filename = fileURLToPath(import.meta.url);
15+
const __dirname = path.dirname(__filename);
1616

1717
const compat = new FlatCompat({
1818
baseDirectory: __dirname,
1919
recommendedConfig: js.configs.recommended,
2020
allConfig: js.configs.all,
21-
})
21+
});
2222

2323
export default [
2424
...compat.extends(
@@ -66,4 +66,4 @@ export default [
6666
'import/extensions': 'off',
6767
},
6868
},
69-
]
69+
];

0 commit comments

Comments
 (0)