@matanetwork/sovereign-id 0.1.0 → 0.1.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/LICENSE +21 -21
- package/package.json +8 -5
- package/src/index.d.ts +229 -216
- package/src/index.js +132 -14
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 MATA Network
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MATA Network
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matanetwork/sovereign-id",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Page-side SDK for MATA Sovereign ID — the permissionless self-issued identity protocol (mID). Probes for the MATA browser extension or native app, dispatches sign-in requests, and resolves with a signed JWT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -37,13 +37,16 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=18"
|
|
39
39
|
},
|
|
40
|
-
"homepage": "https://github.com/
|
|
40
|
+
"homepage": "https://github.com/Remade-With-Rust/sovereign-id/tree/main/packages/sovereign-id#readme",
|
|
41
41
|
"bugs": {
|
|
42
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/Remade-With-Rust/sovereign-id/issues"
|
|
43
43
|
},
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "git",
|
|
46
|
-
"url": "git+https://github.com/
|
|
47
|
-
"directory": "packages/
|
|
46
|
+
"url": "git+https://github.com/Remade-With-Rust/sovereign-id.git",
|
|
47
|
+
"directory": "packages/sovereign-id"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
48
51
|
}
|
|
49
52
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,216 +1,229 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type declarations for @matanetwork/sovereign-id.
|
|
3
|
-
*
|
|
4
|
-
* The runtime is pure JS (no TypeScript build step). This `.d.ts`
|
|
5
|
-
* exists so consumers using TypeScript get full type-checking +
|
|
6
|
-
* IDE intellisense.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ─── Wire protocol constants ───────────────────────────────────────────────
|
|
10
|
-
|
|
11
|
-
export const WINDOW_MID_GLOBAL: '__mata_mid__';
|
|
12
|
-
export const MESSAGE_DISCRIMINATOR: '__mata_mid_v1';
|
|
13
|
-
export const KIND_SIGN_IN_REQUEST: 'sign_in_request';
|
|
14
|
-
export const KIND_SIGN_IN_RESPONSE: 'sign_in_response';
|
|
15
|
-
export const URL_SCHEME: 'mata-mid';
|
|
16
|
-
export const SCHEME_PATH_REQUEST: 'request';
|
|
17
|
-
export const QUERY_PARAM_PAYLOAD: 'payload';
|
|
18
|
-
export const FRAGMENT_KEY_RESPONSE: 'mid_response';
|
|
19
|
-
export const PROTOCOL_VERSION: 1;
|
|
20
|
-
|
|
21
|
-
// ─── Standard error codes ──────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
export const ERR_USER_DENIED: 'user_denied';
|
|
24
|
-
export const ERR_ORIGIN_MISMATCH: 'origin_mismatch';
|
|
25
|
-
export const ERR_INVALID_REQUEST: 'invalid_request';
|
|
26
|
-
export const ERR_WALLET_UNAVAILABLE: 'wallet_unavailable';
|
|
27
|
-
export const ERR_REQUIRED_CLAIM_UNAVAILABLE: 'required_claim_unavailable';
|
|
28
|
-
export const ERR_INTERNAL: 'internal_error';
|
|
29
|
-
export const ERR_NO_WALLET_INSTALLED: 'no_wallet_installed';
|
|
30
|
-
export const ERR_TIMEOUT: 'timeout';
|
|
31
|
-
export const ERR_UPSELL_CANCELED: 'upsell_canceled';
|
|
32
|
-
|
|
33
|
-
export type ErrorCode =
|
|
34
|
-
| typeof ERR_USER_DENIED
|
|
35
|
-
| typeof ERR_ORIGIN_MISMATCH
|
|
36
|
-
| typeof ERR_INVALID_REQUEST
|
|
37
|
-
| typeof ERR_WALLET_UNAVAILABLE
|
|
38
|
-
| typeof ERR_REQUIRED_CLAIM_UNAVAILABLE
|
|
39
|
-
| typeof ERR_INTERNAL
|
|
40
|
-
| typeof ERR_NO_WALLET_INSTALLED
|
|
41
|
-
| typeof ERR_TIMEOUT
|
|
42
|
-
| typeof ERR_UPSELL_CANCELED;
|
|
43
|
-
|
|
44
|
-
// ─── Request / response types ──────────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
export interface CustomClaim {
|
|
47
|
-
optional: true;
|
|
48
|
-
description?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface SignInRequest {
|
|
52
|
-
/** The RP's bare origin, e.g. `"https://acme.com"`. */
|
|
53
|
-
rpOrigin: string;
|
|
54
|
-
/** RP-issued single-use nonce; appears in the JWT for replay defense. */
|
|
55
|
-
nonce: string;
|
|
56
|
-
/** Claim catalog the wallet should disclose. */
|
|
57
|
-
claims: {
|
|
58
|
-
/** Claims that MUST be approved; denial blocks sign-in. */
|
|
59
|
-
required: string[];
|
|
60
|
-
/** Claims the user can include or skip. */
|
|
61
|
-
optional?: string[];
|
|
62
|
-
/** Arbitrary custom keys from the user's profile_kv (v0: ignored). */
|
|
63
|
-
custom?: Record<string, CustomClaim>;
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface SignInOptions {
|
|
68
|
-
/** Hard request timeout. Default: 120 seconds. */
|
|
69
|
-
timeoutMs?: number;
|
|
70
|
-
/**
|
|
71
|
-
* URL the native app's response will redirect to. Default:
|
|
72
|
-
* `window.location.href`. Only honored when the SDK falls through
|
|
73
|
-
* to the native-app deep link.
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
*
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
|
|
216
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for @matanetwork/sovereign-id.
|
|
3
|
+
*
|
|
4
|
+
* The runtime is pure JS (no TypeScript build step). This `.d.ts`
|
|
5
|
+
* exists so consumers using TypeScript get full type-checking +
|
|
6
|
+
* IDE intellisense.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─── Wire protocol constants ───────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export const WINDOW_MID_GLOBAL: '__mata_mid__';
|
|
12
|
+
export const MESSAGE_DISCRIMINATOR: '__mata_mid_v1';
|
|
13
|
+
export const KIND_SIGN_IN_REQUEST: 'sign_in_request';
|
|
14
|
+
export const KIND_SIGN_IN_RESPONSE: 'sign_in_response';
|
|
15
|
+
export const URL_SCHEME: 'mata-mid';
|
|
16
|
+
export const SCHEME_PATH_REQUEST: 'request';
|
|
17
|
+
export const QUERY_PARAM_PAYLOAD: 'payload';
|
|
18
|
+
export const FRAGMENT_KEY_RESPONSE: 'mid_response';
|
|
19
|
+
export const PROTOCOL_VERSION: 1;
|
|
20
|
+
|
|
21
|
+
// ─── Standard error codes ──────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
export const ERR_USER_DENIED: 'user_denied';
|
|
24
|
+
export const ERR_ORIGIN_MISMATCH: 'origin_mismatch';
|
|
25
|
+
export const ERR_INVALID_REQUEST: 'invalid_request';
|
|
26
|
+
export const ERR_WALLET_UNAVAILABLE: 'wallet_unavailable';
|
|
27
|
+
export const ERR_REQUIRED_CLAIM_UNAVAILABLE: 'required_claim_unavailable';
|
|
28
|
+
export const ERR_INTERNAL: 'internal_error';
|
|
29
|
+
export const ERR_NO_WALLET_INSTALLED: 'no_wallet_installed';
|
|
30
|
+
export const ERR_TIMEOUT: 'timeout';
|
|
31
|
+
export const ERR_UPSELL_CANCELED: 'upsell_canceled';
|
|
32
|
+
|
|
33
|
+
export type ErrorCode =
|
|
34
|
+
| typeof ERR_USER_DENIED
|
|
35
|
+
| typeof ERR_ORIGIN_MISMATCH
|
|
36
|
+
| typeof ERR_INVALID_REQUEST
|
|
37
|
+
| typeof ERR_WALLET_UNAVAILABLE
|
|
38
|
+
| typeof ERR_REQUIRED_CLAIM_UNAVAILABLE
|
|
39
|
+
| typeof ERR_INTERNAL
|
|
40
|
+
| typeof ERR_NO_WALLET_INSTALLED
|
|
41
|
+
| typeof ERR_TIMEOUT
|
|
42
|
+
| typeof ERR_UPSELL_CANCELED;
|
|
43
|
+
|
|
44
|
+
// ─── Request / response types ──────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
export interface CustomClaim {
|
|
47
|
+
optional: true;
|
|
48
|
+
description?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SignInRequest {
|
|
52
|
+
/** The RP's bare origin, e.g. `"https://acme.com"`. */
|
|
53
|
+
rpOrigin: string;
|
|
54
|
+
/** RP-issued single-use nonce; appears in the JWT for replay defense. */
|
|
55
|
+
nonce: string;
|
|
56
|
+
/** Claim catalog the wallet should disclose. */
|
|
57
|
+
claims: {
|
|
58
|
+
/** Claims that MUST be approved; denial blocks sign-in. */
|
|
59
|
+
required: string[];
|
|
60
|
+
/** Claims the user can include or skip. */
|
|
61
|
+
optional?: string[];
|
|
62
|
+
/** Arbitrary custom keys from the user's profile_kv (v0: ignored). */
|
|
63
|
+
custom?: Record<string, CustomClaim>;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface SignInOptions {
|
|
68
|
+
/** Hard request timeout. Default: 120 seconds. */
|
|
69
|
+
timeoutMs?: number;
|
|
70
|
+
/**
|
|
71
|
+
* URL the native app's response will redirect to. Default:
|
|
72
|
+
* `window.location.href`. Only honored when the SDK falls through
|
|
73
|
+
* to the native-app deep link. For a native host, set this to your
|
|
74
|
+
* app's own custom scheme (e.g. `"myapp://mid-callback"`) so the
|
|
75
|
+
* wallet's callback routes back to your app instead of a browser tab.
|
|
76
|
+
*/
|
|
77
|
+
nativeAppCallback?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Set `true` when this SDK runs inside a native webview
|
|
80
|
+
* (Dioxus/Tauri/wry/Electron) rather than a real browser tab. Skips
|
|
81
|
+
* the browser-only "no wallet installed" visibility heuristic — which
|
|
82
|
+
* would false-positive because a native webview never backgrounds when
|
|
83
|
+
* the OS opens the deep link — and relies on the hard `timeoutMs`
|
|
84
|
+
* instead. Pair with `nativeAppCallback` and have your host inject the
|
|
85
|
+
* returned `#mid_response=...` fragment back into the webview so the
|
|
86
|
+
* SDK's existing resume listener resolves the `signIn()` promise.
|
|
87
|
+
*/
|
|
88
|
+
nativeHost?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Whether to show the install upsell modal when no wallet is
|
|
91
|
+
* detected on the user's device. Default: `true`. Set to `false`
|
|
92
|
+
* to get a raw `ERR_NO_WALLET_INSTALLED` rejection and handle
|
|
93
|
+
* the upsell UI yourself.
|
|
94
|
+
*/
|
|
95
|
+
installUpsell?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Referral code attributed to signups that flow through the install
|
|
98
|
+
* upsell. Default: the hostname extracted from `rpOrigin` (e.g.
|
|
99
|
+
* `"acme.com"`), so RPs get attribution by default without any
|
|
100
|
+
* extra wiring. Pass `null` to opt out of attribution entirely, or
|
|
101
|
+
* a custom string to override (e.g. a configured MATA referral
|
|
102
|
+
* code your team uses).
|
|
103
|
+
*
|
|
104
|
+
* Stamped onto the install CTA and the "create your account" link
|
|
105
|
+
* inside the upsell modal as `?ref=<code>`, following the existing
|
|
106
|
+
* MATA referral convention captured by my.mata.network's welcome
|
|
107
|
+
* view + signup form.
|
|
108
|
+
*/
|
|
109
|
+
ref?: string | null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* One of the CTAs the install upsell can present. Pick by browser/OS.
|
|
114
|
+
* Exposed for RPs that want to render their own upsell UI from the
|
|
115
|
+
* same recommendation engine.
|
|
116
|
+
*/
|
|
117
|
+
export interface InstallCta {
|
|
118
|
+
/** Button text (e.g. `"Install MATA for Chrome"`). */
|
|
119
|
+
label: string;
|
|
120
|
+
/** Where the button navigates to. */
|
|
121
|
+
url: string;
|
|
122
|
+
/** Sub-text under the CTA (e.g. `"Opens the Chrome Web Store in a new tab"`). */
|
|
123
|
+
hint: string;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface InstallUpsellOptions {
|
|
127
|
+
/** Shown prominently as the requesting RP. */
|
|
128
|
+
rpOrigin: string;
|
|
129
|
+
/**
|
|
130
|
+
* Inversion-of-control hook so the SDK's `hasExtension()` is used
|
|
131
|
+
* in production and tests can stub it.
|
|
132
|
+
*/
|
|
133
|
+
hasExtensionFn?: () => boolean;
|
|
134
|
+
/** Override the auto-picked CTA. */
|
|
135
|
+
cta?: InstallCta;
|
|
136
|
+
/** Default 1000 ms. */
|
|
137
|
+
pollIntervalMs?: number;
|
|
138
|
+
/**
|
|
139
|
+
* Referral code attributed to signups that flow through this
|
|
140
|
+
* upsell. Default: hostname of `rpOrigin`. Pass `null` to disable.
|
|
141
|
+
* See `SignInOptions.ref` for the full attribution model.
|
|
142
|
+
*/
|
|
143
|
+
ref?: string | null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type InstallUpsellResult = 'installed' | 'canceled';
|
|
147
|
+
|
|
148
|
+
export interface SignInSuccess {
|
|
149
|
+
/** JWS compact-form mID token. */
|
|
150
|
+
jwt: string;
|
|
151
|
+
/** Which surface produced the JWT. */
|
|
152
|
+
surface: 'extension' | 'native_app';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export class SignInError extends Error {
|
|
156
|
+
constructor(code: ErrorCode, message: string);
|
|
157
|
+
readonly code: ErrorCode;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Public functions ──────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
export function signIn(
|
|
163
|
+
request: SignInRequest,
|
|
164
|
+
options?: SignInOptions
|
|
165
|
+
): Promise<SignInSuccess>;
|
|
166
|
+
|
|
167
|
+
export function hasExtension(): boolean;
|
|
168
|
+
|
|
169
|
+
/** Internal — exposed for SDK consumers that need to roll their own. */
|
|
170
|
+
export function generateRequestId(): string;
|
|
171
|
+
/** Internal — exposed for the verifier package + tests. */
|
|
172
|
+
export function base64UrlEncode(str: string): string;
|
|
173
|
+
/** Internal — exposed for the verifier package + tests. */
|
|
174
|
+
export function base64UrlDecode(b64url: string): string;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Pick the right install CTA for the user's browser / OS. Auto-called
|
|
178
|
+
* by `signIn()` when the upsell fires; exposed so RPs that opted out
|
|
179
|
+
* of the inline modal can use the same recommendation engine in
|
|
180
|
+
* their own UI.
|
|
181
|
+
*/
|
|
182
|
+
export function pickInstallCta(): InstallCta;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Extract the default referral code from an RP origin — the bare
|
|
186
|
+
* hostname (e.g. `"https://acme.com"` → `"acme.com"`). Exposed so
|
|
187
|
+
* RPs can preview what attribution string they'd get without
|
|
188
|
+
* actually triggering the upsell.
|
|
189
|
+
*
|
|
190
|
+
* Returns `null` for malformed origins.
|
|
191
|
+
*/
|
|
192
|
+
export function defaultRefFromOrigin(rpOrigin: string): string | null;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Render the install upsell overlay and resolve when the user either
|
|
196
|
+
* cancels or completes the install. Auto-called by `signIn()` on
|
|
197
|
+
* `ERR_NO_WALLET_INSTALLED`; exposed for RPs who set
|
|
198
|
+
* `installUpsell: false` and want to invoke it on their own
|
|
199
|
+
* conditions.
|
|
200
|
+
*/
|
|
201
|
+
export function showInstallUpsell(
|
|
202
|
+
options: InstallUpsellOptions
|
|
203
|
+
): Promise<InstallUpsellResult>;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Resume a sign-in that was interrupted by a page reload during the
|
|
207
|
+
* install upsell.
|
|
208
|
+
*
|
|
209
|
+
* Call once at app boot — ideally as early as possible — so a user
|
|
210
|
+
* who installed MATA and reloaded sees their sign-in continue without
|
|
211
|
+
* a visible flicker through the logged-out state.
|
|
212
|
+
*
|
|
213
|
+
* - Returns `{jwt, surface}` if a pending request was found, the
|
|
214
|
+
* extension is now installed, and the resumed sign-in completed.
|
|
215
|
+
* - Returns `null` if no resume is pending, the stash is stale, or
|
|
216
|
+
* the extension is still missing (the user reloaded before
|
|
217
|
+
* actually installing).
|
|
218
|
+
* - Rejects with `SignInError` when a pending request exists and the
|
|
219
|
+
* extension is present but the sign-in itself failed (`user_denied`,
|
|
220
|
+
* `timeout`, etc.). Same error shape as `signIn()`.
|
|
221
|
+
*/
|
|
222
|
+
export function resumePendingSignIn(): Promise<SignInSuccess | null>;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Imperatively drop any pending resume entry. Useful when the RP
|
|
226
|
+
* has navigated to a different sign-in flow that supersedes the
|
|
227
|
+
* pending mID request.
|
|
228
|
+
*/
|
|
229
|
+
export function clearPendingSignIn(): void;
|
package/src/index.js
CHANGED
|
@@ -38,6 +38,12 @@ export const KIND_SIGN_IN_REQUEST = 'sign_in_request';
|
|
|
38
38
|
/** The `kind` value on the sign-in response message. @internal */
|
|
39
39
|
export const KIND_SIGN_IN_RESPONSE = 'sign_in_response';
|
|
40
40
|
|
|
41
|
+
/** The `kind` value on the IAMHUMAN statement-sign request message. @internal */
|
|
42
|
+
export const KIND_STATEMENT_SIGN_REQUEST = 'statement_sign_request';
|
|
43
|
+
|
|
44
|
+
/** The `kind` value on the IAMHUMAN statement-sign response message. @internal */
|
|
45
|
+
export const KIND_STATEMENT_SIGN_RESPONSE = 'statement_sign_response';
|
|
46
|
+
|
|
41
47
|
/** The native app's URL scheme. @internal */
|
|
42
48
|
export const URL_SCHEME = 'mata-mid';
|
|
43
49
|
|
|
@@ -122,7 +128,8 @@ const DEFAULT_TIMEOUT_MS = 120_000;
|
|
|
122
128
|
* @param {Record<string, {optional: true, description?: string}>} [request.claims.custom] - custom claims
|
|
123
129
|
* @param {object} [options]
|
|
124
130
|
* @param {number} [options.timeoutMs] - request timeout (default: 2 min)
|
|
125
|
-
* @param {string} [options.nativeAppCallback] - URL the native app opens to return (default: window.location.href)
|
|
131
|
+
* @param {string} [options.nativeAppCallback] - URL the native app opens to return (default: window.location.href). For a native host, set this to your app's own custom scheme (e.g. `"myapp://mid-callback"`) so the wallet's callback routes back to your app rather than a browser tab.
|
|
132
|
+
* @param {boolean} [options.nativeHost] - set `true` when this SDK runs inside a native webview (Dioxus/Tauri/wry/Electron) rather than a real browser tab. Skips the browser-only "no wallet" visibility heuristic (which would false-positive because a native webview never backgrounds) and relies on the hard `timeoutMs` instead. Pair with `nativeAppCallback` + your host injecting the `#mid_response` fragment back into the webview.
|
|
126
133
|
* @param {boolean} [options.installUpsell] - whether to show the install upsell when no wallet is detected (default: true). Set to false to get the raw `ERR_NO_WALLET_INSTALLED` error and handle the upsell UI yourself.
|
|
127
134
|
* @param {string | null} [options.ref] - referral code attributed to signups that flow through the install upsell. Defaults to the hostname extracted from `rpOrigin` (e.g., `"acme.com"`) so RPs get attribution by default. Pass `null` to opt out, or a custom string to override (e.g., a configured MATA referral code). Follows the existing `?ref=` convention captured by my.mata.network's signup flow.
|
|
128
135
|
* @returns {Promise<{jwt: string, surface: "extension" | "native_app"}>}
|
|
@@ -397,6 +404,106 @@ function signInViaExtension(request, timeoutMs) {
|
|
|
397
404
|
});
|
|
398
405
|
}
|
|
399
406
|
|
|
407
|
+
// ─── signStatement() — IAMHUMAN content signing (H0-broker) ─────────────────
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Ask the user's MATA wallet to sign content as their personal DID (an
|
|
411
|
+
* IAMHUMAN post/comment). The private key never leaves the wallet — it signs
|
|
412
|
+
* and returns a self-contained `mata-sign` token that the IAMHUMAN host
|
|
413
|
+
* accepts as-is. Parallel to `signIn()`, but produces a content signature.
|
|
414
|
+
*
|
|
415
|
+
* Extension surface only for now; native `mata-mid://statement` is a follow-up.
|
|
416
|
+
*
|
|
417
|
+
* @param {object} request
|
|
418
|
+
* @param {string} request.rpOrigin - the requesting site's bare origin
|
|
419
|
+
* @param {string} request.content - the exact content to sign
|
|
420
|
+
* @param {string} request.context - domain tag, e.g. "iamhuman"
|
|
421
|
+
* @param {string} [request.contentType] - media type (default "text/plain")
|
|
422
|
+
* @param {string | null} [request.nonce] - optional single-use nonce
|
|
423
|
+
* @param {object} [options]
|
|
424
|
+
* @param {number} [options.timeoutMs] - request timeout (default 2 min)
|
|
425
|
+
* @returns {Promise<{token: string, surface: "extension"}>}
|
|
426
|
+
* @throws {SignInError} with `.code` matching one of the `ERR_*` constants
|
|
427
|
+
*/
|
|
428
|
+
export async function signStatement(request, options = {}) {
|
|
429
|
+
if (
|
|
430
|
+
!request ||
|
|
431
|
+
typeof request.rpOrigin !== 'string' ||
|
|
432
|
+
typeof request.content !== 'string' ||
|
|
433
|
+
typeof request.context !== 'string'
|
|
434
|
+
) {
|
|
435
|
+
throw new SignInError(
|
|
436
|
+
ERR_INVALID_REQUEST,
|
|
437
|
+
'signStatement requires { rpOrigin, content, context }',
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
441
|
+
if (hasExtension()) {
|
|
442
|
+
return await signStatementViaExtension(request, timeoutMs);
|
|
443
|
+
}
|
|
444
|
+
throw new SignInError(
|
|
445
|
+
ERR_NO_WALLET_INSTALLED,
|
|
446
|
+
'MATA extension not detected (native statement signing is a follow-up)',
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Send the statement-sign request via the extension's `window.postMessage`
|
|
452
|
+
* bridge and await the wallet's response.
|
|
453
|
+
* @internal
|
|
454
|
+
*/
|
|
455
|
+
function signStatementViaExtension(request, timeoutMs) {
|
|
456
|
+
return new Promise((resolve, reject) => {
|
|
457
|
+
const requestId = generateRequestId();
|
|
458
|
+
let timeoutHandle = null;
|
|
459
|
+
|
|
460
|
+
const messageHandler = (event) => {
|
|
461
|
+
const data = event.data;
|
|
462
|
+
if (typeof data !== 'object' || data === null) return;
|
|
463
|
+
if (data[MESSAGE_DISCRIMINATOR] !== true) return;
|
|
464
|
+
if (data.kind !== KIND_STATEMENT_SIGN_RESPONSE) return;
|
|
465
|
+
if (data.request_id !== requestId) return;
|
|
466
|
+
|
|
467
|
+
window.removeEventListener('message', messageHandler);
|
|
468
|
+
if (timeoutHandle !== null) clearTimeout(timeoutHandle);
|
|
469
|
+
|
|
470
|
+
const result = data.result;
|
|
471
|
+
if (typeof result !== 'object' || result === null) {
|
|
472
|
+
reject(new SignInError(ERR_INTERNAL, 'malformed response from wallet'));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (result.outcome === 'ok') {
|
|
476
|
+
resolve({ token: result.token, surface: 'extension' });
|
|
477
|
+
} else if (result.outcome === 'denied') {
|
|
478
|
+
reject(new SignInError(ERR_USER_DENIED, 'user denied signing'));
|
|
479
|
+
} else if (result.outcome === 'error') {
|
|
480
|
+
reject(new SignInError(result.error_code ?? ERR_INTERNAL, result.message ?? ''));
|
|
481
|
+
} else {
|
|
482
|
+
reject(new SignInError(ERR_INTERNAL, `unknown outcome: ${result.outcome}`));
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
window.addEventListener('message', messageHandler);
|
|
487
|
+
|
|
488
|
+
const payload = {
|
|
489
|
+
[MESSAGE_DISCRIMINATOR]: true,
|
|
490
|
+
kind: KIND_STATEMENT_SIGN_REQUEST,
|
|
491
|
+
request_id: requestId,
|
|
492
|
+
rp_origin: request.rpOrigin,
|
|
493
|
+
context: request.context,
|
|
494
|
+
content: request.content,
|
|
495
|
+
content_type: request.contentType ?? 'text/plain',
|
|
496
|
+
nonce: request.nonce ?? null,
|
|
497
|
+
};
|
|
498
|
+
window.postMessage(payload, '*');
|
|
499
|
+
|
|
500
|
+
timeoutHandle = setTimeout(() => {
|
|
501
|
+
window.removeEventListener('message', messageHandler);
|
|
502
|
+
reject(new SignInError(ERR_TIMEOUT, `no response within ${timeoutMs}ms`));
|
|
503
|
+
}, timeoutMs);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
400
507
|
// ─── Native app surface ────────────────────────────────────────────────────
|
|
401
508
|
|
|
402
509
|
/**
|
|
@@ -477,19 +584,30 @@ function signInViaNativeApp(request, options, timeoutMs) {
|
|
|
477
584
|
// app. If the page is still visible 1.5s after dispatching the
|
|
478
585
|
// deep link AND no hash response has arrived, assume no app is
|
|
479
586
|
// installed and reject with `no_wallet_installed`.
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
587
|
+
//
|
|
588
|
+
// This heuristic is browser-only: it relies on the OS deep link
|
|
589
|
+
// backgrounding the tab. Inside a native host (a Dioxus/Tauri/wry/
|
|
590
|
+
// Electron webview that embeds this SDK), the page never loses
|
|
591
|
+
// visibility, so the check would false-positive on every call. Hosts
|
|
592
|
+
// that deliver the callback themselves — registering their own scheme
|
|
593
|
+
// and injecting the `#mid_response` fragment back into the webview —
|
|
594
|
+
// pass `options.nativeHost = true` to skip it and rely on the hard
|
|
595
|
+
// timeout instead.
|
|
596
|
+
let noAppCheckHandle = null;
|
|
597
|
+
if (!options.nativeHost) {
|
|
598
|
+
const NO_APP_DETECT_MS = 1500;
|
|
599
|
+
noAppCheckHandle = setTimeout(() => {
|
|
600
|
+
if (document.visibilityState === 'visible') {
|
|
601
|
+
cleanup();
|
|
602
|
+
reject(
|
|
603
|
+
new SignInError(
|
|
604
|
+
ERR_NO_WALLET_INSTALLED,
|
|
605
|
+
'no MATA app responded to the deep link'
|
|
606
|
+
)
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}, NO_APP_DETECT_MS);
|
|
610
|
+
}
|
|
493
611
|
|
|
494
612
|
// Hard timeout — user has the configured timeoutMs to complete.
|
|
495
613
|
timeoutHandle = setTimeout(() => {
|