@rdlabo/workers-hono-kit 0.2.0 → 0.2.1
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/README.md +126 -11
- package/dist/ai/gateway.d.ts +54 -16
- package/dist/ai/gateway.js +37 -12
- package/dist/aws/cloudfront.d.ts +23 -5
- package/dist/aws/cloudfront.js +45 -6
- package/dist/aws/secrets-manager.d.ts +38 -4
- package/dist/aws/secrets-manager.js +48 -3
- package/dist/cache/kv-cache.d.ts +173 -10
- package/dist/cache/kv-cache.js +139 -7
- package/dist/db/connection.d.ts +56 -14
- package/dist/db/connection.js +39 -13
- package/dist/db/database.d.ts +159 -23
- package/dist/db/database.js +49 -5
- package/dist/db/index.d.ts +11 -0
- package/dist/db/index.js +11 -2
- package/dist/db/jst.d.ts +89 -6
- package/dist/db/jst.js +89 -23
- package/dist/db/orm-config.d.ts +61 -19
- package/dist/db/orm-config.js +43 -14
- package/dist/db/retry.d.ts +25 -3
- package/dist/db/retry.js +25 -3
- package/dist/db/write-result.d.ts +27 -4
- package/dist/db/write-result.js +22 -1
- package/dist/firebase/firebase-verifier.d.ts +53 -4
- package/dist/firebase/identity-toolkit.d.ts +54 -5
- package/dist/firebase/identity-toolkit.js +51 -0
- package/dist/firebase/jose-firebase-verifier.d.ts +79 -7
- package/dist/firebase/jose-firebase-verifier.js +68 -7
- package/dist/firebase/remote-verifier.d.ts +42 -4
- package/dist/firebase/remote-verifier.js +58 -9
- package/dist/http/app-env.d.ts +41 -8
- package/dist/http/app-env.js +38 -8
- package/dist/http/app-info.d.ts +25 -3
- package/dist/http/app-info.js +16 -2
- package/dist/http/http-status.d.ts +12 -3
- package/dist/http/http-status.js +12 -3
- package/dist/http/nest-error.d.ts +90 -29
- package/dist/http/nest-error.js +59 -18
- package/dist/http/user-protocol.d.ts +23 -3
- package/dist/http/user-protocol.js +14 -2
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -3
- package/dist/middleware/auth.d.ts +74 -13
- package/dist/middleware/auth.js +30 -6
- package/dist/middleware/finalize-response.d.ts +30 -0
- package/dist/middleware/finalize-response.js +41 -12
- package/dist/middleware/validation.d.ts +83 -9
- package/dist/middleware/validation.js +52 -9
- package/dist/middleware/zod-coerce.d.ts +56 -2
- package/dist/middleware/zod-coerce.js +68 -9
- package/dist/stripe/client.d.ts +46 -9
- package/dist/stripe/client.js +41 -3
- package/dist/testing/auth.d.ts +58 -10
- package/dist/testing/auth.js +58 -10
- package/dist/testing/configurable-fake.d.ts +20 -9
- package/dist/testing/configurable-fake.js +23 -11
- package/dist/testing/db.d.ts +81 -12
- package/dist/testing/db.js +23 -1
- package/dist/testing/fakes.d.ts +77 -9
- package/dist/testing/fakes.js +69 -7
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +10 -5
- package/dist/testing/stripe-fixtures.d.ts +93 -3
- package/dist/testing/stripe-fixtures.js +93 -3
- package/package.json +1 -1
- package/src/ai/gateway.ts +66 -27
- package/src/aws/cloudfront.ts +46 -6
- package/src/aws/secrets-manager.ts +56 -7
- package/src/cache/kv-cache.ts +194 -12
- package/src/db/connection.ts +56 -14
- package/src/db/database.ts +160 -24
- package/src/db/index.ts +11 -2
- package/src/db/jst.ts +89 -23
- package/src/db/orm-config.ts +61 -19
- package/src/db/retry.ts +25 -3
- package/src/db/write-result.ts +27 -4
- package/src/firebase/firebase-verifier.ts +53 -4
- package/src/firebase/identity-toolkit.ts +57 -5
- package/src/firebase/jose-firebase-verifier.ts +79 -9
- package/src/firebase/remote-verifier.ts +58 -9
- package/src/http/app-env.ts +41 -8
- package/src/http/app-info.ts +25 -3
- package/src/http/http-status.ts +12 -3
- package/src/http/nest-error.ts +106 -37
- package/src/http/user-protocol.ts +23 -3
- package/src/index.ts +11 -3
- package/src/middleware/auth.ts +77 -15
- package/src/middleware/finalize-response.ts +41 -12
- package/src/middleware/validation.ts +89 -15
- package/src/middleware/zod-coerce.ts +68 -9
- package/src/stripe/client.ts +46 -9
- package/src/testing/auth.ts +58 -10
- package/src/testing/configurable-fake.ts +23 -11
- package/src/testing/db.ts +82 -13
- package/src/testing/fakes.ts +77 -9
- package/src/testing/index.ts +10 -5
- package/src/testing/stripe-fixtures.ts +93 -3
|
@@ -1,32 +1,104 @@
|
|
|
1
1
|
import type { CryptoKey, JWK, JWTVerifyGetKey, KeyObject } from 'jose';
|
|
2
2
|
import type { DecodedIdToken, FirebaseVerifier } from './firebase-verifier.js';
|
|
3
3
|
import type { IdentityToolkit } from './identity-toolkit.js';
|
|
4
|
-
type KeyInput = CryptoKey | KeyObject | JWK | Uint8Array | JWTVerifyGetKey;
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Union of every key shape `jose`'s `jwtVerify` accepts.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* `jose` v6 removed `KeyLike`, so the verification key is modelled here as either a static
|
|
9
|
+
* key (production uses `createRemoteJWKSet`, tests use a generated `CryptoKey`) or a dynamic
|
|
10
|
+
* `JWTVerifyGetKey` resolver function. This union covers both `jwtVerify` overloads' key
|
|
11
|
+
* parameters.
|
|
9
12
|
*
|
|
10
|
-
*
|
|
11
|
-
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
type KeyInput = CryptoKey | KeyObject | JWK | Uint8Array | JWTVerifyGetKey;
|
|
16
|
+
/**
|
|
17
|
+
* URL of Google's securetoken JWKS endpoint, which serves the public keys used to sign
|
|
18
|
+
* Firebase ID tokens.
|
|
12
19
|
*
|
|
13
|
-
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* Passed to `createRemoteJWKSet` (see `createRemoteFirebaseVerifier`) so RS256 signatures can
|
|
22
|
+
* be verified against Google's rotating public keys.
|
|
14
23
|
*/
|
|
15
24
|
export declare const SECURETOKEN_JWK_URL = "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com";
|
|
25
|
+
/**
|
|
26
|
+
* Verifies Firebase ID tokens with `jose` RS256 against Google's securetoken JWKS, and
|
|
27
|
+
* optionally looks up or deletes users via the Google Identity Toolkit REST API.
|
|
28
|
+
*
|
|
29
|
+
* This replaces the `firebase-admin` Auth surface (`verifyIdToken` / `getUser` /
|
|
30
|
+
* `deleteUser`) in environments where the Node SDK cannot run, such as Cloudflare Workers.
|
|
31
|
+
* Token verification mirrors the admin SDK's checks: issuer and audience equal to the
|
|
32
|
+
* project id, an RS256 signature, a non-empty subject (the uid), and a valid `auth_time`.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* The verification key is supplied as `keyResolver`:
|
|
36
|
+
* - Production: `createRemoteJWKSet(new URL(SECURETOKEN_JWK_URL))`, which fetches and caches
|
|
37
|
+
* Google's public keys.
|
|
38
|
+
* - Tests: a generated public key, allowing fully offline verification with no network.
|
|
39
|
+
*
|
|
40
|
+
* `getUser` / `deleteUser` delegate to {@link IdentityToolkit} (a network call); when no
|
|
41
|
+
* `IdentityToolkit` is configured they throw.
|
|
42
|
+
*
|
|
43
|
+
* @see {@link FirebaseVerifier} for the abstract boundary this implements.
|
|
44
|
+
*/
|
|
16
45
|
export declare class JoseFirebaseVerifier implements FirebaseVerifier {
|
|
17
46
|
private readonly opts;
|
|
47
|
+
/**
|
|
48
|
+
* Create a verifier.
|
|
49
|
+
*
|
|
50
|
+
* @param opts - Verifier configuration.
|
|
51
|
+
* @param opts.projectId - The Firebase project id, used as both the expected token issuer
|
|
52
|
+
* (`https://securetoken.google.com/<projectId>`) and audience.
|
|
53
|
+
* @param opts.keyResolver - The RS256 verification key or a dynamic key resolver function.
|
|
54
|
+
* @param opts.identity - Optional Identity Toolkit client enabling `getUser` / `deleteUser`.
|
|
55
|
+
* @param opts.now - Optional clock returning the current time in seconds; injectable for
|
|
56
|
+
* deterministic tests. Defaults to the system clock.
|
|
57
|
+
*/
|
|
18
58
|
constructor(opts: {
|
|
19
59
|
projectId: string;
|
|
20
60
|
keyResolver: KeyInput;
|
|
21
61
|
identity?: IdentityToolkit;
|
|
22
62
|
now?: () => number;
|
|
23
63
|
});
|
|
64
|
+
/**
|
|
65
|
+
* Verify a Firebase ID token and return its decoded payload.
|
|
66
|
+
*
|
|
67
|
+
* Checks the RS256 signature against the configured key, enforces the expected issuer and
|
|
68
|
+
* audience (the project id), and applies the admin SDK's extra checks: a non-empty string
|
|
69
|
+
* subject of at most 128 characters and an `auth_time` that is a number not in the future.
|
|
70
|
+
*
|
|
71
|
+
* @param idToken - The raw Firebase ID token (JWT) to verify.
|
|
72
|
+
* @returns The decoded payload, with `uid` set from `sub` and `email` lifted to a top-level field.
|
|
73
|
+
* @throws If the signature, issuer, audience, or expiry are invalid, if the subject is
|
|
74
|
+
* missing/non-string/too long, or if `auth_time` is missing or in the future.
|
|
75
|
+
*/
|
|
24
76
|
verifyIdToken(idToken: string): Promise<DecodedIdToken>;
|
|
77
|
+
/**
|
|
78
|
+
* Look up a user record by uid via the Identity Toolkit REST API.
|
|
79
|
+
*
|
|
80
|
+
* @param uid - The user's unique id.
|
|
81
|
+
* @returns The user's `uid` and optional `email`, or `null` when the user does not exist.
|
|
82
|
+
* @throws If no Identity Toolkit client was configured on this verifier.
|
|
83
|
+
*/
|
|
25
84
|
getUser(uid: string): Promise<{
|
|
26
85
|
uid: string;
|
|
27
86
|
email?: string;
|
|
28
87
|
} | null>;
|
|
88
|
+
/**
|
|
89
|
+
* Delete a user by uid via the Identity Toolkit REST API.
|
|
90
|
+
*
|
|
91
|
+
* @param uid - The user's unique id.
|
|
92
|
+
* @returns A promise that resolves once the user has been deleted.
|
|
93
|
+
* @throws If no Identity Toolkit client was configured on this verifier, or the deletion fails.
|
|
94
|
+
*/
|
|
29
95
|
deleteUser(uid: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Return the current time in seconds, using the injected clock when provided.
|
|
98
|
+
*
|
|
99
|
+
* @returns The current Unix time in seconds.
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
30
102
|
private nowSeconds;
|
|
31
103
|
}
|
|
32
104
|
export {};
|
|
@@ -1,20 +1,61 @@
|
|
|
1
1
|
import { jwtVerify } from 'jose';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* RS256, a non-empty subject (the uid), and a valid auth_time.
|
|
3
|
+
* URL of Google's securetoken JWKS endpoint, which serves the public keys used to sign
|
|
4
|
+
* Firebase ID tokens.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* getUser/deleteUser delegate to Identity Toolkit REST (network); absent it throws.
|
|
6
|
+
* @remarks
|
|
7
|
+
* Passed to `createRemoteJWKSet` (see `createRemoteFirebaseVerifier`) so RS256 signatures can
|
|
8
|
+
* be verified against Google's rotating public keys.
|
|
11
9
|
*/
|
|
12
10
|
export const SECURETOKEN_JWK_URL = 'https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com';
|
|
11
|
+
/**
|
|
12
|
+
* Verifies Firebase ID tokens with `jose` RS256 against Google's securetoken JWKS, and
|
|
13
|
+
* optionally looks up or deletes users via the Google Identity Toolkit REST API.
|
|
14
|
+
*
|
|
15
|
+
* This replaces the `firebase-admin` Auth surface (`verifyIdToken` / `getUser` /
|
|
16
|
+
* `deleteUser`) in environments where the Node SDK cannot run, such as Cloudflare Workers.
|
|
17
|
+
* Token verification mirrors the admin SDK's checks: issuer and audience equal to the
|
|
18
|
+
* project id, an RS256 signature, a non-empty subject (the uid), and a valid `auth_time`.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* The verification key is supplied as `keyResolver`:
|
|
22
|
+
* - Production: `createRemoteJWKSet(new URL(SECURETOKEN_JWK_URL))`, which fetches and caches
|
|
23
|
+
* Google's public keys.
|
|
24
|
+
* - Tests: a generated public key, allowing fully offline verification with no network.
|
|
25
|
+
*
|
|
26
|
+
* `getUser` / `deleteUser` delegate to {@link IdentityToolkit} (a network call); when no
|
|
27
|
+
* `IdentityToolkit` is configured they throw.
|
|
28
|
+
*
|
|
29
|
+
* @see {@link FirebaseVerifier} for the abstract boundary this implements.
|
|
30
|
+
*/
|
|
13
31
|
export class JoseFirebaseVerifier {
|
|
14
32
|
opts;
|
|
33
|
+
/**
|
|
34
|
+
* Create a verifier.
|
|
35
|
+
*
|
|
36
|
+
* @param opts - Verifier configuration.
|
|
37
|
+
* @param opts.projectId - The Firebase project id, used as both the expected token issuer
|
|
38
|
+
* (`https://securetoken.google.com/<projectId>`) and audience.
|
|
39
|
+
* @param opts.keyResolver - The RS256 verification key or a dynamic key resolver function.
|
|
40
|
+
* @param opts.identity - Optional Identity Toolkit client enabling `getUser` / `deleteUser`.
|
|
41
|
+
* @param opts.now - Optional clock returning the current time in seconds; injectable for
|
|
42
|
+
* deterministic tests. Defaults to the system clock.
|
|
43
|
+
*/
|
|
15
44
|
constructor(opts) {
|
|
16
45
|
this.opts = opts;
|
|
17
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Verify a Firebase ID token and return its decoded payload.
|
|
49
|
+
*
|
|
50
|
+
* Checks the RS256 signature against the configured key, enforces the expected issuer and
|
|
51
|
+
* audience (the project id), and applies the admin SDK's extra checks: a non-empty string
|
|
52
|
+
* subject of at most 128 characters and an `auth_time` that is a number not in the future.
|
|
53
|
+
*
|
|
54
|
+
* @param idToken - The raw Firebase ID token (JWT) to verify.
|
|
55
|
+
* @returns The decoded payload, with `uid` set from `sub` and `email` lifted to a top-level field.
|
|
56
|
+
* @throws If the signature, issuer, audience, or expiry are invalid, if the subject is
|
|
57
|
+
* missing/non-string/too long, or if `auth_time` is missing or in the future.
|
|
58
|
+
*/
|
|
18
59
|
async verifyIdToken(idToken) {
|
|
19
60
|
const options = {
|
|
20
61
|
issuer: `https://securetoken.google.com/${this.opts.projectId}`,
|
|
@@ -34,18 +75,38 @@ export class JoseFirebaseVerifier {
|
|
|
34
75
|
}
|
|
35
76
|
return { ...payload, uid: payload.sub, email: payload.email };
|
|
36
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Look up a user record by uid via the Identity Toolkit REST API.
|
|
80
|
+
*
|
|
81
|
+
* @param uid - The user's unique id.
|
|
82
|
+
* @returns The user's `uid` and optional `email`, or `null` when the user does not exist.
|
|
83
|
+
* @throws If no Identity Toolkit client was configured on this verifier.
|
|
84
|
+
*/
|
|
37
85
|
async getUser(uid) {
|
|
38
86
|
if (!this.opts.identity) {
|
|
39
87
|
throw new Error('Identity Toolkit not configured');
|
|
40
88
|
}
|
|
41
89
|
return this.opts.identity.lookup(uid, this.nowSeconds());
|
|
42
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Delete a user by uid via the Identity Toolkit REST API.
|
|
93
|
+
*
|
|
94
|
+
* @param uid - The user's unique id.
|
|
95
|
+
* @returns A promise that resolves once the user has been deleted.
|
|
96
|
+
* @throws If no Identity Toolkit client was configured on this verifier, or the deletion fails.
|
|
97
|
+
*/
|
|
43
98
|
async deleteUser(uid) {
|
|
44
99
|
if (!this.opts.identity) {
|
|
45
100
|
throw new Error('Identity Toolkit not configured');
|
|
46
101
|
}
|
|
47
102
|
await this.opts.identity.remove(uid, this.nowSeconds());
|
|
48
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Return the current time in seconds, using the injected clock when provided.
|
|
106
|
+
*
|
|
107
|
+
* @returns The current Unix time in seconds.
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
49
110
|
nowSeconds() {
|
|
50
111
|
return this.opts.now ? this.opts.now() : Math.floor(Date.now() / 1000);
|
|
51
112
|
}
|
|
@@ -1,9 +1,47 @@
|
|
|
1
1
|
import { JoseFirebaseVerifier } from './jose-firebase-verifier.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a token-verification-only Firebase verifier for the given project.
|
|
4
|
+
*
|
|
5
|
+
* Uses `createRemoteJWKSet` to fetch Google's securetoken public keys and returns a
|
|
6
|
+
* {@link JoseFirebaseVerifier}. This factory is for token verification only; it configures no
|
|
7
|
+
* Identity Toolkit client, so `getUser` / `deleteUser` are unavailable.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* The remote JWKS is created once per isolate and shared, and the returned verifier is
|
|
11
|
+
* memoized per `projectId`. This preserves the caching behaviour of a module-level JWKS so
|
|
12
|
+
* repeated calls do not re-fetch keys or allocate new verifiers.
|
|
13
|
+
*
|
|
14
|
+
* @param projectId - The Firebase project id whose tokens will be verified.
|
|
15
|
+
* @returns A verifier that validates ID tokens for `projectId`.
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const verifier = createRemoteFirebaseVerifier('my-firebase-project');
|
|
19
|
+
* const decoded = await verifier.verifyIdToken(idToken);
|
|
20
|
+
* console.log(decoded.uid);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
2
23
|
export declare function createRemoteFirebaseVerifier(projectId: string): JoseFirebaseVerifier;
|
|
3
24
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
25
|
+
* Create a Firebase verifier from a service-account JSON string.
|
|
26
|
+
*
|
|
27
|
+
* Unlike {@link createRemoteFirebaseVerifier}, the returned {@link JoseFirebaseVerifier}
|
|
28
|
+
* embeds an {@link IdentityToolkit} client, enabling `getUser` and `deleteUser` in addition to
|
|
29
|
+
* token verification.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* The verifier is cached for the lifetime of the isolate, keyed by the service-account JSON
|
|
33
|
+
* string, and is only rebuilt when that secret changes. The remote JWKS is shared with
|
|
34
|
+
* {@link createRemoteFirebaseVerifier}.
|
|
35
|
+
*
|
|
36
|
+
* @param serviceAccountJson - The service-account key as a JSON string (parsed into {@link ServiceAccount}).
|
|
37
|
+
* @returns A verifier that validates ID tokens and can look up or delete users.
|
|
38
|
+
* @throws If `serviceAccountJson` is not valid JSON.
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const verifier = createServiceAccountVerifier(env.FIREBASE_SERVICE_ACCOUNT);
|
|
42
|
+
* const decoded = await verifier.verifyIdToken(idToken);
|
|
43
|
+
* const user = await verifier.getUser(decoded.uid);
|
|
44
|
+
* await verifier.deleteUser(decoded.uid);
|
|
45
|
+
* ```
|
|
8
46
|
*/
|
|
9
47
|
export declare function createServiceAccountVerifier(serviceAccountJson: string): JoseFirebaseVerifier;
|
|
@@ -2,15 +2,42 @@ import { createRemoteJWKSet } from 'jose';
|
|
|
2
2
|
import { IdentityToolkit } from './identity-toolkit.js';
|
|
3
3
|
import { JoseFirebaseVerifier, SECURETOKEN_JWK_URL } from './jose-firebase-verifier.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* `JoseFirebaseVerifier` を返す。トークン検証のみ用途(getUser/deleteUser は不要 = Identity Toolkit 無し)。
|
|
5
|
+
* Shared remote JWKS for Google's securetoken keys.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* The JWKS URL is fixed, so the set is created once per isolate and shared across verifiers
|
|
9
|
+
* (`jose` caches the fetched keys internally). Lazily initialised on first use.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
11
12
|
*/
|
|
12
13
|
let jwks;
|
|
14
|
+
/**
|
|
15
|
+
* Per-`projectId` cache of token-only verifiers, memoized for the lifetime of the isolate.
|
|
16
|
+
*
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
13
19
|
const verifiers = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Create a token-verification-only Firebase verifier for the given project.
|
|
22
|
+
*
|
|
23
|
+
* Uses `createRemoteJWKSet` to fetch Google's securetoken public keys and returns a
|
|
24
|
+
* {@link JoseFirebaseVerifier}. This factory is for token verification only; it configures no
|
|
25
|
+
* Identity Toolkit client, so `getUser` / `deleteUser` are unavailable.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* The remote JWKS is created once per isolate and shared, and the returned verifier is
|
|
29
|
+
* memoized per `projectId`. This preserves the caching behaviour of a module-level JWKS so
|
|
30
|
+
* repeated calls do not re-fetch keys or allocate new verifiers.
|
|
31
|
+
*
|
|
32
|
+
* @param projectId - The Firebase project id whose tokens will be verified.
|
|
33
|
+
* @returns A verifier that validates ID tokens for `projectId`.
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const verifier = createRemoteFirebaseVerifier('my-firebase-project');
|
|
37
|
+
* const decoded = await verifier.verifyIdToken(idToken);
|
|
38
|
+
* console.log(decoded.uid);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
14
41
|
export function createRemoteFirebaseVerifier(projectId) {
|
|
15
42
|
jwks ??= createRemoteJWKSet(new URL(SECURETOKEN_JWK_URL));
|
|
16
43
|
let verifier = verifiers.get(projectId);
|
|
@@ -20,12 +47,34 @@ export function createRemoteFirebaseVerifier(projectId) {
|
|
|
20
47
|
}
|
|
21
48
|
return verifier;
|
|
22
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Single-entry cache of the service-account verifier, keyed by the raw service-account JSON.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
23
55
|
let saVerifierCache = null;
|
|
24
56
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
57
|
+
* Create a Firebase verifier from a service-account JSON string.
|
|
58
|
+
*
|
|
59
|
+
* Unlike {@link createRemoteFirebaseVerifier}, the returned {@link JoseFirebaseVerifier}
|
|
60
|
+
* embeds an {@link IdentityToolkit} client, enabling `getUser` and `deleteUser` in addition to
|
|
61
|
+
* token verification.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* The verifier is cached for the lifetime of the isolate, keyed by the service-account JSON
|
|
65
|
+
* string, and is only rebuilt when that secret changes. The remote JWKS is shared with
|
|
66
|
+
* {@link createRemoteFirebaseVerifier}.
|
|
67
|
+
*
|
|
68
|
+
* @param serviceAccountJson - The service-account key as a JSON string (parsed into {@link ServiceAccount}).
|
|
69
|
+
* @returns A verifier that validates ID tokens and can look up or delete users.
|
|
70
|
+
* @throws If `serviceAccountJson` is not valid JSON.
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const verifier = createServiceAccountVerifier(env.FIREBASE_SERVICE_ACCOUNT);
|
|
74
|
+
* const decoded = await verifier.verifyIdToken(idToken);
|
|
75
|
+
* const user = await verifier.getUser(decoded.uid);
|
|
76
|
+
* await verifier.deleteUser(decoded.uid);
|
|
77
|
+
* ```
|
|
29
78
|
*/
|
|
30
79
|
export function createServiceAccountVerifier(serviceAccountJson) {
|
|
31
80
|
if (saVerifierCache?.key !== serviceAccountJson) {
|
package/dist/http/app-env.d.ts
CHANGED
|
@@ -1,19 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The resolved runtime environment of the Worker.
|
|
3
|
+
*/
|
|
1
4
|
export type AppEnv = 'development' | 'production';
|
|
2
5
|
/**
|
|
3
|
-
*
|
|
6
|
+
* Resolve the runtime environment (development or production) from the Worker `env` binding.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Cloudflare Workers have no filesystem or `child_process` at runtime, so the environment cannot be
|
|
10
|
+
* inferred from the presence of a `.git` directory the way a Node/NestJS process might. Instead, the
|
|
11
|
+
* development signal is carried in the committed launch command: `wrangler dev --var APP_ENV:development`
|
|
12
|
+
* injects `APP_ENV`, while `wrangler deploy` injects nothing. Only `env.APP_ENV === 'development'`
|
|
13
|
+
* resolves to `'development'`; every other case (including a missing binding, i.e. a production deploy)
|
|
14
|
+
* resolves to `'production'`. This "absence defaults to production" semantic keeps the safe side as the
|
|
15
|
+
* default.
|
|
16
|
+
*
|
|
17
|
+
* Because it reads from `env` rather than a request header, it works in both `fetch` and `scheduled`
|
|
18
|
+
* contexts and cannot be spoofed by an incoming request.
|
|
4
19
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* **コミット済みの起動コマンド**に置く: `wrangler dev --var APP_ENV:development`(`deploy` は無注入)。
|
|
8
|
-
* よって `env.APP_ENV === 'development'` の時だけ development、それ以外(注入無し=本番デプロイ)は
|
|
9
|
-
* production に倒す。これは `/api` の「absence/catch = 本番(安全側)」と同じ意味論。
|
|
20
|
+
* @param env - The Worker environment binding, or `null`/`undefined` when unavailable.
|
|
21
|
+
* @returns `'development'` only when `env.APP_ENV` is exactly `'development'`; otherwise `'production'`.
|
|
10
22
|
*
|
|
11
|
-
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* export default {
|
|
26
|
+
* fetch(req, env) {
|
|
27
|
+
* if (resolveAppEnv(env) === 'development') {
|
|
28
|
+
* // enable verbose logging
|
|
29
|
+
* }
|
|
30
|
+
* },
|
|
31
|
+
* };
|
|
32
|
+
* ```
|
|
12
33
|
*/
|
|
13
34
|
export declare function resolveAppEnv(env: {
|
|
14
35
|
APP_ENV?: string;
|
|
15
36
|
} | null | undefined): AppEnv;
|
|
16
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Shorthand for checking whether the resolved environment is production.
|
|
39
|
+
*
|
|
40
|
+
* @param env - The Worker environment binding, or `null`/`undefined` when unavailable.
|
|
41
|
+
* @returns `true` when {@link resolveAppEnv} resolves to `'production'`.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* if (isProductionEnv(env)) {
|
|
46
|
+
* // skip dev-only diagnostics
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
17
50
|
export declare const isProductionEnv: (env: {
|
|
18
51
|
APP_ENV?: string;
|
|
19
52
|
} | null | undefined) => boolean;
|
package/dist/http/app-env.js
CHANGED
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Resolve the runtime environment (development or production) from the Worker `env` binding.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Workers
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Cloudflare Workers have no filesystem or `child_process` at runtime, so the environment cannot be
|
|
6
|
+
* inferred from the presence of a `.git` directory the way a Node/NestJS process might. Instead, the
|
|
7
|
+
* development signal is carried in the committed launch command: `wrangler dev --var APP_ENV:development`
|
|
8
|
+
* injects `APP_ENV`, while `wrangler deploy` injects nothing. Only `env.APP_ENV === 'development'`
|
|
9
|
+
* resolves to `'development'`; every other case (including a missing binding, i.e. a production deploy)
|
|
10
|
+
* resolves to `'production'`. This "absence defaults to production" semantic keeps the safe side as the
|
|
11
|
+
* default.
|
|
9
12
|
*
|
|
10
|
-
* `env`
|
|
13
|
+
* Because it reads from `env` rather than a request header, it works in both `fetch` and `scheduled`
|
|
14
|
+
* contexts and cannot be spoofed by an incoming request.
|
|
15
|
+
*
|
|
16
|
+
* @param env - The Worker environment binding, or `null`/`undefined` when unavailable.
|
|
17
|
+
* @returns `'development'` only when `env.APP_ENV` is exactly `'development'`; otherwise `'production'`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* export default {
|
|
22
|
+
* fetch(req, env) {
|
|
23
|
+
* if (resolveAppEnv(env) === 'development') {
|
|
24
|
+
* // enable verbose logging
|
|
25
|
+
* }
|
|
26
|
+
* },
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
11
29
|
*/
|
|
12
30
|
export function resolveAppEnv(env) {
|
|
13
31
|
return env?.APP_ENV === 'development' ? 'development' : 'production';
|
|
14
32
|
}
|
|
15
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Shorthand for checking whether the resolved environment is production.
|
|
35
|
+
*
|
|
36
|
+
* @param env - The Worker environment binding, or `null`/`undefined` when unavailable.
|
|
37
|
+
* @returns `true` when {@link resolveAppEnv} resolves to `'production'`.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* if (isProductionEnv(env)) {
|
|
42
|
+
* // skip dev-only diagnostics
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
16
46
|
export const isProductionEnv = (env) => resolveAppEnv(env) === 'production';
|
package/dist/http/app-info.d.ts
CHANGED
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
import type { Context } from 'hono';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Client application metadata derived from the `x-amz-meta-*` request headers.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Reproduces the per-request app identity that a NestJS service would expose so a Hono app
|
|
7
|
+
* can read the same client-supplied version/uuid pair without changing the wire contract.
|
|
8
|
+
*/
|
|
3
9
|
export interface AppInfo {
|
|
10
|
+
/** Client application version from `x-amz-meta-version`, or `null` when the header is absent. */
|
|
4
11
|
version: string | null;
|
|
12
|
+
/** Client installation identifier from `x-amz-meta-uuid`, or `null` when the header is absent. */
|
|
5
13
|
uuid: string | null;
|
|
6
14
|
}
|
|
7
15
|
/**
|
|
8
|
-
* `x-amz-meta-version` / `x-amz-meta-uuid`
|
|
9
|
-
*
|
|
16
|
+
* Read {@link AppInfo} from the `x-amz-meta-version` / `x-amz-meta-uuid` request headers.
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* Typically called by an auth middleware that stores the result per request via
|
|
20
|
+
* `c.set('appInfo', getAppInfo(c))`. Missing headers resolve to `null` rather than throwing.
|
|
21
|
+
*
|
|
22
|
+
* @param c - The Hono request context to read headers from.
|
|
23
|
+
* @returns The client application metadata for the current request.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* app.use(async (c, next) => {
|
|
28
|
+
* c.set('appInfo', getAppInfo(c));
|
|
29
|
+
* await next();
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
10
32
|
*/
|
|
11
33
|
export declare const getAppInfo: (c: Context) => AppInfo;
|
package/dist/http/app-info.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `x-amz-meta-version` / `x-amz-meta-uuid`
|
|
3
|
-
*
|
|
2
|
+
* Read {@link AppInfo} from the `x-amz-meta-version` / `x-amz-meta-uuid` request headers.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Typically called by an auth middleware that stores the result per request via
|
|
6
|
+
* `c.set('appInfo', getAppInfo(c))`. Missing headers resolve to `null` rather than throwing.
|
|
7
|
+
*
|
|
8
|
+
* @param c - The Hono request context to read headers from.
|
|
9
|
+
* @returns The client application metadata for the current request.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* app.use(async (c, next) => {
|
|
14
|
+
* c.set('appInfo', getAppInfo(c));
|
|
15
|
+
* await next();
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
4
18
|
*/
|
|
5
19
|
export const getAppInfo = (c) => ({
|
|
6
20
|
version: c.req.header('x-amz-meta-version') ?? null,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* HTTP status codes mirroring the `HttpStatus` enum from `@nestjs/common`.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Provides a single source of truth for referencing status codes by the same names NestJS uses, so a
|
|
6
|
+
* Hono app can emit responses whose status matches a NestJS service byte-for-byte. The member set and
|
|
7
|
+
* numeric values intentionally track `@nestjs/common` rather than the IANA registry, including a few
|
|
8
|
+
* non-standard codes that NestJS ships.
|
|
5
9
|
*/
|
|
6
10
|
export declare enum HttpStatus {
|
|
7
11
|
CONTINUE = 100,
|
|
@@ -17,7 +21,9 @@ export declare enum HttpStatus {
|
|
|
17
21
|
PARTIAL_CONTENT = 206,
|
|
18
22
|
MULTI_STATUS = 207,
|
|
19
23
|
ALREADY_REPORTED = 208,
|
|
24
|
+
/** Non-standard WebDAV extension carried over from NestJS. */
|
|
20
25
|
CONTENT_DIFFERENT = 210,
|
|
26
|
+
/** Multiple Choices (300). Named `AMBIGUOUS` to match NestJS. */
|
|
21
27
|
AMBIGUOUS = 300,
|
|
22
28
|
MOVED_PERMANENTLY = 301,
|
|
23
29
|
FOUND = 302,
|
|
@@ -43,13 +49,16 @@ export declare enum HttpStatus {
|
|
|
43
49
|
UNSUPPORTED_MEDIA_TYPE = 415,
|
|
44
50
|
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
|
45
51
|
EXPECTATION_FAILED = 417,
|
|
52
|
+
/** "I'm a teapot" (418), from RFC 2324. */
|
|
46
53
|
I_AM_A_TEAPOT = 418,
|
|
54
|
+
/** Misdirected Request (421). Named `MISDIRECTED` to match NestJS. */
|
|
47
55
|
MISDIRECTED = 421,
|
|
48
56
|
UNPROCESSABLE_ENTITY = 422,
|
|
49
57
|
LOCKED = 423,
|
|
50
58
|
FAILED_DEPENDENCY = 424,
|
|
51
59
|
PRECONDITION_REQUIRED = 428,
|
|
52
60
|
TOO_MANY_REQUESTS = 429,
|
|
61
|
+
/** Non-standard code carried over from NestJS. */
|
|
53
62
|
UNRECOVERABLE_ERROR = 456,
|
|
54
63
|
INTERNAL_SERVER_ERROR = 500,
|
|
55
64
|
NOT_IMPLEMENTED = 501,
|
package/dist/http/http-status.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* HTTP status codes mirroring the `HttpStatus` enum from `@nestjs/common`.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Provides a single source of truth for referencing status codes by the same names NestJS uses, so a
|
|
6
|
+
* Hono app can emit responses whose status matches a NestJS service byte-for-byte. The member set and
|
|
7
|
+
* numeric values intentionally track `@nestjs/common` rather than the IANA registry, including a few
|
|
8
|
+
* non-standard codes that NestJS ships.
|
|
5
9
|
*/
|
|
6
10
|
export var HttpStatus;
|
|
7
11
|
(function (HttpStatus) {
|
|
@@ -18,7 +22,9 @@ export var HttpStatus;
|
|
|
18
22
|
HttpStatus[HttpStatus["PARTIAL_CONTENT"] = 206] = "PARTIAL_CONTENT";
|
|
19
23
|
HttpStatus[HttpStatus["MULTI_STATUS"] = 207] = "MULTI_STATUS";
|
|
20
24
|
HttpStatus[HttpStatus["ALREADY_REPORTED"] = 208] = "ALREADY_REPORTED";
|
|
25
|
+
/** Non-standard WebDAV extension carried over from NestJS. */
|
|
21
26
|
HttpStatus[HttpStatus["CONTENT_DIFFERENT"] = 210] = "CONTENT_DIFFERENT";
|
|
27
|
+
/** Multiple Choices (300). Named `AMBIGUOUS` to match NestJS. */
|
|
22
28
|
HttpStatus[HttpStatus["AMBIGUOUS"] = 300] = "AMBIGUOUS";
|
|
23
29
|
HttpStatus[HttpStatus["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY";
|
|
24
30
|
HttpStatus[HttpStatus["FOUND"] = 302] = "FOUND";
|
|
@@ -44,13 +50,16 @@ export var HttpStatus;
|
|
|
44
50
|
HttpStatus[HttpStatus["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
|
|
45
51
|
HttpStatus[HttpStatus["REQUESTED_RANGE_NOT_SATISFIABLE"] = 416] = "REQUESTED_RANGE_NOT_SATISFIABLE";
|
|
46
52
|
HttpStatus[HttpStatus["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED";
|
|
53
|
+
/** "I'm a teapot" (418), from RFC 2324. */
|
|
47
54
|
HttpStatus[HttpStatus["I_AM_A_TEAPOT"] = 418] = "I_AM_A_TEAPOT";
|
|
55
|
+
/** Misdirected Request (421). Named `MISDIRECTED` to match NestJS. */
|
|
48
56
|
HttpStatus[HttpStatus["MISDIRECTED"] = 421] = "MISDIRECTED";
|
|
49
57
|
HttpStatus[HttpStatus["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
|
|
50
58
|
HttpStatus[HttpStatus["LOCKED"] = 423] = "LOCKED";
|
|
51
59
|
HttpStatus[HttpStatus["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
|
|
52
60
|
HttpStatus[HttpStatus["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
|
|
53
61
|
HttpStatus[HttpStatus["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
|
|
62
|
+
/** Non-standard code carried over from NestJS. */
|
|
54
63
|
HttpStatus[HttpStatus["UNRECOVERABLE_ERROR"] = 456] = "UNRECOVERABLE_ERROR";
|
|
55
64
|
HttpStatus[HttpStatus["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
|
56
65
|
HttpStatus[HttpStatus["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
|