@o3co/auth-provider-foundation 0.14.0 → 0.16.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.
@@ -1,4 +1,4 @@
1
- import type { FederatedIdentityLink, LinkFederatedIdentityResult, User, UserRepository } from "@o3co/auth-provider-core";
1
+ import { type FederatedIdentityLink, type FederatedIdentityLookup, type FederatedIdentityLookupResult, type FederatedIdentityRegistration, type LinkFederatedIdentityResult, type User, type UserRepository } from "@o3co/auth-provider-core";
2
2
  /**
3
3
  * Default ceiling on an upstream response body, in bytes.
4
4
  *
@@ -7,36 +7,114 @@ import type { FederatedIdentityLink, LinkFederatedIdentityResult, User, UserRepo
7
7
  * process out of memory one login at a time (#285).
8
8
  */
9
9
  export declare const DEFAULT_MAX_RESPONSE_BYTES: number;
10
+ /**
11
+ * What the Store declares it can place (#613): identities issued under one
12
+ * registration — the federation's name, the issuer and the client, exactly as
13
+ * a federation-grant connection is configured — given at least these claims
14
+ * from the verified id_token. The synchronous probe D7 check 5 asks at boot
15
+ * cannot reach the Store, so the operator relays the Store's own claim here;
16
+ * boot then holds every connection to it. `requiredClaims = []` is a strategy
17
+ * on the registration and the `sub` alone.
18
+ */
19
+ export interface FederatedIdentityLookupCoverage {
20
+ readonly provider: string;
21
+ readonly issuer: string;
22
+ readonly clientId: string;
23
+ readonly requiredClaims: readonly string[];
24
+ }
10
25
  /**
11
26
  * `UserRepository` backed by "the Store" — the upstream user service defined
12
27
  * on core's `User` doc (`@o3co/auth-provider-core`, `src/repositories/types.mts`).
13
28
  *
14
- * Both endpoints receive plaintext user credentials, so both are validated at
15
- * **construction**: a deployment configured with an `http://` Store URL fails
16
- * at boot rather than leaking the first user's password (#285). `http://` is
17
- * accepted for loopback hosts only — see `src/endpointUrl.mts` for the
18
- * carve-out and its rationale.
29
+ * Every endpoint receives a plaintext credential or a verified identity, so
30
+ * each is validated at **construction**: a deployment configured with an
31
+ * `http://` Store URL fails at boot rather than leaking the first user's
32
+ * password (#285). `http://` is accepted for loopback hosts only — see
33
+ * `src/endpointUrl.mts` for the carve-out and its rationale.
34
+ *
35
+ * No request follows a redirect, so a URL that passed that check is the only
36
+ * place its body is ever sent and the only one whose answer is taken: a `3xx`
37
+ * from the Store is an upstream failure, thrown like any other unexpected
38
+ * status, and its `Location` is never contacted.
39
+ *
40
+ * With `bearerToken` configured, every request — authentication, linking and
41
+ * the identity lookup alike — carries `Authorization: Bearer <token>`, so the
42
+ * Store can refuse a caller that is not this deployment; without it, no
43
+ * request carries an `Authorization` header. The Store says it refused THIS
44
+ * deployment by answering `401` or `403` with a `Bearer` challenge (RFC 6750
45
+ * §3), and that answer, while a token was sent, throws a
46
+ * {@link StoreCredentialRefusedError} on every request — never "no such user"
47
+ * or a refused link, which is what either status without the challenge still
48
+ * means.
49
+ *
50
+ * A transport's own error is never thrown: undici's parser errors quote the
51
+ * bytes they rejected, and a peer that reflects the request puts the
52
+ * `Authorization` header — or a password — there. A request that cannot be
53
+ * made, or an answer that cannot be read, throws a `StoreTransportError`
54
+ * (`src/repositories/storeErrors.mts`): a fixed message naming the endpoint
55
+ * and what failed, at most an allowlisted transport code, no cause.
56
+ *
57
+ * Every message names an endpoint by its origin and path alone
58
+ * (`endpointForMessage`, `src/endpointUrl.mts`): a Store URL may carry a
59
+ * query string, and a query may carry a credential, while every caller logs
60
+ * what this throws.
19
61
  */
20
62
  export declare class HttpUserRepository implements UserRepository {
63
+ #private;
21
64
  private authenticateUrl;
22
65
  private authenticateByTokenUrl;
23
66
  private timeout;
24
67
  private maxResponseBytes;
25
68
  private linkFederatedIdentityUrl?;
69
+ private findSubjectByFederatedIdentityUrl?;
70
+ private readonly coverage;
26
71
  /**
27
72
  * #482 — see {@link UserRepository.linkFederatedIdentity}. Present only when
28
73
  * `linkFederatedIdentityUrl` is configured, which is how the federation routes
29
74
  * know to refuse `?link=1` up front instead of at the callback.
30
75
  */
31
76
  readonly linkFederatedIdentity?: (userId: string, identity: FederatedIdentityLink) => Promise<LinkFederatedIdentityResult>;
32
- constructor({ authenticateUrl, authenticateByTokenUrl, linkFederatedIdentityUrl, timeout, maxResponseBytes, }: {
77
+ /**
78
+ * #613 — see {@link UserRepository.supportsFederatedIdentityLookup}. Present
79
+ * together with the lookup, only when `findSubjectByFederatedIdentityUrl` is
80
+ * configured: absent, a deployment that requires the lookup is refused at
81
+ * boot by the method's name, which is the message that says what to set.
82
+ * Answers from the declaration alone — the Store is not asked at boot.
83
+ */
84
+ readonly supportsFederatedIdentityLookup?: (registration: FederatedIdentityRegistration, identityClaims: readonly string[]) => boolean;
85
+ /** #613 — see {@link UserRepository.findSubjectByFederatedIdentity}. Present with the probe. */
86
+ readonly findSubjectByFederatedIdentity?: (identity: FederatedIdentityLookup) => Promise<FederatedIdentityLookupResult>;
87
+ constructor({ authenticateUrl, authenticateByTokenUrl, linkFederatedIdentityUrl, findSubjectByFederatedIdentityUrl, federatedIdentityLookupCoverage, bearerToken, timeout, maxResponseBytes, }: {
33
88
  authenticateUrl: string;
34
89
  authenticateByTokenUrl: string;
35
90
  /** #482: optional; the Store's link endpoint. Same https rule as the other two. */
36
91
  linkFederatedIdentityUrl?: string;
92
+ /** #613: optional; the Store's identity lookup. Same https rule; carries a verified identity. */
93
+ findSubjectByFederatedIdentityUrl?: string;
94
+ /** #613: which registrations the Store's lookup covers. Needs the URL; `[]` covers none. */
95
+ federatedIdentityLookupCoverage?: readonly FederatedIdentityLookupCoverage[];
96
+ /**
97
+ * Optional; sent to the Store on every request as `Authorization: Bearer
98
+ * <token>`. A bare RFC 6750 token (no scheme) of at least
99
+ * `MIN_SECRET_ENTROPY_BYTES` of key material — `openssl rand -hex 32`.
100
+ */
101
+ bearerToken?: string;
37
102
  timeout: number;
38
103
  maxResponseBytes?: number;
39
104
  });
105
+ /** What every request carries: the body's type and, when configured, the credential. */
106
+ private headers;
107
+ /**
108
+ * Throws {@link StoreCredentialRefusedError} when a non-`2xx` answer is the
109
+ * Store refusing this deployment's credential: a `401` or `403` with a
110
+ * `Bearer` challenge, to a request that carried the token. Read before any
111
+ * other reading of the status — without it a token the Store does not
112
+ * accept is every login "no such user" and every link "refused", with
113
+ * nothing logged. Without a token sent, a challenge is not about one, and a
114
+ * Store whose stack challenges every refusal keeps the wire meaning it has
115
+ * always had.
116
+ */
117
+ private assertCredentialAccepted;
40
118
  authenticate(username: string, password: string): Promise<User | null>;
41
119
  authenticateByToken(token: string): Promise<User | null>;
42
120
  /**
@@ -46,6 +124,32 @@ export declare class HttpUserRepository implements UserRepository {
46
124
  * provider, …); `409` says the identity is already someone else's.
47
125
  */
48
126
  private linkViaHttp;
127
+ /** The declaration for a registration, if the operator made one. Exact on all three. */
128
+ private declared;
129
+ /**
130
+ * POST `{ provider, issuer, clientId, sub, claims }` to the Store's lookup
131
+ * (#613) and read one of the port's three answers. Answered locally, with
132
+ * no request, where the declaration already decides: a registration nobody
133
+ * declared is `registration_not_covered`, and a declared one arriving
134
+ * without a claim its strategy needs is `identity_not_resolvable`. Every
135
+ * claim the connection supplied is sent, not only the required ones: what
136
+ * the Store matches on is the Store's business.
137
+ */
138
+ private lookupViaHttp;
139
+ /**
140
+ * The lookup's transport: the same deadline, cap and abort as {@link post},
141
+ * and like it never follows a redirect — the body carries a verified
142
+ * identity, and a `Location` is not a configured endpoint. None of
143
+ * {@link post}'s readings, though. A `401`/`403` is not "nobody", a `409` is
144
+ * not a conflict, a `404` is not an absence: only a `2xx` carrying one of the
145
+ * three answers is an answer, and everything else throws — as an outage,
146
+ * which is what a lookup that could not be made is — a `401` or `403` with a
147
+ * `Bearer` challenge, to a request that carried the token, as the refused
148
+ * credential it is. What is thrown names the endpoint by origin and path — and, for an answer
149
+ * with a status, the status; for a transport failure, at most its code — and
150
+ * never the body, the identity, a status text or an underlying cause.
151
+ */
152
+ private postLookup;
49
153
  private post;
50
154
  }
51
155
  //# sourceMappingURL=HttpUserRepository.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HttpUserRepository.d.mts","sourceRoot":"","sources":["../../src/repositories/HttpUserRepository.mts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACX,qBAAqB,EACrB,2BAA2B,EAC3B,IAAI,EACJ,cAAc,EACd,MAAM,0BAA0B,CAAC;AAMlC;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAAc,CAAC;AAkItD;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACxD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,qBAAqB,KAC3B,OAAO,CAAC,2BAA2B,CAAC,CAAC;gBAE9B,EACX,eAAe,EACf,sBAAsB,EACtB,wBAAwB,EACxB,OAAO,EACP,gBAA6C,GAC7C,EAAE;QACF,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,mFAAmF;QACnF,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,OAAO,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC1B;IA4BK,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAKtE,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAI9D;;;;;OAKG;YACW,WAAW;YAWX,IAAI;CA4ElB"}
1
+ {"version":3,"file":"HttpUserRepository.d.mts","sourceRoot":"","sources":["../../src/repositories/HttpUserRepository.mts"],"names":[],"mappings":"AAgBA,OAAO,EAEN,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAGhC,KAAK,IAAI,EACT,KAAK,cAAc,EACnB,MAAM,0BAA0B,CAAC;AAQlC;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,QAAc,CAAC;AA8BtD;;;;;;;;GAQG;AACH,MAAM,WAAW,+BAA+B;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AA4RD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,qBAAa,kBAAmB,YAAW,cAAc;;IAOxD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,OAAO,CAAC,iCAAiC,CAAC,CAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IACtE;;;;OAIG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAChC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,qBAAqB,KAC3B,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAC1C,YAAY,EAAE,6BAA6B,EAC3C,cAAc,EAAE,SAAS,MAAM,EAAE,KAC7B,OAAO,CAAC;IACb,gGAAgG;IAChG,QAAQ,CAAC,8BAA8B,CAAC,EAAE,CACzC,QAAQ,EAAE,uBAAuB,KAC7B,OAAO,CAAC,6BAA6B,CAAC,CAAC;gBAEhC,EACX,eAAe,EACf,sBAAsB,EACtB,wBAAwB,EACxB,iCAAiC,EACjC,+BAA+B,EAC/B,WAAW,EACX,OAAO,EACP,gBAA6C,GAC7C,EAAE;QACF,eAAe,EAAE,MAAM,CAAC;QACxB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,mFAAmF;QACnF,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,iGAAiG;QACjG,iCAAiC,CAAC,EAAE,MAAM,CAAC;QAC3C,4FAA4F;QAC5F,+BAA+B,CAAC,EAAE,SAAS,+BAA+B,EAAE,CAAC;QAC7E;;;;WAIG;QACH,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC1B;IAmDD,wFAAwF;IACxF,OAAO,CAAC,OAAO;IAMf;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB;IAU1B,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAKtE,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAI9D;;;;;OAKG;YACW,WAAW;IAWzB,wFAAwF;IACxF,OAAO,CAAC,QAAQ;IAWhB;;;;;;;;OAQG;YACW,aAAa;IAoB3B;;;;;;;;;;;;OAYG;YACW,UAAU;YA8EV,IAAI;CAiHlB"}