@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.
@@ -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
+ }