camusdb 0.1.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/LICENSE +21 -0
- package/README.md +950 -0
- package/dist/auth/credentials.d.ts +30 -0
- package/dist/auth/credentials.d.ts.map +1 -0
- package/dist/auth/credentials.js +23 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/login-client.d.ts +37 -0
- package/dist/auth/login-client.d.ts.map +1 -0
- package/dist/auth/login-client.js +8 -0
- package/dist/auth/login-client.js.map +1 -0
- package/dist/auth/rest-login-client.d.ts +17 -0
- package/dist/auth/rest-login-client.d.ts.map +1 -0
- package/dist/auth/rest-login-client.js +71 -0
- package/dist/auth/rest-login-client.js.map +1 -0
- package/dist/auth/token-provider.d.ts +123 -0
- package/dist/auth/token-provider.d.ts.map +1 -0
- package/dist/auth/token-provider.js +209 -0
- package/dist/auth/token-provider.js.map +1 -0
- package/dist/backup.d.ts +98 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +153 -0
- package/dist/backup.js.map +1 -0
- package/dist/cache.d.ts +90 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +121 -0
- package/dist/cache.js.map +1 -0
- package/dist/client.d.ts +406 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +724 -0
- package/dist/client.js.map +1 -0
- package/dist/column-type.d.ts +33 -0
- package/dist/column-type.d.ts.map +1 -0
- package/dist/column-type.js +50 -0
- package/dist/column-type.js.map +1 -0
- package/dist/column-value.d.ts +77 -0
- package/dist/column-value.d.ts.map +1 -0
- package/dist/column-value.js +60 -0
- package/dist/column-value.js.map +1 -0
- package/dist/config.d.ts +181 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +315 -0
- package/dist/config.js.map +1 -0
- package/dist/connection-string.d.ts +45 -0
- package/dist/connection-string.d.ts.map +1 -0
- package/dist/connection-string.js +170 -0
- package/dist/connection-string.js.map +1 -0
- package/dist/endpoint-pool.d.ts +70 -0
- package/dist/endpoint-pool.d.ts.map +1 -0
- package/dist/endpoint-pool.js +120 -0
- package/dist/endpoint-pool.js.map +1 -0
- package/dist/error-codes.d.ts +44 -0
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +43 -0
- package/dist/error-codes.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +33 -0
- package/dist/errors.js.map +1 -0
- package/dist/hlc.d.ts +19 -0
- package/dist/hlc.d.ts.map +1 -0
- package/dist/hlc.js +11 -0
- package/dist/hlc.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +41 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +345 -0
- package/dist/json.js.map +1 -0
- package/dist/object-id.d.ts +39 -0
- package/dist/object-id.d.ts.map +1 -0
- package/dist/object-id.js +99 -0
- package/dist/object-id.js.map +1 -0
- package/dist/options.d.ts +77 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +68 -0
- package/dist/options.js.map +1 -0
- package/dist/prepared/binder.d.ts +20 -0
- package/dist/prepared/binder.d.ts.map +1 -0
- package/dist/prepared/binder.js +32 -0
- package/dist/prepared/binder.js.map +1 -0
- package/dist/prepared/policy.d.ts +81 -0
- package/dist/prepared/policy.d.ts.map +1 -0
- package/dist/prepared/policy.js +164 -0
- package/dist/prepared/policy.js.map +1 -0
- package/dist/query-stream.d.ts +55 -0
- package/dist/query-stream.d.ts.map +1 -0
- package/dist/query-stream.js +94 -0
- package/dist/query-stream.js.map +1 -0
- package/dist/result-set.d.ts +62 -0
- package/dist/result-set.d.ts.map +1 -0
- package/dist/result-set.js +100 -0
- package/dist/result-set.js.map +1 -0
- package/dist/retry.d.ts +38 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +93 -0
- package/dist/retry.js.map +1 -0
- package/dist/routing/advice.d.ts +63 -0
- package/dist/routing/advice.d.ts.map +1 -0
- package/dist/routing/advice.js +61 -0
- package/dist/routing/advice.js.map +1 -0
- package/dist/routing/route-cache.d.ts +53 -0
- package/dist/routing/route-cache.d.ts.map +1 -0
- package/dist/routing/route-cache.js +123 -0
- package/dist/routing/route-cache.js.map +1 -0
- package/dist/routing/router.d.ts +53 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/routing/router.js +89 -0
- package/dist/routing/router.js.map +1 -0
- package/dist/row-source.d.ts +48 -0
- package/dist/row-source.d.ts.map +1 -0
- package/dist/row-source.js +35 -0
- package/dist/row-source.js.map +1 -0
- package/dist/runtime.d.ts +58 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +137 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shared-registry.d.ts +32 -0
- package/dist/shared-registry.d.ts.map +1 -0
- package/dist/shared-registry.js +48 -0
- package/dist/shared-registry.js.map +1 -0
- package/dist/sql-syntax.d.ts +44 -0
- package/dist/sql-syntax.d.ts.map +1 -0
- package/dist/sql-syntax.js +83 -0
- package/dist/sql-syntax.js.map +1 -0
- package/dist/statements.d.ts +15 -0
- package/dist/statements.d.ts.map +1 -0
- package/dist/statements.js +90 -0
- package/dist/statements.js.map +1 -0
- package/dist/transaction.d.ts +117 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +204 -0
- package/dist/transaction.js.map +1 -0
- package/dist/transport/authenticating-transport.d.ts +63 -0
- package/dist/transport/authenticating-transport.d.ts.map +1 -0
- package/dist/transport/authenticating-transport.js +119 -0
- package/dist/transport/authenticating-transport.js.map +1 -0
- package/dist/transport/branch-statements.d.ts +39 -0
- package/dist/transport/branch-statements.d.ts.map +1 -0
- package/dist/transport/branch-statements.js +74 -0
- package/dist/transport/branch-statements.js.map +1 -0
- package/dist/transport/error-text.d.ts +26 -0
- package/dist/transport/error-text.d.ts.map +1 -0
- package/dist/transport/error-text.js +75 -0
- package/dist/transport/error-text.js.map +1 -0
- package/dist/transport/grpc/batcher.d.ts +127 -0
- package/dist/transport/grpc/batcher.d.ts.map +1 -0
- package/dist/transport/grpc/batcher.js +399 -0
- package/dist/transport/grpc/batcher.js.map +1 -0
- package/dist/transport/grpc/codec.d.ts +40 -0
- package/dist/transport/grpc/codec.d.ts.map +1 -0
- package/dist/transport/grpc/codec.js +231 -0
- package/dist/transport/grpc/codec.js.map +1 -0
- package/dist/transport/grpc/grpc-transport.d.ts +115 -0
- package/dist/transport/grpc/grpc-transport.d.ts.map +1 -0
- package/dist/transport/grpc/grpc-transport.js +577 -0
- package/dist/transport/grpc/grpc-transport.js.map +1 -0
- package/dist/transport/grpc/messages.d.ts +221 -0
- package/dist/transport/grpc/messages.d.ts.map +1 -0
- package/dist/transport/grpc/messages.js +56 -0
- package/dist/transport/grpc/messages.js.map +1 -0
- package/dist/transport/grpc/proto.d.ts +71 -0
- package/dist/transport/grpc/proto.d.ts.map +1 -0
- package/dist/transport/grpc/proto.js +67 -0
- package/dist/transport/grpc/proto.js.map +1 -0
- package/dist/transport/http.d.ts +62 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +169 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/ndjson-row-source.d.ts +38 -0
- package/dist/transport/ndjson-row-source.d.ts.map +1 -0
- package/dist/transport/ndjson-row-source.js +197 -0
- package/dist/transport/ndjson-row-source.js.map +1 -0
- package/dist/transport/rest-decode.d.ts +33 -0
- package/dist/transport/rest-decode.d.ts.map +1 -0
- package/dist/transport/rest-decode.js +162 -0
- package/dist/transport/rest-decode.js.map +1 -0
- package/dist/transport/rest-prepared-cache.d.ts +50 -0
- package/dist/transport/rest-prepared-cache.d.ts.map +1 -0
- package/dist/transport/rest-prepared-cache.js +101 -0
- package/dist/transport/rest-prepared-cache.js.map +1 -0
- package/dist/transport/rest-transport.d.ts +88 -0
- package/dist/transport/rest-transport.d.ts.map +1 -0
- package/dist/transport/rest-transport.js +388 -0
- package/dist/transport/rest-transport.js.map +1 -0
- package/dist/transport/transport-pool.d.ts +40 -0
- package/dist/transport/transport-pool.d.ts.map +1 -0
- package/dist/transport/transport-pool.js +48 -0
- package/dist/transport/transport-pool.js.map +1 -0
- package/dist/transport/transport.d.ts +199 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +11 -0
- package/dist/transport/transport.js.map +1 -0
- package/dist/values/decode.d.ts +39 -0
- package/dist/values/decode.d.ts.map +1 -0
- package/dist/values/decode.js +75 -0
- package/dist/values/decode.js.map +1 -0
- package/dist/values/encode.d.ts +45 -0
- package/dist/values/encode.d.ts.map +1 -0
- package/dist/values/encode.js +271 -0
- package/dist/values/encode.js.map +1 -0
- package/dist/values/ticks.d.ts +31 -0
- package/dist/values/ticks.d.ts.map +1 -0
- package/dist/values/ticks.js +52 -0
- package/dist/values/ticks.js.map +1 -0
- package/dist/values/typed.d.ts +67 -0
- package/dist/values/typed.d.ts.map +1 -0
- package/dist/values/typed.js +134 -0
- package/dist/values/typed.js.map +1 -0
- package/dist/values/uuid.d.ts +27 -0
- package/dist/values/uuid.d.ts.map +1 -0
- package/dist/values/uuid.js +76 -0
- package/dist/values/uuid.js.map +1 -0
- package/dist/vector.d.ts +26 -0
- package/dist/vector.d.ts.map +1 -0
- package/dist/vector.js +48 -0
- package/dist/vector.js.map +1 -0
- package/package.json +86 -0
- package/proto/camus_sql.proto +566 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* What the driver was given to authenticate with.
|
|
9
|
+
*
|
|
10
|
+
* Either a user and password pair, which the driver exchanges for a short-lived bearer token and
|
|
11
|
+
* re-exchanges when the token expires, or a token the caller obtained elsewhere and hands over as
|
|
12
|
+
* written. A value with neither is `NO_CREDENTIALS`: the driver sends no `Authorization` header at
|
|
13
|
+
* all, which is correct against a server with authentication off — the default.
|
|
14
|
+
*/
|
|
15
|
+
export interface CamusCredentials {
|
|
16
|
+
/** The user to authenticate as, or undefined when a token was supplied directly. */
|
|
17
|
+
readonly user?: string | undefined;
|
|
18
|
+
/** The user's password. It is only ever sent to `/login`, never with an ordinary statement. */
|
|
19
|
+
readonly password?: string | undefined;
|
|
20
|
+
/** A token obtained elsewhere, used as written. It excludes `user`. */
|
|
21
|
+
readonly accessToken?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
export declare const NO_CREDENTIALS: CamusCredentials;
|
|
24
|
+
export declare function credentialsFromPassword(user: string, password: string): CamusCredentials;
|
|
25
|
+
export declare function credentialsFromToken(accessToken: string): CamusCredentials;
|
|
26
|
+
/** True when there is anything to authenticate with. */
|
|
27
|
+
export declare function hasCredentials(credentials: CamusCredentials): boolean;
|
|
28
|
+
/** True when the driver can mint a fresh token on its own, because it holds the password. */
|
|
29
|
+
export declare function canRenew(credentials: CamusCredentials): boolean;
|
|
30
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEnC,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,eAAO,MAAM,cAAc,EAAE,gBAAoC,CAAC;AAElE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAExF;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAE1E;AAED,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAKrE;AAED,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAE/D"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
export const NO_CREDENTIALS = Object.freeze({});
|
|
8
|
+
export function credentialsFromPassword(user, password) {
|
|
9
|
+
return { user, password };
|
|
10
|
+
}
|
|
11
|
+
export function credentialsFromToken(accessToken) {
|
|
12
|
+
return { accessToken };
|
|
13
|
+
}
|
|
14
|
+
/** True when there is anything to authenticate with. */
|
|
15
|
+
export function hasCredentials(credentials) {
|
|
16
|
+
return ((credentials.user !== undefined && credentials.user.length > 0) ||
|
|
17
|
+
(credentials.accessToken !== undefined && credentials.accessToken.length > 0));
|
|
18
|
+
}
|
|
19
|
+
/** True when the driver can mint a fresh token on its own, because it holds the password. */
|
|
20
|
+
export function canRenew(credentials) {
|
|
21
|
+
return credentials.user !== undefined && credentials.user.length > 0 && credentials.password !== undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/auth/credentials.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,MAAM,CAAC,MAAM,cAAc,GAAqB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAElE,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,QAAgB;IACpE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,OAAO,EAAE,WAAW,EAAE,CAAC;AACzB,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,cAAc,CAAC,WAA6B;IAC1D,OAAO,CACL,CAAC,WAAW,CAAC,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC/D,CAAC,WAAW,CAAC,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAC9E,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,QAAQ,CAAC,WAA6B;IACpD,OAAO,WAAW,CAAC,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,KAAK,SAAS,CAAC;AAC7G,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A minted bearer token and, when the server reported one, how long it is good for.
|
|
9
|
+
*
|
|
10
|
+
* `expiresInMs` is a duration rather than an instant on purpose: the server measures it when it
|
|
11
|
+
* issues the reply, so a client whose clock disagrees with the server's still renews on time. It
|
|
12
|
+
* is `undefined` against a server that predates the field, in which case the driver falls back to
|
|
13
|
+
* its configured token lifetime.
|
|
14
|
+
*/
|
|
15
|
+
export interface CamusLoginResult {
|
|
16
|
+
readonly token: string;
|
|
17
|
+
readonly expiresInMs?: number | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The credential-exchange half of the protocol.
|
|
21
|
+
*
|
|
22
|
+
* It is kept apart from the statement transport because it is the one exchange reachable without a
|
|
23
|
+
* token: REST serves it at `/login`, gRPC on the dedicated `CamusAuth` service, and each transport
|
|
24
|
+
* implements it against its own endpoint. Keeping it a separate interface makes that
|
|
25
|
+
* unauthenticated surface explicit, and lets the token provider be tested without a server.
|
|
26
|
+
*/
|
|
27
|
+
export interface CamusLoginClient {
|
|
28
|
+
/**
|
|
29
|
+
* Exchanges a password for a short-lived bearer token.
|
|
30
|
+
*
|
|
31
|
+
* @throws {CamusError} `CADB0516` when the credentials are rejected.
|
|
32
|
+
*/
|
|
33
|
+
login(endpoint: string, user: string, password: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusLoginResult>;
|
|
34
|
+
/** Revokes a token on the server. */
|
|
35
|
+
logout(endpoint: string, token: string, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=login-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-client.d.ts","sourceRoot":"","sources":["../../src/auth/login-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAE7B,qCAAqC;IACrC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-client.js","sourceRoot":"","sources":["../../src/auth/login-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import type { CamusLoginClient, CamusLoginResult } from './login-client.js';
|
|
8
|
+
/**
|
|
9
|
+
* The login client over the server's HTTP `/login` and `/logout` routes — the only routes exempt
|
|
10
|
+
* from the server's own authentication middleware, and the only place a password is ever put on
|
|
11
|
+
* the wire.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RestLoginClient implements CamusLoginClient {
|
|
14
|
+
login(endpoint: string, user: string, password: string, timeoutSeconds: number, signal?: AbortSignal): Promise<CamusLoginResult>;
|
|
15
|
+
logout(endpoint: string, token: string, timeoutSeconds: number, signal?: AbortSignal): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=rest-login-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest-login-client.d.ts","sourceRoot":"","sources":["../../src/auth/rest-login-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAW5E;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IAChD,KAAK,CACT,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,gBAAgB,CAAC;IA0BtB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB3G"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import { CamusError } from '../errors.js';
|
|
8
|
+
import { CamusErrorCode } from '../error-codes.js';
|
|
9
|
+
import { asNumber, parseLossless } from '../json.js';
|
|
10
|
+
import { readBody, sendHttp, translateErrorBody } from '../transport/http.js';
|
|
11
|
+
/**
|
|
12
|
+
* The login client over the server's HTTP `/login` and `/logout` routes — the only routes exempt
|
|
13
|
+
* from the server's own authentication middleware, and the only place a password is ever put on
|
|
14
|
+
* the wire.
|
|
15
|
+
*/
|
|
16
|
+
export class RestLoginClient {
|
|
17
|
+
async login(endpoint, user, password, timeoutSeconds, signal) {
|
|
18
|
+
const { response } = await sendHttp(undefined, {
|
|
19
|
+
endpoint,
|
|
20
|
+
path: ['login'],
|
|
21
|
+
method: 'POST',
|
|
22
|
+
body: { user, password },
|
|
23
|
+
timeoutSeconds,
|
|
24
|
+
signal,
|
|
25
|
+
});
|
|
26
|
+
const text = await readBody(response);
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
throw translateErrorBody(response.status, text);
|
|
29
|
+
const body = (text.length > 0 ? parseLossless(text) : null);
|
|
30
|
+
if (body === null || body.status !== 'ok' || body.token === undefined || body.token.length === 0) {
|
|
31
|
+
throw new CamusError(body?.code ?? CamusErrorCode.AuthenticationFailed, body?.message ?? 'Authentication failed');
|
|
32
|
+
}
|
|
33
|
+
return { token: body.token, expiresInMs: readExpiryMs(body) };
|
|
34
|
+
}
|
|
35
|
+
async logout(endpoint, token, timeoutSeconds, signal) {
|
|
36
|
+
const { response } = await sendHttp(undefined, {
|
|
37
|
+
endpoint,
|
|
38
|
+
path: ['logout'],
|
|
39
|
+
method: 'POST',
|
|
40
|
+
token,
|
|
41
|
+
timeoutSeconds,
|
|
42
|
+
signal,
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw translateErrorBody(response.status, await readBody(response));
|
|
46
|
+
// The body carries nothing this driver reads, but it must be drained so the connection returns
|
|
47
|
+
// to the pool instead of staying half-read.
|
|
48
|
+
await readBody(response);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How long a minted token is good for.
|
|
53
|
+
*
|
|
54
|
+
* The server-measured duration is preferred, because it is immune to clock skew between client and
|
|
55
|
+
* server. The absolute instant is the fallback, and it is not. A server that reports neither leaves
|
|
56
|
+
* the driver on its configured fallback lifetime. A value that is not positive is treated as
|
|
57
|
+
* absent rather than as an already-dead token.
|
|
58
|
+
*/
|
|
59
|
+
function readExpiryMs(body) {
|
|
60
|
+
const seconds = asNumber(body.expiresInSeconds, 0);
|
|
61
|
+
if (seconds > 0)
|
|
62
|
+
return seconds * 1000;
|
|
63
|
+
const expiresAt = asNumber(body.expiresAtUnixMs, 0);
|
|
64
|
+
if (expiresAt > 0) {
|
|
65
|
+
const remaining = expiresAt - Date.now();
|
|
66
|
+
if (remaining > 0)
|
|
67
|
+
return remaining;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=rest-login-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest-login-client.js","sourceRoot":"","sources":["../../src/auth/rest-login-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAY9E;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,KAAK,CACT,QAAgB,EAChB,IAAY,EACZ,QAAgB,EAChB,cAAsB,EACtB,MAAoB;QAEpB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE;YAC7C,QAAQ;YACR,IAAI,EAAE,CAAC,OAAO,CAAC;YACf,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxB,cAAc;YACd,MAAM;SACP,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEtC,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAElE,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAA6B,CAAC;QAExF,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjG,MAAM,IAAI,UAAU,CAClB,IAAI,EAAE,IAAI,IAAI,cAAc,CAAC,oBAAoB,EACjD,IAAI,EAAE,OAAO,IAAI,uBAAuB,CACzC,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,QAAgB,EAAE,KAAa,EAAE,cAAsB,EAAE,MAAoB;QACxF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE;YAC7C,QAAQ;YACR,IAAI,EAAE,CAAC,QAAQ,CAAC;YAChB,MAAM,EAAE,MAAM;YACd,KAAK;YACL,cAAc;YACd,MAAM;SACP,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,kBAAkB,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEtF,+FAA+F;QAC/F,4CAA4C;QAC5C,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;CACF;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,IAAuB;IAC3C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;IAEnD,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,OAAO,GAAG,IAAI,CAAC;IAEvC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAEpD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,IAAI,SAAS,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;IACtC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import type { CamusCredentials } from './credentials.js';
|
|
8
|
+
import type { CamusLoginClient } from './login-client.js';
|
|
9
|
+
/** The fallback token lifetime, comfortably inside the server's 15-minute default. */
|
|
10
|
+
export declare const DEFAULT_TOKEN_LIFETIME_MS = 600000;
|
|
11
|
+
/**
|
|
12
|
+
* Owns a client's bearer token: mints it from the configured credentials on first use, hands the
|
|
13
|
+
* same token to every request until it goes stale, and mints a new one when it does.
|
|
14
|
+
*
|
|
15
|
+
* One provider is shared by every client that presents the same identity to the same deployment,
|
|
16
|
+
* so a pool of clients performs one login rather than one each. Password verification is
|
|
17
|
+
* deliberately expensive on the server — PBKDF2 with 600,000 iterations by default — and rate
|
|
18
|
+
* limited per account, so a login stampede is exactly what must be avoided.
|
|
19
|
+
*
|
|
20
|
+
* **Single flight.** Concurrent callers that find no usable token await one promise; the winner
|
|
21
|
+
* logs in and the rest observe its result instead of issuing their own login.
|
|
22
|
+
*
|
|
23
|
+
* **Renewal.** The login reply reports how long the token is good for, and the provider renews at
|
|
24
|
+
* 80% of that. Against a server that predates the field it falls back to its own conservative
|
|
25
|
+
* lifetime. That proactive renewal is the primary path. `invalidate`, driven by a `CADB0516` from
|
|
26
|
+
* a real request, is the backstop for what a clock cannot predict: a rotated password, a dropped
|
|
27
|
+
* user, a logout from elsewhere, or a server whose token lifetime is shorter than the fallback.
|
|
28
|
+
*
|
|
29
|
+
* A token supplied directly is used as written and never renewed — the driver has no password to
|
|
30
|
+
* mint a replacement with — so a `CADB0516` on that path reaches the caller rather than a retry.
|
|
31
|
+
*/
|
|
32
|
+
export declare class CamusTokenProvider {
|
|
33
|
+
private static readonly shared;
|
|
34
|
+
/**
|
|
35
|
+
* The process-wide provider for an identity, built on first use. Keyed by credentials rather
|
|
36
|
+
* than by the whole configuration, so clients that differ only in database or timeout share one
|
|
37
|
+
* token.
|
|
38
|
+
*/
|
|
39
|
+
static forKey(key: string, factory: () => CamusTokenProvider): CamusTokenProvider;
|
|
40
|
+
/** @internal Test hook: drops every shared provider. */
|
|
41
|
+
static resetShared(): void;
|
|
42
|
+
/**
|
|
43
|
+
* A per-process random salt for `sharingKey`.
|
|
44
|
+
*
|
|
45
|
+
* The key lives in a map for the life of the process, and an unsalted hash of a password is an
|
|
46
|
+
* offline verifier for anyone who can read process memory. The key only has to be stable inside
|
|
47
|
+
* one process, so a salt costs nothing.
|
|
48
|
+
*/
|
|
49
|
+
private static readonly sharingSalt;
|
|
50
|
+
/**
|
|
51
|
+
* A stable, non-reversible identity for a credential set plus a deployment. It is hashed, and
|
|
52
|
+
* salted per process, so a long-lived map key neither holds a plaintext password nor can be
|
|
53
|
+
* tested against a guessed one outside this process.
|
|
54
|
+
*/
|
|
55
|
+
static sharingKey(credentials: CamusCredentials, deploymentKey: string): string;
|
|
56
|
+
private credentials;
|
|
57
|
+
private token;
|
|
58
|
+
private renewAfter;
|
|
59
|
+
private readonly resolveLoginClient;
|
|
60
|
+
private readonly resolveEndpoint;
|
|
61
|
+
private readonly resolveTimeoutSeconds;
|
|
62
|
+
private readonly lifetimeMs;
|
|
63
|
+
/** The single login in flight, if any. Every racing caller awaits this same promise. */
|
|
64
|
+
private inFlight;
|
|
65
|
+
/** @internal Test hook: the wall clock this provider reads. */
|
|
66
|
+
clock: () => number;
|
|
67
|
+
constructor(init: {
|
|
68
|
+
credentials: CamusCredentials;
|
|
69
|
+
resolveLoginClient: () => CamusLoginClient | Promise<CamusLoginClient>;
|
|
70
|
+
resolveEndpoint: () => string;
|
|
71
|
+
resolveTimeoutSeconds: () => number;
|
|
72
|
+
lifetimeMs?: number;
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* True when this client has anything to authenticate with. When false the driver sends no
|
|
76
|
+
* `Authorization` header, which is what a server with authentication off expects.
|
|
77
|
+
*/
|
|
78
|
+
get isEnabled(): boolean;
|
|
79
|
+
/** True when the provider holds a password and can therefore replace a rejected token. */
|
|
80
|
+
get canRenew(): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* The cached token, without triggering a login. The gRPC batch-stream factory reads it, because
|
|
83
|
+
* it builds a stream synchronously; every such call site awaits `getToken` first, so the cache
|
|
84
|
+
* is warm by then.
|
|
85
|
+
*/
|
|
86
|
+
get currentToken(): string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* A usable bearer token, logging in when there is none or the cached one has aged out. Reports
|
|
89
|
+
* `undefined` when no credentials are configured.
|
|
90
|
+
*/
|
|
91
|
+
getToken(signal?: AbortSignal): Promise<string | undefined>;
|
|
92
|
+
/**
|
|
93
|
+
* Discards a token so the next `getToken` mints a new one.
|
|
94
|
+
*
|
|
95
|
+
* Comparing against the cached value keeps a late rejection of an already-replaced token from
|
|
96
|
+
* throwing away the good one a concurrent caller just obtained. It does nothing when the token
|
|
97
|
+
* cannot be renewed, so the caller reports the failure instead of looping.
|
|
98
|
+
*/
|
|
99
|
+
invalidate(staleToken: string | undefined): void;
|
|
100
|
+
/**
|
|
101
|
+
* Authenticates explicitly, replacing whatever credentials were configured, and reports the
|
|
102
|
+
* minted token. Any cached token is dropped.
|
|
103
|
+
*/
|
|
104
|
+
login(user: string, password: string, signal?: AbortSignal): Promise<string>;
|
|
105
|
+
/**
|
|
106
|
+
* Revokes the cached token on the server and forgets it. The configured credentials are kept, so
|
|
107
|
+
* a later statement authenticates again on its own: this ends a session, it does not switch
|
|
108
|
+
* authentication off. It does nothing when no token has been minted.
|
|
109
|
+
*/
|
|
110
|
+
logout(signal?: AbortSignal): Promise<void>;
|
|
111
|
+
private tryGetCached;
|
|
112
|
+
private loginOnce;
|
|
113
|
+
/**
|
|
114
|
+
* When to mint the next token.
|
|
115
|
+
*
|
|
116
|
+
* The server's reported lifetime is authoritative — its own setting is configurable and may be
|
|
117
|
+
* shorter than any value the driver defaults to — so the provider renews at 80% of it, leaving
|
|
118
|
+
* headroom for the request that carries the token to finish. Without a reported value it falls
|
|
119
|
+
* back to the configured lifetime, which is already conservative.
|
|
120
|
+
*/
|
|
121
|
+
private renewalDelayMs;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=token-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-provider.d.ts","sourceRoot":"","sources":["../../src/auth/token-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,mBAAmB,CAAC;AAE5E,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAA4C;IAE1E;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,GAAG,kBAAkB;IAIjF,wDAAwD;IACxD,MAAM,CAAC,WAAW,IAAI,IAAI;IAI1B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAEtD;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM;IAW/E,OAAO,CAAC,WAAW,CAAmB;IAEtC,OAAO,CAAC,KAAK,CAAqB;IAElC,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqD;IAExF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAE/C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAe;IAErD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,wFAAwF;IACxF,OAAO,CAAC,QAAQ,CAA8B;IAE9C,+DAA+D;IAC/D,KAAK,EAAE,MAAM,MAAM,CAAoB;gBAE3B,IAAI,EAAE;QAChB,WAAW,EAAE,gBAAgB,CAAC;QAC9B,kBAAkB,EAAE,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACvE,eAAe,EAAE,MAAM,MAAM,CAAC;QAC9B,qBAAqB,EAAE,MAAM,MAAM,CAAC;QACpC,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB;IAYD;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,0FAA0F;IAC1F,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;OAIG;IACH,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED;;;OAGG;IACG,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAajE;;;;;;OAMG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAShD;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAalF;;;;OAIG;IACG,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjD,OAAO,CAAC,YAAY;YAON,SAAS;IA6BvB;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;CAMvB"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of CamusDB
|
|
3
|
+
*
|
|
4
|
+
* For the full copyright and license information, please view the LICENSE.txt
|
|
5
|
+
* file that was distributed with this source code.
|
|
6
|
+
*/
|
|
7
|
+
import { createHmac, randomBytes } from 'node:crypto';
|
|
8
|
+
import { CamusError } from '../errors.js';
|
|
9
|
+
import { CamusErrorCode } from '../error-codes.js';
|
|
10
|
+
import { SharedRegistry } from '../shared-registry.js';
|
|
11
|
+
import { canRenew, credentialsFromPassword, hasCredentials } from './credentials.js';
|
|
12
|
+
/** The fallback token lifetime, comfortably inside the server's 15-minute default. */
|
|
13
|
+
export const DEFAULT_TOKEN_LIFETIME_MS = 600_000;
|
|
14
|
+
/**
|
|
15
|
+
* Owns a client's bearer token: mints it from the configured credentials on first use, hands the
|
|
16
|
+
* same token to every request until it goes stale, and mints a new one when it does.
|
|
17
|
+
*
|
|
18
|
+
* One provider is shared by every client that presents the same identity to the same deployment,
|
|
19
|
+
* so a pool of clients performs one login rather than one each. Password verification is
|
|
20
|
+
* deliberately expensive on the server — PBKDF2 with 600,000 iterations by default — and rate
|
|
21
|
+
* limited per account, so a login stampede is exactly what must be avoided.
|
|
22
|
+
*
|
|
23
|
+
* **Single flight.** Concurrent callers that find no usable token await one promise; the winner
|
|
24
|
+
* logs in and the rest observe its result instead of issuing their own login.
|
|
25
|
+
*
|
|
26
|
+
* **Renewal.** The login reply reports how long the token is good for, and the provider renews at
|
|
27
|
+
* 80% of that. Against a server that predates the field it falls back to its own conservative
|
|
28
|
+
* lifetime. That proactive renewal is the primary path. `invalidate`, driven by a `CADB0516` from
|
|
29
|
+
* a real request, is the backstop for what a clock cannot predict: a rotated password, a dropped
|
|
30
|
+
* user, a logout from elsewhere, or a server whose token lifetime is shorter than the fallback.
|
|
31
|
+
*
|
|
32
|
+
* A token supplied directly is used as written and never renewed — the driver has no password to
|
|
33
|
+
* mint a replacement with — so a `CADB0516` on that path reaches the caller rather than a retry.
|
|
34
|
+
*/
|
|
35
|
+
export class CamusTokenProvider {
|
|
36
|
+
static shared = new SharedRegistry();
|
|
37
|
+
/**
|
|
38
|
+
* The process-wide provider for an identity, built on first use. Keyed by credentials rather
|
|
39
|
+
* than by the whole configuration, so clients that differ only in database or timeout share one
|
|
40
|
+
* token.
|
|
41
|
+
*/
|
|
42
|
+
static forKey(key, factory) {
|
|
43
|
+
return CamusTokenProvider.shared.get(key, factory);
|
|
44
|
+
}
|
|
45
|
+
/** @internal Test hook: drops every shared provider. */
|
|
46
|
+
static resetShared() {
|
|
47
|
+
CamusTokenProvider.shared.clear();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A per-process random salt for `sharingKey`.
|
|
51
|
+
*
|
|
52
|
+
* The key lives in a map for the life of the process, and an unsalted hash of a password is an
|
|
53
|
+
* offline verifier for anyone who can read process memory. The key only has to be stable inside
|
|
54
|
+
* one process, so a salt costs nothing.
|
|
55
|
+
*/
|
|
56
|
+
static sharingSalt = randomBytes(32);
|
|
57
|
+
/**
|
|
58
|
+
* A stable, non-reversible identity for a credential set plus a deployment. It is hashed, and
|
|
59
|
+
* salted per process, so a long-lived map key neither holds a plaintext password nor can be
|
|
60
|
+
* tested against a guessed one outside this process.
|
|
61
|
+
*/
|
|
62
|
+
static sharingKey(credentials, deploymentKey) {
|
|
63
|
+
const material = [
|
|
64
|
+
credentials.user ?? '',
|
|
65
|
+
credentials.password ?? '',
|
|
66
|
+
credentials.accessToken ?? '',
|
|
67
|
+
deploymentKey,
|
|
68
|
+
].join('\n');
|
|
69
|
+
return createHmac('sha256', CamusTokenProvider.sharingSalt).update(material, 'utf8').digest('hex');
|
|
70
|
+
}
|
|
71
|
+
credentials;
|
|
72
|
+
token;
|
|
73
|
+
renewAfter;
|
|
74
|
+
resolveLoginClient;
|
|
75
|
+
resolveEndpoint;
|
|
76
|
+
resolveTimeoutSeconds;
|
|
77
|
+
lifetimeMs;
|
|
78
|
+
/** The single login in flight, if any. Every racing caller awaits this same promise. */
|
|
79
|
+
inFlight;
|
|
80
|
+
/** @internal Test hook: the wall clock this provider reads. */
|
|
81
|
+
clock = () => Date.now();
|
|
82
|
+
constructor(init) {
|
|
83
|
+
this.credentials = init.credentials;
|
|
84
|
+
this.resolveLoginClient = init.resolveLoginClient;
|
|
85
|
+
this.resolveEndpoint = init.resolveEndpoint;
|
|
86
|
+
this.resolveTimeoutSeconds = init.resolveTimeoutSeconds;
|
|
87
|
+
this.lifetimeMs = init.lifetimeMs ?? DEFAULT_TOKEN_LIFETIME_MS;
|
|
88
|
+
// A token supplied directly is the cached token, and never ages out on this side.
|
|
89
|
+
this.token = init.credentials.accessToken;
|
|
90
|
+
this.renewAfter = Number.POSITIVE_INFINITY;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* True when this client has anything to authenticate with. When false the driver sends no
|
|
94
|
+
* `Authorization` header, which is what a server with authentication off expects.
|
|
95
|
+
*/
|
|
96
|
+
get isEnabled() {
|
|
97
|
+
return hasCredentials(this.credentials);
|
|
98
|
+
}
|
|
99
|
+
/** True when the provider holds a password and can therefore replace a rejected token. */
|
|
100
|
+
get canRenew() {
|
|
101
|
+
return canRenew(this.credentials);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The cached token, without triggering a login. The gRPC batch-stream factory reads it, because
|
|
105
|
+
* it builds a stream synchronously; every such call site awaits `getToken` first, so the cache
|
|
106
|
+
* is warm by then.
|
|
107
|
+
*/
|
|
108
|
+
get currentToken() {
|
|
109
|
+
return this.token;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A usable bearer token, logging in when there is none or the cached one has aged out. Reports
|
|
113
|
+
* `undefined` when no credentials are configured.
|
|
114
|
+
*/
|
|
115
|
+
async getToken(signal) {
|
|
116
|
+
const cached = this.tryGetCached();
|
|
117
|
+
if (cached !== undefined)
|
|
118
|
+
return cached;
|
|
119
|
+
if (!this.isEnabled)
|
|
120
|
+
return undefined;
|
|
121
|
+
// A caller that arrives during a login awaits that login rather than issuing its own.
|
|
122
|
+
this.inFlight ??= this.loginOnce(signal).finally(() => {
|
|
123
|
+
this.inFlight = undefined;
|
|
124
|
+
});
|
|
125
|
+
return this.inFlight;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Discards a token so the next `getToken` mints a new one.
|
|
129
|
+
*
|
|
130
|
+
* Comparing against the cached value keeps a late rejection of an already-replaced token from
|
|
131
|
+
* throwing away the good one a concurrent caller just obtained. It does nothing when the token
|
|
132
|
+
* cannot be renewed, so the caller reports the failure instead of looping.
|
|
133
|
+
*/
|
|
134
|
+
invalidate(staleToken) {
|
|
135
|
+
if (staleToken === undefined || staleToken.length === 0)
|
|
136
|
+
return;
|
|
137
|
+
if (!this.canRenew)
|
|
138
|
+
return;
|
|
139
|
+
if (this.token !== staleToken)
|
|
140
|
+
return;
|
|
141
|
+
this.token = undefined;
|
|
142
|
+
this.renewAfter = 0;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Authenticates explicitly, replacing whatever credentials were configured, and reports the
|
|
146
|
+
* minted token. Any cached token is dropped.
|
|
147
|
+
*/
|
|
148
|
+
async login(user, password, signal) {
|
|
149
|
+
this.credentials = credentialsFromPassword(user, password);
|
|
150
|
+
this.token = undefined;
|
|
151
|
+
this.renewAfter = 0;
|
|
152
|
+
const pending = this.loginOnce(signal).finally(() => {
|
|
153
|
+
this.inFlight = undefined;
|
|
154
|
+
});
|
|
155
|
+
this.inFlight = pending;
|
|
156
|
+
return pending;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Revokes the cached token on the server and forgets it. The configured credentials are kept, so
|
|
160
|
+
* a later statement authenticates again on its own: this ends a session, it does not switch
|
|
161
|
+
* authentication off. It does nothing when no token has been minted.
|
|
162
|
+
*/
|
|
163
|
+
async logout(signal) {
|
|
164
|
+
const revoked = this.token;
|
|
165
|
+
this.token = undefined;
|
|
166
|
+
this.renewAfter = 0;
|
|
167
|
+
if (revoked === undefined || revoked.length === 0)
|
|
168
|
+
return;
|
|
169
|
+
const client = await this.resolveLoginClient();
|
|
170
|
+
await client.logout(this.resolveEndpoint(), revoked, this.resolveTimeoutSeconds(), signal);
|
|
171
|
+
}
|
|
172
|
+
tryGetCached() {
|
|
173
|
+
if (!this.isEnabled)
|
|
174
|
+
return undefined;
|
|
175
|
+
if (this.token === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
return this.clock() < this.renewAfter ? this.token : undefined;
|
|
178
|
+
}
|
|
179
|
+
async loginOnce(signal) {
|
|
180
|
+
// Re-checked inside the single-flight gate: the caller that queued behind the winner finds the
|
|
181
|
+
// token it minted.
|
|
182
|
+
const cached = this.tryGetCached();
|
|
183
|
+
if (cached !== undefined)
|
|
184
|
+
return cached;
|
|
185
|
+
if (!this.canRenew) {
|
|
186
|
+
throw new CamusError(CamusErrorCode.AuthenticationFailed, 'The supplied access token was rejected and this client has no credentials to obtain a new one.');
|
|
187
|
+
}
|
|
188
|
+
const client = await this.resolveLoginClient();
|
|
189
|
+
const minted = await client.login(this.resolveEndpoint(), this.credentials.user, this.credentials.password, this.resolveTimeoutSeconds(), signal);
|
|
190
|
+
this.token = minted.token;
|
|
191
|
+
this.renewAfter = this.clock() + this.renewalDelayMs(minted.expiresInMs);
|
|
192
|
+
return minted.token;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* When to mint the next token.
|
|
196
|
+
*
|
|
197
|
+
* The server's reported lifetime is authoritative — its own setting is configurable and may be
|
|
198
|
+
* shorter than any value the driver defaults to — so the provider renews at 80% of it, leaving
|
|
199
|
+
* headroom for the request that carries the token to finish. Without a reported value it falls
|
|
200
|
+
* back to the configured lifetime, which is already conservative.
|
|
201
|
+
*/
|
|
202
|
+
renewalDelayMs(reported) {
|
|
203
|
+
if (reported === undefined)
|
|
204
|
+
return this.lifetimeMs;
|
|
205
|
+
const renewal = reported * 0.8;
|
|
206
|
+
return renewal > 0 ? renewal : 1000;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=token-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-provider.js","sourceRoot":"","sources":["../../src/auth/token-provider.ts"],"names":[],"mappings":"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"}
|