@dereekb/firebase-server 14.3.0 → 14.4.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/calcom/package.json +10 -10
- package/discord/package.json +10 -10
- package/mailgun/package.json +9 -9
- package/mcp/package.json +12 -11
- package/model/package.json +9 -9
- package/oidc/index.esm.js +2027 -1107
- package/oidc/package.json +12 -10
- package/oidc/src/lib/index.d.ts +1 -0
- package/oidc/src/lib/middleware/oauth-auth.module.d.ts +3 -0
- package/oidc/src/lib/oidc.module.d.ts +1 -38
- package/oidc/src/lib/oidc.resource-server.d.ts +170 -0
- package/oidc/src/lib/service/index.d.ts +1 -0
- package/oidc/src/lib/service/oidc.jwt-signing.service.d.ts +77 -0
- package/oidc/src/lib/service/oidc.jwt-verify.d.ts +37 -0
- package/oidc/src/lib/service/oidc.service.d.ts +35 -4
- package/package.json +14 -12
- package/test/index.esm.js +168 -43
- package/test/package.json +12 -11
- package/test/src/lib/oidc/oidc.test.flow.d.ts +160 -11
- package/twilio/package.json +8 -8
- package/zoho/package.json +10 -10
package/oidc/package.json
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/oidc",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.4.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@dereekb/analytics": "14.
|
|
8
|
-
"@dereekb/date": "14.
|
|
9
|
-
"@dereekb/firebase": "14.
|
|
10
|
-
"@dereekb/firebase-server": "14.
|
|
11
|
-
"@dereekb/model": "14.
|
|
12
|
-
"@dereekb/nestjs": "14.
|
|
13
|
-
"@dereekb/
|
|
14
|
-
"@dereekb/
|
|
15
|
-
"@dereekb/
|
|
7
|
+
"@dereekb/analytics": "14.4.0",
|
|
8
|
+
"@dereekb/date": "14.4.0",
|
|
9
|
+
"@dereekb/firebase": "14.4.0",
|
|
10
|
+
"@dereekb/firebase-server": "14.4.0",
|
|
11
|
+
"@dereekb/model": "14.4.0",
|
|
12
|
+
"@dereekb/nestjs": "14.4.0",
|
|
13
|
+
"@dereekb/oauth-resource": "14.4.0",
|
|
14
|
+
"@dereekb/rxjs": "14.4.0",
|
|
15
|
+
"@dereekb/util": "14.4.0",
|
|
16
|
+
"@dereekb/zoho": "14.4.0",
|
|
16
17
|
"@nestjs/common": "^12.0.1",
|
|
17
18
|
"@nestjs/config": "^12.0.0",
|
|
18
19
|
"express": "^5.2.1",
|
|
19
20
|
"firebase-admin": "^13.10.0",
|
|
21
|
+
"jose": "^6.2.12",
|
|
20
22
|
"nanoid": "^6.0.1",
|
|
21
23
|
"oidc-provider": "^9.8.2"
|
|
22
24
|
},
|
package/oidc/src/lib/index.d.ts
CHANGED
|
@@ -52,6 +52,9 @@ export declare abstract class OidcAuthMiddlewareConfig {
|
|
|
52
52
|
* included as the RFC 9728 `resource_metadata` hint so clients can locate the
|
|
53
53
|
* discovery doc directly instead of relying on origin-rooted path-walkback.
|
|
54
54
|
*
|
|
55
|
+
* Delegates to `@dereekb/oauth-resource`'s builder so the challenge an off-box resource server
|
|
56
|
+
* emits and the one this API emits are produced by the same code.
|
|
57
|
+
*
|
|
55
58
|
* @param error - The RFC 6750 `error` token (e.g. `invalid_token`, `invalid_request`).
|
|
56
59
|
* @param resourceMetadataUrl - Optional protected-resource metadata URL.
|
|
57
60
|
* @returns Header value, e.g. `Bearer resource_metadata="…", error="invalid_token"`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ModuleMetadata } from '@nestjs/common';
|
|
2
2
|
import { ConfigService } from '@nestjs/config';
|
|
3
|
-
import { OidcModuleConfig
|
|
3
|
+
import { OidcModuleConfig } from './oidc.config';
|
|
4
4
|
import { type FirestoreContext } from '@dereekb/firebase';
|
|
5
5
|
import { FirebaseServerEnvService } from '@dereekb/firebase-server';
|
|
6
6
|
import { OidcServerFirestoreCollections } from './model/model';
|
|
@@ -88,43 +88,6 @@ export declare function oidcModuleConfigFactory(configService: ConfigService, en
|
|
|
88
88
|
* @returns The discovery URL, or `undefined` if `appMcpUrl` is not set.
|
|
89
89
|
*/
|
|
90
90
|
export declare function deriveResourceMetadataUrlFromEnv(envService: FirebaseServerEnvService): string | undefined;
|
|
91
|
-
/**
|
|
92
|
-
* Input for {@link buildFirebaseServerMcpResourceServer}.
|
|
93
|
-
*/
|
|
94
|
-
export interface BuildFirebaseServerMcpResourceServerInput {
|
|
95
|
-
/**
|
|
96
|
-
* The Firebase server environment service. The MCP URL is read from `envService.appMcpUrl`.
|
|
97
|
-
*/
|
|
98
|
-
readonly envService: FirebaseServerEnvService;
|
|
99
|
-
/**
|
|
100
|
-
* The OIDC provider config whose scopes back the resource server's `scope` value.
|
|
101
|
-
*/
|
|
102
|
-
readonly providerConfig: OidcProviderConfig;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Builds a single-entry {@link OidcResourceServerInfo} map for the MCP endpoint
|
|
106
|
-
* declared on `envService.appMcpUrl`, with `scope` set to every scope declared
|
|
107
|
-
* on `providerConfig.claims` (so any scope the provider issues is valid on the
|
|
108
|
-
* resource server) and `audience` set to the MCP URL.
|
|
109
|
-
*
|
|
110
|
-
* Returns `undefined` when no `appMcpUrl` is configured.
|
|
111
|
-
*
|
|
112
|
-
* Used internally by {@link oidcModuleMetadata} when {@link OidcModuleConfig.configureMcpResourceServer}
|
|
113
|
-
* is enabled, and exported for apps that need to combine the MCP resource server
|
|
114
|
-
* with additional app-specific entries:
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```ts
|
|
118
|
-
* resourceServers: {
|
|
119
|
-
* ...(buildFirebaseServerMcpResourceServer({ envService, providerConfig: APP_PROVIDER_CONFIG }) ?? {}),
|
|
120
|
-
* 'https://api.example.com/extras': {
|
|
121
|
-
* scope: 'openid profile',
|
|
122
|
-
* audience: 'https://api.example.com/extras'
|
|
123
|
-
* }
|
|
124
|
-
* }
|
|
125
|
-
* ```
|
|
126
|
-
*/
|
|
127
|
-
export declare function buildFirebaseServerMcpResourceServer(input: BuildFirebaseServerMcpResourceServerInput): Record<string, OidcResourceServerInfo> | undefined;
|
|
128
91
|
/**
|
|
129
92
|
* Factory that creates {@link OidcServerFirestoreCollections} using the provided Firestore context
|
|
130
93
|
* and JWKS encryption config from {@link OidcModuleConfig}.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { type BuildIssuerProfilesConfig, type BuildIssuerProfilesOidcIssuerInput } from '@dereekb/oauth-resource';
|
|
2
|
+
import { type FirebaseServerEnvService } from '@dereekb/firebase-server';
|
|
3
|
+
import { type Maybe, type Seconds, type WebsiteUrlWithPrefix } from '@dereekb/util';
|
|
4
|
+
import { type OidcModuleConfig, type OidcProviderConfig, type OidcResourceServerInfo } from './oidc.config';
|
|
5
|
+
export interface BuildOidcResourceServerConfig {
|
|
6
|
+
/**
|
|
7
|
+
* The RFC 8707 resource indicator — the value a client sends as `resource=` on `/authorize` and
|
|
8
|
+
* `/token`, and the key this entry is registered under. Usually the resource's full URL, e.g.
|
|
9
|
+
* `https://db.example.com/mcp`.
|
|
10
|
+
*/
|
|
11
|
+
readonly url: WebsiteUrlWithPrefix;
|
|
12
|
+
/**
|
|
13
|
+
* Space-delimited scopes valid on this resource server. Issued access tokens are filtered to the
|
|
14
|
+
* intersection of the client-requested scopes and this allow-list, so keep it to what the
|
|
15
|
+
* resource actually understands rather than every scope the provider issues.
|
|
16
|
+
*/
|
|
17
|
+
readonly scope: string;
|
|
18
|
+
/**
|
|
19
|
+
* `aud` claim placed on tokens bound to this resource. Defaults to {@link url}.
|
|
20
|
+
*
|
|
21
|
+
* An OFF-BOX resource server usually wants its ORIGIN here (`https://db.example.com`) rather
|
|
22
|
+
* than the `/mcp` resource-indicator URL, so one audience covers every route it serves.
|
|
23
|
+
*/
|
|
24
|
+
readonly audience?: Maybe<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Access-token format. Defaults to oidc-provider's `'opaque'`.
|
|
27
|
+
*
|
|
28
|
+
* **Set `'jwt'` for any resource server that is not this API process.** An opaque access token is
|
|
29
|
+
* a database key: it can only be validated by the issuer's own adapter store, so a remote service
|
|
30
|
+
* fundamentally cannot verify it. A `'jwt'` token is an RS256 JWT signed with the provider's
|
|
31
|
+
* active JWKS key (no extra provider config needed — `getResourceServerConfig` falls back to
|
|
32
|
+
* `clientDefaults.id_token_signed_response_alg`, i.e. `RS256`), which any service can verify via
|
|
33
|
+
* `@dereekb/oauth-resource` against the published `/.well-known/jwks.json`.
|
|
34
|
+
*
|
|
35
|
+
* The trade-off: a JWT access token is NOT persisted by oidc-provider, so it has no adapter
|
|
36
|
+
* record and **cannot be revoked before `exp`**. Revoking the Grant stops refresh and future
|
|
37
|
+
* issuance but leaves outstanding JWTs valid — keep {@link accessTokenTTL} short.
|
|
38
|
+
*/
|
|
39
|
+
readonly accessTokenFormat?: Maybe<'opaque' | 'jwt'>;
|
|
40
|
+
/**
|
|
41
|
+
* Access-token TTL for this resource, in seconds. Falls back to the provider's `ttl.AccessToken`.
|
|
42
|
+
*/
|
|
43
|
+
readonly accessTokenTTL?: Maybe<Seconds>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Builds a single-entry {@link OidcResourceServerInfo} map for an RFC 8707 resource indicator.
|
|
47
|
+
*
|
|
48
|
+
* This is the supported way to declare a resource server the provider issues tokens for —
|
|
49
|
+
* including one that runs OFF-BOX (a Docker'd sidecar, a worker, a standalone MCP host), which is
|
|
50
|
+
* what {@link BuildOidcResourceServerConfig.accessTokenFormat} `'jwt'` exists for.
|
|
51
|
+
*
|
|
52
|
+
* @param config - The resource indicator, its scopes, and the token shape to issue for it.
|
|
53
|
+
* @returns A single-entry map keyed by the resource indicator.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const DB_ORIGIN = 'https://db.example.com';
|
|
58
|
+
*
|
|
59
|
+
* oidcModuleMetadata({
|
|
60
|
+
* dependencyModule: MyOidcDependencyModule,
|
|
61
|
+
* config: {
|
|
62
|
+
* resourceServers: buildOidcResourceServer({
|
|
63
|
+
* url: `${DB_ORIGIN}/mcp`,
|
|
64
|
+
* scope: 'openid profile email offline_access',
|
|
65
|
+
* audience: DB_ORIGIN,
|
|
66
|
+
* accessTokenFormat: 'jwt',
|
|
67
|
+
* accessTokenTTL: 3600
|
|
68
|
+
* })
|
|
69
|
+
* }
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildOidcResourceServer(config: BuildOidcResourceServerConfig): Record<string, OidcResourceServerInfo>;
|
|
74
|
+
/**
|
|
75
|
+
* Input for {@link buildFirebaseServerMcpResourceServer}.
|
|
76
|
+
*/
|
|
77
|
+
export interface BuildFirebaseServerMcpResourceServerInput {
|
|
78
|
+
/**
|
|
79
|
+
* The Firebase server environment service. The MCP URL is read from `envService.appMcpUrl`.
|
|
80
|
+
*/
|
|
81
|
+
readonly envService: FirebaseServerEnvService;
|
|
82
|
+
/**
|
|
83
|
+
* The OIDC provider config whose scopes back the resource server's `scope` value.
|
|
84
|
+
*/
|
|
85
|
+
readonly providerConfig: OidcProviderConfig;
|
|
86
|
+
/**
|
|
87
|
+
* Access-token format. Defaults to oidc-provider's `'opaque'`.
|
|
88
|
+
*
|
|
89
|
+
* See {@link BuildOidcResourceServerConfig.accessTokenFormat}.
|
|
90
|
+
*/
|
|
91
|
+
readonly accessTokenFormat?: Maybe<'opaque' | 'jwt'>;
|
|
92
|
+
/**
|
|
93
|
+
* Access-token TTL for the MCP resource, in seconds.
|
|
94
|
+
*/
|
|
95
|
+
readonly accessTokenTTL?: Maybe<Seconds>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Builds a single-entry {@link OidcResourceServerInfo} map for the MCP endpoint
|
|
99
|
+
* declared on `envService.appMcpUrl`, with `scope` set to every scope declared
|
|
100
|
+
* on `providerConfig.claims` (so any scope the provider issues is valid on the
|
|
101
|
+
* resource server) and `audience` set to the MCP URL.
|
|
102
|
+
*
|
|
103
|
+
* Returns `undefined` when no `appMcpUrl` is configured.
|
|
104
|
+
*
|
|
105
|
+
* Used internally by `oidcModuleMetadata` when `OidcModuleConfig.configureMcpResourceServer`
|
|
106
|
+
* is enabled.
|
|
107
|
+
*
|
|
108
|
+
* @param input - The environment service and provider config backing the entry.
|
|
109
|
+
* @returns The single-entry map, or `undefined` when no MCP URL is configured.
|
|
110
|
+
*
|
|
111
|
+
* @deprecated Prefer {@link buildOidcResourceServer}, which takes the URL, scope, audience, and
|
|
112
|
+
* token format explicitly instead of deriving a fixed shape from the environment.
|
|
113
|
+
*/
|
|
114
|
+
export declare function buildFirebaseServerMcpResourceServer(input: BuildFirebaseServerMcpResourceServerInput): Record<string, OidcResourceServerInfo> | undefined;
|
|
115
|
+
export interface FirebaseServerIssuerProfilesConfig {
|
|
116
|
+
/**
|
|
117
|
+
* The provider's own module config. Its `issuer` becomes the trusted OIDC issuer, and every
|
|
118
|
+
* registered `resourceServers` entry contributes its key and `audience` to the accepted
|
|
119
|
+
* audience list.
|
|
120
|
+
*/
|
|
121
|
+
readonly oidcModuleConfig: Pick<OidcModuleConfig, 'issuer' | 'resourceServers'>;
|
|
122
|
+
/**
|
|
123
|
+
* Firebase project ids whose ID tokens the resource server also trusts.
|
|
124
|
+
*/
|
|
125
|
+
readonly firebaseProjectIds?: Maybe<readonly string[]>;
|
|
126
|
+
/**
|
|
127
|
+
* Extra audiences to accept beyond the ones derived from `resourceServers` — typically the
|
|
128
|
+
* resource server's own origin when it is not itself a registered resource indicator.
|
|
129
|
+
*/
|
|
130
|
+
readonly audiences?: Maybe<readonly string[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Overrides how the provider's verification keys are resolved, bypassing discovery. The API
|
|
133
|
+
* verifying its OWN tokens in-process passes a local key set here rather than making an HTTP
|
|
134
|
+
* call back to itself.
|
|
135
|
+
*/
|
|
136
|
+
readonly getKey?: Maybe<Required<Exclude<BuildIssuerProfilesOidcIssuerInput, string>>['getKey']>;
|
|
137
|
+
/**
|
|
138
|
+
* `fetch` used for OIDC discovery; injected in specs.
|
|
139
|
+
*/
|
|
140
|
+
readonly fetch?: Maybe<typeof fetch>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Emits the `@dereekb/oauth-resource` {@link BuildIssuerProfilesConfig} for THIS provider, so an
|
|
144
|
+
* API and the satellite services it issues tokens for cannot drift on issuer / audience strings —
|
|
145
|
+
* both sides read the same `OidcModuleConfig`.
|
|
146
|
+
*
|
|
147
|
+
* The type dependency points `firebase-server → oauth-resource` and never the reverse: the light
|
|
148
|
+
* verify package must stay installable in a plain Express service with no Firebase, Nest, or
|
|
149
|
+
* oidc-provider in its install graph.
|
|
150
|
+
*
|
|
151
|
+
* @param config - The provider's module config plus any extra trusted projects / audiences.
|
|
152
|
+
* @returns The issuer-profile config to hand to `buildIssuerProfiles()`.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* // inside the API, to hand its satellites a config they cannot get wrong
|
|
157
|
+
* const profilesConfig = firebaseServerIssuerProfiles({ oidcModuleConfig, firebaseProjectIds: [projectId] });
|
|
158
|
+
* const profiles = buildIssuerProfiles(profilesConfig);
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function firebaseServerIssuerProfiles(config: FirebaseServerIssuerProfilesConfig): BuildIssuerProfilesConfig;
|
|
162
|
+
/**
|
|
163
|
+
* Reads every audience a registered resource-server map can put on an access token: each entry's
|
|
164
|
+
* key (the resource indicator, which oidc-provider uses as the audience by default) plus its
|
|
165
|
+
* explicit `audience` when one is configured.
|
|
166
|
+
*
|
|
167
|
+
* @param resourceServers - The provider's registered resource servers, if any.
|
|
168
|
+
* @returns The unique audience values.
|
|
169
|
+
*/
|
|
170
|
+
export declare function oidcResourceServerAudiences(resourceServers: Maybe<Record<string, OidcResourceServerInfo>>): string[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { type Maybe, type Milliseconds, type Seconds } from '@dereekb/util';
|
|
2
|
+
import { OidcModuleConfig } from '../oidc.config';
|
|
3
|
+
import { JwksService } from './oidc.jwks.service';
|
|
4
|
+
/**
|
|
5
|
+
* Default lifetime of a minted JWT, in seconds.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_OIDC_SIGNED_JWT_EXPIRES_IN_SECONDS: Seconds;
|
|
8
|
+
/**
|
|
9
|
+
* How long the active signing key is cached before it is re-read from the JWKS store.
|
|
10
|
+
*
|
|
11
|
+
* A rotation is picked up within this window; rotated keys stay in the public JWKS for 30 days, so
|
|
12
|
+
* tokens signed with the previous key keep verifying in the meantime.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_OIDC_JWT_SIGNING_KEY_CACHE_MS: Milliseconds;
|
|
15
|
+
/**
|
|
16
|
+
* Header `typ` for a minted JWT: an RFC 9068 JWT access token, matching the format oidc-provider
|
|
17
|
+
* uses for the `accessTokenFormat: 'jwt'` resource-server tokens.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_OIDC_SIGNED_JWT_TYP = "at+jwt";
|
|
20
|
+
export interface OidcSignJwtInput {
|
|
21
|
+
/**
|
|
22
|
+
* `aud` claim(s) — the resource the token is for, e.g. an off-box service's origin.
|
|
23
|
+
*/
|
|
24
|
+
readonly audience: string | readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* `sub` claim — the first-party identity of the caller, e.g. `demo-api`.
|
|
27
|
+
*/
|
|
28
|
+
readonly subject: string;
|
|
29
|
+
/**
|
|
30
|
+
* Lifetime in seconds. Defaults to {@link DEFAULT_OIDC_SIGNED_JWT_EXPIRES_IN_SECONDS}.
|
|
31
|
+
*/
|
|
32
|
+
readonly expiresIn?: Maybe<Seconds>;
|
|
33
|
+
/**
|
|
34
|
+
* Extra claims merged onto the payload, e.g. `{ client_id, scope }`.
|
|
35
|
+
*/
|
|
36
|
+
readonly claims?: Maybe<Record<string, unknown>>;
|
|
37
|
+
/**
|
|
38
|
+
* Header `typ`. Defaults to {@link DEFAULT_OIDC_SIGNED_JWT_TYP}.
|
|
39
|
+
*/
|
|
40
|
+
readonly typ?: Maybe<string>;
|
|
41
|
+
}
|
|
42
|
+
export interface OidcSignedJwt {
|
|
43
|
+
readonly token: string;
|
|
44
|
+
readonly expiresAt: Date;
|
|
45
|
+
/**
|
|
46
|
+
* `kid` of the JWKS key that signed the token.
|
|
47
|
+
*/
|
|
48
|
+
readonly kid: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Signs first-party JWTs with the OIDC provider's active JWKS signing key.
|
|
52
|
+
*
|
|
53
|
+
* The result is an RS256 token with `iss` = this provider's issuer, so any resource server that
|
|
54
|
+
* already trusts the provider's JWKS for OAuth access tokens verifies it with no new secret or
|
|
55
|
+
* trust anchor: "the API mints a token for the satellite service" is literally "sign with the
|
|
56
|
+
* active key, `aud` = the satellite's origin". The counterpart on the resource server is
|
|
57
|
+
* `verifyBearerJwt` from `@dereekb/oauth-resource`, whose `oidc` issuer profile discovers this
|
|
58
|
+
* provider's `jwks_uri` from its own discovery document.
|
|
59
|
+
*
|
|
60
|
+
* Registered and exported by `oidcModuleMetadata`, so a downstream app injects it with no extra
|
|
61
|
+
* wiring. Pair it with `createCachedTokenProvider` from `@dereekb/util/oidc` to hold a minted
|
|
62
|
+
* token until it nears expiry instead of re-signing per call.
|
|
63
|
+
*/
|
|
64
|
+
export declare class OidcJwtSigningService {
|
|
65
|
+
private readonly _jwks;
|
|
66
|
+
private readonly _config;
|
|
67
|
+
private _cached;
|
|
68
|
+
constructor(_jwks: JwksService, _config: OidcModuleConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Mints a signed JWT.
|
|
71
|
+
*
|
|
72
|
+
* @param input - Audience, subject, lifetime, and extra claims.
|
|
73
|
+
* @returns The compact token, its expiry, and the signing key id.
|
|
74
|
+
*/
|
|
75
|
+
signJwt(input: OidcSignJwtInput): Promise<OidcSignedJwt>;
|
|
76
|
+
private _loadSigningKey;
|
|
77
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type IssuerProfiles } from '@dereekb/oauth-resource';
|
|
2
|
+
import { type JWTVerifyGetKey } from 'jose';
|
|
3
|
+
import { type OidcModuleConfig } from '../oidc.config';
|
|
4
|
+
import { type JwksService } from './oidc.jwks.service';
|
|
5
|
+
/**
|
|
6
|
+
* Builds a jose {@link JWTVerifyGetKey} backed by the provider's OWN published JWKS, read directly
|
|
7
|
+
* from {@link JwksService} rather than over HTTP.
|
|
8
|
+
*
|
|
9
|
+
* An in-process verifier must not `createRemoteJWKSet` against its own `/.well-known/jwks.json`:
|
|
10
|
+
* that would make every bearer-authenticated request depend on the process being able to reach
|
|
11
|
+
* itself over the network (which it may not be, behind a load balancer, in a Functions emulator,
|
|
12
|
+
* or under supertest).
|
|
13
|
+
*
|
|
14
|
+
* The key set is cached and re-read only when a token presents a `kid` the cache does not hold —
|
|
15
|
+
* so a freshly rotated key is picked up on its first use instead of after a fixed TTL.
|
|
16
|
+
*
|
|
17
|
+
* @param jwksService - The provider's JWKS service.
|
|
18
|
+
* @returns The key resolver.
|
|
19
|
+
*/
|
|
20
|
+
export declare function jwksServiceVerifyGetKey(jwksService: JwksService): JWTVerifyGetKey;
|
|
21
|
+
/**
|
|
22
|
+
* Builds the {@link IssuerProfiles} the provider uses to verify its OWN JWT-format access tokens
|
|
23
|
+
* in-process.
|
|
24
|
+
*
|
|
25
|
+
* A JWT access token is issued for a specific `resource=`, so it carries THAT resource server's
|
|
26
|
+
* audience — not the API's own identity. The accepted audiences are therefore every registered
|
|
27
|
+
* `resourceServers` key and audience, plus the issuer itself. The trust decision that implies is
|
|
28
|
+
* deliberate and narrow: a resource server the user consented to may present the token it was
|
|
29
|
+
* given back to the API that issued it. Scope enforcement is unchanged and still does the real
|
|
30
|
+
* authorization work (e.g. the admin-only `session.firestore` consent gate and the endpoint's own
|
|
31
|
+
* admin predicate).
|
|
32
|
+
*
|
|
33
|
+
* @param config - The provider's module config.
|
|
34
|
+
* @param jwksService - The provider's JWKS service, used for local key resolution.
|
|
35
|
+
* @returns The issuer profiles, holding exactly this provider.
|
|
36
|
+
*/
|
|
37
|
+
export declare function oidcProviderIssuerProfiles(config: OidcModuleConfig, jwksService: JwksService): IssuerProfiles;
|
|
@@ -23,6 +23,11 @@ export interface ResolveLoginDurationTier {
|
|
|
23
23
|
*/
|
|
24
24
|
readonly hasServiceScope: boolean;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Claims a JWT access token carries that the {@link OidcAuthData} shape sets explicitly, and that
|
|
28
|
+
* are therefore NOT app account claims.
|
|
29
|
+
*/
|
|
30
|
+
export declare const OIDC_JWT_ACCESS_TOKEN_RESERVED_CLAIMS: readonly string[];
|
|
26
31
|
/**
|
|
27
32
|
* Core OIDC service that wraps the oidc-provider instance and exposes
|
|
28
33
|
* typed methods for interaction handling, provider initialization, and JWKS management.
|
|
@@ -36,6 +41,7 @@ export declare class OidcService {
|
|
|
36
41
|
private readonly encryptionService;
|
|
37
42
|
private readonly _logger;
|
|
38
43
|
private readonly _getProvider;
|
|
44
|
+
private readonly _getIssuerProfiles;
|
|
39
45
|
constructor(config: OidcModuleConfig, providerConfigService: OidcProviderConfigService, jwksService: JwksService, accountService: OidcAccountService, collections: OidcServerFirestoreCollections, encryptionService: OidcEncryptionService);
|
|
40
46
|
/**
|
|
41
47
|
* Returns the oidc-provider instance, initializing it on first access.
|
|
@@ -67,15 +73,40 @@ export declare class OidcService {
|
|
|
67
73
|
dbx_max_session_ttl?: number;
|
|
68
74
|
} | undefined, tier: ResolveLoginDurationTier): number;
|
|
69
75
|
/**
|
|
70
|
-
* Verifies an
|
|
76
|
+
* Verifies an access token and returns the {@link OidcAuthData}.
|
|
77
|
+
*
|
|
78
|
+
* Two formats are accepted, because oidc-provider issues two:
|
|
79
|
+
*
|
|
80
|
+
* - **opaque** (the default) — a database key. Looked up through the provider's `AccessToken`
|
|
81
|
+
* model, which reads the Firestore adapter record written at issuance.
|
|
82
|
+
* - **JWT** (a resource server registered with `accessTokenFormat: 'jwt'`) — verified by
|
|
83
|
+
* signature against this provider's own JWKS via `@dereekb/oauth-resource`. A JWT access token
|
|
84
|
+
* is deliberately NOT persisted by oidc-provider (`lib/models/formats/jwt.js` returns no
|
|
85
|
+
* payload, so `base_model.js` skips the `adapter.upsert`), which means `AccessToken.find()`
|
|
86
|
+
* always misses for one and the store lookup alone would 401 every token the provider just
|
|
87
|
+
* issued.
|
|
71
88
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
89
|
+
* The same absence of an adapter record is why a JWT access token **cannot be revoked before
|
|
90
|
+
* `exp`**: {@link revokeGrant} deletes the Grant and every persisted token referencing it, which
|
|
91
|
+
* stops refresh and future issuance, but an outstanding JWT stays verifiable until it expires.
|
|
92
|
+
* Keep `accessTokenTTL` short on any resource server that opts into the JWT format.
|
|
74
93
|
*
|
|
75
|
-
* @param rawToken - The
|
|
94
|
+
* @param rawToken - The access token string, opaque or JWT.
|
|
76
95
|
* @returns The auth context, or `undefined` if the token is invalid or expired.
|
|
77
96
|
*/
|
|
78
97
|
verifyAccessToken(rawToken: string): Promise<OidcAuthData | undefined>;
|
|
98
|
+
/**
|
|
99
|
+
* Verifies a JWT-format access token issued by THIS provider for one of its registered resource
|
|
100
|
+
* servers, and builds the same {@link OidcAuthData} shape the opaque path produces.
|
|
101
|
+
*
|
|
102
|
+
* `sub` is the `accountId` oidc-provider stamped at issuance, which IS the Firebase uid (the
|
|
103
|
+
* opaque path reads the identical value off `accessToken.accountId`), so no mapping table is
|
|
104
|
+
* involved.
|
|
105
|
+
*
|
|
106
|
+
* @param rawToken - The raw token, which may not be a JWT at all.
|
|
107
|
+
* @returns The auth context, or `undefined` when the token is not a verifiable JWT from this provider.
|
|
108
|
+
*/
|
|
109
|
+
private _verifyJwtAccessToken;
|
|
79
110
|
/**
|
|
80
111
|
* Revokes a Grant entry and every grantable token entry that references it.
|
|
81
112
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -58,17 +58,18 @@
|
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@cantoo/pdf-lib": ">=2.6.5 <2.11.0",
|
|
61
|
-
"@dereekb/analytics": "14.
|
|
62
|
-
"@dereekb/calcom": "14.
|
|
63
|
-
"@dereekb/date": "14.
|
|
64
|
-
"@dereekb/dbx-core": "14.
|
|
65
|
-
"@dereekb/discord": "14.
|
|
66
|
-
"@dereekb/firebase": "14.
|
|
67
|
-
"@dereekb/model": "14.
|
|
68
|
-
"@dereekb/nestjs": "14.
|
|
69
|
-
"@dereekb/
|
|
70
|
-
"@dereekb/
|
|
71
|
-
"@dereekb/
|
|
61
|
+
"@dereekb/analytics": "14.4.0",
|
|
62
|
+
"@dereekb/calcom": "14.4.0",
|
|
63
|
+
"@dereekb/date": "14.4.0",
|
|
64
|
+
"@dereekb/dbx-core": "14.4.0",
|
|
65
|
+
"@dereekb/discord": "14.4.0",
|
|
66
|
+
"@dereekb/firebase": "14.4.0",
|
|
67
|
+
"@dereekb/model": "14.4.0",
|
|
68
|
+
"@dereekb/nestjs": "14.4.0",
|
|
69
|
+
"@dereekb/oauth-resource": "14.4.0",
|
|
70
|
+
"@dereekb/rxjs": "14.4.0",
|
|
71
|
+
"@dereekb/util": "14.4.0",
|
|
72
|
+
"@dereekb/zoho": "14.4.0",
|
|
72
73
|
"@google-cloud/firestore": "^7.11.6",
|
|
73
74
|
"@google-cloud/storage": "^7.22.0",
|
|
74
75
|
"@modelcontextprotocol/node": "2.0.0",
|
|
@@ -85,6 +86,7 @@
|
|
|
85
86
|
"firebase-admin": "^13.10.0",
|
|
86
87
|
"firebase-functions": "^7.3.2",
|
|
87
88
|
"firebase-functions-test": "3.5.0",
|
|
89
|
+
"jose": "^6.2.12",
|
|
88
90
|
"jsonwebtoken": "^9.0.3",
|
|
89
91
|
"make-error": "^1.3.6",
|
|
90
92
|
"nanoid": "^6.0.1",
|