@asturiu/v0-sdk 0.0.0-stage → 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/CHANGELOG.md +42 -0
- package/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +82 -2
- package/contract/assertion-vectors.json +90 -0
- package/contract/schemas/branding.schema.json +46 -0
- package/contract/schemas/definitions.schema.json +376 -0
- package/contract/schemas/portal.schema.json +56 -0
- package/contract/tenant-api-routes.json +165 -0
- package/dist/branding.d.ts +131 -0
- package/dist/branding.d.ts.map +1 -0
- package/dist/branding.js +206 -0
- package/dist/branding.js.map +1 -0
- package/dist/changesets.d.ts +58 -0
- package/dist/changesets.d.ts.map +1 -0
- package/dist/changesets.js +18 -0
- package/dist/changesets.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +12 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/changesets.d.ts +14 -0
- package/dist/cli/changesets.d.ts.map +1 -0
- package/dist/cli/changesets.js +252 -0
- package/dist/cli/changesets.js.map +1 -0
- package/dist/cli/cli.d.ts +37 -0
- package/dist/cli/cli.d.ts.map +1 -0
- package/dist/cli/cli.js +415 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/context.d.ts +147 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +258 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/engine.d.ts +59 -0
- package/dist/cli/engine.d.ts.map +1 -0
- package/dist/cli/engine.js +85 -0
- package/dist/cli/engine.js.map +1 -0
- package/dist/cli/feedback.d.ts +40 -0
- package/dist/cli/feedback.d.ts.map +1 -0
- package/dist/cli/feedback.js +420 -0
- package/dist/cli/feedback.js.map +1 -0
- package/dist/cli/files.d.ts +83 -0
- package/dist/cli/files.d.ts.map +1 -0
- package/dist/cli/files.js +389 -0
- package/dist/cli/files.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +8 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/overview.d.ts +6 -0
- package/dist/cli/overview.d.ts.map +1 -0
- package/dist/cli/overview.js +379 -0
- package/dist/cli/overview.js.map +1 -0
- package/dist/cli/parts.d.ts +5 -0
- package/dist/cli/parts.d.ts.map +1 -0
- package/dist/cli/parts.js +414 -0
- package/dist/cli/parts.js.map +1 -0
- package/dist/cli/sources.d.ts +37 -0
- package/dist/cli/sources.d.ts.map +1 -0
- package/dist/cli/sources.js +91 -0
- package/dist/cli/sources.js.map +1 -0
- package/dist/cli/terminal.d.ts +135 -0
- package/dist/cli/terminal.d.ts.map +1 -0
- package/dist/cli/terminal.js +353 -0
- package/dist/cli/terminal.js.map +1 -0
- package/dist/cli/ui-page.d.ts +8 -0
- package/dist/cli/ui-page.d.ts.map +1 -0
- package/dist/cli/ui-page.js +216 -0
- package/dist/cli/ui-page.js.map +1 -0
- package/dist/cli/ui.d.ts +54 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +251 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/client.d.ts +88 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +165 -0
- package/dist/client.js.map +1 -0
- package/dist/contacts.d.ts +43 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +46 -0
- package/dist/contacts.js.map +1 -0
- package/dist/definition-set.d.ts +731 -0
- package/dist/definition-set.d.ts.map +1 -0
- package/dist/definition-set.js +659 -0
- package/dist/definition-set.js.map +1 -0
- package/dist/definitions.d.ts +42 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +154 -0
- package/dist/definitions.js.map +1 -0
- package/dist/enrolment.d.ts +9 -0
- package/dist/enrolment.d.ts.map +1 -0
- package/dist/enrolment.js +8 -0
- package/dist/enrolment.js.map +1 -0
- package/dist/fill.d.ts +73 -0
- package/dist/fill.d.ts.map +1 -0
- package/dist/fill.js +46 -0
- package/dist/fill.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications.d.ts +33 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +36 -0
- package/dist/notifications.js.map +1 -0
- package/dist/oidc.d.ts +122 -0
- package/dist/oidc.d.ts.map +1 -0
- package/dist/oidc.js +150 -0
- package/dist/oidc.js.map +1 -0
- package/dist/overlay.d.ts +109 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +58 -0
- package/dist/overlay.js.map +1 -0
- package/dist/passes.d.ts +95 -0
- package/dist/passes.d.ts.map +1 -0
- package/dist/passes.js +140 -0
- package/dist/passes.js.map +1 -0
- package/dist/portal-options.d.ts +65 -0
- package/dist/portal-options.d.ts.map +1 -0
- package/dist/portal-options.js +120 -0
- package/dist/portal-options.js.map +1 -0
- package/dist/reads.d.ts +41 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +44 -0
- package/dist/reads.js.map +1 -0
- package/dist/recovery-words.d.ts +8 -0
- package/dist/recovery-words.d.ts.map +1 -0
- package/dist/recovery-words.js +136 -0
- package/dist/recovery-words.js.map +1 -0
- package/dist/recovery.d.ts +198 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +112 -0
- package/dist/recovery.js.map +1 -0
- package/dist/reveal.d.ts +35 -0
- package/dist/reveal.d.ts.map +1 -0
- package/dist/reveal.js +66 -0
- package/dist/reveal.js.map +1 -0
- package/dist/seal.d.ts +15 -0
- package/dist/seal.d.ts.map +1 -0
- package/dist/seal.js +0 -0
- package/dist/seal.js.map +1 -0
- package/dist/session.d.ts +66 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +61 -0
- package/dist/session.js.map +1 -0
- package/dist/signing-in-mark.d.ts +14 -0
- package/dist/signing-in-mark.d.ts.map +1 -0
- package/dist/signing-in-mark.js +24 -0
- package/dist/signing-in-mark.js.map +1 -0
- package/dist/signing-in.d.ts +22 -0
- package/dist/signing-in.d.ts.map +1 -0
- package/dist/signing-in.js +203 -0
- package/dist/signing-in.js.map +1 -0
- package/dist/tags.d.ts +96 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +88 -0
- package/dist/tags.js.map +1 -0
- package/dist/tenant-subscription.d.ts +47 -0
- package/dist/tenant-subscription.d.ts.map +1 -0
- package/dist/tenant-subscription.js +7 -0
- package/dist/tenant-subscription.js.map +1 -0
- package/dist/testing/index.d.ts +125 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +320 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/theme.d.ts +237 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +504 -0
- package/dist/theme.js.map +1 -0
- package/dist/tickets.d.ts +152 -0
- package/dist/tickets.d.ts.map +1 -0
- package/dist/tickets.js +65 -0
- package/dist/tickets.js.map +1 -0
- package/dist/tiers.d.ts +168 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +341 -0
- package/dist/tiers.js.map +1 -0
- package/dist/types.d.ts +885 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/urlid.d.ts +50 -0
- package/dist/urlid.d.ts.map +1 -0
- package/dist/urlid.js +78 -0
- package/dist/urlid.js.map +1 -0
- package/dist/version.d.ts +15 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +15 -0
- package/dist/version.js.map +1 -0
- package/make/asturiu.mk +43 -0
- package/package.json +63 -4
- package/src/branding.ts +221 -0
- package/src/changesets.ts +61 -0
- package/src/cli/bin.ts +12 -0
- package/src/cli/changesets.ts +233 -0
- package/src/cli/cli.ts +425 -0
- package/src/cli/context.ts +299 -0
- package/src/cli/engine.ts +112 -0
- package/src/cli/feedback.ts +406 -0
- package/src/cli/files.ts +371 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/overview.ts +343 -0
- package/src/cli/parts.ts +371 -0
- package/src/cli/sources.ts +111 -0
- package/src/cli/terminal.ts +384 -0
- package/src/cli/ui-page.ts +215 -0
- package/src/cli/ui.ts +266 -0
- package/src/client.ts +202 -0
- package/src/contacts.ts +61 -0
- package/src/definition-set.ts +965 -0
- package/src/definitions.ts +156 -0
- package/src/enrolment.ts +10 -0
- package/src/fill.ts +83 -0
- package/src/index.ts +38 -0
- package/src/notifications.ts +55 -0
- package/src/oidc.ts +199 -0
- package/src/overlay.ts +137 -0
- package/src/passes.ts +168 -0
- package/src/portal-options.ts +125 -0
- package/src/reads.ts +71 -0
- package/src/recovery-words.ts +137 -0
- package/src/recovery.ts +269 -0
- package/src/reveal.ts +72 -0
- package/src/seal.ts +0 -0
- package/src/session.ts +93 -0
- package/src/signing-in-mark.ts +23 -0
- package/src/signing-in.ts +213 -0
- package/src/tags.ts +146 -0
- package/src/tenant-subscription.ts +44 -0
- package/src/testing/index.ts +337 -0
- package/src/theme.ts +628 -0
- package/src/tickets.ts +195 -0
- package/src/tiers.ts +397 -0
- package/src/types.ts +847 -0
- package/src/urlid.ts +77 -0
- package/src/version.ts +15 -0
- package/templates/asturiu/branding.json +4 -0
- package/templates/asturiu/definitions.json +23 -0
- package/templates/asturiu/portal.json +16 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery contacts and recovery cards (ADR-050): the lists, codes and card
|
|
3
|
+
* format every surface shares, and what the Interface API answers. Pure and
|
|
4
|
+
* dependency-free: the Engine validates with these lists and checks a card's
|
|
5
|
+
* checksum itself; the Customer Portal and a Tenant's app read with them.
|
|
6
|
+
*/
|
|
7
|
+
import { normalizePassCode } from "./passes.js";
|
|
8
|
+
import { RECOVERY_WORDS } from "./recovery-words.js";
|
|
9
|
+
export { RECOVERY_WORDS };
|
|
10
|
+
/** A vault recovery ends in a Grant on that vault; a sign-in recovery in an Account reset (§1). */
|
|
11
|
+
export declare const RECOVERY_SUBJECTS: readonly ["vault", "sign_in"];
|
|
12
|
+
export type RecoverySubject = (typeof RECOVERY_SUBJECTS)[number];
|
|
13
|
+
export declare const RECOVERY_LIMITS: {
|
|
14
|
+
readonly contactsMin: 2;
|
|
15
|
+
readonly contactsMax: 7;
|
|
16
|
+
readonly quorumMin: 2;
|
|
17
|
+
readonly note: 500;
|
|
18
|
+
readonly name: 120;
|
|
19
|
+
readonly notAfterDaysMax: 3650;
|
|
20
|
+
/** A claim never completes sooner than this after its last card (ADR-050 build note 27), whatever its waiting period. */
|
|
21
|
+
readonly lastCardHours: 24;
|
|
22
|
+
};
|
|
23
|
+
/** The waiting period in days (§3), counted from the first card (§6), never ending sooner than `lastCardHours` after the last; the owner can cancel during it. */
|
|
24
|
+
export declare const RECOVERY_WAITING_DAYS: readonly [1, 3, 7, 14, 30];
|
|
25
|
+
export type RecoveryWaitingDays = (typeof RECOVERY_WAITING_DAYS)[number];
|
|
26
|
+
export declare const RECOVERY_DEFAULT_WAITING_DAYS: RecoveryWaitingDays;
|
|
27
|
+
/** What a completed vault claim grants unless the owner changes it, and what the owner may add (§3; `transfer` is added, build note 23). */
|
|
28
|
+
export declare const RECOVERY_DEFAULT_CAPABILITIES: readonly ["read", "reveal", "share"];
|
|
29
|
+
export declare const RECOVERY_OPTIONAL_CAPABILITIES: readonly ["transfer", "update", "delete"];
|
|
30
|
+
export declare const RECOVERY_CAPABILITIES: readonly ["read", "reveal", "share", "transfer", "update", "delete"];
|
|
31
|
+
export type RecoveryCapability = (typeof RECOVERY_CAPABILITIES)[number];
|
|
32
|
+
/** What the owner sees per contact (§3). */
|
|
33
|
+
export declare const RECOVERY_CONTACT_STATES: readonly ["invited", "ready", "declined", "expired", "unreachable"];
|
|
34
|
+
export type RecoveryContactState = (typeof RECOVERY_CONTACT_STATES)[number];
|
|
35
|
+
/** A claim's life (§6). `collecting` and `approved` are open; the rest are ends. */
|
|
36
|
+
export declare const RECOVERY_CLAIM_STATES: readonly ["collecting", "approved", "completed", "failed", "cancelled", "expired"];
|
|
37
|
+
export type RecoveryClaimState = (typeof RECOVERY_CLAIM_STATES)[number];
|
|
38
|
+
export declare const RECOVERY_OPEN_CLAIM_STATES: readonly RecoveryClaimState[];
|
|
39
|
+
/**
|
|
40
|
+
* Why a recovery call answered `409`: `reason` beside the English `detail`, so a frontend words it itself.
|
|
41
|
+
* `contact_address`: a sign-in claim's new address is one of that recovery's contacts (ADR-050 build notes).
|
|
42
|
+
*/
|
|
43
|
+
export declare const RECOVERY_CONFLICTS: readonly ["not_set_up", "card_saved", "card_declined", "card_state", "claim_closed", "claim_expired", "card_given", "own_recovery", "not_a_claimant", "contact_address", "too_many_failures"];
|
|
44
|
+
export type RecoveryConflict = (typeof RECOVERY_CONFLICTS)[number];
|
|
45
|
+
/**
|
|
46
|
+
* Why a well-typed card was refused (`card` on `401 recovery-attempt-failed` with `reason: card`; build note 25):
|
|
47
|
+
* its words don't fit together, no card has its card code (mistyped, or the cards were replaced), it serves the other
|
|
48
|
+
* kind of recovery or another Tenant's, the claim is bound to someone else's recovery, or its number was removed or
|
|
49
|
+
* replaced. Each still counts as a wrong try.
|
|
50
|
+
*/
|
|
51
|
+
export declare const RECOVERY_CARD_REFUSALS: readonly ["unreadable", "unknown", "other_kind", "other_product", "other_person", "retired"];
|
|
52
|
+
export type RecoveryCardRefusal = (typeof RECOVERY_CARD_REFUSALS)[number];
|
|
53
|
+
/** The label printed on every card of one recovery: 8 characters from the pass alphabet, never the ARID (§4). */
|
|
54
|
+
export declare const RECOVERY_HANDLE_LENGTH = 8;
|
|
55
|
+
/** The code the claimant gives each contact by phone or in person (§6): 6 characters without look-alikes. */
|
|
56
|
+
export declare const RECOVERY_CLAIM_CODE_LENGTH = 6;
|
|
57
|
+
/** What a person typed, as the Engine compares it: capitals, with spaces, dashes and dots dropped. */
|
|
58
|
+
export declare const normalizeRecoveryCode: typeof normalizePassCode;
|
|
59
|
+
export declare function isRecoveryHandle(value: string): boolean;
|
|
60
|
+
export declare function isRecoveryClaimCode(value: string): boolean;
|
|
61
|
+
/** `ABCDEFGH` → `ABCD-EFGH`; `ABCDEF` → `ABC-DEF`. */
|
|
62
|
+
export declare function formatRecoveryCode(code: string): string;
|
|
63
|
+
/** 12 words: the 128-bit card and a 4-bit checksum, 11 bits a word, as BIP-39 encodes 128 bits (§4). */
|
|
64
|
+
export declare const RECOVERY_CARD_WORDS = 12;
|
|
65
|
+
/** A word's 11-bit value, or -1. Every word of the list is fixed by its first four letters, so a word cut there still counts. */
|
|
66
|
+
export declare function recoveryWordIndex(input: string): number;
|
|
67
|
+
/** The list's spelling of what was typed, or null. */
|
|
68
|
+
export declare function recoveryWord(input: string): string | null;
|
|
69
|
+
/** What a card carries and what the helper page sends back (§4). */
|
|
70
|
+
export interface RecoveryCardFace {
|
|
71
|
+
label: string;
|
|
72
|
+
/** The card's Shamir x, 1 to 7. */
|
|
73
|
+
number: number;
|
|
74
|
+
words: string[];
|
|
75
|
+
}
|
|
76
|
+
/** The text in a card's QR code. */
|
|
77
|
+
export declare const RECOVERY_CARD_PREFIX = "ASTURIU-CARD";
|
|
78
|
+
export declare function recoveryCardText(card: RecoveryCardFace): string;
|
|
79
|
+
/**
|
|
80
|
+
* A card from what was scanned or typed: the QR code's text, or label,
|
|
81
|
+
* number and words in that order, with or without the prefix. Words are
|
|
82
|
+
* written out from the list; whether they hold a valid card is the Engine's
|
|
83
|
+
* check (the checksum). Null when the shape is wrong.
|
|
84
|
+
*/
|
|
85
|
+
export declare function parseRecoveryCard(text: string): RecoveryCardFace | null;
|
|
86
|
+
/** What a completed vault claim grants (§3). `claimants` null: any Customer of the Tenant may claim. */
|
|
87
|
+
export interface RecoveryTerms {
|
|
88
|
+
capabilities: RecoveryCapability[];
|
|
89
|
+
/** The Grant ends this many days after completion; null for no end. */
|
|
90
|
+
not_after_days: number | null;
|
|
91
|
+
claimants: string[] | null;
|
|
92
|
+
}
|
|
93
|
+
export interface RecoveryContact {
|
|
94
|
+
/** The card's number, once cards are issued. */
|
|
95
|
+
number: number | null;
|
|
96
|
+
/** Vault recovery: the contact's Principal, and the owner's Contact for it (ADR-024). */
|
|
97
|
+
principal?: string;
|
|
98
|
+
contact?: string | null;
|
|
99
|
+
/** The name the owner gave (sign-in recovery) or the Principal's display name (vault recovery). */
|
|
100
|
+
name: string | null;
|
|
101
|
+
/** Sign-in recovery: the address the invitation goes to. */
|
|
102
|
+
email?: string;
|
|
103
|
+
state: RecoveryContactState;
|
|
104
|
+
invited_at: string | null;
|
|
105
|
+
collected_at: string | null;
|
|
106
|
+
/** Too many wrong codes on the invitation; Replace all cards sends a new one. */
|
|
107
|
+
locked: boolean;
|
|
108
|
+
}
|
|
109
|
+
/** A claim as the owner sees it on the recovery (§7). */
|
|
110
|
+
export interface RecoveryClaimSummary {
|
|
111
|
+
id: string;
|
|
112
|
+
state: RecoveryClaimState;
|
|
113
|
+
received: number;
|
|
114
|
+
needed: number;
|
|
115
|
+
opened_at: string;
|
|
116
|
+
bound_at: string | null;
|
|
117
|
+
approved_at: string | null;
|
|
118
|
+
completes_at: string | null;
|
|
119
|
+
}
|
|
120
|
+
/** `GET /v0/recovery`, `/v0/me/recovery`, `/v0/customer/sign-in/recovery`. No answer carries a card. */
|
|
121
|
+
export interface Recovery {
|
|
122
|
+
configured: boolean;
|
|
123
|
+
arid: string | null;
|
|
124
|
+
subject: RecoverySubject;
|
|
125
|
+
/** Vault recovery: the vault it covers. */
|
|
126
|
+
vault?: string;
|
|
127
|
+
contacts: RecoveryContact[];
|
|
128
|
+
quorum: number;
|
|
129
|
+
/** Contacts whose card is saved; below `quorum` the page warns. */
|
|
130
|
+
ready: number;
|
|
131
|
+
waiting_days: RecoveryWaitingDays;
|
|
132
|
+
/** Vault recovery only. */
|
|
133
|
+
terms?: RecoveryTerms;
|
|
134
|
+
cards_issued_at: string | null;
|
|
135
|
+
/** Open claims. */
|
|
136
|
+
claims: RecoveryClaimSummary[];
|
|
137
|
+
}
|
|
138
|
+
/** A claim as its claimant or the owner reads it (`GET /v0/recovery/claims/{id}`, `/v0/recovery/sign-in-claims/{id}`). */
|
|
139
|
+
export interface RecoveryClaim {
|
|
140
|
+
id: string;
|
|
141
|
+
kind: RecoverySubject;
|
|
142
|
+
state: RecoveryClaimState;
|
|
143
|
+
role: "claimant" | "owner";
|
|
144
|
+
received: number;
|
|
145
|
+
/** Null until the first card binds the claim to a recovery. */
|
|
146
|
+
needed: number | null;
|
|
147
|
+
/** The display name of the person whose recovery the first card bound the claim to; null before that. */
|
|
148
|
+
owner_name: string | null;
|
|
149
|
+
note: string | null;
|
|
150
|
+
opened_at: string;
|
|
151
|
+
/** Collecting ends here, when the claim expires. */
|
|
152
|
+
expires_at: string;
|
|
153
|
+
bound_at: string | null;
|
|
154
|
+
approved_at: string | null;
|
|
155
|
+
completes_at: string | null;
|
|
156
|
+
closed_at: string | null;
|
|
157
|
+
}
|
|
158
|
+
/** What opening a claim answers, once: the code is never shown again (§6). */
|
|
159
|
+
export interface OpenedRecoveryClaim {
|
|
160
|
+
claim: string;
|
|
161
|
+
code: string;
|
|
162
|
+
expires_at: string;
|
|
163
|
+
}
|
|
164
|
+
/** The contact page (`POST /v0/recovery/invites/open`): who asks, and where the card stands. */
|
|
165
|
+
export interface RecoveryInvite {
|
|
166
|
+
subject: RecoverySubject;
|
|
167
|
+
owner_name: string | null;
|
|
168
|
+
/** The Tenant's product name, for a vault recovery and an `isolated` sign-in recovery. */
|
|
169
|
+
product: string | null;
|
|
170
|
+
contact_name: string | null;
|
|
171
|
+
state: RecoveryContactState;
|
|
172
|
+
number: number;
|
|
173
|
+
}
|
|
174
|
+
/** A card in transit to its contact (`POST /v0/recovery/invites/accept`). Shown until the contact types two words back. */
|
|
175
|
+
export interface RecoveryCard extends RecoveryCardFace {
|
|
176
|
+
/** Which kind of recovery the card serves, printed on it: access to a vault, or a sign-in. */
|
|
177
|
+
subject: RecoverySubject;
|
|
178
|
+
owner_name: string | null;
|
|
179
|
+
product: string | null;
|
|
180
|
+
/** The helper page on the host the recovery belongs to. */
|
|
181
|
+
help_url: string;
|
|
182
|
+
/** The two word positions (1-based) the contact types back to confirm it was saved. */
|
|
183
|
+
confirm: [number, number];
|
|
184
|
+
}
|
|
185
|
+
/** The helper page after the claim code (`POST /v0/recovery/help/claim`). */
|
|
186
|
+
export interface RecoveryHelpClaim {
|
|
187
|
+
claim: string;
|
|
188
|
+
kind: RecoverySubject;
|
|
189
|
+
/** Whose recovery the claim is bound to, so a contact who keeps several cards picks the right one; null before the first card. */
|
|
190
|
+
owner_name: string | null;
|
|
191
|
+
/** The product a vault claim's recovery lives in, or the platform's name for a sign-in claim on the shared realm, known before any card. */
|
|
192
|
+
product: string | null;
|
|
193
|
+
note: string | null;
|
|
194
|
+
received: number;
|
|
195
|
+
needed: number | null;
|
|
196
|
+
state: RecoveryClaimState;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAsB,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,mGAAmG;AACnG,eAAO,MAAM,iBAAiB,+BAAgC,CAAC;AAC/D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjE,eAAO,MAAM,eAAe;;;;;;;IAO1B,yHAAyH;;CAEjH,CAAC;AAEX,kKAAkK;AAClK,eAAO,MAAM,qBAAqB,4BAA6B,CAAC;AAChE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,eAAO,MAAM,6BAA6B,EAAE,mBAAuB,CAAC;AAEpE,4IAA4I;AAC5I,eAAO,MAAM,6BAA6B,sCAAuC,CAAC;AAClF,eAAO,MAAM,8BAA8B,2CAA4C,CAAC;AACxF,eAAO,MAAM,qBAAqB,sEAAiF,CAAC;AACpH,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB,qEAAsE,CAAC;AAC3G,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,oFAAqF,CAAC;AACxH,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AACxE,eAAO,MAAM,0BAA0B,EAAE,SAAS,kBAAkB,EAA+B,CAAC;AAEpG;;;GAGG;AACH,eAAO,MAAM,kBAAkB,+LAAgM,CAAC;AAChO,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,8FAA+F,CAAC;AACnI,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAC;AAI1E,iHAAiH;AACjH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,6GAA6G;AAC7G,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAI5C,sGAAsG;AACtG,eAAO,MAAM,qBAAqB,0BAAoB,CAAC;AAEvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvD;AAID,wGAAwG;AACxG,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC,iIAAiI;AACjI,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGzD;AAID,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,oCAAoC;AACpC,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AAEnD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAUvE;AAID,wGAAwG;AACxG,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,uEAAuE;IACvE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mGAAmG;IACnG,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iFAAiF;IACjF,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,yDAAyD;AACzD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wGAAwG;AACxG,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,mBAAmB,CAAC;IAClC,2BAA2B;IAC3B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mBAAmB;IACnB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,0HAA0H;AAC1H,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yGAAyG;IACzG,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,gGAAgG;AAChG,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,0FAA0F;IAC1F,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,oBAAoB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2HAA2H;AAC3H,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IACpD,8FAA8F;IAC9F,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3B;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,eAAe,CAAC;IACtB,kIAAkI;IAClI,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4IAA4I;IAC5I,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,kBAAkB,CAAC;CAC3B"}
|
package/dist/recovery.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovery contacts and recovery cards (ADR-050): the lists, codes and card
|
|
3
|
+
* format every surface shares, and what the Interface API answers. Pure and
|
|
4
|
+
* dependency-free: the Engine validates with these lists and checks a card's
|
|
5
|
+
* checksum itself; the Customer Portal and a Tenant's app read with them.
|
|
6
|
+
*/
|
|
7
|
+
import { PASS_CODE_ALPHABET, normalizePassCode } from "./passes.js";
|
|
8
|
+
import { RECOVERY_WORDS } from "./recovery-words.js";
|
|
9
|
+
export { RECOVERY_WORDS };
|
|
10
|
+
/** A vault recovery ends in a Grant on that vault; a sign-in recovery in an Account reset (§1). */
|
|
11
|
+
export const RECOVERY_SUBJECTS = ["vault", "sign_in"];
|
|
12
|
+
export const RECOVERY_LIMITS = {
|
|
13
|
+
contactsMin: 2,
|
|
14
|
+
contactsMax: 7,
|
|
15
|
+
quorumMin: 2,
|
|
16
|
+
note: 500,
|
|
17
|
+
name: 120,
|
|
18
|
+
notAfterDaysMax: 3650,
|
|
19
|
+
/** A claim never completes sooner than this after its last card (ADR-050 build note 27), whatever its waiting period. */
|
|
20
|
+
lastCardHours: 24,
|
|
21
|
+
};
|
|
22
|
+
/** The waiting period in days (§3), counted from the first card (§6), never ending sooner than `lastCardHours` after the last; the owner can cancel during it. */
|
|
23
|
+
export const RECOVERY_WAITING_DAYS = [1, 3, 7, 14, 30];
|
|
24
|
+
export const RECOVERY_DEFAULT_WAITING_DAYS = 3;
|
|
25
|
+
/** What a completed vault claim grants unless the owner changes it, and what the owner may add (§3; `transfer` is added, build note 23). */
|
|
26
|
+
export const RECOVERY_DEFAULT_CAPABILITIES = ["read", "reveal", "share"];
|
|
27
|
+
export const RECOVERY_OPTIONAL_CAPABILITIES = ["transfer", "update", "delete"];
|
|
28
|
+
export const RECOVERY_CAPABILITIES = [...RECOVERY_DEFAULT_CAPABILITIES, ...RECOVERY_OPTIONAL_CAPABILITIES];
|
|
29
|
+
/** What the owner sees per contact (§3). */
|
|
30
|
+
export const RECOVERY_CONTACT_STATES = ["invited", "ready", "declined", "expired", "unreachable"];
|
|
31
|
+
/** A claim's life (§6). `collecting` and `approved` are open; the rest are ends. */
|
|
32
|
+
export const RECOVERY_CLAIM_STATES = ["collecting", "approved", "completed", "failed", "cancelled", "expired"];
|
|
33
|
+
export const RECOVERY_OPEN_CLAIM_STATES = ["collecting", "approved"];
|
|
34
|
+
/**
|
|
35
|
+
* Why a recovery call answered `409`: `reason` beside the English `detail`, so a frontend words it itself.
|
|
36
|
+
* `contact_address`: a sign-in claim's new address is one of that recovery's contacts (ADR-050 build notes).
|
|
37
|
+
*/
|
|
38
|
+
export const RECOVERY_CONFLICTS = ["not_set_up", "card_saved", "card_declined", "card_state", "claim_closed", "claim_expired", "card_given", "own_recovery", "not_a_claimant", "contact_address", "too_many_failures"];
|
|
39
|
+
/**
|
|
40
|
+
* Why a well-typed card was refused (`card` on `401 recovery-attempt-failed` with `reason: card`; build note 25):
|
|
41
|
+
* its words don't fit together, no card has its card code (mistyped, or the cards were replaced), it serves the other
|
|
42
|
+
* kind of recovery or another Tenant's, the claim is bound to someone else's recovery, or its number was removed or
|
|
43
|
+
* replaced. Each still counts as a wrong try.
|
|
44
|
+
*/
|
|
45
|
+
export const RECOVERY_CARD_REFUSALS = ["unreadable", "unknown", "other_kind", "other_product", "other_person", "retired"];
|
|
46
|
+
// ------------------------------------------------------------- codes
|
|
47
|
+
/** The label printed on every card of one recovery: 8 characters from the pass alphabet, never the ARID (§4). */
|
|
48
|
+
export const RECOVERY_HANDLE_LENGTH = 8;
|
|
49
|
+
/** The code the claimant gives each contact by phone or in person (§6): 6 characters without look-alikes. */
|
|
50
|
+
export const RECOVERY_CLAIM_CODE_LENGTH = 6;
|
|
51
|
+
const ofAlphabet = (v, length) => v.length === length && [...v].every((c) => PASS_CODE_ALPHABET.includes(c));
|
|
52
|
+
/** What a person typed, as the Engine compares it: capitals, with spaces, dashes and dots dropped. */
|
|
53
|
+
export const normalizeRecoveryCode = normalizePassCode;
|
|
54
|
+
export function isRecoveryHandle(value) {
|
|
55
|
+
return ofAlphabet(value, RECOVERY_HANDLE_LENGTH);
|
|
56
|
+
}
|
|
57
|
+
export function isRecoveryClaimCode(value) {
|
|
58
|
+
return ofAlphabet(value, RECOVERY_CLAIM_CODE_LENGTH);
|
|
59
|
+
}
|
|
60
|
+
/** `ABCDEFGH` → `ABCD-EFGH`; `ABCDEF` → `ABC-DEF`. */
|
|
61
|
+
export function formatRecoveryCode(code) {
|
|
62
|
+
const c = normalizeRecoveryCode(code);
|
|
63
|
+
const half = c.length / 2;
|
|
64
|
+
return c.length === RECOVERY_HANDLE_LENGTH || c.length === RECOVERY_CLAIM_CODE_LENGTH ? `${c.slice(0, half)}-${c.slice(half)}` : c;
|
|
65
|
+
}
|
|
66
|
+
// ------------------------------------------------------------- words
|
|
67
|
+
/** 12 words: the 128-bit card and a 4-bit checksum, 11 bits a word, as BIP-39 encodes 128 bits (§4). */
|
|
68
|
+
export const RECOVERY_CARD_WORDS = 12;
|
|
69
|
+
const WORD_INDEX = new Map(RECOVERY_WORDS.map((w, i) => [w, i]));
|
|
70
|
+
/** A word's 11-bit value, or -1. Every word of the list is fixed by its first four letters, so a word cut there still counts. */
|
|
71
|
+
export function recoveryWordIndex(input) {
|
|
72
|
+
const w = input.trim().toLowerCase();
|
|
73
|
+
const exact = WORD_INDEX.get(w);
|
|
74
|
+
if (exact !== undefined)
|
|
75
|
+
return exact;
|
|
76
|
+
if (w.length < 4)
|
|
77
|
+
return -1;
|
|
78
|
+
const matches = RECOVERY_WORDS.filter((x) => x.startsWith(w.slice(0, 4)) && (w.length === 4 || x.startsWith(w)));
|
|
79
|
+
return matches.length === 1 ? (WORD_INDEX.get(matches[0]) ?? -1) : -1;
|
|
80
|
+
}
|
|
81
|
+
/** The list's spelling of what was typed, or null. */
|
|
82
|
+
export function recoveryWord(input) {
|
|
83
|
+
const i = recoveryWordIndex(input);
|
|
84
|
+
return i < 0 ? null : RECOVERY_WORDS[i];
|
|
85
|
+
}
|
|
86
|
+
/** The text in a card's QR code. */
|
|
87
|
+
export const RECOVERY_CARD_PREFIX = "ASTURIU-CARD";
|
|
88
|
+
export function recoveryCardText(card) {
|
|
89
|
+
return [RECOVERY_CARD_PREFIX, formatRecoveryCode(card.label), String(card.number), ...card.words].join(" ");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A card from what was scanned or typed: the QR code's text, or label,
|
|
93
|
+
* number and words in that order, with or without the prefix. Words are
|
|
94
|
+
* written out from the list; whether they hold a valid card is the Engine's
|
|
95
|
+
* check (the checksum). Null when the shape is wrong.
|
|
96
|
+
*/
|
|
97
|
+
export function parseRecoveryCard(text) {
|
|
98
|
+
const parts = text.trim().split(/[\s,;]+/).filter(Boolean);
|
|
99
|
+
if (parts[0]?.toUpperCase() === RECOVERY_CARD_PREFIX)
|
|
100
|
+
parts.shift();
|
|
101
|
+
if (parts.length !== RECOVERY_CARD_WORDS + 2)
|
|
102
|
+
return null;
|
|
103
|
+
const label = normalizeRecoveryCode(parts[0]);
|
|
104
|
+
const number = Number.parseInt(parts[1], 10);
|
|
105
|
+
if (!isRecoveryHandle(label) || !Number.isInteger(number) || number < 1 || number > RECOVERY_LIMITS.contactsMax || String(number) !== parts[1])
|
|
106
|
+
return null;
|
|
107
|
+
const words = parts.slice(2).map(recoveryWord);
|
|
108
|
+
if (words.some((w) => w === null))
|
|
109
|
+
return null;
|
|
110
|
+
return { label, number, words: words };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,mGAAmG;AACnG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,SAAS,CAAU,CAAC;AAG/D,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,CAAC;IACd,SAAS,EAAE,CAAC;IACZ,IAAI,EAAE,GAAG;IACT,IAAI,EAAE,GAAG;IACT,eAAe,EAAE,IAAI;IACrB,yHAAyH;IACzH,aAAa,EAAE,EAAE;CACT,CAAC;AAEX,kKAAkK;AAClK,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAU,CAAC;AAEhE,MAAM,CAAC,MAAM,6BAA6B,GAAwB,CAAC,CAAC;AAEpE,4IAA4I;AAC5I,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAC;AAClF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AACxF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,6BAA6B,EAAE,GAAG,8BAA8B,CAAU,CAAC;AAGpH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,CAAU,CAAC;AAG3G,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,CAAU,CAAC;AAExH,MAAM,CAAC,MAAM,0BAA0B,GAAkC,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AAEpG;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,CAAU,CAAC;AAGhO;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,CAAU,CAAC;AAGnI,sEAAsE;AAEtE,iHAAiH;AACjH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,6GAA6G;AAC7G,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,MAAc,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7H,sGAAsG;AACtG,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAEvD,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,UAAU,CAAC,KAAK,EAAE,sBAAsB,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,UAAU,CAAC,KAAK,EAAE,0BAA0B,CAAC,CAAC;AACvD,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,MAAM,KAAK,sBAAsB,IAAI,CAAC,CAAC,MAAM,KAAK,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACrI,CAAC;AAED,sEAAsE;AAEtE,wGAAwG;AACxG,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,MAAM,UAAU,GAAgC,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAE9F,iIAAiI;AACjI,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjH,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC;AAYD,oCAAoC;AACpC,MAAM,CAAC,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAEnD,MAAM,UAAU,gBAAgB,CAAC,IAAsB;IACrD,OAAO,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9G,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,oBAAoB;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACpE,IAAI,KAAK,CAAC,MAAM,KAAK,mBAAmB,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,KAAK,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7C,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,eAAe,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5J,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAiB,EAAE,CAAC;AACrD,CAAC"}
|
package/dist/reveal.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reveal levels of a secret Field (ADR-018). Shared by the Engine (Definition
|
|
3
|
+
* schema, the reveal gate) and every frontend (what to ask of the person):
|
|
4
|
+
* pure and dependency-free so the Frontend kit can carry it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const REVEAL_LEVELS: readonly ["visible", "click", "captcha", "password", "2fa"];
|
|
7
|
+
export type RevealLevel = (typeof REVEAL_LEVELS)[number];
|
|
8
|
+
/** What the reveal gate needs before it returns the value; `null` = nothing beyond the capability. */
|
|
9
|
+
export type RevealStep = "captcha" | "password" | "mfa";
|
|
10
|
+
export declare function revealStep(level: RevealLevel | null | undefined): RevealStep | null;
|
|
11
|
+
/** The level in effect for a Field Definition: `null` for a plain Field, `click` for a secret one that names none. */
|
|
12
|
+
export declare function revealLevelOf(def: {
|
|
13
|
+
secret?: boolean;
|
|
14
|
+
reveal?: RevealLevel | string | null;
|
|
15
|
+
} | null | undefined): RevealLevel | null;
|
|
16
|
+
/** When a session last proved each factor (ISO timestamps). A second factor implies the password. */
|
|
17
|
+
export interface StepUpState {
|
|
18
|
+
password?: string;
|
|
19
|
+
mfa?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What a fresh OIDC token proves (components/11-authentik.md §Step-up flows).
|
|
23
|
+
* Authentik reports `pwd` for a password stage and `mfa` once an authenticator
|
|
24
|
+
* was validated in the same login; `auth_time` is that login's moment. A
|
|
25
|
+
* token whose login is older than `maxAgeSeconds` proves nothing now.
|
|
26
|
+
*/
|
|
27
|
+
export declare function stepUpFromClaims(claims: {
|
|
28
|
+
amr?: string[];
|
|
29
|
+
auth_time?: number;
|
|
30
|
+
}, now: Date, maxAgeSeconds: number): StepUpState;
|
|
31
|
+
/** Whether a recorded step-up still satisfies `step` at `now`. */
|
|
32
|
+
export declare function stepUpSatisfies(step: RevealStep | null, state: StepUpState | undefined, now: Date, ttlSeconds: number): boolean;
|
|
33
|
+
/** When a recorded step-up lapses, or `undefined` when there is none. */
|
|
34
|
+
export declare function stepUpUntil(at: string | undefined, ttlSeconds: number): string | undefined;
|
|
35
|
+
//# sourceMappingURL=reveal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reveal.d.ts","sourceRoot":"","sources":["../src/reveal.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,aAAa,6DAA8D,CAAC;AACzF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;AAExD,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,IAAI,CAWnF;AAED,sHAAsH;AACtH,wBAAgB,aAAa,CAAC,GAAG,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,IAAI,CAGpI;AAED,qGAAqG;AACrG,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,GAAG,WAAW,CAY9H;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO/H;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1F"}
|
package/dist/reveal.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reveal levels of a secret Field (ADR-018). Shared by the Engine (Definition
|
|
3
|
+
* schema, the reveal gate) and every frontend (what to ask of the person):
|
|
4
|
+
* pure and dependency-free so the Frontend kit can carry it.
|
|
5
|
+
*/
|
|
6
|
+
export const REVEAL_LEVELS = ["visible", "click", "captcha", "password", "2fa"];
|
|
7
|
+
export function revealStep(level) {
|
|
8
|
+
switch (level) {
|
|
9
|
+
case "captcha":
|
|
10
|
+
return "captcha";
|
|
11
|
+
case "password":
|
|
12
|
+
return "password";
|
|
13
|
+
case "2fa":
|
|
14
|
+
return "mfa";
|
|
15
|
+
default:
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** The level in effect for a Field Definition: `null` for a plain Field, `click` for a secret one that names none. */
|
|
20
|
+
export function revealLevelOf(def) {
|
|
21
|
+
if (!def?.secret)
|
|
22
|
+
return null;
|
|
23
|
+
return REVEAL_LEVELS.includes(def.reveal ?? "") ? def.reveal : "click";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* What a fresh OIDC token proves (components/11-authentik.md §Step-up flows).
|
|
27
|
+
* Authentik reports `pwd` for a password stage and `mfa` once an authenticator
|
|
28
|
+
* was validated in the same login; `auth_time` is that login's moment. A
|
|
29
|
+
* token whose login is older than `maxAgeSeconds` proves nothing now.
|
|
30
|
+
*/
|
|
31
|
+
export function stepUpFromClaims(claims, now, maxAgeSeconds) {
|
|
32
|
+
if (typeof claims.auth_time !== "number" || !Number.isFinite(claims.auth_time))
|
|
33
|
+
return {};
|
|
34
|
+
const at = new Date(claims.auth_time * 1000);
|
|
35
|
+
if (now.getTime() - at.getTime() > maxAgeSeconds * 1000 || at.getTime() - now.getTime() > 60_000)
|
|
36
|
+
return {};
|
|
37
|
+
const amr = claims.amr ?? [];
|
|
38
|
+
const out = {};
|
|
39
|
+
if (amr.includes("mfa")) {
|
|
40
|
+
out.mfa = at.toISOString();
|
|
41
|
+
out.password = at.toISOString();
|
|
42
|
+
}
|
|
43
|
+
if (amr.includes("pwd"))
|
|
44
|
+
out.password = at.toISOString();
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** Whether a recorded step-up still satisfies `step` at `now`. */
|
|
48
|
+
export function stepUpSatisfies(step, state, now, ttlSeconds) {
|
|
49
|
+
if (step === null)
|
|
50
|
+
return true;
|
|
51
|
+
if (step === "captcha")
|
|
52
|
+
return false;
|
|
53
|
+
const at = state?.[step];
|
|
54
|
+
if (!at)
|
|
55
|
+
return false;
|
|
56
|
+
const t = Date.parse(at);
|
|
57
|
+
return Number.isFinite(t) && now.getTime() - t <= ttlSeconds * 1000;
|
|
58
|
+
}
|
|
59
|
+
/** When a recorded step-up lapses, or `undefined` when there is none. */
|
|
60
|
+
export function stepUpUntil(at, ttlSeconds) {
|
|
61
|
+
if (!at)
|
|
62
|
+
return undefined;
|
|
63
|
+
const t = Date.parse(at);
|
|
64
|
+
return Number.isFinite(t) ? new Date(t + ttlSeconds * 1000).toISOString() : undefined;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=reveal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reveal.js","sourceRoot":"","sources":["../src/reveal.ts"],"names":[],"mappings":"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"}
|
package/dist/seal.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application-layer sealing for the Frontend layer's ephemeral state — the
|
|
3
|
+
* PKCE verifier and `state` of one login attempt (components/02-customer-ui-web.md
|
|
4
|
+
* §Data). AES-256-GCM through Web Crypto; the key is derived from a secret the
|
|
5
|
+
* Infrastructure layer delivers to the Web UI process. Nothing here is a
|
|
6
|
+
* resource key: OpenBao holds those, and the Web UI never sees one.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SealError extends Error {
|
|
9
|
+
constructor(message?: string);
|
|
10
|
+
}
|
|
11
|
+
/** Seal a JSON value under `secret` for `purpose`. Output is URL- and cookie-safe. */
|
|
12
|
+
export declare function seal(value: unknown, secret: string, purpose: string): Promise<string>;
|
|
13
|
+
/** Open a value sealed by {@link seal}. Throws {@link SealError} on any tampering or key mismatch. */
|
|
14
|
+
export declare function unseal<T = unknown>(token: string, secret: string, purpose: string): Promise<T>;
|
|
15
|
+
//# sourceMappingURL=seal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.d.ts","sourceRoot":"","sources":["../src/seal.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2BH,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,SAAkC;CAItD;AAED,sFAAsF;AACtF,wBAAsB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK3F;AAED,sGAAsG;AACtG,wBAAsB,MAAM,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAUpG"}
|
package/dist/seal.js
ADDED
|
Binary file
|
package/dist/seal.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seal.js","sourceRoot":"","sources":["../src/seal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every Frontend component holds, and how (ADR-003; components/02 §Security):
|
|
3
|
+
* the opaque session identifier in an `HttpOnly` cookie, forwarded as
|
|
4
|
+
* `X-Asturiu-Session`. Nothing else — no token, no claims, no resource data.
|
|
5
|
+
*/
|
|
6
|
+
/** The header the Interface API reads on every request except `POST /v0/sessions`. */
|
|
7
|
+
export declare const SESSION_HEADER = "x-asturiu-session";
|
|
8
|
+
export interface CookiePolicy {
|
|
9
|
+
httpOnly: true;
|
|
10
|
+
sameSite: "strict" | "lax";
|
|
11
|
+
path: string;
|
|
12
|
+
/** `Secure` whenever the page is served over TLS; local development is the only exception. */
|
|
13
|
+
secure: boolean;
|
|
14
|
+
maxAge?: number;
|
|
15
|
+
}
|
|
16
|
+
/** The Engine session cookie: `HttpOnly`, `Secure`, `SameSite=Strict`, host-only. */
|
|
17
|
+
export declare function sessionCookiePolicy(opts: {
|
|
18
|
+
secure: boolean;
|
|
19
|
+
maxAge?: number;
|
|
20
|
+
}): CookiePolicy;
|
|
21
|
+
/**
|
|
22
|
+
* The login-attempt cookie (sealed PKCE verifier + `state`). `Lax`, not
|
|
23
|
+
* `Strict`: the browser arrives at the callback from Authentik's origin and a
|
|
24
|
+
* `Strict` cookie would not be sent on that top-level navigation.
|
|
25
|
+
*/
|
|
26
|
+
export declare function loginCookiePolicy(opts: {
|
|
27
|
+
secure: boolean;
|
|
28
|
+
maxAgeSeconds?: number;
|
|
29
|
+
path?: string;
|
|
30
|
+
}): CookiePolicy;
|
|
31
|
+
/** What one login attempt needs to remember between `/auth/login` and `/auth/callback`. */
|
|
32
|
+
export interface LoginAttempt {
|
|
33
|
+
verifier: string;
|
|
34
|
+
state: string;
|
|
35
|
+
/** Site-relative path to land on afterwards. */
|
|
36
|
+
next: string;
|
|
37
|
+
created_at: string;
|
|
38
|
+
/** Set when the attempt is a step-up (ADR-018): the callback elevates the existing session instead of starting one. */
|
|
39
|
+
step_up?: "password" | "2fa";
|
|
40
|
+
}
|
|
41
|
+
/** Only site-relative paths may be used as post-login targets (no open redirect). */
|
|
42
|
+
export declare function safeNextPath(candidate: string | null | undefined, fallback?: string): string;
|
|
43
|
+
/** How `continuePage` writes its page. */
|
|
44
|
+
export interface ContinuePageOptions {
|
|
45
|
+
/** The label and the page title. Default "Signing in". */
|
|
46
|
+
text?: string;
|
|
47
|
+
/** A Content-Security-Policy nonce for the page's one `<style>` element. */
|
|
48
|
+
nonce?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The page a login callback answers with once the session cookie is set
|
|
52
|
+
* (ADR-032). The callback was reached through a redirect chain the sign-in host
|
|
53
|
+
* started — another site — so a redirect from it is still cross-site and a
|
|
54
|
+
* `SameSite=Strict` session cookie would not go with the next request: the app
|
|
55
|
+
* would see no session and start a login again, in a loop. A page that moves on
|
|
56
|
+
* by itself starts a fresh navigation from the app's own origin, which carries
|
|
57
|
+
* the cookie. `path` must already be a same-origin path (`safeNextPath`).
|
|
58
|
+
*
|
|
59
|
+
* The page is the "Signing in" screen the sign-in host showed while leaving,
|
|
60
|
+
* picked up at the same point of its animation, so the hand-over reads as one
|
|
61
|
+
* screen. It carries no script; its one `<style>` takes `nonce`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function continuePage(path: string, options?: string | ContinuePageOptions): string;
|
|
64
|
+
/** Seconds until an ISO timestamp, floored at zero. */
|
|
65
|
+
export declare function secondsUntil(iso: string | undefined, fallbackSeconds?: number): number;
|
|
66
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,sFAAsF;AACtF,eAAO,MAAM,cAAc,sBAAsB,CAAC;AAElD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,IAAI,CAAC;IACf,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAE5F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAEhH;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,uHAAuH;IACvH,OAAO,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;CAC9B;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,SAAM,GAAG,MAAM,CAKzF;AAED,0CAA0C;AAC1C,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,mBAAwB,GAAG,MAAM,CAW7F;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,eAAe,SAAO,GAAG,MAAM,CAIpF"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every Frontend component holds, and how (ADR-003; components/02 §Security):
|
|
3
|
+
* the opaque session identifier in an `HttpOnly` cookie, forwarded as
|
|
4
|
+
* `X-Asturiu-Session`. Nothing else — no token, no claims, no resource data.
|
|
5
|
+
*/
|
|
6
|
+
import { SIGNING_IN_CSS, signingInMarkup, signingInPhase } from "./signing-in.js";
|
|
7
|
+
/** The header the Interface API reads on every request except `POST /v0/sessions`. */
|
|
8
|
+
export const SESSION_HEADER = "x-asturiu-session";
|
|
9
|
+
/** The Engine session cookie: `HttpOnly`, `Secure`, `SameSite=Strict`, host-only. */
|
|
10
|
+
export function sessionCookiePolicy(opts) {
|
|
11
|
+
return { httpOnly: true, sameSite: "strict", path: "/", secure: opts.secure, maxAge: opts.maxAge };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The login-attempt cookie (sealed PKCE verifier + `state`). `Lax`, not
|
|
15
|
+
* `Strict`: the browser arrives at the callback from Authentik's origin and a
|
|
16
|
+
* `Strict` cookie would not be sent on that top-level navigation.
|
|
17
|
+
*/
|
|
18
|
+
export function loginCookiePolicy(opts) {
|
|
19
|
+
return { httpOnly: true, sameSite: "lax", path: opts.path ?? "/auth", secure: opts.secure, maxAge: opts.maxAgeSeconds ?? 600 };
|
|
20
|
+
}
|
|
21
|
+
/** Only site-relative paths may be used as post-login targets (no open redirect). */
|
|
22
|
+
export function safeNextPath(candidate, fallback = "/") {
|
|
23
|
+
if (!candidate)
|
|
24
|
+
return fallback;
|
|
25
|
+
if (!candidate.startsWith("/") || candidate.startsWith("//") || candidate.startsWith("/\\"))
|
|
26
|
+
return fallback;
|
|
27
|
+
if (/[\r\n]/.test(candidate))
|
|
28
|
+
return fallback;
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The page a login callback answers with once the session cookie is set
|
|
33
|
+
* (ADR-032). The callback was reached through a redirect chain the sign-in host
|
|
34
|
+
* started — another site — so a redirect from it is still cross-site and a
|
|
35
|
+
* `SameSite=Strict` session cookie would not go with the next request: the app
|
|
36
|
+
* would see no session and start a login again, in a loop. A page that moves on
|
|
37
|
+
* by itself starts a fresh navigation from the app's own origin, which carries
|
|
38
|
+
* the cookie. `path` must already be a same-origin path (`safeNextPath`).
|
|
39
|
+
*
|
|
40
|
+
* The page is the "Signing in" screen the sign-in host showed while leaving,
|
|
41
|
+
* picked up at the same point of its animation, so the hand-over reads as one
|
|
42
|
+
* screen. It carries no script; its one `<style>` takes `nonce`.
|
|
43
|
+
*/
|
|
44
|
+
export function continuePage(path, options = {}) {
|
|
45
|
+
const { text = "Signing in", nonce } = typeof options === "string" ? { text: options } : options;
|
|
46
|
+
const esc = (s) => s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
47
|
+
const to = esc(safeNextPath(path, "/"));
|
|
48
|
+
const css = `html{background:#f4f4f1}@media (prefers-color-scheme:dark){html{background:#0a0b0d}}body{margin:0}.signing-in{--si-phase:${signingInPhase()}}${SIGNING_IN_CSS}`;
|
|
49
|
+
return (`<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=${to}">` +
|
|
50
|
+
`<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta name="color-scheme" content="light dark"><meta name="robots" content="noindex">` +
|
|
51
|
+
`<title>${esc(text)}</title><style${nonce ? ` nonce="${esc(nonce)}"` : ""}>${css}</style></head>` +
|
|
52
|
+
`<body><div class="signing-in" role="status">${signingInMarkup({ text, href: safeNextPath(path, "/") })}</div></body></html>`);
|
|
53
|
+
}
|
|
54
|
+
/** Seconds until an ISO timestamp, floored at zero. */
|
|
55
|
+
export function secondsUntil(iso, fallbackSeconds = 3600) {
|
|
56
|
+
if (!iso)
|
|
57
|
+
return fallbackSeconds;
|
|
58
|
+
const ms = Date.parse(iso) - Date.now();
|
|
59
|
+
return Number.isFinite(ms) ? Math.max(0, Math.floor(ms / 1000)) : fallbackSeconds;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|