@burnt-labs/expo-satya-attest 3.1.0 → 3.2.0
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 +15 -0
- package/PROVIDER_CONFIG.md +66 -0
- package/README.md +110 -0
- package/android/build.gradle +2 -2
- package/build/SatyaProviderAttestButton.d.ts +1 -1
- package/build/SatyaProviderAttestButton.d.ts.map +1 -1
- package/build/SatyaProviderAttestButton.js +26 -2
- package/build/SatyaProviderAttestButton.js.map +1 -1
- package/build/components/providerAttest/ProviderAttestModal.d.ts.map +1 -1
- package/build/components/providerAttest/ProviderAttestModal.js +34 -10
- package/build/components/providerAttest/ProviderAttestModal.js.map +1 -1
- package/build/components/providerAttest/copy.d.ts +1 -0
- package/build/components/providerAttest/copy.d.ts.map +1 -1
- package/build/components/providerAttest/copy.js +15 -0
- package/build/components/providerAttest/copy.js.map +1 -1
- package/build/components/providerAttest/flowSelection.d.ts +18 -0
- package/build/components/providerAttest/flowSelection.d.ts.map +1 -0
- package/build/components/providerAttest/flowSelection.js +25 -0
- package/build/components/providerAttest/flowSelection.js.map +1 -0
- package/build/components/providerAttest/teels/resolveTeelsProvider.d.ts +26 -0
- package/build/components/providerAttest/teels/resolveTeelsProvider.d.ts.map +1 -0
- package/build/components/providerAttest/teels/resolveTeelsProvider.js +71 -0
- package/build/components/providerAttest/teels/resolveTeelsProvider.js.map +1 -0
- package/build/components/providerAttest/teels/teelsProtocol.d.ts +41 -0
- package/build/components/providerAttest/teels/teelsProtocol.d.ts.map +1 -0
- package/build/components/providerAttest/teels/teelsProtocol.js +257 -0
- package/build/components/providerAttest/teels/teelsProtocol.js.map +1 -0
- package/build/components/providerAttest/teels/useTeelsProviderFlow.d.ts +11 -0
- package/build/components/providerAttest/teels/useTeelsProviderFlow.d.ts.map +1 -0
- package/build/components/providerAttest/teels/useTeelsProviderFlow.js +489 -0
- package/build/components/providerAttest/teels/useTeelsProviderFlow.js.map +1 -0
- package/build/components/providerAttest/types.d.ts +12 -1
- package/build/components/providerAttest/types.d.ts.map +1 -1
- package/build/components/providerAttest/types.js.map +1 -1
- package/build/components/providerAttest/useProviderAttestFlow.d.ts +9 -1
- package/build/components/providerAttest/useProviderAttestFlow.d.ts.map +1 -1
- package/build/components/providerAttest/useProviderAttestFlow.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/satyaConfig.d.ts +7 -0
- package/build/satyaConfig.d.ts.map +1 -1
- package/build/satyaConfig.js +15 -1
- package/build/satyaConfig.js.map +1 -1
- package/build/sdkMetadata.generated.d.ts +1 -1
- package/build/sdkMetadata.generated.js +1 -1
- package/build/sdkMetadata.generated.js.map +1 -1
- package/build/types/error.types.d.ts +5 -1
- package/build/types/error.types.d.ts.map +1 -1
- package/build/types/error.types.js +13 -0
- package/build/types/error.types.js.map +1 -1
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64/libsatya_ffi.a +0 -0
- package/ios/Frameworks/libsatya_ffi-rs.xcframework/ios-arm64_x86_64-simulator/libsatya_ffi.a +0 -0
- package/ios/SatyaAttest.podspec +1 -1
- package/package.json +2 -2
- package/src/SatyaProviderAttestButton.tsx +56 -2
- package/src/components/providerAttest/ProviderAttestModal.tsx +47 -8
- package/src/components/providerAttest/copy.ts +16 -0
- package/src/components/providerAttest/flowSelection.ts +44 -0
- package/src/components/providerAttest/teels/resolveTeelsProvider.ts +135 -0
- package/src/components/providerAttest/teels/teelsProtocol.ts +294 -0
- package/src/components/providerAttest/teels/useTeelsProviderFlow.ts +648 -0
- package/src/components/providerAttest/types.ts +19 -1
- package/src/components/providerAttest/useProviderAttestFlow.ts +12 -1
- package/src/index.ts +2 -0
- package/src/satyaConfig.ts +29 -1
- package/src/sdkMetadata.generated.ts +1 -1
- package/src/types/error.types.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.2.0 - 2026-08-07
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- An opt-in, SDK-only TeeLS hosted passthrough selected with
|
|
10
|
+
`initSatyaAttest({ providerId, appId, teelsFlow: true })`. The flow preserves
|
|
11
|
+
normal provider grants, performs one existing provider-policy access preflight,
|
|
12
|
+
opens the registry login URL in an origin-bound WebView bridge, and returns the
|
|
13
|
+
opaque JSON result through `onTeelsComplete` without native replay, SATYA
|
|
14
|
+
artifact construction, local verification, or verifier artifact submission.
|
|
15
|
+
- TeeLS-specific progress phases and errors for null/invalid results, hosted
|
|
16
|
+
failures, timeout, and WebView failure. The feature is disabled by default;
|
|
17
|
+
omitting `teelsFlow` in a later init restores native routing for subsequently
|
|
18
|
+
mounted buttons.
|
|
19
|
+
|
|
5
20
|
## 3.1.0 - 2026-07-31
|
|
6
21
|
|
|
7
22
|
### Added
|
package/PROVIDER_CONFIG.md
CHANGED
|
@@ -564,6 +564,72 @@ unrelated to the registry catalog flag `metadata.isSatyaReady`.)
|
|
|
564
564
|
> This is distinct from endpoint `capture`: `injection` is page JavaScript at the provider
|
|
565
565
|
> level; `capture` is per-endpoint active-fetch/readiness config.
|
|
566
566
|
|
|
567
|
+
### Hosted TeeLS passthrough provider injection
|
|
568
|
+
|
|
569
|
+
An SDK session initialized with `teelsFlow: true` may use a normal granted
|
|
570
|
+
provider as a hosted TeeLS passthrough. There is no Registry flow discriminator:
|
|
571
|
+
the SDK binds TeeLS mode to the configured provider/app pair, performs the
|
|
572
|
+
normal provider-policy access preflight, opens `login.url`, and skips request
|
|
573
|
+
capture and native replay.
|
|
574
|
+
|
|
575
|
+
Keep the provider structurally valid for the normal Registry. Its existing
|
|
576
|
+
endpoint, SNI, pins, grants, and verifier policy remain ordinary provider
|
|
577
|
+
policy even though the TeeLS attempt does not fire or replay that endpoint.
|
|
578
|
+
The hosted page origin may therefore differ from the inert endpoint/SNI host;
|
|
579
|
+
the TeeLS WebView origin is always derived from `login.url`, while the access
|
|
580
|
+
preflight continues to validate the governed provider policy.
|
|
581
|
+
|
|
582
|
+
Generate the canonical adapter injection from the Expo SDK checkout:
|
|
583
|
+
|
|
584
|
+
```bash
|
|
585
|
+
cd platform_sdks/expo-satya-attest
|
|
586
|
+
node scripts/generate-hosted-teels-provider-injection.js \
|
|
587
|
+
--origin https://hosted-teels.example \
|
|
588
|
+
--json
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Put the resulting JSON string in the provider's top-level `injection`. The
|
|
592
|
+
adapter is additive. It reuses, in order:
|
|
593
|
+
|
|
594
|
+
1. standard `TeeLS.create({ onSuccess, onError })` callbacks by intercepting the
|
|
595
|
+
initial `window.TeeLS` assignment before the hosted bundle runs;
|
|
596
|
+
2. the `teels:attestation` `CustomEvent`, reading
|
|
597
|
+
`event.detail.attestation` and optional `event.detail.data`; and
|
|
598
|
+
3. the durable `window.__teels.attestation` snapshot for success recovery when
|
|
599
|
+
the adapter attaches after completion.
|
|
600
|
+
|
|
601
|
+
The adapter calls the page's original callbacks first and forwards to SATYA in
|
|
602
|
+
`finally`, preserving callback arguments, `this`, return values, thrown errors,
|
|
603
|
+
the returned TeeLS handle, and every other TeeLS SDK export. The second argument
|
|
604
|
+
to SATYA is always the attestation; if present, the first `onSuccess` argument is
|
|
605
|
+
forwarded as `claimData` and added to the completion payload as `claimData`.
|
|
606
|
+
Null success maps to `attestation_missing`; all other TeeLS errors map to
|
|
607
|
+
`teels_flow_failed`.
|
|
608
|
+
The SDK bridge and adapter both enforce exactly-once terminal settlement.
|
|
609
|
+
|
|
610
|
+
The terminal API is distinct from native login readiness and supports the optional
|
|
611
|
+
`claimData` passthrough:
|
|
612
|
+
|
|
613
|
+
```js
|
|
614
|
+
window.__SATYA_TEELS_BRIDGE__.complete(attestation, claimData);
|
|
615
|
+
window.__SATYA_TEELS_BRIDGE__.fail('attestation_missing');
|
|
616
|
+
window.__SATYA_TEELS_BRIDGE__.fail('teels_flow_failed');
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Do not emit or overload `__satyaReady` for TeeLS completion. Do not post the
|
|
620
|
+
opaque result through the native-replay interceptor. The adapter must remain
|
|
621
|
+
main-frame and exact-origin scoped and must tolerate being evaluated only once;
|
|
622
|
+
SATYA installs the bridge before executing the Registry injection.
|
|
623
|
+
|
|
624
|
+
Success can be recovered from the durable snapshot after a late injection, but
|
|
625
|
+
a past error cannot. Before using a hosted provider beyond development, prove
|
|
626
|
+
on physical iOS and Android—both with the external TeeLS bundle cached and
|
|
627
|
+
uncached—that `injectedJavaScriptBeforeContentLoaded` wraps `TeeLS.create`
|
|
628
|
+
before the page calls it. If that ordering cannot be established, add a minimal
|
|
629
|
+
page-owned error seam instead of relying on best-effort console interception.
|
|
630
|
+
Also confirm the hosted TeeLS SDK invokes its configured `onError` for every
|
|
631
|
+
terminal failure; a rendered SDK error alone cannot be observed by the adapter.
|
|
632
|
+
|
|
567
633
|
---
|
|
568
634
|
|
|
569
635
|
## 10. Writing regex patterns (HTML / text bodies)
|
package/README.md
CHANGED
|
@@ -26,6 +26,9 @@ with `CONFIG_INVALID`. An unregistered or unapproved id fails at the verifier
|
|
|
26
26
|
with `APP_NOT_REGISTERED` / `APP_DISABLED`, and an ungranted provider fails with
|
|
27
27
|
`PROVIDER_ACCESS_DENIED`. See
|
|
28
28
|
[`docs/MULTI_TENANT_VERIFICATION.md`](../../docs/MULTI_TENANT_VERIFICATION.md).
|
|
29
|
+
Flows also fail with `app_platform_not_configured` when the app has no
|
|
30
|
+
registered identity for the current platform, so register every platform you
|
|
31
|
+
develop and test on.
|
|
29
32
|
|
|
30
33
|
## Current Flow
|
|
31
34
|
|
|
@@ -51,6 +54,113 @@ The earlier mock and plain `webview-observed` modes are no longer exposed throug
|
|
|
51
54
|
high-level Expo client because they do not prove a provider response fetched through a
|
|
52
55
|
pinned native TLS connection.
|
|
53
56
|
|
|
57
|
+
## TeeLS Hosted Passthrough (Opt-In)
|
|
58
|
+
|
|
59
|
+
The SDK also has an isolated, opt-in TeeLS hosted flow. It uses a normal
|
|
60
|
+
registry provider and the normal app/provider grant, but it does **not** run
|
|
61
|
+
request capture, native replay, SATYA artifact construction, local SATYA
|
|
62
|
+
verification, or verifier artifact submission. SATYA performs one provider-policy
|
|
63
|
+
request before opening the WebView solely to confirm that the configured app is
|
|
64
|
+
active and has access to the provider.
|
|
65
|
+
|
|
66
|
+
Enable the flow for exactly one provider/app pair at initialization:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import {
|
|
70
|
+
initSatyaAttest,
|
|
71
|
+
SatyaProviderAttestButton,
|
|
72
|
+
type SatyaTeelsAttestation,
|
|
73
|
+
type SatyaTeelsClaimData,
|
|
74
|
+
} from '@burnt-labs/expo-satya-attest';
|
|
75
|
+
|
|
76
|
+
initSatyaAttest({
|
|
77
|
+
providerId: 'satya.teels.v1',
|
|
78
|
+
appId: 'app_your-registered-app-id',
|
|
79
|
+
teelsFlow: true,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
function TeelsAttestButton() {
|
|
83
|
+
const verifyTeelsResult = async (
|
|
84
|
+
attestation: SatyaTeelsAttestation,
|
|
85
|
+
claimData?: SatyaTeelsClaimData
|
|
86
|
+
) => {
|
|
87
|
+
// Required: verify the TeeLS quote, freshness, and expected data binding
|
|
88
|
+
// in your application or backend before trusting the result.
|
|
89
|
+
// `claimData` is the first argument to TeeLS success, usually the
|
|
90
|
+
// JSON-safe payload forwarded by the hosted provider.
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<SatyaProviderAttestButton
|
|
95
|
+
providerId="satya.teels.v1"
|
|
96
|
+
transportConfig={{
|
|
97
|
+
verifierUrl: 'https://satya-verifier-v2-staging.burnt.com',
|
|
98
|
+
appId: 'app_your-registered-app-id',
|
|
99
|
+
}}
|
|
100
|
+
onTeelsComplete={verifyTeelsResult}
|
|
101
|
+
onError={(error) => console.error(error.code, error.message)}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The provider's existing `login.url` opens in the SDK WebView and its normal
|
|
108
|
+
Registry `injection` adapts the hosted page to the SDK-owned bridge. A page that
|
|
109
|
+
already uses the standard TeeLS callbacks and EarnOS success event/snapshot
|
|
110
|
+
does not need frontend changes. See
|
|
111
|
+
[Provider Config](./PROVIDER_CONFIG.md#hosted-teels-passthrough-provider-injection)
|
|
112
|
+
for the adapter and device-validation contract.
|
|
113
|
+
|
|
114
|
+
Only a non-null, JSON-safe attestation object can complete the flow. If provided,
|
|
115
|
+
`claimData` is additionally forwarded as a second argument to `onTeelsComplete`.
|
|
116
|
+
Success is returned only through `onTeelsComplete`; invalid/null data, hosted
|
|
117
|
+
failures, timeout, WebView failure, and access denial use the normal `onError`
|
|
118
|
+
path. Closing the sheet before a terminal result uses `onCancel`.
|
|
119
|
+
|
|
120
|
+
Native-replay-only props are not used by TeeLS. A component may keep an
|
|
121
|
+
`onComplete` handler for normal mode, but TeeLS never calls it. Initialize the
|
|
122
|
+
SDK before mounting the button. A later init with `teelsFlow: false` or with
|
|
123
|
+
the option omitted disables TeeLS for subsequently mounted buttons; normal
|
|
124
|
+
providers continue to use the existing native replay flow unchanged.
|
|
125
|
+
|
|
126
|
+
The returned object is an opaque, unverified TeeLS result and is delivered as
|
|
127
|
+
the first argument. `claimData` is the hosted-provider payload whose inner
|
|
128
|
+
`data` object is hashed into the quote's `report_data`; treat both as
|
|
129
|
+
untrusted input until verified. Do not treat callback delivery as a SATYA
|
|
130
|
+
verification claim, and do not log or persist the result by default.
|
|
131
|
+
|
|
132
|
+
### Verifying the TeeLS result
|
|
133
|
+
|
|
134
|
+
The SATYA SDK performs no verification of the TeeLS result — it neither
|
|
135
|
+
verifies the quote nor checks that the claim data matches it. Before trusting
|
|
136
|
+
the result, the consuming application or its backend must verify it:
|
|
137
|
+
|
|
138
|
+
1. **DCAP quote.** `attestation.quote` is a hex-encoded Intel TDX quote. Post
|
|
139
|
+
it to the independent TeeLS verifier service and require `success: true`,
|
|
140
|
+
`quote.verified: true`, and a `tcb_status` your policy accepts:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
curl -X POST https://attest.burnt.com/verify \
|
|
144
|
+
-H 'content-type: application/json' \
|
|
145
|
+
-d '{"hex":"<quote hex>"}'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
2. **Measurements.** Compare the verified quote's `mrtd` and `rtmr0`–`rtmr3`
|
|
149
|
+
against the expected-measurements registry published independently by the
|
|
150
|
+
verifier (`GET https://attest.burnt.com/measurements`); require a match
|
|
151
|
+
against one of its `valid[]` sets.
|
|
152
|
+
3. **Data binding.** Use `reportdata` from the **verified quote response**
|
|
153
|
+
(never the advisory `attestation.reportDataHex` field — it is
|
|
154
|
+
server-supplied). Its first 32 bytes must equal
|
|
155
|
+
`SHA-256(JSON.stringify(data))` over the exact result-data object the page
|
|
156
|
+
delivered (in this flow, `claimData.data`; remaining bytes are
|
|
157
|
+
zero-padded). When the provider binds a policy hash, the envelope is
|
|
158
|
+
`{ data, policyHash }` instead of bare `data`. Any mismatch means the claim
|
|
159
|
+
data was substituted after attestation.
|
|
160
|
+
|
|
161
|
+
The result carries no nonce, so enforce your own freshness and replay policy
|
|
162
|
+
wherever the attestation crosses a trust boundary.
|
|
163
|
+
|
|
54
164
|
## Native Trust Tiers
|
|
55
165
|
|
|
56
166
|
| Platform | Witness key | Platform evidence | Typical tier |
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'expo.modules.satyaattest'
|
|
7
|
-
version = '3.
|
|
7
|
+
version = '3.2.0'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.satyaattest"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 1
|
|
13
|
-
versionName "3.
|
|
13
|
+
versionName "3.2.0"
|
|
14
14
|
// StrongBox key attestation needs API 28+; basic Keystore attestation API 24+.
|
|
15
15
|
minSdkVersion 24
|
|
16
16
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SatyaProviderAttestButtonProps } from './components/providerAttest/types';
|
|
3
|
-
export type { SatyaBackendVerifyResult, SatyaProviderAttestButtonProps, SatyaProviderAttestationResult, SatyaProviderAttestPresentation, SatyaProviderAttestTheme, SatyaProviderFlowPhase, SatyaProviderFlowProgress, } from './components/providerAttest/types';
|
|
3
|
+
export type { SatyaBackendVerifyResult, SatyaProviderAttestButtonProps, SatyaProviderAttestationResult, SatyaProviderAttestPresentation, SatyaProviderAttestTheme, SatyaProviderFlowPhase, SatyaProviderFlowProgress, SatyaTeelsAttestation, SatyaTeelsClaimData, } from './components/providerAttest/types';
|
|
4
4
|
export declare function SatyaProviderAttestButton(props: SatyaProviderAttestButtonProps): React.JSX.Element;
|
|
5
5
|
//# sourceMappingURL=SatyaProviderAttestButton.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaProviderAttestButton.d.ts","sourceRoot":"","sources":["../src/SatyaProviderAttestButton.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"SatyaProviderAttestButton.d.ts","sourceRoot":"","sources":["../src/SatyaProviderAttestButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAUxC,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAC;AAOxF,YAAY,EACV,wBAAwB,EACxB,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAE3C,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,8BAA8B,qBAiB9E"}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import { ActivityIndicator, Pressable, Text } from 'react-native';
|
|
3
3
|
import { ProviderAttestModal } from './components/providerAttest/ProviderAttestModal';
|
|
4
|
+
import { selectProviderFlow, } from './components/providerAttest/flowSelection';
|
|
4
5
|
import { styles } from './components/providerAttest/styles';
|
|
5
|
-
import {
|
|
6
|
+
import { useTeelsProviderFlow } from './components/providerAttest/teels/useTeelsProviderFlow';
|
|
7
|
+
import { useProviderAttestFlow, } from './components/providerAttest/useProviderAttestFlow';
|
|
8
|
+
import { getSatyaRuntimeConfig } from './satyaConfig';
|
|
6
9
|
export function SatyaProviderAttestButton(props) {
|
|
10
|
+
// Bind the runtime config once at mount (the init-before-mount contract),
|
|
11
|
+
// but recompute the flow selection from live props so a providerId/template
|
|
12
|
+
// switch cannot leave the button running a stale flow.
|
|
13
|
+
const [runtime] = useState(() => getSatyaRuntimeConfig());
|
|
14
|
+
const selection = selectProviderFlow({
|
|
15
|
+
providerId: props.providerId,
|
|
16
|
+
providerTemplateId: props.providerTemplate?.providerId,
|
|
17
|
+
}, runtime);
|
|
18
|
+
if (selection.type === 'teels') {
|
|
19
|
+
return <TeelsProviderFlowHost props={props} selection={selection}/>;
|
|
20
|
+
}
|
|
21
|
+
return <NativeProviderFlowHost props={props}/>;
|
|
22
|
+
}
|
|
23
|
+
function NativeProviderFlowHost({ props }) {
|
|
7
24
|
const flow = useProviderAttestFlow(props);
|
|
25
|
+
return <ProviderAttestButtonView props={props} flow={flow}/>;
|
|
26
|
+
}
|
|
27
|
+
function TeelsProviderFlowHost({ props, selection, }) {
|
|
28
|
+
const flow = useTeelsProviderFlow(props, { selection });
|
|
29
|
+
return <ProviderAttestButtonView props={props} flow={flow}/>;
|
|
30
|
+
}
|
|
31
|
+
function ProviderAttestButtonView({ props, flow, }) {
|
|
8
32
|
const isDisabled = props.disabled || flow.running || flow.modalVisible;
|
|
9
33
|
const shouldRenderButton = props.renderButton ?? true;
|
|
10
34
|
const WarmWebView = flow.ResolvedWebView;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SatyaProviderAttestButton.js","sourceRoot":"","sources":["../src/SatyaProviderAttestButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SatyaProviderAttestButton.js","sourceRoot":"","sources":["../src/SatyaProviderAttestButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { ActivityIndicator, Pressable, Text } from 'react-native';\n\nimport { ProviderAttestModal } from './components/providerAttest/ProviderAttestModal';\nimport {\n selectProviderFlow,\n type SatyaProviderFlowSelection,\n} from './components/providerAttest/flowSelection';\nimport { styles } from './components/providerAttest/styles';\nimport { useTeelsProviderFlow } from './components/providerAttest/teels/useTeelsProviderFlow';\nimport type { SatyaProviderAttestButtonProps } from './components/providerAttest/types';\nimport {\n useProviderAttestFlow,\n type ProviderAttestFlow,\n} from './components/providerAttest/useProviderAttestFlow';\nimport { getSatyaRuntimeConfig, type SatyaRuntimeConfig } from './satyaConfig';\n\nexport type {\n SatyaBackendVerifyResult,\n SatyaProviderAttestButtonProps,\n SatyaProviderAttestationResult,\n SatyaProviderAttestPresentation,\n SatyaProviderAttestTheme,\n SatyaProviderFlowPhase,\n SatyaProviderFlowProgress,\n SatyaTeelsAttestation,\n SatyaTeelsClaimData,\n} from './components/providerAttest/types';\n\nexport function SatyaProviderAttestButton(props: SatyaProviderAttestButtonProps) {\n // Bind the runtime config once at mount (the init-before-mount contract),\n // but recompute the flow selection from live props so a providerId/template\n // switch cannot leave the button running a stale flow.\n const [runtime] = useState<SatyaRuntimeConfig>(() => getSatyaRuntimeConfig());\n const selection = selectProviderFlow(\n {\n providerId: props.providerId,\n providerTemplateId: props.providerTemplate?.providerId,\n },\n runtime\n );\n\n if (selection.type === 'teels') {\n return <TeelsProviderFlowHost props={props} selection={selection} />;\n }\n return <NativeProviderFlowHost props={props} />;\n}\n\ninterface ProviderFlowHostProps {\n props: SatyaProviderAttestButtonProps;\n}\n\nfunction NativeProviderFlowHost({ props }: ProviderFlowHostProps) {\n const flow = useProviderAttestFlow(props);\n return <ProviderAttestButtonView props={props} flow={flow} />;\n}\n\nfunction TeelsProviderFlowHost({\n props,\n selection,\n}: ProviderFlowHostProps & {\n selection: Extract<SatyaProviderFlowSelection, { type: 'teels' }>;\n}) {\n const flow = useTeelsProviderFlow(props, { selection });\n return <ProviderAttestButtonView props={props} flow={flow} />;\n}\n\nfunction ProviderAttestButtonView({\n props,\n flow,\n}: {\n props: SatyaProviderAttestButtonProps;\n flow: ProviderAttestFlow;\n}) {\n const isDisabled = props.disabled || flow.running || flow.modalVisible;\n const shouldRenderButton = props.renderButton ?? true;\n const WarmWebView = flow.ResolvedWebView;\n\n return (\n <>\n {shouldRenderButton ? (\n <Pressable\n style={[\n styles.button,\n { backgroundColor: flow.theme.accentColor },\n isDisabled && styles.buttonDisabled,\n ]}\n disabled={isDisabled}\n onPress={flow.openFlow}>\n {flow.running ? <ActivityIndicator color=\"#ffffff\" style={styles.buttonSpinner} /> : null}\n <Text style={styles.buttonText}>\n {flow.running ? 'Generating Attestation' : flow.buttonLabel}\n </Text>\n </Pressable>\n ) : null}\n {flow.prefetch && !flow.modalVisible && WarmWebView ? (\n <WarmWebView\n source={{ uri: 'about:blank' }}\n style={styles.warmWebView}\n pointerEvents=\"none\"\n javaScriptEnabled={false}\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n />\n ) : null}\n <ProviderAttestModal flow={flow} />\n </>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderAttestModal.d.ts","sourceRoot":"","sources":["../../../src/components/providerAttest/ProviderAttestModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ProviderAttestModal.d.ts","sourceRoot":"","sources":["../../../src/components/providerAttest/ProviderAttestModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAqC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,UAAU,wBAAwB;IAChC,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,EAAE,wBAAwB,qBAMrE"}
|
|
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import { AccessibilityInfo, ActivityIndicator, Animated, Easing, Keyboard, LayoutAnimation, Modal, Platform, Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View, } from 'react-native';
|
|
3
3
|
import { initialWindowMetrics, SafeAreaInsetsContext, SafeAreaProvider, } from 'react-native-safe-area-context';
|
|
4
4
|
import { WEBVIEW_BOOTSTRAP_URL } from './constants';
|
|
5
|
-
import { friendlyError, isRetryableError, preparingCopy, processingCopy } from './copy';
|
|
5
|
+
import { friendlyError, friendlyTeelsError, isRetryableError, preparingCopy, processingCopy, } from './copy';
|
|
6
6
|
import { managedPopupKey, managedPopupSource } from './popupRelay';
|
|
7
7
|
import { defaultMobileBrowserUserAgent, providerLoginUrl } from './providerResolution';
|
|
8
8
|
import { styles } from './styles';
|
|
@@ -143,7 +143,7 @@ function AndroidSheetHost({ flow }) {
|
|
|
143
143
|
</Modal>);
|
|
144
144
|
}
|
|
145
145
|
function ProviderSheetContent({ flow, insets }) {
|
|
146
|
-
const { closeFlow, closeManagedPopup, contentReady, effectiveUserAgent, error, errorCode, errorRetryable, handleHardwareBack, handleMessage, handleNavigationStateChange, handleOpenWindow, handlePopupNavigationStateChange, handlePopupOpenWindow, handlePopupShouldStartLoadWithRequest, handleShouldStartLoadWithRequest, handleReload, isVerificationCovered, mainDocumentScriptReady, mainInjectedScript, managedPopup, markContentReady, markLoadEnd, markLoadStart, markPopupContentReady, maybeTriggerHtmlCapture, openFlow, phase, popupContentReady, popupDocumentScriptReady, popupHostRef, popupInjectedScript, popupRef, preparing, presentation, resolved, ResolvedWebView, secureConnection, secureHost, setBrowserChrome, setPopupChrome, theme, title, useNativePopups, webHostRef, webRef, webViewLoading, webViewProps, webViewUrl, returnToProvider, verificationPresentationStalled, } = flow;
|
|
146
|
+
const { closeFlow, closeManagedPopup, contentReady, effectiveUserAgent, error, errorCode, errorRetryable, handleHardwareBack, handleMessage, handleNavigationStateChange, handleOpenWindow, handlePopupNavigationStateChange, handlePopupOpenWindow, handlePopupShouldStartLoadWithRequest, handleShouldStartLoadWithRequest, handleReload, handleContentProcessDidTerminate, handleRenderProcessGone, handleWebViewError, handleWebViewHttpError, isTeelsFlow, isVerificationCovered, mainDocumentScriptReady, mainInjectedScript, managedPopup, markContentReady, markLoadEnd, markLoadStart, markPopupContentReady, maybeTriggerHtmlCapture, openFlow, phase, popupContentReady, popupDocumentScriptReady, popupHostRef, popupInjectedScript, popupRef, preparing, presentation, resolved, ResolvedWebView, secureConnection, secureHost, setBrowserChrome, setPopupChrome, theme, title, useNativePopups, webHostRef, webRef, webViewLoading, webViewProps, webViewUrl, returnToProvider, verificationPresentationStalled, } = flow;
|
|
147
147
|
const useSheet = presentation === 'sheet';
|
|
148
148
|
const [coverOpacity] = useState(() => new Animated.Value(1));
|
|
149
149
|
const [popupCoverOpacity] = useState(() => new Animated.Value(1));
|
|
@@ -249,35 +249,45 @@ function ProviderSheetContent({ flow, insets }) {
|
|
|
249
249
|
]}/>
|
|
250
250
|
</View>
|
|
251
251
|
<View ref={webHostRef} style={[styles.webViewHost, { backgroundColor: theme.surfaceColor }]}>
|
|
252
|
-
{ResolvedWebView && resolved ? (<ResolvedWebView {...(webViewProps ?? {})}
|
|
252
|
+
{ResolvedWebView && resolved ? (<ResolvedWebView {...(webViewProps ?? {})} {...(isTeelsFlow
|
|
253
|
+
? {
|
|
254
|
+
onContentProcessDidTerminate: handleContentProcessDidTerminate,
|
|
255
|
+
onRenderProcessGone: handleRenderProcessGone,
|
|
256
|
+
onError: handleWebViewError,
|
|
257
|
+
onHttpError: handleWebViewHttpError,
|
|
258
|
+
}
|
|
259
|
+
: {})} geolocationEnabled={resolved.template.requiresLocation === true} ref={webRef} source={{
|
|
253
260
|
uri: mainDocumentScriptReady
|
|
254
261
|
? (webViewUrl ?? providerLoginUrl(resolved.template))
|
|
255
262
|
: WEBVIEW_BOOTSTRAP_URL,
|
|
256
263
|
}} style={[styles.webView, webViewProps?.style]} accessibilityElementsHidden={providerContentConcealed} importantForAccessibility={providerContentConcealed ? 'no-hide-descendants' : 'auto'} javaScriptEnabled javaScriptCanOpenWindowsAutomatically domStorageEnabled sharedCookiesEnabled thirdPartyCookiesEnabled userAgent={effectiveUserAgent ?? defaultMobileBrowserUserAgent()} incognito={false} cacheEnabled setSupportMultipleWindows={useNativePopups} injectedJavaScriptBeforeContentLoaded={mainInjectedScript} injectedJavaScript={mainInjectedScript} injectedJavaScriptBeforeContentLoadedForMainFrameOnly={false} injectedJavaScriptForMainFrameOnly={false} onMessage={handleMessage} onOpenWindow={handleOpenWindow} onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest} onNavigationStateChange={handleNavigationStateChange} onLoadStart={(event) => {
|
|
257
264
|
const nextChrome = readBrowserChromeState(event);
|
|
258
|
-
|
|
265
|
+
invokeMainWebViewCallback(webViewProps?.onLoadStart, event, isTeelsFlow, 'before');
|
|
259
266
|
if (!mainDocumentScriptReady && nextChrome.url === WEBVIEW_BOOTSTRAP_URL)
|
|
260
267
|
return;
|
|
261
|
-
markLoadStart();
|
|
268
|
+
markLoadStart(nextChrome.url);
|
|
262
269
|
setBrowserChrome((current) => ({
|
|
263
270
|
...current,
|
|
264
271
|
...nextChrome,
|
|
265
272
|
loading: true,
|
|
266
273
|
}));
|
|
274
|
+
invokeMainWebViewCallback(webViewProps?.onLoadStart, event, isTeelsFlow, 'after');
|
|
267
275
|
}} onLoadEnd={(event) => {
|
|
268
276
|
const nextChrome = readBrowserChromeState(event);
|
|
269
|
-
|
|
277
|
+
invokeMainWebViewCallback(webViewProps?.onLoadEnd, event, isTeelsFlow, 'before');
|
|
270
278
|
if (!mainDocumentScriptReady && nextChrome.url === WEBVIEW_BOOTSTRAP_URL)
|
|
271
279
|
return;
|
|
272
280
|
markContentReady();
|
|
273
|
-
markLoadEnd();
|
|
281
|
+
markLoadEnd(nextChrome.url);
|
|
274
282
|
setBrowserChrome((current) => ({
|
|
275
283
|
...current,
|
|
276
284
|
...nextChrome,
|
|
277
285
|
loading: false,
|
|
278
286
|
}));
|
|
279
287
|
webRef.current?.injectJavaScript?.(mainInjectedScript);
|
|
280
|
-
|
|
288
|
+
if (!isTeelsFlow)
|
|
289
|
+
maybeTriggerHtmlCapture(nextChrome.url);
|
|
290
|
+
invokeMainWebViewCallback(webViewProps?.onLoadEnd, event, isTeelsFlow, 'after');
|
|
281
291
|
}} originWhitelist={['http://*', 'https://*', 'about:*']}/>) : (<View style={styles.webViewFallback}>
|
|
282
292
|
<Text style={styles.errorText}>react-native-webview is not available.</Text>
|
|
283
293
|
</View>)}
|
|
@@ -376,10 +386,10 @@ function ProviderSheetContent({ flow, insets }) {
|
|
|
376
386
|
<Text style={styles.errorIconGlyph}>!</Text>
|
|
377
387
|
</View>
|
|
378
388
|
<Text style={[styles.overlayTitle, { color: theme.onSurfaceColor }]}>
|
|
379
|
-
Could not verify your account
|
|
389
|
+
{isTeelsFlow ? 'Could not complete attestation' : 'Could not verify your account'}
|
|
380
390
|
</Text>
|
|
381
391
|
<Text style={[styles.overlaySubtitle, { color: theme.mutedColor }]}>
|
|
382
|
-
{friendlyError(error, errorCode)}
|
|
392
|
+
{isTeelsFlow ? friendlyTeelsError(error, errorCode) : friendlyError(error, errorCode)}
|
|
383
393
|
</Text>
|
|
384
394
|
{isRetryableError(errorCode, errorRetryable) ? (<Pressable style={[styles.primaryAction, { backgroundColor: theme.accentColor }]} onPress={openFlow}>
|
|
385
395
|
<Text style={styles.primaryActionText}>Try again</Text>
|
|
@@ -401,4 +411,18 @@ function ProviderSheetContent({ flow, insets }) {
|
|
|
401
411
|
</View>
|
|
402
412
|
</View>);
|
|
403
413
|
}
|
|
414
|
+
function invokeMainWebViewCallback(callback, event, isTeelsFlow, timing) {
|
|
415
|
+
if ((!isTeelsFlow && timing !== 'before') || (isTeelsFlow && timing !== 'after'))
|
|
416
|
+
return;
|
|
417
|
+
if (!isTeelsFlow) {
|
|
418
|
+
invokeWebViewCallback(callback, event);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
invokeWebViewCallback(callback, event);
|
|
423
|
+
}
|
|
424
|
+
catch {
|
|
425
|
+
// Observational host callbacks cannot interrupt or settle the TeeLS flow.
|
|
426
|
+
}
|
|
427
|
+
}
|
|
404
428
|
//# sourceMappingURL=ProviderAttestModal.js.map
|