@dereekb/firebase-server 13.33.0 → 13.34.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/index.cjs.js +28 -6
- package/index.esm.js +28 -6
- package/mailgun/package.json +9 -9
- package/mcp/package.json +11 -11
- package/model/index.cjs.js +468 -211
- package/model/index.esm.js +463 -214
- package/model/package.json +9 -9
- package/model/src/lib/index.d.ts +1 -0
- package/model/src/lib/system/index.d.ts +2 -0
- package/model/src/lib/system/system.private.d.ts +63 -0
- package/model/src/lib/system/system.private.module.d.ts +70 -0
- package/oidc/package.json +10 -10
- package/package.json +12 -12
- package/src/lib/firestore/snapshot/snapshot.field.encrypt.d.ts +21 -2
- package/test/package.json +11 -11
- package/twilio/package.json +8 -8
- package/zoho/README.md +30 -0
- package/zoho/index.cjs.js +200 -36
- package/zoho/index.esm.js +193 -35
- package/zoho/package.json +11 -10
- package/zoho/src/lib/index.d.ts +1 -0
- package/zoho/src/lib/zoho.accounts.firebase.d.ts +10 -3
- package/zoho/src/lib/zoho.accounts.firebase.module.d.ts +51 -0
- package/zoho/src/lib/zoho.accounts.firebase.system.d.ts +51 -8
package/model/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/model",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/model": "13.
|
|
10
|
-
"@dereekb/nestjs": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/analytics": "13.34.0",
|
|
6
|
+
"@dereekb/date": "13.34.0",
|
|
7
|
+
"@dereekb/firebase": "13.34.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.34.0",
|
|
9
|
+
"@dereekb/model": "13.34.0",
|
|
10
|
+
"@dereekb/nestjs": "13.34.0",
|
|
11
|
+
"@dereekb/rxjs": "13.34.0",
|
|
12
|
+
"@dereekb/util": "13.34.0",
|
|
13
13
|
"@nestjs/common": "^11.1.19",
|
|
14
14
|
"@nestjs/config": "^4.0.4",
|
|
15
15
|
"archiver": "^7.0.1",
|
package/model/src/lib/index.d.ts
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { AbstractFirestoreDocument, type CollectionReference, type FirestoreCollection, type FirestoreContext, type SystemState, type SystemStateStoredData, type SystemStateStoredDataConverterMap, type SystemStateUnknownTypeBehavior } from '@dereekb/firebase';
|
|
3
|
+
/**
|
|
4
|
+
* Provides access to the {@link SystemStatePrivate} collection.
|
|
5
|
+
*
|
|
6
|
+
* Provided ONLY by a server module. This is deliberately never declared on an app's shared
|
|
7
|
+
* `FirestoreCollections` class: `provideAppFirestoreCollections()` hard-codes a
|
|
8
|
+
* `(context: FirestoreContext) => T` factory, which cannot carry the encryption secrets a private
|
|
9
|
+
* converter needs — so the private collection structurally cannot leak into the client-shared
|
|
10
|
+
* collection set.
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class SystemStatePrivateFirestoreCollections {
|
|
13
|
+
abstract readonly systemStatePrivateCollection: SystemStatePrivateFirestoreCollection;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Firestore model identity for {@link SystemStatePrivate} documents.
|
|
17
|
+
*
|
|
18
|
+
* The server-only counterpart of `systemStateIdentity` from `@dereekb/firebase`. Same document
|
|
19
|
+
* shape, same "document id is the type identifier" singleton convention — but a separate `sysp`
|
|
20
|
+
* collection, so a secret-bearing document is never reachable through the client-shared SystemState
|
|
21
|
+
* collection or the generic admin model service registered against it.
|
|
22
|
+
*
|
|
23
|
+
* This is a server-side only model. It has no provisions for client-side access, and deliberately
|
|
24
|
+
* has NO `firestore.rules` match block — the implicit default-deny is what denies every client, and
|
|
25
|
+
* a block here would only create an opportunity to grant access by accident.
|
|
26
|
+
*/
|
|
27
|
+
export declare const systemStatePrivateIdentity: import("@dereekb/firebase").RootFirestoreModelIdentity<"systemStatePrivate", "sysp">;
|
|
28
|
+
export declare class SystemStatePrivateDocument<T extends SystemStateStoredData = SystemStateStoredData> extends AbstractFirestoreDocument<SystemState<T>, SystemStatePrivateDocument<T>, typeof systemStatePrivateIdentity> {
|
|
29
|
+
get modelIdentity(): import("@dereekb/firebase").RootFirestoreModelIdentity<"systemStatePrivate", "sysp">;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns the Firestore {@link CollectionReference} for {@link SystemStatePrivate} documents.
|
|
33
|
+
*
|
|
34
|
+
* @param context - The Firestore context to create the collection reference from.
|
|
35
|
+
* @returns The typed collection reference.
|
|
36
|
+
*/
|
|
37
|
+
export declare function systemStatePrivateCollectionReference(context: FirestoreContext): CollectionReference<SystemState>;
|
|
38
|
+
export type SystemStatePrivateFirestoreCollection<T extends SystemStateStoredData = SystemStateStoredData> = FirestoreCollection<SystemState<T>, SystemStatePrivateDocument<T>>;
|
|
39
|
+
/**
|
|
40
|
+
* Configuration for creating a {@link SystemStatePrivateFirestoreCollection}.
|
|
41
|
+
*/
|
|
42
|
+
export interface SystemStatePrivateFirestoreCollectionConfig {
|
|
43
|
+
readonly firestoreContext: FirestoreContext;
|
|
44
|
+
/**
|
|
45
|
+
* Server-only stored-data converters, keyed by SystemStateTypeIdentifier.
|
|
46
|
+
*/
|
|
47
|
+
readonly converters: SystemStateStoredDataConverterMap;
|
|
48
|
+
/**
|
|
49
|
+
* Behavior when a document's type has no registered converter. Defaults to `error`.
|
|
50
|
+
*
|
|
51
|
+
* A private type that was not registered must never silently pass through — that would read a
|
|
52
|
+
* secret-bearing document without its field mapping, returning encrypted fields as raw ciphertext
|
|
53
|
+
* and dates as raw `Timestamp`s, with nothing to signal it.
|
|
54
|
+
*/
|
|
55
|
+
readonly unknownTypeBehavior?: Maybe<SystemStateUnknownTypeBehavior>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a {@link SystemStatePrivateFirestoreCollection}.
|
|
59
|
+
*
|
|
60
|
+
* @param config - The Firestore context, converters, and unknown-type behavior.
|
|
61
|
+
* @returns The configured server-only SystemState collection.
|
|
62
|
+
*/
|
|
63
|
+
export declare function systemStatePrivateFirestoreCollection(config: SystemStatePrivateFirestoreCollectionConfig): SystemStatePrivateFirestoreCollection;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ModuleMetadata, type Provider } from '@nestjs/common';
|
|
2
|
+
import { type Maybe } from '@dereekb/util';
|
|
3
|
+
import { type FirestoreContext, type SystemStateStoredDataConverterMap, type SystemStateUnknownTypeBehavior } from '@dereekb/firebase';
|
|
4
|
+
import { SystemStatePrivateFirestoreCollections } from './system.private';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for the {@link SystemStatePrivateFirestoreCollections} provider.
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class SystemStatePrivateModuleConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Server-only stored-data converters, keyed by SystemStateTypeIdentifier.
|
|
11
|
+
*
|
|
12
|
+
* Secret-bearing converters are built by the app, which owns its own `ConfigService`/env access —
|
|
13
|
+
* the framework never sees the secrets.
|
|
14
|
+
*/
|
|
15
|
+
abstract readonly converters: SystemStateStoredDataConverterMap;
|
|
16
|
+
/**
|
|
17
|
+
* Behavior when a document's type has no registered converter. Defaults to `error`.
|
|
18
|
+
*/
|
|
19
|
+
abstract readonly unknownTypeBehavior?: Maybe<SystemStateUnknownTypeBehavior>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates the {@link SystemStatePrivateFirestoreCollections}.
|
|
23
|
+
*
|
|
24
|
+
* This is a bespoke provider rather than a `provideAppFirestoreCollections()` entry because that
|
|
25
|
+
* helper hard-codes a `(context: FirestoreContext) => T` factory and cannot carry the converters
|
|
26
|
+
* (and therefore the encryption secrets) this collection needs.
|
|
27
|
+
*
|
|
28
|
+
* @param firestoreContext - The Firestore context.
|
|
29
|
+
* @param config - The module configuration carrying the server-only converters.
|
|
30
|
+
* @returns The server-only collections.
|
|
31
|
+
*/
|
|
32
|
+
export declare function systemStatePrivateFirestoreCollectionsFactory(firestoreContext: FirestoreContext, config: SystemStatePrivateModuleConfig): SystemStatePrivateFirestoreCollections;
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for {@link appSystemStatePrivateModuleMetadata}.
|
|
35
|
+
*/
|
|
36
|
+
export interface ProvideAppSystemStatePrivateModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
|
|
37
|
+
/**
|
|
38
|
+
* Provider for {@link SystemStatePrivateModuleConfig}.
|
|
39
|
+
*
|
|
40
|
+
* The app declares this so it can inject its own `ConfigService`/env service and build each
|
|
41
|
+
* secret-bearing converter itself.
|
|
42
|
+
*/
|
|
43
|
+
readonly configProvider: Provider;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Generates NestJS {@link ModuleMetadata} for an app's server-only SystemState module.
|
|
47
|
+
*
|
|
48
|
+
* IMPORTANT: do NOT add {@link SystemStatePrivateFirestoreCollections} to an app's shared
|
|
49
|
+
* `FirestoreCollections` class. Keeping it on its own provider is what stops the private collection
|
|
50
|
+
* from reaching client-shared code.
|
|
51
|
+
*
|
|
52
|
+
* @param config - The app's config provider plus optional additional module metadata.
|
|
53
|
+
* @returns Module metadata ready for the `@Module()` decorator.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* @Module(
|
|
58
|
+
* appSystemStatePrivateModuleMetadata({
|
|
59
|
+
* configProvider: {
|
|
60
|
+
* provide: SystemStatePrivateModuleConfig,
|
|
61
|
+
* useFactory: myConfigFactory,
|
|
62
|
+
* inject: [ConfigService, FirebaseServerEnvService]
|
|
63
|
+
* },
|
|
64
|
+
* imports: [ConfigModule]
|
|
65
|
+
* })
|
|
66
|
+
* )
|
|
67
|
+
* export class AppSystemStatePrivateModule {}
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function appSystemStatePrivateModuleMetadata(config: ProvideAppSystemStatePrivateModuleMetadataConfig): ModuleMetadata;
|
package/oidc/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/oidc",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/model": "13.
|
|
10
|
-
"@dereekb/nestjs": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
13
|
-
"@dereekb/zoho": "13.
|
|
5
|
+
"@dereekb/analytics": "13.34.0",
|
|
6
|
+
"@dereekb/date": "13.34.0",
|
|
7
|
+
"@dereekb/firebase": "13.34.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.34.0",
|
|
9
|
+
"@dereekb/model": "13.34.0",
|
|
10
|
+
"@dereekb/nestjs": "13.34.0",
|
|
11
|
+
"@dereekb/rxjs": "13.34.0",
|
|
12
|
+
"@dereekb/util": "13.34.0",
|
|
13
|
+
"@dereekb/zoho": "13.34.0",
|
|
14
14
|
"@nestjs/common": "^11.1.19",
|
|
15
15
|
"@nestjs/config": "^4.0.4",
|
|
16
16
|
"express": "^5.2.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"exports": {
|
|
6
6
|
"./test": {
|
|
@@ -69,17 +69,17 @@
|
|
|
69
69
|
"types": "./src/index.d.ts",
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@cantoo/pdf-lib": "^2.6.5",
|
|
72
|
-
"@dereekb/analytics": "13.
|
|
73
|
-
"@dereekb/calcom": "13.
|
|
74
|
-
"@dereekb/date": "13.
|
|
75
|
-
"@dereekb/dbx-core": "13.
|
|
76
|
-
"@dereekb/discord": "13.
|
|
77
|
-
"@dereekb/firebase": "13.
|
|
78
|
-
"@dereekb/model": "13.
|
|
79
|
-
"@dereekb/nestjs": "13.
|
|
80
|
-
"@dereekb/rxjs": "13.
|
|
81
|
-
"@dereekb/util": "13.
|
|
82
|
-
"@dereekb/zoho": "13.
|
|
72
|
+
"@dereekb/analytics": "13.34.0",
|
|
73
|
+
"@dereekb/calcom": "13.34.0",
|
|
74
|
+
"@dereekb/date": "13.34.0",
|
|
75
|
+
"@dereekb/dbx-core": "13.34.0",
|
|
76
|
+
"@dereekb/discord": "13.34.0",
|
|
77
|
+
"@dereekb/firebase": "13.34.0",
|
|
78
|
+
"@dereekb/model": "13.34.0",
|
|
79
|
+
"@dereekb/nestjs": "13.34.0",
|
|
80
|
+
"@dereekb/rxjs": "13.34.0",
|
|
81
|
+
"@dereekb/util": "13.34.0",
|
|
82
|
+
"@dereekb/zoho": "13.34.0",
|
|
83
83
|
"@google-cloud/firestore": "^7.11.6",
|
|
84
84
|
"@google-cloud/storage": "^7.19.0",
|
|
85
85
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
@@ -14,6 +14,18 @@ export interface FirestoreEncryptedFieldConfig<T> {
|
|
|
14
14
|
* Default value when the field is missing from Firestore.
|
|
15
15
|
*/
|
|
16
16
|
readonly default: GetterOrValue<T>;
|
|
17
|
+
/**
|
|
18
|
+
* Called when a stored value is present but cannot be decrypted — a wrong or rotated key, a
|
|
19
|
+
* corrupt value, or data written before the field was encrypted. Its return value is used in
|
|
20
|
+
* place of the decrypted value.
|
|
21
|
+
*
|
|
22
|
+
* When omitted the decode throws, which is the correct behavior for a field of record: a
|
|
23
|
+
* credential that silently becomes `undefined` is far worse than a loud failure.
|
|
24
|
+
*
|
|
25
|
+
* Only supply this for data that can be regenerated — a cache. See the Zoho access token cache,
|
|
26
|
+
* where an undecryptable entry is a cache miss and the next call re-mints the token.
|
|
27
|
+
*/
|
|
28
|
+
readonly onDecodeFailure?: Maybe<(error: unknown, data: string) => T>;
|
|
17
29
|
}
|
|
18
30
|
/**
|
|
19
31
|
* Configuration for an optional encrypted Firestore field.
|
|
@@ -25,13 +37,20 @@ export interface OptionalFirestoreEncryptedFieldConfig<T> {
|
|
|
25
37
|
* Secret source for the encryption key.
|
|
26
38
|
*/
|
|
27
39
|
readonly secret: GetterOrValue<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Called when a stored value is present but cannot be decrypted. See
|
|
42
|
+
* {@link FirestoreEncryptedFieldConfig.onDecodeFailure}. When omitted the decode throws.
|
|
43
|
+
*/
|
|
44
|
+
readonly onDecodeFailure?: Maybe<(error: unknown, data: string) => Maybe<T>>;
|
|
28
45
|
}
|
|
29
46
|
/**
|
|
30
47
|
* Creates a Firestore field mapping that encrypts/decrypts a JSON-serializable value
|
|
31
48
|
* using AES-256-GCM. The value is stored in Firestore as a base64-encoded string.
|
|
32
49
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
50
|
+
* IMPORTANT: there is NO key rotation. `resolveEncryptionKey()` reads and validates the secret once,
|
|
51
|
+
* here at construction, and closes over the resulting key — changing the secret afterwards orphans
|
|
52
|
+
* every value already written with the old one. Rotation is only survivable for fields that supply
|
|
53
|
+
* {@link FirestoreEncryptedFieldConfig.onDecodeFailure} and hold regenerable data.
|
|
35
54
|
*
|
|
36
55
|
* @param config - Encryption field configuration.
|
|
37
56
|
* @returns A field mapping configuration for encrypted values.
|
package/test/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/test",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/firebase-server/oidc": "13.
|
|
10
|
-
"@dereekb/model": "13.
|
|
11
|
-
"@dereekb/nestjs": "13.
|
|
12
|
-
"@dereekb/rxjs": "13.
|
|
13
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/analytics": "13.34.0",
|
|
6
|
+
"@dereekb/date": "13.34.0",
|
|
7
|
+
"@dereekb/firebase": "13.34.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.34.0",
|
|
9
|
+
"@dereekb/firebase-server/oidc": "13.34.0",
|
|
10
|
+
"@dereekb/model": "13.34.0",
|
|
11
|
+
"@dereekb/nestjs": "13.34.0",
|
|
12
|
+
"@dereekb/rxjs": "13.34.0",
|
|
13
|
+
"@dereekb/util": "13.34.0",
|
|
14
14
|
"@google-cloud/firestore": "^7.11.6",
|
|
15
15
|
"@google-cloud/storage": "^7.19.0",
|
|
16
16
|
"@nestjs/common": "^11.1.19",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"supertest": "^7.2.2"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@dereekb/nestjs": "13.
|
|
26
|
+
"@dereekb/nestjs": "13.34.0"
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
29
|
"./package.json": "./package.json",
|
package/twilio/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/twilio",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/date": "13.
|
|
6
|
-
"@dereekb/firebase": "13.
|
|
7
|
-
"@dereekb/firebase-server": "13.
|
|
8
|
-
"@dereekb/model": "13.
|
|
9
|
-
"@dereekb/nestjs": "13.
|
|
10
|
-
"@dereekb/rxjs": "13.
|
|
11
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/date": "13.34.0",
|
|
6
|
+
"@dereekb/firebase": "13.34.0",
|
|
7
|
+
"@dereekb/firebase-server": "13.34.0",
|
|
8
|
+
"@dereekb/model": "13.34.0",
|
|
9
|
+
"@dereekb/nestjs": "13.34.0",
|
|
10
|
+
"@dereekb/rxjs": "13.34.0",
|
|
11
|
+
"@dereekb/util": "13.34.0"
|
|
12
12
|
},
|
|
13
13
|
"exports": {
|
|
14
14
|
"./package.json": "./package.json",
|
package/zoho/README.md
CHANGED
|
@@ -6,3 +6,33 @@ for the generic `UserExternalConnection` OAuth framework — a controller, servi
|
|
|
6
6
|
mount a per-user Zoho connect flow at `/oauth/zoho`. The two are independent: the cache holds one
|
|
7
7
|
token for the whole app, while the connect flow stores per-user credentials on the private
|
|
8
8
|
connection document.
|
|
9
|
+
|
|
10
|
+
## Access token cache: server-only, encrypted at rest
|
|
11
|
+
|
|
12
|
+
The cached access token is a credential, so back the cache with a **server-only** collection —
|
|
13
|
+
`systemStatePrivateFirestoreCollection()` from `@dereekb/firebase-server/model`, which stores at
|
|
14
|
+
`/sysp` and has no `firestore.rules` match block:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const collections = systemStatePrivateFirestoreCollection({
|
|
18
|
+
firestoreContext,
|
|
19
|
+
converters: {
|
|
20
|
+
...zohoAccessTokenSystemStatePrivateConverterEntry({ encryptionSecret })
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const cacheService = firebaseZohoAccountsAccessTokenCacheService(collections);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Supply `encryptionSecret` from `ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET` (see
|
|
28
|
+
`zohoAccessTokenEncryptionSecretFactory`). Only the `accessToken` string is encrypted — `expiresAt`
|
|
29
|
+
and the other metadata stay plaintext so expired entries can be filtered without decrypting.
|
|
30
|
+
|
|
31
|
+
**Do not** register `zohoAccessTokenSystemStateDataConverter` (now deprecated) in an app's
|
|
32
|
+
client-shared `SystemStateStoredDataConverterMap`. That converter stores the token in plaintext, and
|
|
33
|
+
importing it from client-shared code drags `@dereekb/firebase-server` — and therefore
|
|
34
|
+
`firebase-admin`, `@nestjs/*` and Node builtins — into the browser bundle, which is a hard
|
|
35
|
+
resolution error rather than something a bundler can tree-shake away.
|
|
36
|
+
|
|
37
|
+
Rotating `ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET` is survivable: entries written under the old key
|
|
38
|
+
degrade to a cache miss and the next Zoho call re-mints a token.
|