@aooth/idp 0.1.58 → 0.1.59
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.cts +40 -9
- package/dist/index.d.mts +40 -9
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Clock } from "@aooth/auth";
|
|
2
2
|
import { FederatedIdentity, FederatedIdentityStore, FederatedProfileSnapshot, UserService } from "@aooth/user";
|
|
3
3
|
import { JWTPayload, JWTVerifyGetKey } from "jose";
|
|
4
|
-
|
|
5
4
|
//#region src/errors.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Federated-login failure taxonomy (RFC IDP.md §8). Mirrors `AuthError` /
|
|
@@ -12,7 +11,25 @@ import { JWTPayload, JWTVerifyGetKey } from "jose";
|
|
|
12
11
|
* `OAuthController`'s job; this layer only classifies. Messages are deliberately
|
|
13
12
|
* benign so the controller can surface them without leaking CSRF-vs-expiry.
|
|
14
13
|
*/
|
|
15
|
-
type OAuthErrorType =
|
|
14
|
+
type OAuthErrorType =
|
|
15
|
+
/** `:provider` did not resolve in the registry (→ HTTP 404 in phase 3). */
|
|
16
|
+
"UNKNOWN_PROVIDER" |
|
|
17
|
+
/** Misconfigured provider/registry (missing clientId, issuer, baseUrl, secret). */
|
|
18
|
+
"INVALID_CONFIG" |
|
|
19
|
+
/** Signed `state` failed signature/binding verification (CSRF). */
|
|
20
|
+
"STATE_INVALID" |
|
|
21
|
+
/** Signed `state` is well-formed but past its TTL — restart `/start`. */
|
|
22
|
+
"STATE_EXPIRED" |
|
|
23
|
+
/** Provider returned `?error=` or the user denied consent. */
|
|
24
|
+
"PROVIDER_DENIED" |
|
|
25
|
+
/** Token-endpoint exchange failed: network, 5xx, malformed body, or `code` reuse. */
|
|
26
|
+
"EXCHANGE_FAILED" |
|
|
27
|
+
/** JWKS / discovery document fetch failed — verification fails CLOSED (§7). */
|
|
28
|
+
"JWKS_FAILED" |
|
|
29
|
+
/** OIDC ID-token failed the OIDC Core 3.1.3.7 validation list (§7). */
|
|
30
|
+
"ID_TOKEN_INVALID" |
|
|
31
|
+
/** Policy needed a verified email but the provider returned none. */
|
|
32
|
+
"EMAIL_UNAVAILABLE";
|
|
16
33
|
declare class OAuthError extends Error {
|
|
17
34
|
readonly type: OAuthErrorType;
|
|
18
35
|
readonly details?: Record<string, unknown> | undefined;
|
|
@@ -127,12 +144,16 @@ declare function isConfigurableProvider(p: IdentityProvider): p is ConfigurableP
|
|
|
127
144
|
* How a federated login that matches an existing local account **by email**
|
|
128
145
|
* is handled (RFC §4 — the account-takeover-sensitive knob).
|
|
129
146
|
*/
|
|
130
|
-
type EmailMatchPolicy =
|
|
147
|
+
type EmailMatchPolicy =
|
|
148
|
+
/** Never match by email — always create a fresh account. */
|
|
149
|
+
"create-separate" |
|
|
131
150
|
/**
|
|
132
151
|
* Auto-link only when the provider's `email_verified === true` AND the
|
|
133
152
|
* provider is in `trustEmailVerifiedFrom`. A deliberate security downgrade.
|
|
134
153
|
*/
|
|
135
|
-
|
|
154
|
+
"auto-link-if-verified" |
|
|
155
|
+
/** Default & safest: surface the candidate; require interactive proof-of-control to link. */
|
|
156
|
+
"require-interactive-link";
|
|
136
157
|
interface FederatedPolicy {
|
|
137
158
|
/** Default `'require-interactive-link'`. */
|
|
138
159
|
emailMatch?: EmailMatchPolicy;
|
|
@@ -155,22 +176,32 @@ declare function resolveFederatedPolicy(policy?: FederatedPolicy): ResolvedFeder
|
|
|
155
176
|
* (`linked`/`created`/`auto-linked`), divert to an interactive link sub-flow
|
|
156
177
|
* (`needs-link`), or fail soft (`denied`).
|
|
157
178
|
*/
|
|
158
|
-
type ResolveOutcome =
|
|
179
|
+
type ResolveOutcome =
|
|
180
|
+
/** Known `(provider, subject)` → its owning user. */
|
|
181
|
+
{
|
|
159
182
|
kind: "linked";
|
|
160
183
|
userId: string;
|
|
161
184
|
isNew: false;
|
|
162
|
-
}
|
|
185
|
+
} |
|
|
186
|
+
/** No match → a fresh account was created and linked. */
|
|
187
|
+
{
|
|
163
188
|
kind: "created";
|
|
164
189
|
userId: string;
|
|
165
190
|
isNew: true;
|
|
166
|
-
}
|
|
191
|
+
} |
|
|
192
|
+
/** Email matched an existing account and policy auto-linked it. */
|
|
193
|
+
{
|
|
167
194
|
kind: "auto-linked";
|
|
168
195
|
userId: string;
|
|
169
196
|
isNew: false;
|
|
170
|
-
}
|
|
197
|
+
} |
|
|
198
|
+
/** Email matched an existing account; interactive proof-of-control required to link. */
|
|
199
|
+
{
|
|
171
200
|
kind: "needs-link";
|
|
172
201
|
candidateUserId: string;
|
|
173
|
-
}
|
|
202
|
+
} |
|
|
203
|
+
/** Refused: signup disabled with no match, or policy needed an email the provider lacked. */
|
|
204
|
+
{
|
|
174
205
|
kind: "denied";
|
|
175
206
|
reason: "signup-disabled" | "email-unavailable";
|
|
176
207
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Clock } from "@aooth/auth";
|
|
2
2
|
import { JWTPayload, JWTVerifyGetKey } from "jose";
|
|
3
3
|
import { FederatedIdentity, FederatedIdentityStore, FederatedProfileSnapshot, UserService } from "@aooth/user";
|
|
4
|
-
|
|
5
4
|
//#region src/errors.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Federated-login failure taxonomy (RFC IDP.md §8). Mirrors `AuthError` /
|
|
@@ -12,7 +11,25 @@ import { FederatedIdentity, FederatedIdentityStore, FederatedProfileSnapshot, Us
|
|
|
12
11
|
* `OAuthController`'s job; this layer only classifies. Messages are deliberately
|
|
13
12
|
* benign so the controller can surface them without leaking CSRF-vs-expiry.
|
|
14
13
|
*/
|
|
15
|
-
type OAuthErrorType =
|
|
14
|
+
type OAuthErrorType =
|
|
15
|
+
/** `:provider` did not resolve in the registry (→ HTTP 404 in phase 3). */
|
|
16
|
+
"UNKNOWN_PROVIDER" |
|
|
17
|
+
/** Misconfigured provider/registry (missing clientId, issuer, baseUrl, secret). */
|
|
18
|
+
"INVALID_CONFIG" |
|
|
19
|
+
/** Signed `state` failed signature/binding verification (CSRF). */
|
|
20
|
+
"STATE_INVALID" |
|
|
21
|
+
/** Signed `state` is well-formed but past its TTL — restart `/start`. */
|
|
22
|
+
"STATE_EXPIRED" |
|
|
23
|
+
/** Provider returned `?error=` or the user denied consent. */
|
|
24
|
+
"PROVIDER_DENIED" |
|
|
25
|
+
/** Token-endpoint exchange failed: network, 5xx, malformed body, or `code` reuse. */
|
|
26
|
+
"EXCHANGE_FAILED" |
|
|
27
|
+
/** JWKS / discovery document fetch failed — verification fails CLOSED (§7). */
|
|
28
|
+
"JWKS_FAILED" |
|
|
29
|
+
/** OIDC ID-token failed the OIDC Core 3.1.3.7 validation list (§7). */
|
|
30
|
+
"ID_TOKEN_INVALID" |
|
|
31
|
+
/** Policy needed a verified email but the provider returned none. */
|
|
32
|
+
"EMAIL_UNAVAILABLE";
|
|
16
33
|
declare class OAuthError extends Error {
|
|
17
34
|
readonly type: OAuthErrorType;
|
|
18
35
|
readonly details?: Record<string, unknown> | undefined;
|
|
@@ -127,12 +144,16 @@ declare function isConfigurableProvider(p: IdentityProvider): p is ConfigurableP
|
|
|
127
144
|
* How a federated login that matches an existing local account **by email**
|
|
128
145
|
* is handled (RFC §4 — the account-takeover-sensitive knob).
|
|
129
146
|
*/
|
|
130
|
-
type EmailMatchPolicy =
|
|
147
|
+
type EmailMatchPolicy =
|
|
148
|
+
/** Never match by email — always create a fresh account. */
|
|
149
|
+
"create-separate" |
|
|
131
150
|
/**
|
|
132
151
|
* Auto-link only when the provider's `email_verified === true` AND the
|
|
133
152
|
* provider is in `trustEmailVerifiedFrom`. A deliberate security downgrade.
|
|
134
153
|
*/
|
|
135
|
-
|
|
154
|
+
"auto-link-if-verified" |
|
|
155
|
+
/** Default & safest: surface the candidate; require interactive proof-of-control to link. */
|
|
156
|
+
"require-interactive-link";
|
|
136
157
|
interface FederatedPolicy {
|
|
137
158
|
/** Default `'require-interactive-link'`. */
|
|
138
159
|
emailMatch?: EmailMatchPolicy;
|
|
@@ -155,22 +176,32 @@ declare function resolveFederatedPolicy(policy?: FederatedPolicy): ResolvedFeder
|
|
|
155
176
|
* (`linked`/`created`/`auto-linked`), divert to an interactive link sub-flow
|
|
156
177
|
* (`needs-link`), or fail soft (`denied`).
|
|
157
178
|
*/
|
|
158
|
-
type ResolveOutcome =
|
|
179
|
+
type ResolveOutcome =
|
|
180
|
+
/** Known `(provider, subject)` → its owning user. */
|
|
181
|
+
{
|
|
159
182
|
kind: "linked";
|
|
160
183
|
userId: string;
|
|
161
184
|
isNew: false;
|
|
162
|
-
}
|
|
185
|
+
} |
|
|
186
|
+
/** No match → a fresh account was created and linked. */
|
|
187
|
+
{
|
|
163
188
|
kind: "created";
|
|
164
189
|
userId: string;
|
|
165
190
|
isNew: true;
|
|
166
|
-
}
|
|
191
|
+
} |
|
|
192
|
+
/** Email matched an existing account and policy auto-linked it. */
|
|
193
|
+
{
|
|
167
194
|
kind: "auto-linked";
|
|
168
195
|
userId: string;
|
|
169
196
|
isNew: false;
|
|
170
|
-
}
|
|
197
|
+
} |
|
|
198
|
+
/** Email matched an existing account; interactive proof-of-control required to link. */
|
|
199
|
+
{
|
|
171
200
|
kind: "needs-link";
|
|
172
201
|
candidateUserId: string;
|
|
173
|
-
}
|
|
202
|
+
} |
|
|
203
|
+
/** Refused: signup disabled with no match, or policy needed an email the provider lacked. */
|
|
204
|
+
{
|
|
174
205
|
kind: "denied";
|
|
175
206
|
reason: "signup-disabled" | "email-unavailable";
|
|
176
207
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aooth/idp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.59",
|
|
4
4
|
"description": "Framework-agnostic OAuth2 / OIDC federated-login core for aoothjs (provider registry, PKCE/state, ID-token verification, account resolution)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aoothjs",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"jose": "^6.2.3",
|
|
46
|
-
"@aooth/auth": "0.1.
|
|
47
|
-
"@aooth/user": "0.1.
|
|
46
|
+
"@aooth/auth": "0.1.59",
|
|
47
|
+
"@aooth/user": "0.1.59"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|