@seamless-auth/oidc-provider 0.0.0-stage → 0.1.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 +59 -0
- package/LICENSE +201 -0
- package/README.md +273 -2
- package/dist/adapter.d.ts +44 -0
- package/dist/adapter.js +249 -0
- package/dist/adapter.js.map +1 -0
- package/dist/apiClient.d.ts +35 -0
- package/dist/apiClient.js +217 -0
- package/dist/apiClient.js.map +1 -0
- package/dist/authRequirements.d.ts +37 -0
- package/dist/authRequirements.js +58 -0
- package/dist/authRequirements.js.map +1 -0
- package/dist/clientSecrets.d.ts +10 -0
- package/dist/clientSecrets.js +102 -0
- package/dist/clientSecrets.js.map +1 -0
- package/dist/configCache.d.ts +14 -0
- package/dist/configCache.js +45 -0
- package/dist/configCache.js.map +1 -0
- package/dist/createOidcEdge.d.ts +45 -0
- package/dist/createOidcEdge.js +132 -0
- package/dist/createOidcEdge.js.map +1 -0
- package/dist/deviceFlow.d.ts +53 -0
- package/dist/deviceFlow.js +168 -0
- package/dist/deviceFlow.js.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.js +36 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +40 -0
- package/dist/errors.js.map +1 -0
- package/dist/firstPartySession.d.ts +21 -0
- package/dist/firstPartySession.js +28 -0
- package/dist/firstPartySession.js.map +1 -0
- package/dist/hostedPages.d.ts +51 -0
- package/dist/hostedPages.js +172 -0
- package/dist/hostedPages.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions.d.ts +17 -0
- package/dist/interactions.js +209 -0
- package/dist/interactions.js.map +1 -0
- package/dist/jwtAccessTokens.d.ts +23 -0
- package/dist/jwtAccessTokens.js +125 -0
- package/dist/jwtAccessTokens.js.map +1 -0
- package/dist/logout.d.ts +22 -0
- package/dist/logout.js +86 -0
- package/dist/logout.js.map +1 -0
- package/dist/originGuard.d.ts +10 -0
- package/dist/originGuard.js +32 -0
- package/dist/originGuard.js.map +1 -0
- package/dist/pkce.d.ts +16 -0
- package/dist/pkce.js +32 -0
- package/dist/pkce.js.map +1 -0
- package/dist/providerConfig.d.ts +50 -0
- package/dist/providerConfig.js +305 -0
- package/dist/providerConfig.js.map +1 -0
- package/dist/rateLimit.d.ts +13 -0
- package/dist/rateLimit.js +36 -0
- package/dist/rateLimit.js.map +1 -0
- package/dist/refreshReuse.d.ts +3 -0
- package/dist/refreshReuse.js +56 -0
- package/dist/refreshReuse.js.map +1 -0
- package/dist/requestContext.d.ts +40 -0
- package/dist/requestContext.js +43 -0
- package/dist/requestContext.js.map +1 -0
- package/dist/resourceServers.d.ts +16 -0
- package/dist/resourceServers.js +59 -0
- package/dist/resourceServers.js.map +1 -0
- package/dist/resumeCookie.d.ts +8 -0
- package/dist/resumeCookie.js +46 -0
- package/dist/resumeCookie.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +36 -0
- package/dist/server.js.map +1 -0
- package/dist/signingKey.d.ts +29 -0
- package/dist/signingKey.js +142 -0
- package/dist/signingKey.js.map +1 -0
- package/dist/startup.d.ts +4 -0
- package/dist/startup.js +19 -0
- package/dist/startup.js.map +1 -0
- package/dist/tokenAudit.d.ts +4 -0
- package/dist/tokenAudit.js +42 -0
- package/dist/tokenAudit.js.map +1 -0
- package/dist/tokenLifetimes.d.ts +31 -0
- package/dist/tokenLifetimes.js +51 -0
- package/dist/tokenLifetimes.js.map +1 -0
- package/dist/ui/assets/index-CydLXRvo.js +18 -0
- package/dist/ui/assets/index-y3LME-Gv.css +1 -0
- package/dist/ui/index.html +15 -0
- package/package.json +115 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @seamless-auth/oidc-provider
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 793a30c: Refresh tokens, authorization codes and device codes are now single use under concurrency too. The API's consume answers whether this request marked the record used (`@seamless-auth/types` 0.34.0). A request that loses gets `invalid_grant`. A lost refresh is handled as refresh token reuse, which ends the first-party session, and a lost code revokes its grant, as a sequential reuse does. A token request whose session ended while it ran now gets `invalid_grant` instead of a server error.
|
|
8
|
+
- dcf17cc: Sign users in to OIDC apps with the authorization code flow and PKCE, discovery and userinfo. Interactions finish only after a first-party sign-in through this service's own adapter, through four JSON routes the hosted pages call: `GET /interaction/:uid/details`, and `POST /interaction/:uid/login`, `/consent` and `/abort`. The provider grant is saved against the first-party session before its auth context is read, and every stored record and signature names that session, so the API's grant binding holds. ID tokens carry the scope claims plus `acr`, `amr` and `auth_time` from the API.
|
|
9
|
+
|
|
10
|
+
- A recovery session cannot finish a sign-in, and `prompt=login` or `max_age` refuse a session older than the request.
|
|
11
|
+
- First-party clients have consent implied. Third-party clients do not exist while `third_party_clients_enabled` is off, and see the consent page on every authorization when it is on.
|
|
12
|
+
- The interaction routes refuse a state change unless `Sec-Fetch-Site` is `same-origin` or the `Origin` is the issuer's, so a sibling subdomain cannot drive them.
|
|
13
|
+
|
|
14
|
+
Needs fells-code/seamless-auth-api#524 for a session signed in to more than one app.
|
|
15
|
+
- 1697942: Client authentication per ADR D6. Confidential clients use `private_key_jwt` by default, and each assertion is single-use through the API's replay store (a replay is `invalid_client`, not a 500). Public clients use `none` with PKCE. `client_secret_basic` and `client_secret_post` are offered only when the API allows client secrets (`allow_client_secrets`, read at startup), and only work for clients opted in on the API.
|
|
16
|
+
|
|
17
|
+
A presented secret is checked with `POST /oidc/clients/:id/verify-secret` before the library runs. When the API accepts it, the request carries a one-time value in its place, and the client's metadata carries that value for that request only, so this service never holds a client's real secret. `client_secret_jwt` and HS* algorithms are never offered.
|
|
18
|
+
- 2532a7c: `pkce.required` is now per client. PKCE stays required unless the API sends `urn:seamless:pkce_required: false` for a confidential client (`@seamless-auth/types` 0.34.0), as RFC 9700 section 2.1.1 allows. Such a client must send `nonce` on any code request without PKCE that asks for an ID token, or gets `invalid_request`, unless the API also sends `urn:seamless:nonce_required: false`. A code request without `openid` still needs PKCE, since a nonce only binds an ID token. An opt-out on a public client is ignored, so PKCE stays required for it.
|
|
19
|
+
- d3b779e: Apply each application's `accessTokenTtlSeconds`, `idTokenTtlSeconds` and `refreshTokenTtlSeconds`, which the API now sends as `urn:seamless:access_token_ttl`, `urn:seamless:id_token_ttl` and `urn:seamless:refresh_token_ttl` client metadata (`@seamless-auth/types` 0.34.0). An access token for a resource takes the shorter of the resource's and the application's lifetime. Unset values keep the defaults (600 seconds for access and ID tokens, 14 days for refresh tokens), and `ttl.RefreshToken` is now set explicitly, keeping the library's rule that a public browser app's rotated token never outlives the one it replaced.
|
|
20
|
+
- 4c74314: The device authorization grant (RFC 8628) for first-party clients with the device flow turned on: `/device/auth` returns an 8-character base-20 user code (about 34.5 bits) that lives 10 minutes and an `interval` of 5 seconds, and `/device` is the verification page in the hosted UI. The page names the app and warns, before any sign-in, to continue only on a device you started yourself. Every approval signs in again, even in a browser that is already signed in. Polling sooner than the interval gets `slow_down` and a longer interval, per device code. Code entry is limited to 10 a minute per address and 600 a minute per instance. Device-flow tokens never carry `acr`, including after a refresh, and `acr_values` and `max_age` are refused on device requests (ADR D10). Sign requests now name the grant (`grantType`), which needs `@seamless-auth/types` 0.34.0 and the API with edge contract version 2.
|
|
21
|
+
- 4d9492a: Hosted login and consent pages. A SPA on `@seamless-auth/react`, served by this service at `/interaction/:uid` and the bundled sign-in paths (`/login`, `/verify-email-otp`, `/register-passkey` and the rest), signs users in through this service's own first-party adapter, so every enabled method and every first-party protection applies. It finishes the interaction once the user is signed in, sends a recovery session to enrollment, and has the user sign in again when the app asks for `prompt=login` or `max_age`.
|
|
22
|
+
|
|
23
|
+
- Third-party clients get a consent page naming the client, the host it sends you back to, and what it asks for.
|
|
24
|
+
- Branding is read from the API's config when a page is served (`app_name` today), with no build step. A config change shows within 30 seconds.
|
|
25
|
+
- Every page gets a strict CSP: scripts from this origin only, styles from this origin or carrying the response's nonce, `frame-ancestors 'none'`, plus `no-store`, `nosniff` and `no-referrer`.
|
|
26
|
+
- The adapter routes under `/auth` now need `Sec-Fetch-Site: same-origin` or the issuer's `Origin` for a state change, like the interaction routes.
|
|
27
|
+
- The resume cookie is now `__Host-oidc_resume` with `Path=/`, and resuming an authorization other than the one it names is refused.
|
|
28
|
+
- The package ships the built pages in `dist/ui`. `createOidcEdge` takes `uiDir` to serve another build.
|
|
29
|
+
- a167b9d: The hosted pages take their branding from the API's `hosted_ui` system config, read from `GET /oidc/config` on each page load like `app_name`. `appName` overrides the name, `logoUrl` shows a logo (its origin is added to the page's `img-src`), `colors` and `fontFamily` become CSS variables in a `<style>` carrying the response's nonce. A branded palette replaces the dark palette, since it was chosen against a light page. The bundled sign-in screens now take their accent, text, surface and border colors from the same variables, so branding reaches every screen. Needs `@seamless-auth/types` with `hostedUi` on the config response.
|
|
30
|
+
- a6de395: Token introspection (RFC 7662) and revocation (RFC 7009) at `/token/introspection` and `/token/revocation`, listed in discovery.
|
|
31
|
+
|
|
32
|
+
- A client may introspect its own tokens. A resource server, registered as a confidential application linked to its resource, may introspect tokens whose `aud` is that resource. Every answer comes from the API's store, so a token whose session has ended is inactive at once.
|
|
33
|
+
- JWT access tokens are now stored under their `jti` when issued, so they can be introspected and revoked. A presented JWT is mapped back to its record only after its signature checks out against the provider's keys, and the `jti` alone is not accepted as a token.
|
|
34
|
+
- Revoking a refresh token ends that app's grant and its tokens, not the user's first-party session. Each revocation is recorded in the API's audit log as `token_revoked`.
|
|
35
|
+
- Both endpoints are limited to 1200 requests a minute per client IP, per instance.
|
|
36
|
+
- a81ad61: First release of the Seamless Auth OpenID Provider service, on `oidc-provider` 9.13 and `@seamless-auth/express`. It stores every provider model through the API's `/oidc/payloads/*` routes, reads clients from the API's registry, and signs every token through `POST /oidc/sign`, so it holds no private key and no database. The configuration is locked to the code flow with PKCE for every client, refresh rotation, no dev interactions and no dynamic registration. At startup it checks `GET /oidc/config` and refuses to run when the API has the provider disabled or speaks a different `/oidc/*` contract version. It answers only requests for the issuer's own origin. Ships as a Docker image and as an npm package that exports `createOidcEdge` for mounting into an Express app.
|
|
37
|
+
|
|
38
|
+
Requires a Seamless Auth API with the v0.21 `/oidc/*` routes and `@seamless-auth/types` 0.34.0.
|
|
39
|
+
- 45ea34c: Refresh token reuse detection and the client credentials grant.
|
|
40
|
+
|
|
41
|
+
- A rotated refresh token presented again still revokes the grant, and now also asks the API to end the first-party session it was issued under (`POST /oidc/sessions/:id/revoke`, reason `refresh_token_reuse`).
|
|
42
|
+
- A refresh reads the session's auth context, so the refreshed ID token carries the current `acr`, `amr` and `auth_time`. Before this, any step-up after sign-in made `/oidc/sign` refuse every later refresh.
|
|
43
|
+
- `client_credentials` is on for clients registered for it. The token is an RFC 9068 JWT for a resource (the one named with `resource`, or the client's only linked one, else `invalid_target`), with `sub` and `client_id` set to the client and scopes cut to what it is allowed there.
|
|
44
|
+
- 30b4115: Issue access tokens for a named API. With `resource=<identifier>` (RFC 8707) the access token is an RFC 9068 JWT (`typ: at+jwt`) whose `aud` is the resource, carrying only the scopes the application is allowed there and the resource's own lifetime, read from the API's `GET /oidc/clients/:id/resources`. A resource the application is not linked to is refused with `invalid_target`. A resource named once at authorization is used at the token endpoint too. Without `resource`, the access token is the same opaque userinfo token as before. First-party apps have resource scopes granted with the sign-in. The consent page lists them for third-party apps.
|
|
45
|
+
|
|
46
|
+
Needs fells-code/seamless-auth-api#533 and `@seamless-auth/types` with `OidcEdgeRoutes.clientResources`.
|
|
47
|
+
- b31200c: RP-initiated logout at `/session/end`, confirmed on a hosted page. Signing out ends the provider session and the first-party session behind it through the API, which sends OpenID Back-Channel Logout tokens to every app the user signed in to with it. Signing out of the asking app only ends that app's grant. Discovery now lists `end_session_endpoint`, `backchannel_logout_supported` and `backchannel_logout_session_supported`, and ID tokens carry `sid` for clients registered for back-channel logout. Needs the API with back-channel logout (fells-code/seamless-auth-api#409).
|
|
48
|
+
- b3048a8: Honour `acr_values`, `max_age` and `prompt=login` from the API's auth context, with step-up in the hosted pages.
|
|
49
|
+
|
|
50
|
+
- Every authorization from an existing provider session reads `GET /oidc/sessions/:id/auth-context` and carries the reported `acr`, `amr` and `auth_time` into the tokens, so a step-up made elsewhere shows up on the next authorization and `/oidc/sign` never sees a stale value. A session the API reports as ended or in recovery goes back to sign-in.
|
|
51
|
+
- `acr_values` is met when the session's acr ranks at least the weakest value asked for (`phrh` satisfies `phr`). `max_age` is met by the API's `auth_time`, or an authentication during the request.
|
|
52
|
+
- `POST /interaction/:uid/login` answers `409 { error: 'step_up_required', methods }` instead of `reauthentication_required`. The pages run the step-up through the adapter (`/step-up/webauthn/*`, or `/totp/verify-mfa` for freshness on a session with no acr), then post again with `{ "afterStepUp": true }`. If the requirement is still not met, the client receives `unmet_authentication_requirements`.
|
|
53
|
+
|
|
54
|
+
Needs fells-code/seamless-auth-api#529.
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- 1397fcd: Document how the provider meets OAuth 2.1 and RFC 9700 in `docs/oauth-security.md`, with the test behind each row, and cover RFC 8252 native clients (loopback redirects on any port, the IPv6 loopback, private-use schemes, and refusal of any other host, path or scheme). No behaviour change.
|
|
59
|
+
- dfd86f2: Depend on `@seamless-auth/types` `^0.34.0`, the stable release, instead of the 0.34.0 prerelease.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,274 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Seamless Auth OpenID Provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The OpenID Provider for [Seamless Auth](https://seamlessauth.com). Any app that speaks OpenID
|
|
4
|
+
Connect (Grafana, GitLab, Argo CD, Auth.js and the rest) can sign users in with Seamless Auth
|
|
5
|
+
through this service, with no Seamless SDK.
|
|
6
|
+
|
|
7
|
+
It is the public "login edge" in front of a private Seamless Auth API:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
any OIDC app ──► seamless-auth-oidc-provider (public: OIDC endpoints, hosted pages, cookies)
|
|
11
|
+
│ service token, like any adapter
|
|
12
|
+
▼
|
|
13
|
+
Seamless Auth API (private, no cookies) ── Postgres
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Built on [`oidc-provider`](https://github.com/panva/node-oidc-provider) 9.x.
|
|
17
|
+
- Holds no private keys and no database. The API signs every token after checking its claims,
|
|
18
|
+
and stores all provider state.
|
|
19
|
+
- Code flow with PKCE (a confidential client can be opted out, and then sends `nonce` unless that is waived too), refresh rotation always on, `private_key_jwt` for confidential
|
|
20
|
+
clients, and shared secrets only for clients opted in on the API.
|
|
21
|
+
|
|
22
|
+
The design is in
|
|
23
|
+
[ADR 0001](https://github.com/fells-code/seamless-auth-api/blob/main/docs/adr/0001-openid-provider.md).
|
|
24
|
+
|
|
25
|
+
> **Status:** under construction for Seamless Auth v0.21. Discovery, the authorization code
|
|
26
|
+
> flow with PKCE, userinfo and the hosted login and consent pages work. The remaining grants
|
|
27
|
+
> land in the open [issues](https://github.com/fells-code/seamless-auth-oidc-provider/issues).
|
|
28
|
+
|
|
29
|
+
## Requirements
|
|
30
|
+
|
|
31
|
+
- Node 22 or newer.
|
|
32
|
+
- A Seamless Auth API with the provider enabled (`oidc_provider_enabled`), `oidc_issuer` set
|
|
33
|
+
to this service's public URL, and the provider signing keys (`OIDC_JWKS_*`) configured.
|
|
34
|
+
- This service's hostname must be the API's `rpid` or a subdomain of it, so passkeys work on the
|
|
35
|
+
hosted pages.
|
|
36
|
+
- HTTPS. Provider cookies are `__Host-` cookies, which browsers only accept over TLS.
|
|
37
|
+
|
|
38
|
+
## Run the image
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
docker run -p 5314:5314 --env-file .env ghcr.io/fells-code/seamless-auth-oidc-provider:latest
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
| Variable | Required | Description |
|
|
47
|
+
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
48
|
+
| `SEAMLESS_API_URL` | yes | The private API, as this service reaches it. |
|
|
49
|
+
| `SEAMLESS_API_ISSUER` | no | The API's `ISSUER`, when it differs from `SEAMLESS_API_URL`. |
|
|
50
|
+
| `SEAMLESS_OIDC_SERVICE_TOKEN` | yes | This service's own service token secret, `SEAMLESS_OIDC_SERVICE_TOKEN` on the API. It must differ from the adapters' `SEAMLESS_SERVICE_TOKEN`, which this service never holds. At least 32 characters. |
|
|
51
|
+
| `SEAMLESS_JWKS_KID` | no | The `kid` on the service tokens this service signs. |
|
|
52
|
+
| `SEAMLESS_COOKIE_SECRET` | yes | Signs the first-party session cookies. At least 32 characters. |
|
|
53
|
+
| `OIDC_COOKIE_KEYS` | yes | Comma-separated provider cookie keys. The first signs, the rest verify. |
|
|
54
|
+
| `TRUST_PROXY` | no | `true` when TLS ends at a proxy in front of this service. |
|
|
55
|
+
| `PORT` | no | Defaults to `5314`. |
|
|
56
|
+
|
|
57
|
+
The issuer comes from the API (`oidc_issuer`), not from this service's environment. The service
|
|
58
|
+
answers only requests for the issuer's own scheme and host, and refuses to start when the API
|
|
59
|
+
has the provider disabled or speaks a different version of the `/oidc/*` contract.
|
|
60
|
+
|
|
61
|
+
## Endpoints
|
|
62
|
+
|
|
63
|
+
| Path | What |
|
|
64
|
+
| ----------------------------------- | ------------------------------------------------- |
|
|
65
|
+
| `/.well-known/openid-configuration` | Discovery |
|
|
66
|
+
| `/authorize`, `/token`, `/userinfo` | Authorization code with PKCE, and userinfo |
|
|
67
|
+
| `/jwks` | The provider's public keys, read from the API |
|
|
68
|
+
| `/auth/*` | This service's first-party adapter, for the pages |
|
|
69
|
+
| `GET /interaction/:uid/details` | What the current interaction needs, for the pages |
|
|
70
|
+
| `POST /interaction/:uid/login` | Finishes login, or answers `step_up_required` |
|
|
71
|
+
| `POST /interaction/:uid/consent` | Grants what the client asked for |
|
|
72
|
+
| `POST /interaction/:uid/abort` | Returns `access_denied` to the client |
|
|
73
|
+
| `/session/end` | RP-initiated logout, confirmed on a hosted page |
|
|
74
|
+
| `/device/auth`, `/device` | Device authorization grant and its hosted page |
|
|
75
|
+
|
|
76
|
+
The interaction routes are for the hosted pages only. A state change needs `Sec-Fetch-Site:
|
|
77
|
+
same-origin` or an `Origin` equal to the issuer's, so a page on a sibling subdomain cannot
|
|
78
|
+
drive them.
|
|
79
|
+
|
|
80
|
+
## Step-up (`acr_values`, `max_age`, `prompt=login`)
|
|
81
|
+
|
|
82
|
+
Discovery lists `acr_values_supported: ["phr", "phrh"]`. `phrh` (a device-bound passkey with
|
|
83
|
+
user verification) satisfies a request for `phr`. On every authorization from an existing
|
|
84
|
+
session this service reads the session's auth context from the API and decides from what the
|
|
85
|
+
API reports, never from its own copy:
|
|
86
|
+
|
|
87
|
+
- `acr_values` is met when the session's acr ranks at least the weakest value asked for.
|
|
88
|
+
- `max_age` is met when the API's `auth_time` is within it, or the user authenticated during
|
|
89
|
+
this request. `prompt=login` needs the latter.
|
|
90
|
+
|
|
91
|
+
When either falls short, the hosted pages ask the user to confirm it is them on the same
|
|
92
|
+
session: a passkey, or an authenticator app code when only freshness is missing on a session
|
|
93
|
+
with no acr (an app code never earns an acr). The pages then read the context again and copy
|
|
94
|
+
`acr`, `amr` and `auth_time` into the tokens unchanged. If the step-up still falls short, for
|
|
95
|
+
example a synced passkey when `phrh` was asked for, the app gets
|
|
96
|
+
`unmet_authentication_requirements` rather than a token with a lower acr.
|
|
97
|
+
|
|
98
|
+
## Access tokens for an API (`resource`)
|
|
99
|
+
|
|
100
|
+
An app that calls an API of its own asks for a token for it with `resource=<identifier>`
|
|
101
|
+
(RFC 8707), using an identifier the API's registry lists under `/admin/resources` and links to
|
|
102
|
+
the application under `/admin/applications/:id/resources`. The access token is then an RFC
|
|
103
|
+
9068 JWT (`typ: at+jwt`) whose `aud` is that identifier, carrying `client_id`, `sub`, `scope`
|
|
104
|
+
and `jti`, with only the scopes the application is allowed there and the resource's own
|
|
105
|
+
lifetime. The API checks the same links again before it signs, so an API configured for one
|
|
106
|
+
identifier can reject every token minted for another.
|
|
107
|
+
|
|
108
|
+
- A resource the application is not linked to is refused with `invalid_target`.
|
|
109
|
+
- An app that named one resource at authorization gets its token without repeating
|
|
110
|
+
`resource` at the token endpoint.
|
|
111
|
+
- Without `resource` the access token stays an opaque one for `/userinfo`, as before. A token
|
|
112
|
+
for a resource is not accepted at `/userinfo`.
|
|
113
|
+
- First-party apps have the scopes granted with the sign-in. Third-party apps list them on the
|
|
114
|
+
consent page.
|
|
115
|
+
|
|
116
|
+
## Token lifetimes
|
|
117
|
+
|
|
118
|
+
An application's own `accessTokenTtlSeconds`, `idTokenTtlSeconds` and `refreshTokenTtlSeconds`
|
|
119
|
+
(admin routes) apply to its tokens. Unset ones use the defaults: 600 seconds for access and ID
|
|
120
|
+
tokens, 14 days for refresh tokens. An access token for a resource with its own lifetime gets
|
|
121
|
+
the shorter of the two. A rotated refresh token for a browser app that cannot bind it to a key
|
|
122
|
+
keeps what was left of the one it replaced, whatever the application's lifetime. The grant
|
|
123
|
+
behind them lives at least as long as the application's refresh tokens. The API also
|
|
124
|
+
ends every record when the first-party session behind it ends.
|
|
125
|
+
|
|
126
|
+
## Refresh tokens and client credentials
|
|
127
|
+
|
|
128
|
+
- **Refresh.** A client registered for `refresh_token` gets one with `offline_access`, which
|
|
129
|
+
the library honours only on a request with `prompt=consent` (OpenID Connect Core 11). Refresh
|
|
130
|
+
tokens rotate on every use. A used one presented again revokes the grant and every token
|
|
131
|
+
under it, and the API ends the first-party session it was issued under. Two refreshes
|
|
132
|
+
racing with the same token cannot both rotate: the API's consume says which one marked it
|
|
133
|
+
used, and the other is handled as that same reuse. A refreshed ID
|
|
134
|
+
token carries the session's current `acr`, `amr` and `auth_time`, so a step-up since the
|
|
135
|
+
sign-in shows up on the next refresh.
|
|
136
|
+
- **Client credentials.** A confidential client registered for `client_credentials` gets an
|
|
137
|
+
RFC 9068 JWT whose `sub` and `client_id` are the client itself, only ever for a resource:
|
|
138
|
+
the one named with `resource`, or the client's only linked resource. Scopes are cut to what
|
|
139
|
+
the client is allowed there. An application with account notices turned on also gets the
|
|
140
|
+
built-in `<issuer>/seamless` resource with `notices:send`, which the API lists for it, for
|
|
141
|
+
`POST /applications/account-notices` on the API.
|
|
142
|
+
|
|
143
|
+
## Introspection and revocation
|
|
144
|
+
|
|
145
|
+
`/token/introspection` (RFC 7662) and `/token/revocation` (RFC 7009) are listed in discovery
|
|
146
|
+
and take the same client authentication as `/token`.
|
|
147
|
+
|
|
148
|
+
- **Introspection.** A client may introspect its own tokens. A resource server registers as a
|
|
149
|
+
confidential application linked to its resource, and may then introspect tokens whose `aud`
|
|
150
|
+
is that resource. Any other caller gets `{ "active": false }`. Every answer is read from the
|
|
151
|
+
API's store, so a token whose first-party session has ended is inactive at once.
|
|
152
|
+
- **JWT access tokens.** The library keeps no record of a JWT, so this service stores one
|
|
153
|
+
under its `jti` when it is issued and maps a presented JWT back to it, after checking the
|
|
154
|
+
signature against the provider's own keys. The `jti` alone is never accepted as a token.
|
|
155
|
+
A resource server that verifies JWTs locally still accepts one until it expires; one that
|
|
156
|
+
introspects sees revocation immediately.
|
|
157
|
+
- **Revocation.** A client revokes its own refresh or access token. Revoking a refresh token
|
|
158
|
+
ends the grant and every token under it, not the user's first-party session, so the user's
|
|
159
|
+
other apps stay signed in. Each revocation is recorded in the API's audit log
|
|
160
|
+
(`token_revoked`).
|
|
161
|
+
- **Rate limit.** 1200 requests a minute per client IP across both endpoints, per instance.
|
|
162
|
+
|
|
163
|
+
## Logout
|
|
164
|
+
|
|
165
|
+
`/session/end` (OpenID RP-Initiated Logout 1.0) takes `id_token_hint` or `client_id`, and a
|
|
166
|
+
`post_logout_redirect_uri` registered for the client. The hosted page asks before anything
|
|
167
|
+
ends:
|
|
168
|
+
|
|
169
|
+
- **Sign out** ends the provider session and the first-party session it was made under
|
|
170
|
+
(through the API's `/oidc/sessions/:id/revoke`, reason `provider_logout`), and clears the
|
|
171
|
+
adapter cookies. Every other app the user signed in to with it is signed out too.
|
|
172
|
+
- **Only sign out of** the asking app ends that app's grant and keeps the session.
|
|
173
|
+
|
|
174
|
+
Back-channel logout (OpenID Back-Channel Logout 1.0) is sent by the API, not by this service.
|
|
175
|
+
The API holds the keys and sees every way a session ends (this logout, admin revoke, sign-out
|
|
176
|
+
everywhere, eviction), so a client with a `backchannelLogoutUri` gets a `logout+jwt` token
|
|
177
|
+
whichever one it was. Discovery lists `backchannel_logout_supported` and
|
|
178
|
+
`backchannel_logout_session_supported`, and ID tokens for such a client carry `sid`.
|
|
179
|
+
|
|
180
|
+
## Device authorization grant
|
|
181
|
+
|
|
182
|
+
`/device/auth` (RFC 8628) is for apps that cannot open a browser: TVs, consoles, CLIs. It is
|
|
183
|
+
off unless an admin enables the device flow on a first-party application (ADR D10).
|
|
184
|
+
|
|
185
|
+
- **Codes.** User codes are 8 characters from the RFC's base-20 set (`BCDF-GHJK`), about 34.5
|
|
186
|
+
bits, typed case-insensitively and with or without the dash. They live 10 minutes. The
|
|
187
|
+
device code works once.
|
|
188
|
+
- **Polling.** The response carries `interval: 5`. A poll sooner than that gets `slow_down`
|
|
189
|
+
and five more seconds, tracked per device code on each instance.
|
|
190
|
+
- **Approval.** `/device` is a hosted page. Before any sign-in, the confirmation page names the
|
|
191
|
+
app, shows the code, and says to continue only on a device you started yourself (RFC 8628
|
|
192
|
+
section 5.4). Every approval signs in again, so a browser that is already signed in still
|
|
193
|
+
confirms with the passkey. Code entry is limited to 10 a minute per address and 600 a
|
|
194
|
+
minute per instance.
|
|
195
|
+
- **Assurance.** Device-flow tokens never carry `acr`, after a refresh too, and device requests
|
|
196
|
+
with `acr_values` or `max_age` are refused. The passkey proves who approved, not who holds
|
|
197
|
+
the device. The API refuses `acr` on any token whose sign request names the device grant.
|
|
198
|
+
|
|
199
|
+
## Security defaults
|
|
200
|
+
|
|
201
|
+
The configuration is locked to OAuth 2.1 and RFC 9700: the code flow with `S256` PKCE for
|
|
202
|
+
every client (unless an admin opts a confidential client out, RFC 9700 section 2.1.1, in
|
|
203
|
+
which case it must send `nonce` whenever it asks for an ID token, unless an admin waives
|
|
204
|
+
that as well), exact redirect matching (with RFC 8252 loopback ports for native apps), refresh
|
|
205
|
+
rotation, `iss` on authorization responses and audience-restricted access tokens. The full
|
|
206
|
+
matrix, with the test behind each row, is in [docs/oauth-security.md](docs/oauth-security.md).
|
|
207
|
+
|
|
208
|
+
## Client authentication
|
|
209
|
+
|
|
210
|
+
| Client | Authentication |
|
|
211
|
+
| ----------------------------------------- | --------------------------------------------- |
|
|
212
|
+
| Confidential (server-side apps, services) | `private_key_jwt`, each assertion used once |
|
|
213
|
+
| Public (SPA, native, CLI) | `none`, with PKCE |
|
|
214
|
+
| Confidential, opted in to a shared secret | `client_secret_basic` or `client_secret_post` |
|
|
215
|
+
|
|
216
|
+
Shared secrets are for apps that support nothing else. They need `allow_client_secrets` on
|
|
217
|
+
the API (read when this service starts) and a per-client opt-in there. This service never
|
|
218
|
+
holds a client's secret: it asks the API to check a presented one, then hands the library a
|
|
219
|
+
one-time stand-in for that request. `client_secret_jwt` and HS\* algorithms are never offered.
|
|
220
|
+
|
|
221
|
+
## Hosted pages
|
|
222
|
+
|
|
223
|
+
The login and consent pages are a SPA in `ui/`, built on `@seamless-auth/react` and shipped
|
|
224
|
+
in `dist/ui`. They sign users in through this service's own adapter routes (`/auth/*`), the
|
|
225
|
+
same way any Seamless app does, then finish the OIDC interaction.
|
|
226
|
+
|
|
227
|
+
- **Methods.** Every method the API has enabled in `login_methods`, through the SDK's bundled
|
|
228
|
+
screens. A recovery session goes to enrollment before it can finish a sign-in.
|
|
229
|
+
- **Consent.** First-party clients have consent implied. Third-party clients see the client
|
|
230
|
+
name, the host they are sent back to and the requested scopes, on every authorization.
|
|
231
|
+
- **Branding.** The app name comes from the API's config each time a page is served, so a
|
|
232
|
+
config change needs no build and no restart.
|
|
233
|
+
- **Headers.** A strict CSP (`script-src 'self'`, styles from this origin or with the
|
|
234
|
+
response's nonce, `frame-ancestors 'none'`), `no-store`, `nosniff` and `no-referrer`.
|
|
235
|
+
|
|
236
|
+
Every cookie this service sets is `__Host-` (host-only, `Secure`, `Path=/`), so serve it over
|
|
237
|
+
HTTPS, including in development: the library refuses to set a `Secure` cookie on a plain
|
|
238
|
+
HTTP request.
|
|
239
|
+
|
|
240
|
+
## Embed in an Express app
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import express from 'express';
|
|
244
|
+
import { createOidcEdge } from '@seamless-auth/oidc-provider';
|
|
245
|
+
|
|
246
|
+
const edge = await createOidcEdge({
|
|
247
|
+
apiUrl: process.env.SEAMLESS_API_URL!,
|
|
248
|
+
serviceSecret: process.env.SEAMLESS_OIDC_SERVICE_TOKEN!,
|
|
249
|
+
cookieKeys: process.env.OIDC_COOKIE_KEYS!.split(','),
|
|
250
|
+
adapterCookieSecret: process.env.SEAMLESS_COOKIE_SECRET!,
|
|
251
|
+
trustProxy: true,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const app = express();
|
|
255
|
+
app.set('trust proxy', 1);
|
|
256
|
+
app.use(edge.router);
|
|
257
|
+
app.listen(5314);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Mount `edge.router` at the root of the app that serves the issuer's origin.
|
|
261
|
+
|
|
262
|
+
## Development
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
nvm use
|
|
266
|
+
npm ci
|
|
267
|
+
npm run typecheck && npm run lint && npm run format:check && npm run coverage && npm run build
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
See [AGENTS.md](AGENTS.md) for the repo's standards and [RELEASES.md](RELEASES.md) for releases.
|
|
271
|
+
|
|
272
|
+
## License
|
|
273
|
+
|
|
274
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Adapter, AdapterFactory, AdapterPayload } from 'oidc-provider';
|
|
2
|
+
import { errors } from 'oidc-provider';
|
|
3
|
+
import type { OidcApiClient } from './apiClient.js';
|
|
4
|
+
import type { EdgeConfigCache } from './configCache.js';
|
|
5
|
+
import { DevicePollThrottle } from './deviceFlow.js';
|
|
6
|
+
/** A client assertion or proof presented twice. Answered as `invalid_client`, not a 500. */
|
|
7
|
+
export declare class ReplayDetectedError extends errors.InvalidClientAuth {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare class UnsupportedModelError extends Error {
|
|
11
|
+
constructor(name: string, operation: string);
|
|
12
|
+
}
|
|
13
|
+
/** Stores one library model through the API's `/oidc/payloads/*` routes. */
|
|
14
|
+
export declare class ApiPayloadAdapter implements Adapter {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(name: string, client: OidcApiClient, polls?: DevicePollThrottle);
|
|
17
|
+
upsert(id: string, payload: AdapterPayload, expiresIn?: number): Promise<void>;
|
|
18
|
+
find(id: string): Promise<AdapterPayload | undefined>;
|
|
19
|
+
findByUserCode(userCode: string): Promise<AdapterPayload | undefined>;
|
|
20
|
+
findByUid(uid: string): Promise<AdapterPayload | undefined>;
|
|
21
|
+
consume(id: string): Promise<void>;
|
|
22
|
+
destroy(id: string): Promise<void>;
|
|
23
|
+
revokeByGrantId(grantId: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads clients from the API's registry. Clients are managed there, never here. A third-party
|
|
27
|
+
* client does not exist while `third_party_clients_enabled` is off (ADR D10).
|
|
28
|
+
*
|
|
29
|
+
* The registry never returns a secret. A client on a shared-secret method gets the one-time
|
|
30
|
+
* value `verifyClientSecrets` set for a secret the API accepted on this request, and otherwise
|
|
31
|
+
* a random value nothing can present, which the library needs to load the client at all.
|
|
32
|
+
*/
|
|
33
|
+
export declare class ApiClientAdapter implements Adapter {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(client: OidcApiClient, config: EdgeConfigCache);
|
|
36
|
+
find(id: string): Promise<AdapterPayload | undefined>;
|
|
37
|
+
upsert(): Promise<void>;
|
|
38
|
+
findByUserCode(): Promise<undefined>;
|
|
39
|
+
findByUid(): Promise<undefined>;
|
|
40
|
+
consume(): Promise<void>;
|
|
41
|
+
destroy(): Promise<void>;
|
|
42
|
+
revokeByGrantId(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export declare function createApiAdapterFactory(client: OidcApiClient, config: EdgeConfigCache): AdapterFactory;
|