@chipmobilesdk/rn-auth 0.3.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,113 +1,126 @@
1
- # Changelog
2
-
3
- All notable changes to `@chipmobilesdk/rn-auth` are documented here. This project
4
- adheres to [Semantic Versioning](https://semver.org/).
5
-
6
- ## [0.3.0] - 2026-11-01
7
-
8
- Minor release fixing session integrity. Two type changes can need a one-line code
9
- change (see Changed): `signOutGlobal()` resolves to a result instead of `void`, and
10
- `OidcTokens.refreshToken` / `idToken` are optional.
11
-
12
- ### Fixed
13
-
14
- - **A sign-out during a token refresh no longer revives the session.** The refresh
15
- wrote its refresh token back into memory and set the state to `authenticated`
16
- after the sign-out. Every session start and end now bumps a session generation,
17
- and work that crossed one is discarded.
18
- - **Rotated refresh tokens are persisted.** A rotation only reached memory, so a
19
- restart came back with a token the provider may already have retired. Concurrent
20
- `requestAccessToken` calls for one resource now share one network refresh, and a
21
- cache hit no longer flashes `token-refreshing`.
22
- - **Restore deletes the stored session only on an `invalid_grant` verdict.** Any
23
- error other than a recognised network failure used to delete it, so a timeout or a
24
- 5xx signed users out. Errors are classified by OAuth and AADSTS codes in an ordered
25
- table (`oidc/classifyError.ts`) instead of loose substrings; unclassified errors
26
- restore `offline`.
27
- - **Restore no longer stays `loading`** when the Keychain write fails, and keeps the
28
- stored profile when a refresh returns no id token.
29
- - **Sign-in commits only what it stored.** A failed Keychain write left a refresh
30
- token in memory under an `error` state; an authorize without a refresh token now
31
- reports a developer-facing `configuration-error` instead of a session that never
32
- survives a restart.
33
- - **`signOutGlobal()` sends the id token hint** and reports a failed end-session
34
- instead of swallowing it.
35
- - **A throwing state listener no longer turns a completed sign-in into an error.**
36
- - **The development-only `console.warn` of the raw native error is removed.** It
37
- bypassed redaction.
38
-
39
- ### Changed
40
-
41
- - `signOutGlobal()` resolves to `GlobalSignOutResult` (`{ global: 'ended' | 'failed',
42
- error? }`) instead of `void`.
43
- - `OidcTokens.refreshToken` and `OidcTokens.idToken` are optional; a provider that
44
- omits them is reported as absent rather than as an empty string. The persisted
45
- session artifact gains an optional `idToken`; artifacts written by 0.2.1 still
46
- restore.
47
-
48
- ## [0.2.0] - 2026-06-30
49
-
50
- Add Facebook as a social sign-in provider (additive, backward compatible).
51
-
52
- ### Added
53
-
54
- - `'facebook'` is now a valid `ProviderId`. Facebook is federated inside the
55
- Entra External ID tenant like Google/Apple and routed with `domain_hint`
56
- (`facebook.com`) — no native Facebook SDK and no new dependency.
57
- - `auth.action.continueFacebook` message key with English default
58
- `'Continue with Facebook'`.
59
- - `Auth.getPlatformProviders()` and the `useSignInProviders()` hook return all
60
- available providers for the current platform in configuration order, so a
61
- platform can present multiple sign-in buttons (e.g. Google + Facebook).
62
-
63
- ### Changed
64
-
65
- - `defaultProviderForPlatform` / `getPlatformProvider()` / `useSignInProvider()`
66
- now return the first available provider in configuration order. Behavior is
67
- unchanged for existing single-provider (Google/Apple-only) configurations.
68
-
69
- ### Notes
70
-
71
- - Backward compatible: apps that do not configure Facebook observe no change.
72
- - Adoption: add `{ id: 'facebook', platforms: ['ios','android'], domainHint:
73
- 'facebook.com', labelKey: 'auth.action.continueFacebook' }` to `providers`,
74
- federate Facebook in the tenant, and render `useSignInProviders()` for the
75
- multi-button experience. See the README and
76
- `specs/004-facebook-auth-provider/`.
77
- - iOS: offering Facebook (a third-party login) requires Sign in with Apple to
78
- remain available (Apple guideline 4.8) — it does. Update Apple App Privacy and
79
- Google Play Data Safety to add Facebook before release.
80
-
81
- ## [0.1.0] - 2026-06-24
82
-
83
- Initial release of the reusable Microsoft Entra External ID auth library.
84
-
85
- ### Added
86
-
87
- - `createAuth(config, options?)` controller with a single active account: sign-in,
88
- session restore, local `signOut()` and global `signOutGlobal()`, per-resource
89
- `requestAccessToken()`, `getAccessTier()`, and a `requirePremium()` gate.
90
- - `AuthProvider` plus `useAuth`, `useProfile`, `useAccessTier`, `useAccessToken`,
91
- and `useSignInProvider` hooks.
92
- - Injectable ports `OidcAuthClient` and `SecureTokenStore`, with default adapters
93
- `createAppAuthClient()` (react-native-app-auth, PKCE public client) and
94
- `createKeychainSecureStore()` (react-native-keychain).
95
- - Social routing via per-provider `domain_hint` (Google on Android, Apple on iOS),
96
- with Google/Apple federated inside the tenant — no native social SDK required.
97
- - Access-tier evaluation (`evaluateAccessTier`) that prefers stable group object
98
- ids, ignores display names, and fails closed on missing/stale/overage claims.
99
- - Token + refresh persistence restricted to platform secure storage; access
100
- tokens cached in memory with silent refresh.
101
- - Stable user-facing message keys with English defaults (`DEFAULT_MESSAGES`),
102
- no i18n runtime dependency.
103
- - Diagnostics (`AuthError`/`AuthConfigError`) with secret/token redaction.
104
-
105
- ### Notes
106
-
107
- - New package; no migration required. Adoption notes and the tenant/provider
108
- prerequisite checklist are in the README.
109
- - Native redirect configuration and on-device provider flows must be validated
110
- per `specs/003-mobile-entra-auth/contracts/native-setup.md` (the R8 spike).
111
-
112
- [0.2.0]: #
113
- [0.1.0]: #
1
+ # @chipmobilesdk/rn-auth
2
+
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Packaging only; no code change. The package no longer declares `engines.node`: a
8
+ React Native package does not run on Node, so the field only produced install
9
+ warnings in apps on another Node version. rn-i18n's command-line tooling does run
10
+ on Node; its README's compatibility table states the version it needs. ([#14](https://github.com/operationd8979/chip-mobile-sdk/pull/14))
11
+
12
+ ## [0.3.0] - 2026-11-01
13
+
14
+ Minor release fixing session integrity. Two type changes can need a one-line code
15
+ change (see Changed): `signOutGlobal()` resolves to a result instead of `void`, and
16
+ `OidcTokens.refreshToken` / `idToken` are optional.
17
+
18
+ ### Fixed
19
+
20
+ - **A sign-out during a token refresh no longer revives the session.** The refresh
21
+ wrote its refresh token back into memory and set the state to `authenticated`
22
+ after the sign-out. Every session start and end now bumps a session generation,
23
+ and work that crossed one is discarded.
24
+ - **Rotated refresh tokens are persisted.** A rotation only reached memory, so a
25
+ restart came back with a token the provider may already have retired. Concurrent
26
+ `requestAccessToken` calls for one resource now share one network refresh, and a
27
+ cache hit no longer flashes `token-refreshing`.
28
+ - **Restore deletes the stored session only on an `invalid_grant` verdict.** Any
29
+ error other than a recognised network failure used to delete it, so a timeout or a
30
+ 5xx signed users out. Errors are classified by OAuth and AADSTS codes in an ordered
31
+ table (`oidc/classifyError.ts`) instead of loose substrings; unclassified errors
32
+ restore `offline`.
33
+ - **Restore no longer stays `loading`** when the Keychain write fails, and keeps the
34
+ stored profile when a refresh returns no id token.
35
+ - **Sign-in commits only what it stored.** A failed Keychain write left a refresh
36
+ token in memory under an `error` state; an authorize without a refresh token now
37
+ reports a developer-facing `configuration-error` instead of a session that never
38
+ survives a restart.
39
+ - **`signOutGlobal()` sends the id token hint** and reports a failed end-session
40
+ instead of swallowing it.
41
+ - **A throwing state listener no longer turns a completed sign-in into an error.**
42
+ - **The development-only `console.warn` of the raw native error is removed.** It
43
+ bypassed redaction.
44
+
45
+ ### Changed
46
+
47
+ - `signOutGlobal()` resolves to `GlobalSignOutResult` (`{ global: 'ended' | 'failed',
48
+ error? }`) instead of `void`.
49
+ - `OidcTokens.refreshToken` and `OidcTokens.idToken` are optional; a provider that
50
+ omits them is reported as absent rather than as an empty string. The persisted
51
+ session artifact gains an optional `idToken`; artifacts written by 0.2.1 still
52
+ restore.
53
+
54
+ ## [0.2.1] - 2026-08-01
55
+
56
+ ### Changed
57
+
58
+ - The package now ships its agent skill (`skills/`), which consuming apps
59
+ copy in with `scripts/sync-sdk-skills.mjs`. No code change.
60
+
61
+ ## [0.2.0] - 2026-06-30
62
+
63
+ Add Facebook as a social sign-in provider (additive, backward compatible).
64
+
65
+ ### Added
66
+
67
+ - `'facebook'` is now a valid `ProviderId`. Facebook is federated inside the
68
+ Entra External ID tenant like Google/Apple and routed with `domain_hint`
69
+ (`facebook.com`) — no native Facebook SDK and no new dependency.
70
+ - `auth.action.continueFacebook` message key with English default
71
+ `'Continue with Facebook'`.
72
+ - `Auth.getPlatformProviders()` and the `useSignInProviders()` hook return all
73
+ available providers for the current platform in configuration order, so a
74
+ platform can present multiple sign-in buttons (e.g. Google + Facebook).
75
+
76
+ ### Changed
77
+
78
+ - `defaultProviderForPlatform` / `getPlatformProvider()` / `useSignInProvider()`
79
+ now return the first available provider in configuration order. Behavior is
80
+ unchanged for existing single-provider (Google/Apple-only) configurations.
81
+
82
+ ### Notes
83
+
84
+ - Backward compatible: apps that do not configure Facebook observe no change.
85
+ - Adoption: add `{ id: 'facebook', platforms: ['ios','android'], domainHint:
86
+ 'facebook.com', labelKey: 'auth.action.continueFacebook' }` to `providers`,
87
+ federate Facebook in the tenant, and render `useSignInProviders()` for the
88
+ multi-button experience. See the README and
89
+ `specs/004-facebook-auth-provider/`.
90
+ - iOS: offering Facebook (a third-party login) requires Sign in with Apple to
91
+ remain available (Apple guideline 4.8) — it does. Update Apple App Privacy and
92
+ Google Play Data Safety to add Facebook before release.
93
+
94
+ ## [0.1.0] - 2026-06-24
95
+
96
+ Initial release of the reusable Microsoft Entra External ID auth library.
97
+
98
+ ### Added
99
+
100
+ - `createAuth(config, options?)` controller with a single active account: sign-in,
101
+ session restore, local `signOut()` and global `signOutGlobal()`, per-resource
102
+ `requestAccessToken()`, `getAccessTier()`, and a `requirePremium()` gate.
103
+ - `AuthProvider` plus `useAuth`, `useProfile`, `useAccessTier`, `useAccessToken`,
104
+ and `useSignInProvider` hooks.
105
+ - Injectable ports `OidcAuthClient` and `SecureTokenStore`, with default adapters
106
+ `createAppAuthClient()` (react-native-app-auth, PKCE public client) and
107
+ `createKeychainSecureStore()` (react-native-keychain).
108
+ - Social routing via per-provider `domain_hint` (Google on Android, Apple on iOS),
109
+ with Google/Apple federated inside the tenant — no native social SDK required.
110
+ - Access-tier evaluation (`evaluateAccessTier`) that prefers stable group object
111
+ ids, ignores display names, and fails closed on missing/stale/overage claims.
112
+ - Token + refresh persistence restricted to platform secure storage; access
113
+ tokens cached in memory with silent refresh.
114
+ - Stable user-facing message keys with English defaults (`DEFAULT_MESSAGES`),
115
+ no i18n runtime dependency.
116
+ - Diagnostics (`AuthError`/`AuthConfigError`) with secret/token redaction.
117
+
118
+ ### Notes
119
+
120
+ - New package; no migration required. Adoption notes and the tenant/provider
121
+ prerequisite checklist are in the README.
122
+ - Native redirect configuration and on-device provider flows must be validated
123
+ per `specs/003-mobile-entra-auth/contracts/native-setup.md` (the R8 spike).
124
+
125
+ [0.2.0]: #
126
+ [0.1.0]: #
package/README.md CHANGED
@@ -1,190 +1,190 @@
1
- # @chipmobilesdk/rn-auth
2
-
3
- Reusable Microsoft Entra External ID authentication for ChipMobileSdk-family
4
- React Native apps. The library is a **public OAuth2/OIDC client** (Authorization
5
- Code + PKCE, **no backend, no client secret**). Android presents **Continue with
6
- Google**, iOS presents **Continue with Apple**, and either platform can also
7
- present **Continue with Facebook**; all start the same External ID
8
- sign-up/sign-in user flow, routed to the right social provider with a
9
- `domain_hint`. Google, Apple, and Facebook are federated **inside the External ID
10
- tenant**, so the app embeds no native social SDK and no Sign in with Apple
11
- entitlement. Each platform renders all of its configured providers as separate
12
- buttons in configuration order.
13
-
14
- > **License:** `UNLICENSED`. Published publicly for use by the owner's
15
- > applications; no open-source license is granted.
16
-
17
- - **Version**: `0.3.0`
18
- - **Entrypoint**: `@chipmobilesdk/rn-auth`
19
-
20
- - Single active account, session restore, local + global sign-out.
21
- - Per-resource delegated access tokens for Azure resources.
22
- - Group/role/claim access-tier evaluation that **fails closed**.
23
- - Tokens persist only in platform secure storage (Keychain / Keystore).
24
- - No i18n runtime dependency: user-facing copy is stable keys + English defaults.
25
-
26
- ## Install (consuming app)
27
-
28
- ```sh
29
- npm install @chipmobilesdk/rn-auth react-native-app-auth react-native-keychain
30
- cd ios && pod install
31
- ```
32
-
33
- `react`, `react-native`, `react-native-app-auth`, and `react-native-keychain`
34
- are **peer dependencies**. The two native libraries are only required by the
35
- default adapters — an app injecting custom ports may omit them.
36
-
37
- See [native setup](../../specs/003-mobile-entra-auth/contracts/native-setup.md)
38
- for the iOS `Info.plist` URL scheme and the Android `appAuthRedirectScheme`
39
- Gradle placeholder.
40
-
41
- ## Quick start
42
-
43
- ```tsx
44
- import {
45
- AuthProvider,
46
- createAuth,
47
- createAppAuthClient,
48
- createKeychainSecureStore,
49
- useAuth,
50
- useSignInProvider,
51
- } from '@chipmobilesdk/rn-auth';
52
-
53
- const auth = createAuth({
54
- authority: 'https://<tenant>.ciamlogin.com/<tenant>.onmicrosoft.com',
55
- clientId: '<public-client-id>',
56
- redirectUrl: 'com.yourapp://oauthredirect',
57
- scopes: ['openid', 'profile', 'offline_access'],
58
- providers: [
59
- { id: 'google', platforms: ['android'], domainHint: 'google.com', labelKey: 'auth.action.continueGoogle' },
60
- { id: 'apple', platforms: ['ios'], domainHint: 'apple.com', labelKey: 'auth.action.continueApple' },
61
- // Optional third provider, shown on both platforms after the platform-native one:
62
- { id: 'facebook', platforms: ['ios', 'android'], domainHint: 'facebook.com', labelKey: 'auth.action.continueFacebook' },
63
- ],
64
- resources: [{ key: 'storage', scopes: ['https://storage.azure.com/user_impersonation'] }],
65
- accessTierMapping: { premiumGroupObjectIds: ['<premium-group-object-id>'] },
66
- secureStore: createKeychainSecureStore(),
67
- authClient: createAppAuthClient(),
68
- });
69
-
70
- export const App = () => (
71
- <AuthProvider auth={auth}>
72
- <Screen />
73
- </AuthProvider>
74
- );
75
- ```
76
-
77
- Hooks: `useAuth()` (state + `signIn`/`signOut`/`signOutGlobal`/`restore`/`requirePremium`),
78
- `useProfile()`, `useAccessTier()`, `useAccessToken(resourceKey)`,
79
- `useSignInProviders()` (all platform providers, in config order — map to one
80
- button each) and `useSignInProvider()` (first available, for a single-button UI).
81
-
82
- ### Session lifecycle guarantees
83
-
84
- - **Restore deletes the stored session only when the provider rejects the refresh
85
- token** (`invalid_grant` and the equivalent AADSTS codes). A timeout, a 5xx or an
86
- error the package cannot classify restores `offline`: cached profile, tier
87
- unverified, retried on the next `restore()`.
88
- - **A sign-out wins.** A token refresh still in flight when the user signs out is
89
- discarded; it cannot write a token back or return the state to `authenticated`.
90
- - **Rotated refresh tokens are persisted** as soon as they arrive, and concurrent
91
- `requestAccessToken` calls for one resource share one network refresh.
92
- - **`signOutGlobal()` returns `{ global: 'ended' | 'failed', error? }`.** Local state
93
- is cleared either way; `failed` means the tenant session may still be alive.
94
-
95
- ## Configuration contract (LIB-010)
96
-
97
- The library accepts **public values only**. Provider secrets, Apple keys, and
98
- client secrets are configured in the External ID tenant, never in the app
99
- ([FR-017](../../specs/003-mobile-entra-auth/spec.md)).
100
-
101
- | Section | Keys |
102
- |---------|------|
103
- | A — Tenant / client | `authority`, `clientId`, `redirectUrl`, `scopes` (include `offline_access`) |
104
- | B — Android Google | `providers[google]`: `domainHint`, `platforms: ['android']`, `labelKey` |
105
- | C — iOS Apple | `providers[apple]`: `domainHint`, `platforms: ['ios']`, `labelKey` |
106
- | C2 — Facebook (optional) | `providers[facebook]`: `domainHint: 'facebook.com'`, `platforms: ['ios','android']`, `labelKey: 'auth.action.continueFacebook'`, optional `available` |
107
- | D — Resources | `resources[].key`, `resources[].scopes`, `resources[].displayName?` |
108
- | E — Access tier | `accessTierMapping.premiumGroupObjectIds` (+ optional role names / claim matchers; display names are diagnostics-only) |
109
- | F — Ports | `secureStore` (required), `authClient` (optional), `messages` (optional copy override) |
110
-
111
- Full contract: [auth-config.md](../../specs/003-mobile-entra-auth/contracts/auth-config.md)
112
- and [public-api.md](../../specs/003-mobile-entra-auth/contracts/public-api.md).
113
-
114
- ## Tenant / provider prerequisite checklist (LIB-018)
115
-
116
- These are **external** prerequisites (FR-013/FR-018) completed before the library
117
- can sign users in:
118
-
119
- **External ID tenant**
120
- - [ ] Customer (CIAM) tenant created; sign-up/sign-in user flow configured.
121
- - [ ] App registration created as a **public client**; redirect URI added (must match the native scheme, e.g. `com.yourapp://oauthredirect`).
122
- - [ ] API permissions / scopes for each target Azure resource consented.
123
- - [ ] Premium group(s) created; record the **group object id** (not the name).
124
- - [ ] Groups (or roles) emitted in the token; plan for groups overage.
125
-
126
- **Google (federated in the tenant)**
127
- - [ ] Google Cloud OAuth client configured; client id/secret stored in the **tenant** IdP config (never in the app).
128
- - [ ] Google added as an identity provider in the External ID user flow.
129
-
130
- **Apple (federated in the tenant)**
131
- - [ ] Apple Services ID + Sign in with Apple key configured; stored in the **tenant** IdP config.
132
- - [ ] Apple added as an identity provider in the External ID user flow.
133
- - [ ] Note: Apple sign-in operational credentials expire on a schedule and must be rotated tenant-side.
134
-
135
- **Facebook (federated in the tenant — optional)**
136
- - [ ] Facebook app created in Meta for Developers; **App ID + App Secret** stored in the **tenant** IdP config (never in the app).
137
- - [ ] Facebook added as an identity provider in the External ID user flow; `domain_hint` confirmed (expected `facebook.com`).
138
- - [ ] Facebook app OAuth redirect / valid OAuth domains match the tenant federation redirect.
139
- - [ ] iOS: because Facebook is a third-party login, keep Sign in with Apple offered on iOS (Apple guideline 4.8).
140
- - [ ] Update Apple App Privacy and Google Play Data Safety to include Facebook as an identity provider before release.
141
-
142
- ## Security & privacy
143
-
144
- - Tokens and refresh artifacts persist only in `SecureTokenStore` (Keychain /
145
- Keystore). Access tokens are kept in memory only.
146
- - Access-tier decisions prefer stable group **object ids**; display names never
147
- grant access; missing/overage claims fail closed.
148
- - Diagnostics (`AuthError`) never contain secrets or token values.
149
- - Authentication and profile data are collected and transmitted to the
150
- identity providers and configured Azure resources — update Apple App Privacy
151
- and Google Play Data Safety before release.
152
-
153
- ## Public API
154
-
155
- Everything below is exported from `@chipmobilesdk/rn-auth`; deep imports into
156
- `config/`, `core/`, `tokens/`, `oidc/`, `access/`, `diagnostics/`, or `react/`
157
- are unsupported.
158
-
159
- - Controller and configuration: `createAuth`, `Auth`, `AuthConfig`,
160
- `CreateAuthOptions`, `AuthState`, `AuthStatus`, `RestoreStatus`
161
- - Identity and access: `Profile`, `ProviderConfig`, `ProviderIdentity`,
162
- `ResourceConfig`, `AccessTier`, `AccessTierMapping`, `AccessTokenResult`,
163
- `PremiumGateResult`, `evaluateAccessTier`, `failClosedTier`
164
- - Ports and default adapters: `SecureTokenStore`, `createKeychainSecureStore`,
165
- `AUTH_STORAGE_KEY`, `OidcAuthClient`, `createAppAuthClient` and the public OIDC
166
- request/token types
167
- - React: `AuthProvider`, `useAuth`, `useProfile`, `useAccessTier`,
168
- `useAccessToken`, `useSignInProvider`, `useSignInProviders`
169
- - Diagnostics and copy: `AuthError`, `AuthConfigError`, `isAuthError`, `redact`,
170
- `DEFAULT_MESSAGES`, `resolveMessages` and their public code/message types
171
-
172
- ## Compatibility and validation
173
-
174
- React ≥19 · React Native ≥0.85 · `react-native-app-auth` ≥8 ·
175
- `react-native-keychain` ≥10 · iOS + Android · Node ≥22.11 for tooling.
176
-
177
- The demo integration lives in `src/screens/AuthDemoScreen.tsx`. Run from the
178
- repository root:
179
-
180
- ```bash
181
- npm run typecheck:auth
182
- npm run pack:auth
183
- npm run validate:auth
184
- ```
185
-
186
- ## Distribution and license
187
-
188
- Published as a public npm package (`publishConfig.access: public`) for use by
189
- the owner's applications. License: `UNLICENSED`; no open-source license is
190
- granted.
1
+ # @chipmobilesdk/rn-auth
2
+
3
+ Reusable Microsoft Entra External ID authentication for ChipMobileSdk-family
4
+ React Native apps. The library is a **public OAuth2/OIDC client** (Authorization
5
+ Code + PKCE, **no backend, no client secret**). Android presents **Continue with
6
+ Google**, iOS presents **Continue with Apple**, and either platform can also
7
+ present **Continue with Facebook**; all start the same External ID
8
+ sign-up/sign-in user flow, routed to the right social provider with a
9
+ `domain_hint`. Google, Apple, and Facebook are federated **inside the External ID
10
+ tenant**, so the app embeds no native social SDK and no Sign in with Apple
11
+ entitlement. Each platform renders all of its configured providers as separate
12
+ buttons in configuration order.
13
+
14
+ > **License:** `UNLICENSED`. Published publicly for use by the owner's
15
+ > applications; no open-source license is granted.
16
+
17
+ - **Version**: see [CHANGELOG](CHANGELOG.md)
18
+ - **Entrypoint**: `@chipmobilesdk/rn-auth`
19
+
20
+ - Single active account, session restore, local + global sign-out.
21
+ - Per-resource delegated access tokens for Azure resources.
22
+ - Group/role/claim access-tier evaluation that **fails closed**.
23
+ - Tokens persist only in platform secure storage (Keychain / Keystore).
24
+ - No i18n runtime dependency: user-facing copy is stable keys + English defaults.
25
+
26
+ ## Install (consuming app)
27
+
28
+ ```sh
29
+ npm install @chipmobilesdk/rn-auth react-native-app-auth react-native-keychain
30
+ cd ios && pod install
31
+ ```
32
+
33
+ `react`, `react-native`, `react-native-app-auth`, and `react-native-keychain`
34
+ are **peer dependencies**. The two native libraries are only required by the
35
+ default adapters — an app injecting custom ports may omit them.
36
+
37
+ See [native setup](../../specs/003-mobile-entra-auth/contracts/native-setup.md)
38
+ for the iOS `Info.plist` URL scheme and the Android `appAuthRedirectScheme`
39
+ Gradle placeholder.
40
+
41
+ ## Quick start
42
+
43
+ ```tsx
44
+ import {
45
+ AuthProvider,
46
+ createAuth,
47
+ createAppAuthClient,
48
+ createKeychainSecureStore,
49
+ useAuth,
50
+ useSignInProvider,
51
+ } from '@chipmobilesdk/rn-auth';
52
+
53
+ const auth = createAuth({
54
+ authority: 'https://<tenant>.ciamlogin.com/<tenant>.onmicrosoft.com',
55
+ clientId: '<public-client-id>',
56
+ redirectUrl: 'com.yourapp://oauthredirect',
57
+ scopes: ['openid', 'profile', 'offline_access'],
58
+ providers: [
59
+ { id: 'google', platforms: ['android'], domainHint: 'google.com', labelKey: 'auth.action.continueGoogle' },
60
+ { id: 'apple', platforms: ['ios'], domainHint: 'apple.com', labelKey: 'auth.action.continueApple' },
61
+ // Optional third provider, shown on both platforms after the platform-native one:
62
+ { id: 'facebook', platforms: ['ios', 'android'], domainHint: 'facebook.com', labelKey: 'auth.action.continueFacebook' },
63
+ ],
64
+ resources: [{ key: 'storage', scopes: ['https://storage.azure.com/user_impersonation'] }],
65
+ accessTierMapping: { premiumGroupObjectIds: ['<premium-group-object-id>'] },
66
+ secureStore: createKeychainSecureStore(),
67
+ authClient: createAppAuthClient(),
68
+ });
69
+
70
+ export const App = () => (
71
+ <AuthProvider auth={auth}>
72
+ <Screen />
73
+ </AuthProvider>
74
+ );
75
+ ```
76
+
77
+ Hooks: `useAuth()` (state + `signIn`/`signOut`/`signOutGlobal`/`restore`/`requirePremium`),
78
+ `useProfile()`, `useAccessTier()`, `useAccessToken(resourceKey)`,
79
+ `useSignInProviders()` (all platform providers, in config order — map to one
80
+ button each) and `useSignInProvider()` (first available, for a single-button UI).
81
+
82
+ ### Session lifecycle guarantees
83
+
84
+ - **Restore deletes the stored session only when the provider rejects the refresh
85
+ token** (`invalid_grant` and the equivalent AADSTS codes). A timeout, a 5xx or an
86
+ error the package cannot classify restores `offline`: cached profile, tier
87
+ unverified, retried on the next `restore()`.
88
+ - **A sign-out wins.** A token refresh still in flight when the user signs out is
89
+ discarded; it cannot write a token back or return the state to `authenticated`.
90
+ - **Rotated refresh tokens are persisted** as soon as they arrive, and concurrent
91
+ `requestAccessToken` calls for one resource share one network refresh.
92
+ - **`signOutGlobal()` returns `{ global: 'ended' | 'failed', error? }`.** Local state
93
+ is cleared either way; `failed` means the tenant session may still be alive.
94
+
95
+ ## Configuration contract (LIB-010)
96
+
97
+ The library accepts **public values only**. Provider secrets, Apple keys, and
98
+ client secrets are configured in the External ID tenant, never in the app
99
+ ([FR-017](../../specs/003-mobile-entra-auth/spec.md)).
100
+
101
+ | Section | Keys |
102
+ |---------|------|
103
+ | A — Tenant / client | `authority`, `clientId`, `redirectUrl`, `scopes` (include `offline_access`) |
104
+ | B — Android Google | `providers[google]`: `domainHint`, `platforms: ['android']`, `labelKey` |
105
+ | C — iOS Apple | `providers[apple]`: `domainHint`, `platforms: ['ios']`, `labelKey` |
106
+ | C2 — Facebook (optional) | `providers[facebook]`: `domainHint: 'facebook.com'`, `platforms: ['ios','android']`, `labelKey: 'auth.action.continueFacebook'`, optional `available` |
107
+ | D — Resources | `resources[].key`, `resources[].scopes`, `resources[].displayName?` |
108
+ | E — Access tier | `accessTierMapping.premiumGroupObjectIds` (+ optional role names / claim matchers; display names are diagnostics-only) |
109
+ | F — Ports | `secureStore` (required), `authClient` (optional), `messages` (optional copy override) |
110
+
111
+ Full contract: [auth-config.md](../../specs/003-mobile-entra-auth/contracts/auth-config.md)
112
+ and [public-api.md](../../specs/003-mobile-entra-auth/contracts/public-api.md).
113
+
114
+ ## Tenant / provider prerequisite checklist (LIB-018)
115
+
116
+ These are **external** prerequisites (FR-013/FR-018) completed before the library
117
+ can sign users in:
118
+
119
+ **External ID tenant**
120
+ - [ ] Customer (CIAM) tenant created; sign-up/sign-in user flow configured.
121
+ - [ ] App registration created as a **public client**; redirect URI added (must match the native scheme, e.g. `com.yourapp://oauthredirect`).
122
+ - [ ] API permissions / scopes for each target Azure resource consented.
123
+ - [ ] Premium group(s) created; record the **group object id** (not the name).
124
+ - [ ] Groups (or roles) emitted in the token; plan for groups overage.
125
+
126
+ **Google (federated in the tenant)**
127
+ - [ ] Google Cloud OAuth client configured; client id/secret stored in the **tenant** IdP config (never in the app).
128
+ - [ ] Google added as an identity provider in the External ID user flow.
129
+
130
+ **Apple (federated in the tenant)**
131
+ - [ ] Apple Services ID + Sign in with Apple key configured; stored in the **tenant** IdP config.
132
+ - [ ] Apple added as an identity provider in the External ID user flow.
133
+ - [ ] Note: Apple sign-in operational credentials expire on a schedule and must be rotated tenant-side.
134
+
135
+ **Facebook (federated in the tenant — optional)**
136
+ - [ ] Facebook app created in Meta for Developers; **App ID + App Secret** stored in the **tenant** IdP config (never in the app).
137
+ - [ ] Facebook added as an identity provider in the External ID user flow; `domain_hint` confirmed (expected `facebook.com`).
138
+ - [ ] Facebook app OAuth redirect / valid OAuth domains match the tenant federation redirect.
139
+ - [ ] iOS: because Facebook is a third-party login, keep Sign in with Apple offered on iOS (Apple guideline 4.8).
140
+ - [ ] Update Apple App Privacy and Google Play Data Safety to include Facebook as an identity provider before release.
141
+
142
+ ## Security & privacy
143
+
144
+ - Tokens and refresh artifacts persist only in `SecureTokenStore` (Keychain /
145
+ Keystore). Access tokens are kept in memory only.
146
+ - Access-tier decisions prefer stable group **object ids**; display names never
147
+ grant access; missing/overage claims fail closed.
148
+ - Diagnostics (`AuthError`) never contain secrets or token values.
149
+ - Authentication and profile data are collected and transmitted to the
150
+ identity providers and configured Azure resources — update Apple App Privacy
151
+ and Google Play Data Safety before release.
152
+
153
+ ## Public API
154
+
155
+ Everything below is exported from `@chipmobilesdk/rn-auth`; deep imports into
156
+ `config/`, `core/`, `tokens/`, `oidc/`, `access/`, `diagnostics/`, or `react/`
157
+ are unsupported.
158
+
159
+ - Controller and configuration: `createAuth`, `Auth`, `AuthConfig`,
160
+ `CreateAuthOptions`, `AuthState`, `AuthStatus`, `RestoreStatus`
161
+ - Identity and access: `Profile`, `ProviderConfig`, `ProviderIdentity`,
162
+ `ResourceConfig`, `AccessTier`, `AccessTierMapping`, `AccessTokenResult`,
163
+ `PremiumGateResult`, `evaluateAccessTier`, `failClosedTier`
164
+ - Ports and default adapters: `SecureTokenStore`, `createKeychainSecureStore`,
165
+ `AUTH_STORAGE_KEY`, `OidcAuthClient`, `createAppAuthClient` and the public OIDC
166
+ request/token types
167
+ - React: `AuthProvider`, `useAuth`, `useProfile`, `useAccessTier`,
168
+ `useAccessToken`, `useSignInProvider`, `useSignInProviders`
169
+ - Diagnostics and copy: `AuthError`, `AuthConfigError`, `isAuthError`, `redact`,
170
+ `DEFAULT_MESSAGES`, `resolveMessages` and their public code/message types
171
+
172
+ ## Compatibility and validation
173
+
174
+ React ≥19 · React Native ≥0.85 · `react-native-app-auth` ≥8 ·
175
+ `react-native-keychain` ≥10 · iOS + Android · Node ≥22.11 for tooling.
176
+
177
+ The demo integration lives in `src/screens/AuthDemoScreen.tsx`. Run from the
178
+ repository root:
179
+
180
+ ```bash
181
+ npm run typecheck --workspace @chipmobilesdk/rn-auth
182
+ npm run pack:check --workspace @chipmobilesdk/rn-auth
183
+ npm run validate
184
+ ```
185
+
186
+ ## Distribution and license
187
+
188
+ Published as a public npm package (`publishConfig.access: public`) for use by
189
+ the owner's applications. License: `UNLICENSED`; no open-source license is
190
+ granted.