@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 +126 -113
- package/README.md +190 -190
- package/package.json +51 -54
- package/skills/sdk-rn-auth/SKILL.md +62 -63
- package/src/access/accessTier.ts +88 -88
- package/src/config/types.ts +154 -154
- package/src/config/validateConfig.ts +114 -108
- package/src/core/authMachine.ts +364 -364
- package/src/core/profile.ts +57 -60
- package/src/core/session.ts +105 -112
- package/src/diagnostics/errors.ts +87 -85
- package/src/diagnostics/messageKeys.ts +59 -61
- package/src/index.ts +71 -83
- package/src/oidc/authClient.ts +195 -199
- package/src/oidc/classifyError.ts +76 -76
- package/src/oidc/socialRouting.ts +66 -73
- package/src/react/AuthProvider.tsx +41 -47
- package/src/react/hooks.ts +86 -91
- package/src/tokens/accessToken.ts +159 -164
- package/src/tokens/secureStore.ts +51 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,113 +1,126 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
- **
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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**:
|
|
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
|
|
182
|
-
npm run pack: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.
|
|
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.
|