@rdlabo/workers-hono-kit 0.9.5 → 0.9.6
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.
|
@@ -28,8 +28,9 @@ export declare const SECURETOKEN_JWK_URL = "https://www.googleapis.com/service_a
|
|
|
28
28
|
*
|
|
29
29
|
* This replaces the `firebase-admin` Auth surface (`verifyIdToken` / `getUser` /
|
|
30
30
|
* `deleteUser`) in environments where the Node SDK cannot run, such as Cloudflare Workers.
|
|
31
|
-
* Token verification
|
|
32
|
-
* project id, an RS256 signature, a non-empty subject (the
|
|
31
|
+
* Token verification follows Firebase's documented third-party JWT validation requirements:
|
|
32
|
+
* issuer and audience equal to the project id, an RS256 signature, a non-empty subject (the
|
|
33
|
+
* uid), and valid `exp`, `iat`, and `auth_time` timestamps.
|
|
33
34
|
*
|
|
34
35
|
* @remarks
|
|
35
36
|
* The verification key is supplied as `keyResolver`:
|
|
@@ -65,13 +66,15 @@ export declare class JoseFirebaseVerifier implements FirebaseVerifier {
|
|
|
65
66
|
* Verify a Firebase ID token and return its decoded payload.
|
|
66
67
|
*
|
|
67
68
|
* Checks the RS256 signature against the configured key, enforces the expected issuer and
|
|
68
|
-
* audience (the project id), and applies
|
|
69
|
-
* subject of at most 128 characters
|
|
69
|
+
* audience (the project id), and applies Firebase's documented ID-token checks: a required
|
|
70
|
+
* future `exp`, a non-empty string subject of at most 128 characters, plus finite `iat` and
|
|
71
|
+
* `auth_time` values that are not in the future. Timestamp comparisons use strict zero clock
|
|
72
|
+
* tolerance.
|
|
70
73
|
*
|
|
71
74
|
* @param idToken - The raw Firebase ID token (JWT) to verify.
|
|
72
75
|
* @returns The decoded payload, with `uid` set from `sub` and `email` lifted to a top-level field.
|
|
73
76
|
* @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.
|
|
77
|
+
* missing/non-string/too long, or if `iat`/`auth_time` is missing, non-finite, or in the future.
|
|
75
78
|
*/
|
|
76
79
|
verifyIdToken(idToken: string): Promise<DecodedIdToken>;
|
|
77
80
|
/**
|
|
@@ -14,8 +14,9 @@ export const SECURETOKEN_JWK_URL = 'https://www.googleapis.com/service_accounts/
|
|
|
14
14
|
*
|
|
15
15
|
* This replaces the `firebase-admin` Auth surface (`verifyIdToken` / `getUser` /
|
|
16
16
|
* `deleteUser`) in environments where the Node SDK cannot run, such as Cloudflare Workers.
|
|
17
|
-
* Token verification
|
|
18
|
-
* project id, an RS256 signature, a non-empty subject (the
|
|
17
|
+
* Token verification follows Firebase's documented third-party JWT validation requirements:
|
|
18
|
+
* issuer and audience equal to the project id, an RS256 signature, a non-empty subject (the
|
|
19
|
+
* uid), and valid `exp`, `iat`, and `auth_time` timestamps.
|
|
19
20
|
*
|
|
20
21
|
* @remarks
|
|
21
22
|
* The verification key is supplied as `keyResolver`:
|
|
@@ -48,29 +49,42 @@ export class JoseFirebaseVerifier {
|
|
|
48
49
|
* Verify a Firebase ID token and return its decoded payload.
|
|
49
50
|
*
|
|
50
51
|
* Checks the RS256 signature against the configured key, enforces the expected issuer and
|
|
51
|
-
* audience (the project id), and applies
|
|
52
|
-
* subject of at most 128 characters
|
|
52
|
+
* audience (the project id), and applies Firebase's documented ID-token checks: a required
|
|
53
|
+
* future `exp`, a non-empty string subject of at most 128 characters, plus finite `iat` and
|
|
54
|
+
* `auth_time` values that are not in the future. Timestamp comparisons use strict zero clock
|
|
55
|
+
* tolerance.
|
|
53
56
|
*
|
|
54
57
|
* @param idToken - The raw Firebase ID token (JWT) to verify.
|
|
55
58
|
* @returns The decoded payload, with `uid` set from `sub` and `email` lifted to a top-level field.
|
|
56
59
|
* @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.
|
|
60
|
+
* missing/non-string/too long, or if `iat`/`auth_time` is missing, non-finite, or in the future.
|
|
58
61
|
*/
|
|
59
62
|
async verifyIdToken(idToken) {
|
|
63
|
+
const now = this.nowSeconds();
|
|
60
64
|
const options = {
|
|
61
65
|
issuer: `https://securetoken.google.com/${this.opts.projectId}`,
|
|
62
66
|
audience: this.opts.projectId,
|
|
63
67
|
algorithms: ['RS256'],
|
|
68
|
+
requiredClaims: ['exp'],
|
|
69
|
+
currentDate: new Date(now * 1000),
|
|
70
|
+
clockTolerance: 0,
|
|
64
71
|
};
|
|
65
72
|
// Branch so each call matches a single jwtVerify overload (static key vs getKey fn).
|
|
66
73
|
const key = this.opts.keyResolver;
|
|
67
74
|
const { payload } = typeof key === 'function' ? await jwtVerify(idToken, key, options) : await jwtVerify(idToken, key, options);
|
|
68
|
-
//
|
|
75
|
+
// Apply Firebase's documented checks beyond signature/iss/aud/exp.
|
|
69
76
|
if (!payload.sub || typeof payload.sub !== 'string' || payload.sub.length > 128) {
|
|
70
77
|
throw new Error('Firebase ID token has an invalid subject');
|
|
71
78
|
}
|
|
79
|
+
if (!Number.isFinite(payload.exp)) {
|
|
80
|
+
throw new Error('Firebase ID token has an invalid exp');
|
|
81
|
+
}
|
|
82
|
+
const issuedAt = payload.iat;
|
|
83
|
+
if (typeof issuedAt !== 'number' || !Number.isFinite(issuedAt) || issuedAt > now) {
|
|
84
|
+
throw new Error('Firebase ID token has an invalid iat');
|
|
85
|
+
}
|
|
72
86
|
const authTime = payload.auth_time;
|
|
73
|
-
if (typeof authTime !== 'number' || authTime >
|
|
87
|
+
if (typeof authTime !== 'number' || !Number.isFinite(authTime) || authTime > now) {
|
|
74
88
|
throw new Error('Firebase ID token has an invalid auth_time');
|
|
75
89
|
}
|
|
76
90
|
return { ...payload, uid: payload.sub, email: payload.email };
|
|
@@ -125,6 +139,10 @@ export class JoseFirebaseVerifier {
|
|
|
125
139
|
* @internal
|
|
126
140
|
*/
|
|
127
141
|
nowSeconds() {
|
|
128
|
-
|
|
142
|
+
const now = this.opts.now ? this.opts.now() : Math.floor(Date.now() / 1000);
|
|
143
|
+
if (!Number.isFinite(now)) {
|
|
144
|
+
throw new Error('Firebase verifier clock returned an invalid time');
|
|
145
|
+
}
|
|
146
|
+
return now;
|
|
129
147
|
}
|
|
130
148
|
}
|