pyric-admin 0.1.0-alpha.32 → 0.1.0-alpha.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.ts +5 -4
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +127 -53
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/token-decoders.d.ts +13 -26
- package/dist/auth/token-decoders.d.ts.map +1 -1
- package/dist/auth/token-decoders.js +102 -122
- package/dist/auth/token-decoders.js.map +1 -1
- package/dist/auth/verify-token.d.ts +45 -9
- package/dist/auth/verify-token.d.ts.map +1 -1
- package/dist/auth/verify-token.js +63 -27
- package/dist/auth/verify-token.js.map +1 -1
- package/package.json +2 -2
- package/src/auth/auth.test.ts +221 -57
- package/src/auth/custom-token-exchange.test.ts +100 -0
- package/src/auth/index.ts +132 -53
- package/src/auth/oracle-admin-answers.test.ts +40 -4
- package/src/auth/token-decoders.ts +98 -141
- package/src/auth/verify-token.ts +78 -34
package/src/auth/verify-token.ts
CHANGED
|
@@ -1,42 +1,86 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* `verifyIdToken` for `pyric-admin/auth`, shared by the in-process and remote
|
|
3
|
+
* arms. Each arm passes the project the token must be scoped to and the
|
|
4
|
+
* current time on its own clock.
|
|
4
5
|
*/
|
|
6
|
+
import type { AuthAccountStanding } from 'pyric/sandbox/internal';
|
|
7
|
+
import { PrefixedFirebaseError } from '../app/firebase-error.js';
|
|
5
8
|
import type { DecodedIdToken } from './types.js';
|
|
6
|
-
import {
|
|
9
|
+
import { checkClientIdToken, type IdTokenFailureCode } from './token-decoders.js';
|
|
10
|
+
|
|
11
|
+
/** The codes `checkRevoked` and `revokeRefreshTokens` add to a token check's. */
|
|
12
|
+
type AccountFailureCode = 'user-not-found' | 'user-disabled' | 'id-token-revoked' | 'invalid-uid';
|
|
13
|
+
|
|
14
|
+
/** firebase-admin's FirebaseAuthError: a PrefixedFirebaseError with prefix `auth`. */
|
|
15
|
+
export class FirebaseAuthError extends PrefixedFirebaseError {
|
|
16
|
+
constructor(code: IdTokenFailureCode | AccountFailureCode, message: string) {
|
|
17
|
+
super('auth', code, message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** What one verification checks against. */
|
|
22
|
+
export interface IdTokenExpectation {
|
|
23
|
+
/** The project the token's `aud` and `iss` must name. */
|
|
24
|
+
projectId: string;
|
|
25
|
+
/** The current time, in epoch milliseconds, that `nbf` and `exp` are read against. */
|
|
26
|
+
nowMs: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const NOT_A_STRING = 'First argument to verifyIdToken() must be a Firebase ID token string.';
|
|
7
30
|
|
|
8
31
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Throws on any token that doesn't match a recognized sandbox prefix —
|
|
14
|
-
* including real JWTs that another verifier would parse. The sandbox
|
|
15
|
-
* backends are intentionally not drop-ins for production verification.
|
|
32
|
+
* Throws `auth/argument-error` synchronously for a token that is not a string,
|
|
33
|
+
* as firebase-admin's `verifyJWT` does, so an arm that reads its clock before
|
|
34
|
+
* verifying can check the argument first.
|
|
16
35
|
*/
|
|
17
|
-
export function
|
|
36
|
+
export function assertIdTokenArgument(idToken: unknown): void {
|
|
18
37
|
const isInvalidTokenArgument = typeof idToken !== 'string';
|
|
19
|
-
if (isInvalidTokenArgument)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
if (isInvalidTokenArgument) throw new FirebaseAuthError('argument-error', NOT_A_STRING);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Verifies an ID token minted by pyric's Auth for `expected.projectId`.
|
|
43
|
+
*
|
|
44
|
+
* A token that is not a string throws `auth/argument-error` synchronously, as
|
|
45
|
+
* firebase-admin's `verifyJWT` does. Every other failure rejects with
|
|
46
|
+
* `auth/argument-error`, or `auth/id-token-expired` for an expired token.
|
|
47
|
+
*/
|
|
48
|
+
export function verifySandboxIdToken(idToken: string, expected: IdTokenExpectation): Promise<DecodedIdToken> {
|
|
49
|
+
assertIdTokenArgument(idToken);
|
|
50
|
+
const nowSec = Math.floor(expected.nowMs / 1000);
|
|
51
|
+
return checkClientIdToken(idToken, expected.projectId, nowSec).then((verdict) => {
|
|
52
|
+
if (!verdict.ok) throw new FirebaseAuthError(verdict.code, verdict.message);
|
|
53
|
+
return verdict.token;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* firebase-admin's answer for a uid that is not a non-empty string of at most
|
|
59
|
+
* 128 characters (13.10.0 `validator.isUid`), checked before any request.
|
|
60
|
+
*/
|
|
61
|
+
export function invalidUid(uid: unknown): FirebaseAuthError | null {
|
|
62
|
+
const isUid = typeof uid === 'string' && uid.length > 0 && uid.length <= 128;
|
|
63
|
+
if (isUid) return null;
|
|
64
|
+
return new FirebaseAuthError('invalid-uid', 'The uid must be a non-empty string with at most 128 characters.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** firebase-admin's `getUser` answer for a uid with no record. */
|
|
68
|
+
export function userNotFound(): FirebaseAuthError {
|
|
69
|
+
return new FirebaseAuthError('user-not-found', 'There is no user record corresponding to the provided identifier.');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* firebase-admin's `verifyDecodedJWTNotRevokedOrDisabled` (13.10.0
|
|
74
|
+
* `lib/auth/base-auth.js`), the `checkRevoked` step: the account must exist,
|
|
75
|
+
* be enabled, and have no revocation after the token's `auth_time`, compared
|
|
76
|
+
* in whole seconds as production stores it.
|
|
77
|
+
*/
|
|
78
|
+
export function assertNotRevokedOrDisabled(decoded: DecodedIdToken, standing: AuthAccountStanding): DecodedIdToken {
|
|
79
|
+
const hasNoRecord = standing.kind === 'deleted' || standing.kind === 'unrecorded';
|
|
80
|
+
if (hasNoRecord) throw userNotFound();
|
|
81
|
+
if (standing.kind === 'disabled') throw new FirebaseAuthError('user-disabled', 'The user record is disabled.');
|
|
82
|
+
const validAfterMs = standing.tokensValidAfterMs;
|
|
83
|
+
const revoked = validAfterMs !== null && decoded.auth_time * 1000 < validAfterMs;
|
|
84
|
+
if (revoked) throw new FirebaseAuthError('id-token-revoked', 'The Firebase ID token has been revoked.');
|
|
85
|
+
return decoded;
|
|
42
86
|
}
|