@hawcx/protocol 1.0.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/dist/index.d.ts +328 -0
- package/dist/index.js +54 -0
- package/package.json +25 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hawcx Auth Protocol v1 Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* This file is the source of truth for TYPE SHAPES (request/response structures).
|
|
5
|
+
* Generate Swift/Kotlin/Dart types from this file.
|
|
6
|
+
*
|
|
7
|
+
* For behavioral rules, see protocol-v1-spec.md:
|
|
8
|
+
* - Required HTTP headers (X-Hawcx-SDK, X-Tenant-Id, etc.)
|
|
9
|
+
* - Unknown prompt handling (graceful fallback)
|
|
10
|
+
* - Session lifecycle (empty string on version mismatch)
|
|
11
|
+
* - Rate limiting and retry semantics
|
|
12
|
+
*
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
export declare const PROTOCOL_VERSION: 1;
|
|
16
|
+
export interface AuthRequest {
|
|
17
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
18
|
+
session?: string;
|
|
19
|
+
action: Action;
|
|
20
|
+
}
|
|
21
|
+
export interface AuthResponse {
|
|
22
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
23
|
+
session: string;
|
|
24
|
+
prompt: Prompt;
|
|
25
|
+
meta: ResponseMeta;
|
|
26
|
+
}
|
|
27
|
+
export interface ResponseMeta {
|
|
28
|
+
/** Unique request identifier for tracing */
|
|
29
|
+
traceId: string;
|
|
30
|
+
/** Session expiration (ISO 8601) */
|
|
31
|
+
expiresAt: string;
|
|
32
|
+
}
|
|
33
|
+
export type Action = ActionStart | ActionSelectMethod | ActionSubmitCode | ActionSubmitTotp | ActionSubmitPhone | ActionRequestChallenge | ActionSubmitSignature | ActionOAuthCallback | ActionResend | ActionPoll | ActionCancel;
|
|
34
|
+
export interface ActionStart {
|
|
35
|
+
type: "start";
|
|
36
|
+
/** Server-defined flow type (e.g., "signin", "signup", "account_manage") */
|
|
37
|
+
flowType: string;
|
|
38
|
+
identifier: string;
|
|
39
|
+
/** Required for some flows (e.g., step-up auth) */
|
|
40
|
+
startToken?: string;
|
|
41
|
+
/** Optional device fingerprinting data */
|
|
42
|
+
device?: DeviceInfo;
|
|
43
|
+
/** Device credentials for automatic device trust verification (SDK sends if available) */
|
|
44
|
+
deviceCredentials?: DeviceCredentials;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stored device credentials for device trust verification at flow start.
|
|
48
|
+
* SDK loads these from IndexedDB and sends them automatically for signin flows.
|
|
49
|
+
*/
|
|
50
|
+
export interface DeviceCredentials {
|
|
51
|
+
/** Base64-encoded encrypted keyset containing public/private key pair */
|
|
52
|
+
keyset: string;
|
|
53
|
+
/** Base64-encoded hash index for device lookup */
|
|
54
|
+
hindex: string;
|
|
55
|
+
/** Base64-encoded secret key used to decrypt keyset */
|
|
56
|
+
sk2: string;
|
|
57
|
+
}
|
|
58
|
+
export interface DeviceInfo {
|
|
59
|
+
/** Schema version for handling different DeviceInfo formats from different SDK versions */
|
|
60
|
+
v: 1;
|
|
61
|
+
/** SDK-computed fingerprint hash - the primary device identifier */
|
|
62
|
+
fingerprint: string;
|
|
63
|
+
/** All other device data (userAgent, os, timezone, screen, locale, etc.) */
|
|
64
|
+
signals?: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
export interface ActionSelectMethod {
|
|
67
|
+
type: "select_method";
|
|
68
|
+
methodId: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ActionSubmitCode {
|
|
71
|
+
type: "submit_code";
|
|
72
|
+
code: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ActionSubmitTotp {
|
|
75
|
+
type: "submit_totp";
|
|
76
|
+
/** 6-8 digit TOTP code */
|
|
77
|
+
code: string;
|
|
78
|
+
}
|
|
79
|
+
export interface ActionSubmitPhone {
|
|
80
|
+
type: "submit_phone";
|
|
81
|
+
/** Phone number in E.164 format (e.g., "+1234567890") */
|
|
82
|
+
phone: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Request a device challenge from the server.
|
|
86
|
+
*
|
|
87
|
+
* SDK-INTERNAL: Used during device enrollment to send the generated keyset
|
|
88
|
+
* to the server and receive a challenge to sign. This action is sent
|
|
89
|
+
* automatically by the SDK after receiving a setup_device prompt.
|
|
90
|
+
*/
|
|
91
|
+
export interface ActionRequestChallenge {
|
|
92
|
+
type: "request_challenge";
|
|
93
|
+
/** Base64-encoded encrypted keyset containing the public key */
|
|
94
|
+
keyset: string;
|
|
95
|
+
/** Base64-encoded hash index for key lookup */
|
|
96
|
+
hindex: string;
|
|
97
|
+
/** Base64-encoded SK2 for keyset decryption */
|
|
98
|
+
sk2: string;
|
|
99
|
+
}
|
|
100
|
+
export interface ActionSubmitSignature {
|
|
101
|
+
type: "submit_signature";
|
|
102
|
+
keyId: string;
|
|
103
|
+
signature: string;
|
|
104
|
+
mapp: string;
|
|
105
|
+
}
|
|
106
|
+
export interface ActionOAuthCallback {
|
|
107
|
+
type: "oauth_callback";
|
|
108
|
+
code: string;
|
|
109
|
+
state: string;
|
|
110
|
+
}
|
|
111
|
+
export interface ActionResend {
|
|
112
|
+
type: "resend";
|
|
113
|
+
}
|
|
114
|
+
export interface ActionPoll {
|
|
115
|
+
type: "poll";
|
|
116
|
+
}
|
|
117
|
+
export interface ActionCancel {
|
|
118
|
+
type: "cancel";
|
|
119
|
+
}
|
|
120
|
+
export type Prompt = PromptSelectMethod | PromptEnterCode | PromptEnterTotp | PromptSetupTotp | PromptSetupSms | PromptSetupDevice | PromptDeviceChallenge | PromptRedirect | PromptAwaitApproval | PromptCompleted | PromptError;
|
|
121
|
+
export interface PromptSelectMethod {
|
|
122
|
+
type: "select_method";
|
|
123
|
+
/** Current phase: "primary", "mfa", or "enrollment" */
|
|
124
|
+
phase: "primary" | "mfa" | "enrollment";
|
|
125
|
+
methods: Method[];
|
|
126
|
+
}
|
|
127
|
+
export interface PromptEnterCode {
|
|
128
|
+
type: "enter_code";
|
|
129
|
+
/** Masked destination. Example: "s***@example.com" */
|
|
130
|
+
destination: string;
|
|
131
|
+
/** Expected code length */
|
|
132
|
+
codeLength: number;
|
|
133
|
+
/** Code format for keyboard hints */
|
|
134
|
+
codeFormat: "numeric" | "alphanumeric";
|
|
135
|
+
/** When code expires (ISO 8601) */
|
|
136
|
+
codeExpiresAt: string;
|
|
137
|
+
/** When resend is available (ISO 8601) */
|
|
138
|
+
resendAt: string;
|
|
139
|
+
}
|
|
140
|
+
export interface PromptEnterTotp {
|
|
141
|
+
type: "enter_totp";
|
|
142
|
+
}
|
|
143
|
+
export interface PromptSetupTotp {
|
|
144
|
+
type: "setup_totp";
|
|
145
|
+
/** Base32-encoded TOTP secret */
|
|
146
|
+
secret: string;
|
|
147
|
+
/** otpauth:// URL for QR code generation */
|
|
148
|
+
otpauthUrl: string;
|
|
149
|
+
/** TOTP period in seconds (typically 30) */
|
|
150
|
+
period: number;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* SMS enrollment prompt.
|
|
154
|
+
*
|
|
155
|
+
* Shown when user needs to provide a phone number for SMS OTP.
|
|
156
|
+
* Client should display phone input field.
|
|
157
|
+
*/
|
|
158
|
+
export interface PromptSetupSms {
|
|
159
|
+
type: "setup_sms";
|
|
160
|
+
/** Optional: existing masked phone to replace */
|
|
161
|
+
existingPhone?: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Device enrollment prompt.
|
|
165
|
+
*
|
|
166
|
+
* SDK-INTERNAL: This prompt is handled automatically by SDKs and MUST NOT be
|
|
167
|
+
* rendered to users. When received, SDKs should:
|
|
168
|
+
* 1. Generate an Ed25519 keypair
|
|
169
|
+
* 2. Store the keypair securely (IndexedDB/Keychain)
|
|
170
|
+
* 3. Build the encrypted keyset with hindex
|
|
171
|
+
* 4. Send a request_challenge action with the keyset
|
|
172
|
+
*
|
|
173
|
+
* This prompt is returned during the enrollment phase when no device key
|
|
174
|
+
* exists yet (signup or first signin on new device).
|
|
175
|
+
*/
|
|
176
|
+
export interface PromptSetupDevice {
|
|
177
|
+
type: "setup_device";
|
|
178
|
+
/** Domain for key binding */
|
|
179
|
+
domain: string;
|
|
180
|
+
/** Algorithm for key generation */
|
|
181
|
+
algorithm: "Ed25519";
|
|
182
|
+
/** Version of the enrollment protocol */
|
|
183
|
+
version: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Device verification challenge.
|
|
187
|
+
*
|
|
188
|
+
* SDK-INTERNAL: This prompt is handled automatically by SDKs and MUST NOT be
|
|
189
|
+
* rendered to users. SDKs intercept this prompt, load the private key from
|
|
190
|
+
* secure storage, sign the challenge, and submit automatically.
|
|
191
|
+
*
|
|
192
|
+
* This prompt may be returned immediately after `start` when device trust is
|
|
193
|
+
* enabled and the device is recognized.
|
|
194
|
+
*/
|
|
195
|
+
export interface PromptDeviceChallenge {
|
|
196
|
+
type: "device_challenge";
|
|
197
|
+
/** Challenge bytes to sign */
|
|
198
|
+
challenge: string;
|
|
199
|
+
/** Encoding of challenge */
|
|
200
|
+
challengeEncoding: "base64";
|
|
201
|
+
/** Key ID to use for signing */
|
|
202
|
+
keyId: string;
|
|
203
|
+
/** Signature algorithm */
|
|
204
|
+
algorithm: "Ed25519";
|
|
205
|
+
/** Domain for signature binding */
|
|
206
|
+
domain: string;
|
|
207
|
+
/** Base64-encoded combinedsalt for key unwrap */
|
|
208
|
+
combinedsalt: string;
|
|
209
|
+
/** Expected response encoding */
|
|
210
|
+
responseEncoding: "base64";
|
|
211
|
+
}
|
|
212
|
+
export interface PromptRedirect {
|
|
213
|
+
type: "redirect";
|
|
214
|
+
/** Full redirect URL */
|
|
215
|
+
url: string;
|
|
216
|
+
/** Deep link scheme for mobile */
|
|
217
|
+
returnScheme?: string;
|
|
218
|
+
}
|
|
219
|
+
export interface PromptAwaitApproval {
|
|
220
|
+
type: "await_approval";
|
|
221
|
+
/** QR code data (optional) */
|
|
222
|
+
qrData?: string;
|
|
223
|
+
/** When approval expires (ISO 8601) */
|
|
224
|
+
expiresAt: string;
|
|
225
|
+
/** Suggested poll interval in seconds */
|
|
226
|
+
pollInterval: number;
|
|
227
|
+
}
|
|
228
|
+
export interface PromptCompleted {
|
|
229
|
+
type: "completed";
|
|
230
|
+
/** Authorization code */
|
|
231
|
+
authCode: string;
|
|
232
|
+
/** When auth code expires (ISO 8601) */
|
|
233
|
+
expiresAt: string;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Server-driven error actions.
|
|
237
|
+
*
|
|
238
|
+
* These tell the client what to do. Keep this set small and stable.
|
|
239
|
+
* Unknown actions from newer servers should fall back to showing the message.
|
|
240
|
+
*/
|
|
241
|
+
export type ErrorAction = "retry_input" | "restart_flow" | "wait" | "retry_request" | "abort" | "resend_code" | "select_method";
|
|
242
|
+
/**
|
|
243
|
+
* Error prompt with server-driven action.
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* if (isPromptError(prompt)) {
|
|
248
|
+
* switch (prompt.action) {
|
|
249
|
+
* case "retry_input":
|
|
250
|
+
* showError(prompt.message);
|
|
251
|
+
* focusInput();
|
|
252
|
+
* break;
|
|
253
|
+
* case "wait":
|
|
254
|
+
* await delay(prompt.details.retry_after_seconds * 1000);
|
|
255
|
+
* retry();
|
|
256
|
+
* break;
|
|
257
|
+
* case "restart_flow":
|
|
258
|
+
* client.reset();
|
|
259
|
+
* navigate("/auth");
|
|
260
|
+
* break;
|
|
261
|
+
* default:
|
|
262
|
+
* // Unknown action from newer server
|
|
263
|
+
* showError(prompt.message);
|
|
264
|
+
* }
|
|
265
|
+
* }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
export interface PromptError {
|
|
269
|
+
type: "error";
|
|
270
|
+
/** Machine-readable error code (for logging, not branching) */
|
|
271
|
+
code: string;
|
|
272
|
+
/** Server-driven action - BRANCH ON THIS */
|
|
273
|
+
action: ErrorAction | (string & {});
|
|
274
|
+
/** Human-readable message */
|
|
275
|
+
message: string;
|
|
276
|
+
/** Whether user can retry */
|
|
277
|
+
retryable: boolean;
|
|
278
|
+
/** Additional error details (varies by action) */
|
|
279
|
+
details?: ErrorDetails;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Error details - varies by action.
|
|
283
|
+
*
|
|
284
|
+
* - `wait` action requires `retry_after_seconds`
|
|
285
|
+
* - Other actions may have additional context
|
|
286
|
+
*/
|
|
287
|
+
export interface ErrorDetails {
|
|
288
|
+
/** Seconds to wait before retry (for "wait" action) */
|
|
289
|
+
retry_after_seconds?: number;
|
|
290
|
+
/** Validation errors (for validation failures) */
|
|
291
|
+
errors?: Array<{
|
|
292
|
+
field: string;
|
|
293
|
+
message: string;
|
|
294
|
+
}>;
|
|
295
|
+
/** Any additional context */
|
|
296
|
+
[key: string]: unknown;
|
|
297
|
+
}
|
|
298
|
+
export interface Method {
|
|
299
|
+
/** Unique method identifier */
|
|
300
|
+
id: string;
|
|
301
|
+
/** Human-readable label */
|
|
302
|
+
label: string;
|
|
303
|
+
/** Icon identifier (optional) */
|
|
304
|
+
icon?: string;
|
|
305
|
+
}
|
|
306
|
+
export declare function isPromptSelectMethod(p: Prompt): p is PromptSelectMethod;
|
|
307
|
+
export declare function isPromptEnterCode(p: Prompt): p is PromptEnterCode;
|
|
308
|
+
export declare function isPromptEnterTotp(p: Prompt): p is PromptEnterTotp;
|
|
309
|
+
export declare function isPromptSetupTotp(p: Prompt): p is PromptSetupTotp;
|
|
310
|
+
export declare function isPromptSetupSms(p: Prompt): p is PromptSetupSms;
|
|
311
|
+
export declare function isPromptSetupDevice(p: Prompt): p is PromptSetupDevice;
|
|
312
|
+
export declare function isPromptDeviceChallenge(p: Prompt): p is PromptDeviceChallenge;
|
|
313
|
+
export declare function isPromptRedirect(p: Prompt): p is PromptRedirect;
|
|
314
|
+
export declare function isPromptAwaitApproval(p: Prompt): p is PromptAwaitApproval;
|
|
315
|
+
export declare function isPromptCompleted(p: Prompt): p is PromptCompleted;
|
|
316
|
+
export declare function isPromptError(p: Prompt): p is PromptError;
|
|
317
|
+
/** Extract action type for type narrowing */
|
|
318
|
+
export type ActionType = Action["type"];
|
|
319
|
+
/** Extract prompt type for type narrowing */
|
|
320
|
+
export type PromptType = Prompt["type"];
|
|
321
|
+
/** Get prompt by type */
|
|
322
|
+
export type PromptByType<T extends PromptType> = Extract<Prompt, {
|
|
323
|
+
type: T;
|
|
324
|
+
}>;
|
|
325
|
+
/** Get action by type */
|
|
326
|
+
export type ActionByType<T extends ActionType> = Extract<Action, {
|
|
327
|
+
type: T;
|
|
328
|
+
}>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hawcx Auth Protocol v1 Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* This file is the source of truth for TYPE SHAPES (request/response structures).
|
|
5
|
+
* Generate Swift/Kotlin/Dart types from this file.
|
|
6
|
+
*
|
|
7
|
+
* For behavioral rules, see protocol-v1-spec.md:
|
|
8
|
+
* - Required HTTP headers (X-Hawcx-SDK, X-Tenant-Id, etc.)
|
|
9
|
+
* - Unknown prompt handling (graceful fallback)
|
|
10
|
+
* - Session lifecycle (empty string on version mismatch)
|
|
11
|
+
* - Rate limiting and retry semantics
|
|
12
|
+
*
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Protocol Version
|
|
17
|
+
// ============================================================================
|
|
18
|
+
export const PROTOCOL_VERSION = 1;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Type Guards
|
|
21
|
+
// ============================================================================
|
|
22
|
+
export function isPromptSelectMethod(p) {
|
|
23
|
+
return p.type === "select_method";
|
|
24
|
+
}
|
|
25
|
+
export function isPromptEnterCode(p) {
|
|
26
|
+
return p.type === "enter_code";
|
|
27
|
+
}
|
|
28
|
+
export function isPromptEnterTotp(p) {
|
|
29
|
+
return p.type === "enter_totp";
|
|
30
|
+
}
|
|
31
|
+
export function isPromptSetupTotp(p) {
|
|
32
|
+
return p.type === "setup_totp";
|
|
33
|
+
}
|
|
34
|
+
export function isPromptSetupSms(p) {
|
|
35
|
+
return p.type === "setup_sms";
|
|
36
|
+
}
|
|
37
|
+
export function isPromptSetupDevice(p) {
|
|
38
|
+
return p.type === "setup_device";
|
|
39
|
+
}
|
|
40
|
+
export function isPromptDeviceChallenge(p) {
|
|
41
|
+
return p.type === "device_challenge";
|
|
42
|
+
}
|
|
43
|
+
export function isPromptRedirect(p) {
|
|
44
|
+
return p.type === "redirect";
|
|
45
|
+
}
|
|
46
|
+
export function isPromptAwaitApproval(p) {
|
|
47
|
+
return p.type === "await_approval";
|
|
48
|
+
}
|
|
49
|
+
export function isPromptCompleted(p) {
|
|
50
|
+
return p.type === "completed";
|
|
51
|
+
}
|
|
52
|
+
export function isPromptError(p) {
|
|
53
|
+
return p.type === "error";
|
|
54
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hawcx/protocol",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Canonical protocol types and helpers for the Hawcx auth flow.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist"],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "node ./node_modules/typescript/bin/tsc -p tsconfig.build.json",
|
|
19
|
+
"test": "node ./node_modules/vitest/vitest.mjs run"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.4.0",
|
|
23
|
+
"vitest": "^1.4.0"
|
|
24
|
+
}
|
|
25
|
+
}
|