@dereekb/firebase-server 13.31.0 → 13.33.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/index.cjs.default.js +1 -0
- package/calcom/index.cjs.js +1451 -0
- package/calcom/index.cjs.mjs +2 -0
- package/calcom/index.d.ts +1 -0
- package/calcom/index.esm.js +1437 -0
- package/calcom/package.json +29 -0
- package/calcom/src/index.d.ts +1 -0
- package/calcom/src/lib/calcom.oauth.connection.cache.d.ts +49 -0
- package/calcom/src/lib/calcom.oauth.connection.config.d.ts +75 -0
- package/calcom/src/lib/calcom.oauth.connection.context.d.ts +84 -0
- package/calcom/src/lib/calcom.oauth.connection.controller.d.ts +18 -0
- package/calcom/src/lib/calcom.oauth.connection.module.d.ts +55 -0
- package/calcom/src/lib/calcom.oauth.connection.service.d.ts +33 -0
- package/calcom/src/lib/index.d.ts +6 -0
- package/discord/index.cjs.default.js +1 -0
- package/discord/index.cjs.js +761 -0
- package/discord/index.cjs.mjs +2 -0
- package/discord/index.d.ts +1 -0
- package/discord/index.esm.js +753 -0
- package/discord/package.json +29 -0
- package/discord/src/index.d.ts +1 -0
- package/discord/src/lib/discord.oauth.connection.config.d.ts +69 -0
- package/discord/src/lib/discord.oauth.connection.controller.d.ts +18 -0
- package/discord/src/lib/discord.oauth.connection.module.d.ts +39 -0
- package/discord/src/lib/discord.oauth.connection.service.d.ts +43 -0
- package/discord/src/lib/index.d.ts +4 -0
- package/index.cjs.js +14 -3
- package/index.esm.js +14 -3
- package/mailgun/package.json +9 -9
- package/mcp/package.json +11 -11
- package/model/index.cjs.js +11077 -6072
- package/model/index.esm.js +11020 -6081
- package/model/package.json +10 -9
- package/model/src/lib/index.d.ts +1 -0
- package/model/src/lib/mailgun/index.d.ts +1 -0
- package/model/src/lib/mailgun/notification.healthcheck.mailgun.d.ts +115 -0
- package/model/src/lib/notification/index.d.ts +2 -0
- package/model/src/lib/notification/notification.action.server.d.ts +42 -3
- package/model/src/lib/notification/notification.error.d.ts +31 -0
- package/model/src/lib/notification/notification.healthcheck.d.ts +39 -0
- package/model/src/lib/notification/notification.healthcheck.service.d.ts +118 -0
- package/model/src/lib/notification/notification.send.service.d.ts +22 -0
- package/model/src/lib/userexternalconnection/index.d.ts +8 -0
- package/model/src/lib/userexternalconnection/oauth/index.d.ts +7 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.config.d.ts +110 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.controller.d.ts +68 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.error.d.ts +29 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.refresh.d.ts +24 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.registry.d.ts +54 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.service.d.ts +216 -0
- package/model/src/lib/userexternalconnection/oauth/userexternalconnection.oauth.state.d.ts +114 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.accessor.service.d.ts +111 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.action.server.d.ts +172 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.error.d.ts +44 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.module.d.ts +129 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.private.d.ts +192 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.reader.service.d.ts +157 -0
- package/model/src/lib/userexternalconnection/userexternalconnection.refresh.service.d.ts +46 -0
- package/oidc/index.cjs.js +104 -78
- package/oidc/index.esm.js +104 -78
- package/oidc/package.json +10 -10
- package/package.json +24 -10
- package/src/lib/env/env.config.d.ts +15 -0
- package/src/lib/env/env.service.d.ts +7 -0
- package/src/lib/nest/env/env.service.d.ts +1 -0
- package/test/index.cjs.js +14 -2
- package/test/index.esm.js +15 -3
- package/test/package.json +11 -11
- package/test/src/lib/firebase/firebase.admin.auth.d.ts +1 -1
- package/twilio/package.json +8 -8
- package/zoho/README.md +8 -0
- package/zoho/index.cjs.js +1223 -33
- package/zoho/index.esm.js +1212 -36
- package/zoho/package.json +12 -9
- package/zoho/src/lib/index.d.ts +5 -0
- package/zoho/src/lib/zoho.oauth.connection.cache.d.ts +45 -0
- package/zoho/src/lib/zoho.oauth.connection.config.d.ts +80 -0
- package/zoho/src/lib/zoho.oauth.connection.controller.d.ts +18 -0
- package/zoho/src/lib/zoho.oauth.connection.module.d.ts +43 -0
- package/zoho/src/lib/zoho.oauth.connection.service.d.ts +107 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type FirebaseAuthUserId, type FirestoreContextReference, type UserExternalConnectionDocument, type UserExternalConnectionEntryStatus, type UserExternalConnectionErrorCode, type UserExternalConnectionFirestoreCollections, type UserExternalConnectionProviderType } from '@dereekb/firebase';
|
|
3
|
+
import { type UserExternalConnectionCredentials, type UserExternalConnectionServerFirestoreCollections } from './userexternalconnection.private';
|
|
4
|
+
/**
|
|
5
|
+
* Context required by {@link userExternalConnectionServerActions}.
|
|
6
|
+
*
|
|
7
|
+
* Carries BOTH halves of the pair. Nothing else in the workspace should hold the private collection.
|
|
8
|
+
*/
|
|
9
|
+
export interface UserExternalConnectionServerActionsContext extends FirestoreContextReference, UserExternalConnectionFirestoreCollections, UserExternalConnectionServerFirestoreCollections {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reference to a {@link UserExternalConnectionServerActions} instance.
|
|
13
|
+
*/
|
|
14
|
+
export interface UserExternalConnectionServerActionsRef {
|
|
15
|
+
readonly userExternalConnectionActions: UserExternalConnectionServerActions;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parameters for connecting a user to a provider.
|
|
19
|
+
*
|
|
20
|
+
* NOTE what is absent: there is no parameter for `status`, `scopes`, `externalAccountId`,
|
|
21
|
+
* `expiresAt`, `connectedAt`, `updatedAt`, or the connected-provider array. Every one of those is
|
|
22
|
+
* derived from `credentials`, so a caller has no way to submit a summary that contradicts the
|
|
23
|
+
* credentials it summarizes. `label` rides on the credentials because it is a fact about the grant.
|
|
24
|
+
*/
|
|
25
|
+
export interface UserExternalConnectionConnectParams {
|
|
26
|
+
readonly uid: FirebaseAuthUserId;
|
|
27
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
28
|
+
readonly credentials: UserExternalConnectionCredentials;
|
|
29
|
+
/**
|
|
30
|
+
* Optional instant to apply the change at. Defaults to now.
|
|
31
|
+
*/
|
|
32
|
+
readonly now?: Maybe<Date>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Parameters for replacing a provider's credentials after a token refresh.
|
|
36
|
+
*/
|
|
37
|
+
export type UserExternalConnectionRefreshCredentialsParams = UserExternalConnectionConnectParams;
|
|
38
|
+
/**
|
|
39
|
+
* Parameters for marking a provider's connection as errored.
|
|
40
|
+
*
|
|
41
|
+
* The stored credentials are retained so the connection can be repaired without a full reconnect.
|
|
42
|
+
*/
|
|
43
|
+
export interface UserExternalConnectionMarkErrorParams {
|
|
44
|
+
readonly uid: FirebaseAuthUserId;
|
|
45
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
46
|
+
readonly error?: Maybe<UserExternalConnectionErrorCode>;
|
|
47
|
+
readonly now?: Maybe<Date>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parameters for disconnecting a user from a provider.
|
|
51
|
+
*/
|
|
52
|
+
export interface UserExternalConnectionDisconnectParams {
|
|
53
|
+
readonly uid: FirebaseAuthUserId;
|
|
54
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
55
|
+
/**
|
|
56
|
+
* Whether to retain a `disconnected` history entry on the public document rather than removing the
|
|
57
|
+
* provider's key. The credentials are removed either way. Defaults to false.
|
|
58
|
+
*/
|
|
59
|
+
readonly retainEntry?: Maybe<boolean>;
|
|
60
|
+
readonly now?: Maybe<Date>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parameters for creating a user's connection document.
|
|
64
|
+
*/
|
|
65
|
+
export interface UserExternalConnectionCreateParams {
|
|
66
|
+
readonly uid: FirebaseAuthUserId;
|
|
67
|
+
/**
|
|
68
|
+
* Optional instant to stamp the new document with. Defaults to now.
|
|
69
|
+
*/
|
|
70
|
+
readonly now?: Maybe<Date>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parameters for deleting a user's entire connection pair.
|
|
74
|
+
*/
|
|
75
|
+
export interface UserExternalConnectionDeleteAllParams {
|
|
76
|
+
readonly uid: FirebaseAuthUserId;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Server-only actions for the UserExternalConnection document pair.
|
|
80
|
+
*
|
|
81
|
+
* This is the ENTIRE write surface, and ONLY the write surface. The two collections are never exposed
|
|
82
|
+
* for independent mutation, so there is no way for a caller to write one document without the other —
|
|
83
|
+
* and therefore no sync, reconciliation, or drift-detection process to maintain.
|
|
84
|
+
*
|
|
85
|
+
* Reading is `UserExternalConnectionAccessor` (raw) or `UserExternalConnectionReader` (the one a
|
|
86
|
+
* consumer wants). A read used to live here too, which meant every path that only needed to look at a
|
|
87
|
+
* user's credentials had to hold the write surface to do it.
|
|
88
|
+
*/
|
|
89
|
+
export declare abstract class UserExternalConnectionServerActions {
|
|
90
|
+
abstract createUserExternalConnection(params: UserExternalConnectionCreateParams): Promise<UserExternalConnectionDocument>;
|
|
91
|
+
abstract connectUserExternalConnection(params: UserExternalConnectionConnectParams): Promise<UserExternalConnectionDocument>;
|
|
92
|
+
abstract refreshUserExternalConnectionCredentials(params: UserExternalConnectionRefreshCredentialsParams): Promise<UserExternalConnectionDocument>;
|
|
93
|
+
abstract markUserExternalConnectionError(params: UserExternalConnectionMarkErrorParams): Promise<UserExternalConnectionDocument>;
|
|
94
|
+
abstract disconnectUserExternalConnection(params: UserExternalConnectionDisconnectParams): Promise<UserExternalConnectionDocument>;
|
|
95
|
+
abstract deleteAllUserExternalConnectionsForUser(params: UserExternalConnectionDeleteAllParams): Promise<void>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The single write a per-user token cache needs: persisting credentials the provider just issued.
|
|
99
|
+
*
|
|
100
|
+
* Narrower than {@link UserExternalConnectionServerActions} on purpose. A token cache has no business
|
|
101
|
+
* connecting, disconnecting, or deleting anything, and the resolved document is of no use to it — hence
|
|
102
|
+
* the unconstrained result, which also means a caller can satisfy this without producing a document it
|
|
103
|
+
* would only throw away.
|
|
104
|
+
*/
|
|
105
|
+
export interface UserExternalConnectionCredentialsWriter {
|
|
106
|
+
refreshUserExternalConnectionCredentials(params: UserExternalConnectionRefreshCredentialsParams): Promise<unknown>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The two writes a reader performs: persisting a refresh, and recording that a provider rejected the
|
|
110
|
+
* credentials.
|
|
111
|
+
*
|
|
112
|
+
* Also narrower than {@link UserExternalConnectionServerActions} on purpose — a read surface has no
|
|
113
|
+
* business creating or deleting a connection — and for the same reason unconstrained in its results.
|
|
114
|
+
*/
|
|
115
|
+
export interface UserExternalConnectionCredentialsAndFailureWriter extends UserExternalConnectionCredentialsWriter {
|
|
116
|
+
markUserExternalConnectionError(params: UserExternalConnectionMarkErrorParams): Promise<unknown>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Creates a {@link UserExternalConnectionServerActions} bound to the given context.
|
|
120
|
+
*
|
|
121
|
+
* @param context - The context carrying both halves of the connection pair.
|
|
122
|
+
* @returns A concrete UserExternalConnectionServerActions implementation.
|
|
123
|
+
*/
|
|
124
|
+
export declare function userExternalConnectionServerActions(context: UserExternalConnectionServerActionsContext): UserExternalConnectionServerActions;
|
|
125
|
+
/**
|
|
126
|
+
* Creates a function that creates a user's connection document.
|
|
127
|
+
*
|
|
128
|
+
* Only the public half is written: the private half exists to hold credentials, and the paired write
|
|
129
|
+
* creates it on the first connect. Creation runs in a transaction so two concurrent calls cannot both
|
|
130
|
+
* see an absent document and both write one.
|
|
131
|
+
*
|
|
132
|
+
* @param context - The context carrying both halves of the pair.
|
|
133
|
+
* @returns A function that creates the document for a uid, throwing if it already exists.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createUserExternalConnectionFactory(context: UserExternalConnectionServerActionsContext): (params: UserExternalConnectionCreateParams) => Promise<UserExternalConnectionDocument>;
|
|
136
|
+
/**
|
|
137
|
+
* Parameters for the paired write.
|
|
138
|
+
*/
|
|
139
|
+
export interface WriteUserExternalConnectionPairParams {
|
|
140
|
+
readonly uid: FirebaseAuthUserId;
|
|
141
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
142
|
+
/**
|
|
143
|
+
* The outcome the operation produced. Drives BOTH sides of the pair.
|
|
144
|
+
*/
|
|
145
|
+
readonly outcome: UserExternalConnectionEntryStatus;
|
|
146
|
+
/**
|
|
147
|
+
* The credentials the outcome produced. Required for a `connected` outcome; ignored for a
|
|
148
|
+
* `disconnected` one (which always removes the stored credentials).
|
|
149
|
+
*/
|
|
150
|
+
readonly credentials?: Maybe<UserExternalConnectionCredentials>;
|
|
151
|
+
readonly error?: Maybe<UserExternalConnectionErrorCode>;
|
|
152
|
+
readonly retainEntry?: Maybe<boolean>;
|
|
153
|
+
readonly now?: Maybe<Date>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Creates the single function through which every mutation of the connection pair flows.
|
|
157
|
+
*
|
|
158
|
+
* Both documents are loaded from the same transaction, all reads happen before any write (a
|
|
159
|
+
* Firestore transaction requirement), and both are written with the COMPLETE next value derived from
|
|
160
|
+
* one input. A failure at any point leaves neither document changed.
|
|
161
|
+
*
|
|
162
|
+
* @param context - The context carrying both halves of the pair.
|
|
163
|
+
* @returns A function that applies one provider's outcome to both documents atomically.
|
|
164
|
+
*/
|
|
165
|
+
export declare function writeUserExternalConnectionPairInTransactionFactory(context: UserExternalConnectionServerActionsContext): (params: WriteUserExternalConnectionPairParams) => Promise<UserExternalConnectionDocument>;
|
|
166
|
+
/**
|
|
167
|
+
* Creates a function that deletes a user's entire connection pair in one transaction.
|
|
168
|
+
*
|
|
169
|
+
* @param context - The context carrying both halves of the pair.
|
|
170
|
+
* @returns A function that removes both documents for a uid.
|
|
171
|
+
*/
|
|
172
|
+
export declare function deleteAllUserExternalConnectionsForUserFactory(context: UserExternalConnectionServerActionsContext): (params: UserExternalConnectionDeleteAllParams) => Promise<void>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type FirebaseAuthUserId, type UserExternalConnectionProviderType } from '@dereekb/firebase';
|
|
2
|
+
export declare const USER_EXTERNAL_CONNECTION_PROVIDER_NOT_CONNECTED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_PROVIDER_NOT_CONNECTED";
|
|
3
|
+
export declare const USER_EXTERNAL_CONNECTION_PROVIDER_NOT_ALLOWED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_PROVIDER_NOT_ALLOWED";
|
|
4
|
+
export declare const USER_EXTERNAL_CONNECTION_ALREADY_EXISTS_ERROR_CODE = "USER_EXTERNAL_CONNECTION_ALREADY_EXISTS";
|
|
5
|
+
export declare const USER_EXTERNAL_CONNECTION_CREDENTIALS_EXPIRED_ERROR_CODE = "USER_EXTERNAL_CONNECTION_CREDENTIALS_EXPIRED";
|
|
6
|
+
/**
|
|
7
|
+
* Creates an error indicating the user already has a connection document.
|
|
8
|
+
*
|
|
9
|
+
* There is exactly one per user, so creating a second is a caller mistake rather than a conflict to
|
|
10
|
+
* resolve. Clients that create on page load are expected to skip the call when the document is
|
|
11
|
+
* already loaded, and to treat this code as success when two of them race.
|
|
12
|
+
*
|
|
13
|
+
* @param uid - The user whose document already exists.
|
|
14
|
+
* @returns A precondition-conflict HttpsError.
|
|
15
|
+
*/
|
|
16
|
+
export declare function userExternalConnectionAlreadyExistsError(uid: FirebaseAuthUserId): import("firebase-functions/https").HttpsError;
|
|
17
|
+
/**
|
|
18
|
+
* Creates an error indicating the user has no connection to the requested provider.
|
|
19
|
+
*
|
|
20
|
+
* @param providerType - The provider that was requested.
|
|
21
|
+
* @returns A precondition-conflict HttpsError.
|
|
22
|
+
*/
|
|
23
|
+
export declare function userExternalConnectionProviderNotConnectedError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
|
|
24
|
+
/**
|
|
25
|
+
* Creates an error indicating the user's credentials for a provider have expired and could not be
|
|
26
|
+
* renewed.
|
|
27
|
+
*
|
|
28
|
+
* Distinct from {@link userExternalConnectionProviderNotConnectedError}: the user IS connected and the
|
|
29
|
+
* credentials are still stored, they just cannot be used right now. Either no refresher was configured,
|
|
30
|
+
* the provider has no refresh path, or the refresh itself failed. The remedy for the last of those is a
|
|
31
|
+
* reconnect; for the first two it is a configuration change, so the code is deliberately the same and
|
|
32
|
+
* the distinction is left to the server logs.
|
|
33
|
+
*
|
|
34
|
+
* @param providerType - The provider whose credentials expired.
|
|
35
|
+
* @returns A precondition-conflict HttpsError.
|
|
36
|
+
*/
|
|
37
|
+
export declare function userExternalConnectionCredentialsExpiredError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
|
|
38
|
+
/**
|
|
39
|
+
* Creates an error indicating the requested provider is not one this app allows connecting to.
|
|
40
|
+
*
|
|
41
|
+
* @param providerType - The provider that was requested.
|
|
42
|
+
* @returns A precondition-conflict HttpsError.
|
|
43
|
+
*/
|
|
44
|
+
export declare function userExternalConnectionProviderNotAllowedError(providerType: UserExternalConnectionProviderType): import("firebase-functions/https").HttpsError;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { type InjectionToken, type ModuleMetadata, type Provider } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
import { type Maybe, type Milliseconds } from '@dereekb/util';
|
|
4
|
+
import { type FirestoreContext, type FirestoreContextReference, type UserExternalConnectionFirestoreCollections } from '@dereekb/firebase';
|
|
5
|
+
import { FirebaseServerEnvService } from '@dereekb/firebase-server';
|
|
6
|
+
import { type AES256GCMEncryptionSecret } from '@dereekb/nestjs';
|
|
7
|
+
import { type UserExternalConnectionPrivateConverterConfig, UserExternalConnectionServerFirestoreCollections } from './userexternalconnection.private';
|
|
8
|
+
import { type UserExternalConnectionServerActionsContext } from './userexternalconnection.action.server';
|
|
9
|
+
/**
|
|
10
|
+
* Environment variable name for the external-connection credentials encryption secret
|
|
11
|
+
* (hex-encoded AES-256 key).
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT: there is NO key rotation. `firestoreEncryptedField` resolves and validates the key once
|
|
14
|
+
* at converter construction and closes over it, so changing this value makes every existing
|
|
15
|
+
* `uecp` document permanently undecryptable. Treat it as write-once per environment.
|
|
16
|
+
*/
|
|
17
|
+
export declare const USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET_ENV_KEY = "USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET";
|
|
18
|
+
/**
|
|
19
|
+
* Deterministic secret used when running in a testing environment and no real secret is configured,
|
|
20
|
+
* so specs never need a live credential.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately distinct from the OIDC JWKS testing secret so a leaked emulator blob is attributable.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TESTING_USER_EXTERNAL_CONNECTION_ENCRYPTION_SECRET: AES256GCMEncryptionSecret;
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the UserExternalConnection server module.
|
|
27
|
+
*/
|
|
28
|
+
export declare abstract class UserExternalConnectionModuleConfig {
|
|
29
|
+
abstract readonly userExternalConnectionPrivateConverterConfig: UserExternalConnectionPrivateConverterConfig;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Builds the {@link UserExternalConnectionModuleConfig} from the environment.
|
|
33
|
+
*
|
|
34
|
+
* @param configService - The Nest config service used to read the encryption secret.
|
|
35
|
+
* @param envService - Used to detect a testing environment for the secret fallback.
|
|
36
|
+
* @returns The module configuration.
|
|
37
|
+
* @throws {Error} When the configured secret is invalid outside a testing environment.
|
|
38
|
+
*/
|
|
39
|
+
export declare function userExternalConnectionModuleConfigFactory(configService: ConfigService, envService: FirebaseServerEnvService): UserExternalConnectionModuleConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Creates the {@link UserExternalConnectionServerFirestoreCollections}.
|
|
42
|
+
*
|
|
43
|
+
* This is a bespoke provider rather than a `provideAppFirestoreCollections()` entry because that
|
|
44
|
+
* helper hard-codes a `(context: FirestoreContext) => T` factory and cannot carry the encryption
|
|
45
|
+
* secret this collection needs.
|
|
46
|
+
*
|
|
47
|
+
* @param firestoreContext - The Firestore context.
|
|
48
|
+
* @param config - The module configuration carrying the encryption secret.
|
|
49
|
+
* @returns The server-only collections.
|
|
50
|
+
*/
|
|
51
|
+
export declare function userExternalConnectionServerFirestoreCollectionsFactory(firestoreContext: FirestoreContext, config: UserExternalConnectionModuleConfig): UserExternalConnectionServerFirestoreCollections;
|
|
52
|
+
/**
|
|
53
|
+
* Assembles the {@link UserExternalConnectionServerActionsContext} from the app-supplied public
|
|
54
|
+
* collections and the module-owned private collection.
|
|
55
|
+
*
|
|
56
|
+
* @param appCollections - The app's collections, carrying the public UserExternalConnection collection.
|
|
57
|
+
* @param serverCollections - The module-owned private collection.
|
|
58
|
+
* @returns The assembled server actions context.
|
|
59
|
+
*/
|
|
60
|
+
export declare function userExternalConnectionServerActionsContextFactory(appCollections: UserExternalConnectionFirestoreCollections & FirestoreContextReference, serverCollections: UserExternalConnectionServerFirestoreCollections): UserExternalConnectionServerActionsContext;
|
|
61
|
+
/**
|
|
62
|
+
* NestJS injection token for the assembled {@link UserExternalConnectionServerActionsContext}.
|
|
63
|
+
*/
|
|
64
|
+
export declare const USER_EXTERNAL_CONNECTION_SERVER_ACTIONS_CONTEXT_TOKEN: InjectionToken;
|
|
65
|
+
export interface ProvideAppUserExternalConnectionModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
|
|
66
|
+
/**
|
|
67
|
+
* Module that exports the app's collections token. When provided, it is automatically included in
|
|
68
|
+
* the generated `imports` array.
|
|
69
|
+
*/
|
|
70
|
+
readonly dependencyModule?: Maybe<Required<ModuleMetadata>['imports']['0']>;
|
|
71
|
+
/**
|
|
72
|
+
* Token that resolves the app's collections — anything implementing both
|
|
73
|
+
* `UserExternalConnectionFirestoreCollections` and `FirestoreContextReference`.
|
|
74
|
+
*
|
|
75
|
+
* Taking this as a token is what keeps the package from ever naming an app's collections class.
|
|
76
|
+
*/
|
|
77
|
+
readonly appCollectionsToken: InjectionToken;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Convenience function used to generate ModuleMetadata for an app's UserExternalConnectionModule.
|
|
81
|
+
*
|
|
82
|
+
* By default this module exports:
|
|
83
|
+
* - UserExternalConnectionServerActions
|
|
84
|
+
* - UserExternalConnectionAccessor
|
|
85
|
+
* - UserExternalConnectionServerFirestoreCollections
|
|
86
|
+
* - UserExternalConnectionModuleConfig
|
|
87
|
+
* - UserExternalConnectionStateCoder
|
|
88
|
+
*
|
|
89
|
+
* NOTE what is absent: `UserExternalConnectionReader`. The reader can be configured with a refresher,
|
|
90
|
+
* and the only refresher an app normally wants is backed by the OAuth provider registry — which this
|
|
91
|
+
* module cannot see without importing the provider modules that import it. Provide the reader with
|
|
92
|
+
* `userExternalConnectionReaderProvider()` from wherever the registry is declared.
|
|
93
|
+
*
|
|
94
|
+
* @param config - The module configuration.
|
|
95
|
+
* @returns The assembled {@link ModuleMetadata}.
|
|
96
|
+
*/
|
|
97
|
+
export declare function appUserExternalConnectionModuleMetadata(config: ProvideAppUserExternalConnectionModuleMetadataConfig): ModuleMetadata;
|
|
98
|
+
/**
|
|
99
|
+
* Configuration for {@link userExternalConnectionReaderProvider}.
|
|
100
|
+
*/
|
|
101
|
+
export interface UserExternalConnectionReaderProviderConfig {
|
|
102
|
+
/**
|
|
103
|
+
* Whether to build the reader's refresher from the {@link UserExternalConnectionOAuthProviderRegistry}.
|
|
104
|
+
* Defaults to true.
|
|
105
|
+
*
|
|
106
|
+
* Set false for an app that registers no OAuth provider services — the registry token would not
|
|
107
|
+
* resolve, and a reader with no refresher is still useful for reading.
|
|
108
|
+
*/
|
|
109
|
+
readonly refreshWithOAuthProviderRegistry?: Maybe<boolean>;
|
|
110
|
+
/**
|
|
111
|
+
* Overrides how long before its stated expiration a credential is treated as expired.
|
|
112
|
+
*/
|
|
113
|
+
readonly expirationBuffer?: Maybe<Milliseconds>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates the NestJS provider for the {@link UserExternalConnectionReader}.
|
|
117
|
+
*
|
|
118
|
+
* Declared by the app rather than by the UserExternalConnection module, for the same reason
|
|
119
|
+
* {@link userExternalConnectionOAuthProviderRegistryProvider} is: the reader refreshes through the
|
|
120
|
+
* registry, and the registry can only be assembled somewhere that is able to import the provider
|
|
121
|
+
* modules — each of which imports the UserExternalConnection module. Put this beside the registry
|
|
122
|
+
* provider.
|
|
123
|
+
*
|
|
124
|
+
* @param config - Optional configuration. By default the reader refreshes through the registry.
|
|
125
|
+
* @returns The NestJS provider.
|
|
126
|
+
*
|
|
127
|
+
* @__NO_SIDE_EFFECTS__
|
|
128
|
+
*/
|
|
129
|
+
export declare function userExternalConnectionReaderProvider(config?: Maybe<UserExternalConnectionReaderProviderConfig>): Provider;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { type ISO8601DateString, type Maybe } from '@dereekb/util';
|
|
2
|
+
import { AbstractFirestoreDocument, type CollectionReference, type FirebaseAuthUserId, type FirestoreCollection, type FirestoreContext, type UserExternalConnectionCapability, type UserExternalConnectionExternalAccountId, type UserExternalConnectionGrantSummary, type UserExternalConnectionProviderType, type UserRelated, type UserRelatedById } from '@dereekb/firebase';
|
|
3
|
+
import { type AES256GCMEncryptionSecretSource } from '@dereekb/nestjs';
|
|
4
|
+
/**
|
|
5
|
+
* Provides access to the {@link UserExternalConnectionPrivate} collection.
|
|
6
|
+
*
|
|
7
|
+
* Provided ONLY by the UserExternalConnectionModule. This is deliberately never declared on an
|
|
8
|
+
* app's shared `FirestoreCollections` class: `provideAppFirestoreCollections()` hard-codes a
|
|
9
|
+
* `(context: FirestoreContext) => T` factory, which cannot carry the encryption secret this
|
|
10
|
+
* collection's converter needs — so the private collection structurally cannot leak into the
|
|
11
|
+
* client-shared collection set.
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class UserExternalConnectionServerFirestoreCollections {
|
|
14
|
+
abstract readonly userExternalConnectionPrivateCollection: UserExternalConnectionPrivateFirestoreCollection;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Firestore model identity for {@link UserExternalConnectionPrivate} documents.
|
|
18
|
+
*
|
|
19
|
+
* This is a server-side only model. It has no provisions for client-side access, and deliberately
|
|
20
|
+
* has NO `firestore.rules` match block — the root catch-all `allow read, write: if false;` is what
|
|
21
|
+
* denies every client, and a block here would only create an opportunity to grant access by accident.
|
|
22
|
+
*/
|
|
23
|
+
export declare const userExternalConnectionPrivateIdentity: import("@dereekb/firebase").RootFirestoreModelIdentity<"userExternalConnectionPrivate", "uecp">;
|
|
24
|
+
/**
|
|
25
|
+
* Credentials granted by a third-party provider for a single user.
|
|
26
|
+
*
|
|
27
|
+
* NOTE: every timestamp here is an {@link ISO8601DateString}, NOT a `Date`. The whole credentials
|
|
28
|
+
* map is JSON round-tripped through `encryptValue`/`decryptValue`, so a `Date` would silently come
|
|
29
|
+
* back as a string.
|
|
30
|
+
*/
|
|
31
|
+
export interface UserExternalConnectionCredentials {
|
|
32
|
+
readonly accessToken: string;
|
|
33
|
+
readonly refreshToken?: Maybe<string>;
|
|
34
|
+
readonly tokenType?: Maybe<string>;
|
|
35
|
+
readonly issuedAt: ISO8601DateString;
|
|
36
|
+
readonly expiresAt?: Maybe<ISO8601DateString>;
|
|
37
|
+
readonly scopes?: Maybe<UserExternalConnectionCapability[]>;
|
|
38
|
+
readonly externalAccountId?: Maybe<UserExternalConnectionExternalAccountId>;
|
|
39
|
+
readonly label?: Maybe<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Any additional provider-specific values that must be retained to use the credentials.
|
|
42
|
+
*/
|
|
43
|
+
readonly extra?: Maybe<Record<string, Maybe<string | number | boolean>>>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Map of provider type to the credentials stored for that provider.
|
|
47
|
+
*/
|
|
48
|
+
export type UserExternalConnectionCredentialsMap = Record<UserExternalConnectionProviderType, UserExternalConnectionCredentials>;
|
|
49
|
+
/**
|
|
50
|
+
* The server-only half of a user's third-party OAuth connection state.
|
|
51
|
+
*
|
|
52
|
+
* Shares its document id (the user's uid) with the client-readable `UserExternalConnection` in
|
|
53
|
+
* `@dereekb/firebase`, and is only ever written in the same transaction as it. See
|
|
54
|
+
* `writeUserExternalConnectionPairInTransactionFactory`.
|
|
55
|
+
*/
|
|
56
|
+
export interface UserExternalConnectionPrivate extends UserRelated, UserRelatedById {
|
|
57
|
+
/**
|
|
58
|
+
* Per-provider credentials. Stored as ONE encrypted string covering the whole map.
|
|
59
|
+
*
|
|
60
|
+
* A single encrypted field rather than a map of encrypted values: there is no server-side need to
|
|
61
|
+
* query inside credentials, and one field keeps the whole blob atomic with the paired write.
|
|
62
|
+
*/
|
|
63
|
+
cr: UserExternalConnectionCredentialsMap;
|
|
64
|
+
/**
|
|
65
|
+
* Date this document was last updated at.
|
|
66
|
+
*/
|
|
67
|
+
uat: Date;
|
|
68
|
+
}
|
|
69
|
+
export declare class UserExternalConnectionPrivateDocument extends AbstractFirestoreDocument<UserExternalConnectionPrivate, UserExternalConnectionPrivateDocument, typeof userExternalConnectionPrivateIdentity> {
|
|
70
|
+
get modelIdentity(): import("@dereekb/firebase").RootFirestoreModelIdentity<"userExternalConnectionPrivate", "uecp">;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Configuration for creating a {@link UserExternalConnectionPrivate} snapshot converter.
|
|
74
|
+
*/
|
|
75
|
+
export interface UserExternalConnectionPrivateConverterConfig {
|
|
76
|
+
/**
|
|
77
|
+
* Encryption secret source for the credentials field.
|
|
78
|
+
*/
|
|
79
|
+
readonly encryptionSecret: AES256GCMEncryptionSecretSource;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Creates a snapshot converter for {@link UserExternalConnectionPrivate} documents.
|
|
83
|
+
*
|
|
84
|
+
* This is a factory rather than a module-level const because `firestoreEncryptedField` resolves and
|
|
85
|
+
* validates the encryption key eagerly at construction — the secret must be known at runtime.
|
|
86
|
+
*
|
|
87
|
+
* @param config - Encryption configuration for the credentials field.
|
|
88
|
+
* @returns Snapshot converter functions for UserExternalConnectionPrivate documents.
|
|
89
|
+
*/
|
|
90
|
+
export declare function userExternalConnectionPrivateConverter(config: UserExternalConnectionPrivateConverterConfig): import("@dereekb/firebase").SnapshotConverterFunctions<UserExternalConnectionPrivate, Partial<import("@dereekb/util").ReplaceType<UserExternalConnectionPrivate, import("@dereekb/util").MaybeMap<object>, any>>>;
|
|
91
|
+
/**
|
|
92
|
+
* Copies the document id into `uid` on write, so the stored uid can never drift from the document id.
|
|
93
|
+
*/
|
|
94
|
+
export declare const userExternalConnectionPrivateAccessorFactory: import("@dereekb/firebase").InterceptAccessorFactoryFunction<UserExternalConnectionPrivate, import("@dereekb/firebase").DocumentData>;
|
|
95
|
+
/**
|
|
96
|
+
* Returns the Firestore {@link CollectionReference} for {@link UserExternalConnectionPrivate} documents.
|
|
97
|
+
*
|
|
98
|
+
* @param context - The Firestore context to create the collection reference from.
|
|
99
|
+
* @returns The typed collection reference.
|
|
100
|
+
*/
|
|
101
|
+
export declare function userExternalConnectionPrivateCollectionReference(context: FirestoreContext): CollectionReference<UserExternalConnectionPrivate>;
|
|
102
|
+
export type UserExternalConnectionPrivateFirestoreCollection = FirestoreCollection<UserExternalConnectionPrivate, UserExternalConnectionPrivateDocument>;
|
|
103
|
+
/**
|
|
104
|
+
* Configuration for creating a {@link UserExternalConnectionPrivateFirestoreCollection}.
|
|
105
|
+
*/
|
|
106
|
+
export interface UserExternalConnectionPrivateFirestoreCollectionConfig extends UserExternalConnectionPrivateConverterConfig {
|
|
107
|
+
readonly firestoreContext: FirestoreContext;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Creates a {@link UserExternalConnectionPrivateFirestoreCollection} with encrypted credentials support.
|
|
111
|
+
*
|
|
112
|
+
* @param config - Configuration including the Firestore context and encryption settings.
|
|
113
|
+
* @returns The configured collection.
|
|
114
|
+
*/
|
|
115
|
+
export declare function userExternalConnectionPrivateFirestoreCollection(config: UserExternalConnectionPrivateFirestoreCollectionConfig): UserExternalConnectionPrivateFirestoreCollection;
|
|
116
|
+
/**
|
|
117
|
+
* Projects the client-readable facts out of a set of credentials.
|
|
118
|
+
*
|
|
119
|
+
* This is the ONLY bridge between the private and public halves of the pair. Because the summary
|
|
120
|
+
* entry is derived from this projection, the public document cannot describe scopes, an account, or
|
|
121
|
+
* an expiration that the credentials do not actually carry.
|
|
122
|
+
*
|
|
123
|
+
* @param credentials - The credentials to project.
|
|
124
|
+
* @returns The grant summary the public entry is derived from.
|
|
125
|
+
*/
|
|
126
|
+
export declare function userExternalConnectionGrantSummaryFromCredentials(credentials: UserExternalConnectionCredentials): UserExternalConnectionGrantSummary;
|
|
127
|
+
/**
|
|
128
|
+
* Input for {@link mergeRefreshedUserExternalConnectionCredentials}.
|
|
129
|
+
*/
|
|
130
|
+
export interface MergeRefreshedUserExternalConnectionCredentialsInput {
|
|
131
|
+
/**
|
|
132
|
+
* The credentials that were stored before the refresh.
|
|
133
|
+
*/
|
|
134
|
+
readonly previous: UserExternalConnectionCredentials;
|
|
135
|
+
/**
|
|
136
|
+
* The credentials a refresh produced.
|
|
137
|
+
*/
|
|
138
|
+
readonly refreshed: UserExternalConnectionCredentials;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Merges refreshed credentials over the stored ones, retaining anything the refresh response omitted.
|
|
142
|
+
*
|
|
143
|
+
* Required because the paired write replaces a provider's credentials WHOLESALE (see
|
|
144
|
+
* {@link applyUserExternalConnectionCredentials}) — so persisting a refresh response verbatim erases
|
|
145
|
+
* every field the provider did not resend, while leaving the entry `connected`. Two of those are
|
|
146
|
+
* load-bearing:
|
|
147
|
+
*
|
|
148
|
+
* - `refreshToken`, which Zoho (and any provider that issues one only on first consent) omits on
|
|
149
|
+
* refresh. Losing it yields a connection that can never be refreshed again and does not look broken.
|
|
150
|
+
* - `extra`, which carries the values needed to USE the credentials — Zoho's `apiDomain` and the
|
|
151
|
+
* `accountsServer` datacenter a later refresh must be sent to. Dropping `accountsServer` breaks the
|
|
152
|
+
* refresh AFTER this one, which is considerably harder to attribute.
|
|
153
|
+
*
|
|
154
|
+
* `extra` is merged key-by-key rather than replaced, so a refresh that resends only `apiDomain` still
|
|
155
|
+
* updates it without discarding the rest. Keys the refresh left UNDEFINED are dropped before merging,
|
|
156
|
+
* because an object literal built with an absent optional field carries the key with an undefined value
|
|
157
|
+
* and a plain spread would use it to erase the stored one. A refresh that means to CLEAR a key can
|
|
158
|
+
* still say so with an explicit null.
|
|
159
|
+
*
|
|
160
|
+
* The generalization of `AbstractUserExternalConnectionOAuthService.credentialsRetainingStoredRefreshToken`,
|
|
161
|
+
* which does the same thing for the authorization-code exchange.
|
|
162
|
+
*
|
|
163
|
+
* @param input - The stored credentials and the refreshed ones.
|
|
164
|
+
* @returns The credentials to persist.
|
|
165
|
+
*/
|
|
166
|
+
export declare function mergeRefreshedUserExternalConnectionCredentials(input: MergeRefreshedUserExternalConnectionCredentialsInput): UserExternalConnectionCredentials;
|
|
167
|
+
/**
|
|
168
|
+
* Input for {@link applyUserExternalConnectionCredentials}.
|
|
169
|
+
*/
|
|
170
|
+
export interface ApplyUserExternalConnectionCredentialsInput {
|
|
171
|
+
/**
|
|
172
|
+
* The currently stored private document, when one exists.
|
|
173
|
+
*/
|
|
174
|
+
readonly current?: Maybe<UserExternalConnectionPrivate>;
|
|
175
|
+
readonly uid: FirebaseAuthUserId;
|
|
176
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
177
|
+
/**
|
|
178
|
+
* The next credentials for this provider, or null to remove the provider's key entirely.
|
|
179
|
+
*/
|
|
180
|
+
readonly credentials: Maybe<UserExternalConnectionCredentials>;
|
|
181
|
+
readonly now: Date;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Applies a single provider's credentials and returns the COMPLETE next private document.
|
|
185
|
+
*
|
|
186
|
+
* The counterpart of `applyUserExternalConnectionEntry()` — both are always called with the same
|
|
187
|
+
* provider and instant inside one transaction.
|
|
188
|
+
*
|
|
189
|
+
* @param input - The current document plus the credentials to apply.
|
|
190
|
+
* @returns The next UserExternalConnectionPrivate value to write.
|
|
191
|
+
*/
|
|
192
|
+
export declare function applyUserExternalConnectionCredentials(input: ApplyUserExternalConnectionCredentialsInput): UserExternalConnectionPrivate;
|