@dereekb/calcom 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/index.cjs.js +286 -136
- package/index.esm.js +276 -138
- package/nestjs/index.cjs.js +280 -47
- package/nestjs/index.esm.js +282 -50
- package/nestjs/package.json +5 -5
- package/nestjs/src/lib/calcom/calcom.api.d.ts +5 -5
- package/nestjs/src/lib/oauth/oauth.api.d.ts +35 -10
- package/nestjs/src/lib/oauth/oauth.service.d.ts +31 -7
- package/package.json +4 -4
- package/src/lib/calcom/calcom.config.d.ts +10 -17
- package/src/lib/oauth/index.d.ts +1 -0
- package/src/lib/oauth/oauth.api.d.ts +11 -4
- package/src/lib/oauth/oauth.authorize.d.ts +91 -0
- package/src/lib/oauth/oauth.config.d.ts +112 -17
- package/src/lib/oauth/oauth.d.ts +7 -0
- package/src/lib/oauth/oauth.factory.d.ts +41 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { type CalcomAccessToken, type CalcomAccessTokenCache, type CalcomRefreshToken } from '@dereekb/calcom';
|
|
1
|
+
import { type CalcomAccessToken, type CalcomAccessTokenCache, type CalcomAccessTokenCacheKey, type CalcomRefreshToken } from '@dereekb/calcom';
|
|
2
2
|
import { type Maybe } from '@dereekb/util';
|
|
3
3
|
/**
|
|
4
4
|
* Service used for retrieving CalcomAccessTokenCache for Cal.com services.
|
|
5
5
|
*
|
|
6
6
|
* Implementations store and retrieve OAuth access tokens (and the rotated refresh tokens
|
|
7
7
|
* embedded in them). The service supports both a server-level cache and per-user caches
|
|
8
|
-
* keyed by the user's
|
|
8
|
+
* keyed either by a caller-owned key ({@link cacheForKey}) or by the user's refresh token
|
|
9
|
+
* ({@link cacheForRefreshToken}).
|
|
9
10
|
*/
|
|
10
11
|
export declare abstract class CalcomOAuthAccessTokenCacheService {
|
|
11
12
|
/**
|
|
@@ -13,15 +14,27 @@ export declare abstract class CalcomOAuthAccessTokenCacheService {
|
|
|
13
14
|
*/
|
|
14
15
|
abstract loadCalcomAccessTokenCache(): CalcomAccessTokenCache;
|
|
15
16
|
/**
|
|
16
|
-
* Creates or retrieves a cache for a specific user context, keyed by
|
|
17
|
+
* Creates or retrieves a cache for a specific user context, keyed by a stable caller-owned key.
|
|
17
18
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
|
|
19
|
+
* Prefer this over {@link cacheForRefreshToken}: a key the caller already owns (a user or
|
|
20
|
+
* profile id) survives refresh-token rotation, so the same entry is found on every boot.
|
|
21
|
+
*/
|
|
22
|
+
abstract cacheForKey?(key: CalcomAccessTokenCacheKey): CalcomAccessTokenCache;
|
|
23
|
+
/**
|
|
24
|
+
* Creates or retrieves a cache for a specific user context, keyed by a hash of the refresh token.
|
|
25
|
+
*
|
|
26
|
+
* The cache instance persists the rotated token in-place, so reads return the latest token for
|
|
27
|
+
* as long as the *original* refresh token keeps being used as the lookup key. Because Cal.com
|
|
28
|
+
* rotates the refresh token on every use, that only holds within a single process lifetime:
|
|
29
|
+
* once a rotated token is persisted and then used as the key, the key changes and the previous
|
|
30
|
+
* entry is orphaned.
|
|
31
|
+
*
|
|
32
|
+
* Prefer {@link cacheForKey} with a stable id for anything that outlives one process.
|
|
21
33
|
*/
|
|
22
34
|
abstract cacheForRefreshToken?(refreshToken: CalcomRefreshToken): CalcomAccessTokenCache;
|
|
23
35
|
}
|
|
24
|
-
export type CalcomOAuthAccessTokenCacheServiceWithRefreshToken = Required<CalcomOAuthAccessTokenCacheService
|
|
36
|
+
export type CalcomOAuthAccessTokenCacheServiceWithRefreshToken = Required<Pick<CalcomOAuthAccessTokenCacheService, 'cacheForRefreshToken'>> & CalcomOAuthAccessTokenCacheService;
|
|
37
|
+
export type CalcomOAuthAccessTokenCacheServiceWithKey = Required<Pick<CalcomOAuthAccessTokenCacheService, 'cacheForKey'>> & CalcomOAuthAccessTokenCacheService;
|
|
25
38
|
/**
|
|
26
39
|
* Derives a short, filesystem-safe cache key from a refresh token.
|
|
27
40
|
*
|
|
@@ -31,6 +44,17 @@ export type CalcomOAuthAccessTokenCacheServiceWithRefreshToken = Required<Calcom
|
|
|
31
44
|
* @returns A 16-character hex string suitable for use as a cache key.
|
|
32
45
|
*/
|
|
33
46
|
export declare function calcomRefreshTokenCacheKey(refreshToken: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Converts a cache key into a filesystem-safe path segment.
|
|
49
|
+
*
|
|
50
|
+
* A key that is already safe (such as the hex output of {@link calcomRefreshTokenCacheKey}) is
|
|
51
|
+
* returned unchanged. Otherwise the unsafe characters are replaced and a hash of the original key
|
|
52
|
+
* is appended, so two different keys can never collapse onto the same file.
|
|
53
|
+
*
|
|
54
|
+
* @param key - The cache key to convert.
|
|
55
|
+
* @returns A filesystem-safe path segment for the key.
|
|
56
|
+
*/
|
|
57
|
+
export declare function calcomAccessTokenCacheFileKey(key: CalcomAccessTokenCacheKey): string;
|
|
34
58
|
export type LogMergeCalcomOAuthAccessTokenCacheServiceErrorFunction = (failedUpdates: (readonly [CalcomAccessTokenCache, unknown])[]) => void;
|
|
35
59
|
/**
|
|
36
60
|
* Default error logging function for {@link mergeCalcomOAuthAccessTokenCacheServices}.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/calcom",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.33.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./nestjs": {
|
|
6
6
|
"module": "./nestjs/index.esm.js",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@dereekb/nestjs": "13.
|
|
21
|
-
"@dereekb/rxjs": "13.
|
|
22
|
-
"@dereekb/util": "13.
|
|
20
|
+
"@dereekb/nestjs": "13.33.0",
|
|
21
|
+
"@dereekb/rxjs": "13.33.0",
|
|
22
|
+
"@dereekb/util": "13.33.0",
|
|
23
23
|
"@nestjs/common": "^11.1.19",
|
|
24
24
|
"@nestjs/config": "^4.0.4",
|
|
25
25
|
"express": "^5.2.1",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { type FactoryWithRequiredInput
|
|
1
|
+
import { type FactoryWithRequiredInput } from '@dereekb/util';
|
|
2
2
|
import { type ConfiguredFetch, type FetchJsonFunction } from '@dereekb/util/fetch';
|
|
3
|
-
import { type CalcomConfig
|
|
4
|
-
import { type
|
|
3
|
+
import { type CalcomConfig } from '../calcom.config';
|
|
4
|
+
import { type CalcomAccessTokenStringFactory } from '../oauth/oauth';
|
|
5
|
+
import { type CalcomRefreshTokenCredential } from '../oauth/oauth.config';
|
|
5
6
|
import { type CalcomRateLimiterRef } from '../calcom.limit';
|
|
6
7
|
export interface CalcomFetchFactoryInput {
|
|
7
8
|
readonly calcomAccessTokenStringFactory: CalcomAccessTokenStringFactory;
|
|
@@ -30,22 +31,14 @@ export interface CalcomUserContext extends CalcomContext {
|
|
|
30
31
|
readonly userFetch: ConfiguredFetch;
|
|
31
32
|
readonly userFetchJson: FetchJsonFunction;
|
|
32
33
|
}
|
|
33
|
-
export interface CalcomUserContextFactoryInput {
|
|
34
|
-
/**
|
|
35
|
-
* The user's refresh token.
|
|
36
|
-
*/
|
|
37
|
-
readonly refreshToken: CalcomRefreshToken;
|
|
38
|
-
/**
|
|
39
|
-
* Optional cache to use for the user's access token.
|
|
40
|
-
*
|
|
41
|
-
* The cache should only be configured for the user that owns the refresh token.
|
|
42
|
-
*/
|
|
43
|
-
readonly accessTokenCache?: Maybe<CalcomAccessTokenCache>;
|
|
44
|
-
}
|
|
45
34
|
/**
|
|
46
|
-
* Creates a CalcomUserContext from
|
|
35
|
+
* Creates a CalcomUserContext from a user's credential.
|
|
36
|
+
*
|
|
37
|
+
* Deliberately the refresh-token arm of {@link CalcomAuthCredential} rather than the full union: a
|
|
38
|
+
* user context acts as a connected user, while an api key acts as whoever created it — which is the
|
|
39
|
+
* server context's job.
|
|
47
40
|
*/
|
|
48
|
-
export type CalcomUserContextFactory = FactoryWithRequiredInput<CalcomUserContext,
|
|
41
|
+
export type CalcomUserContextFactory = FactoryWithRequiredInput<CalcomUserContext, CalcomRefreshTokenCredential>;
|
|
49
42
|
/**
|
|
50
43
|
* Context for making public (unauthenticated) requests to the Cal.com API.
|
|
51
44
|
*/
|
package/src/lib/oauth/index.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type CalcomOAuthContext } from './oauth.config';
|
|
2
2
|
import { type CalcomRefreshToken } from '../calcom.config';
|
|
3
3
|
import { type CalcomAccessTokenScopesString, type CalcomAccessTokenString } from './oauth';
|
|
4
|
-
import { type
|
|
4
|
+
import { type Seconds } from '@dereekb/util';
|
|
5
5
|
export interface CalcomOAuthRefreshTokenInput {
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The refresh token to exchange.
|
|
8
|
+
*
|
|
9
|
+
* Required, and deliberately not defaulted from the context's configuration: Cal.com rotates
|
|
10
|
+
* refresh tokens on every use, so the configured value is spent the first time the context
|
|
11
|
+
* refreshes. Every caller therefore has to name the token it actually holds.
|
|
12
|
+
*/
|
|
13
|
+
readonly refreshToken: CalcomRefreshToken;
|
|
7
14
|
}
|
|
8
15
|
export interface CalcomOAuthExchangeAuthorizationCodeInput {
|
|
9
16
|
readonly code: string;
|
|
@@ -26,7 +33,7 @@ export interface CalcomOAuthAccessTokenErrorResponse {
|
|
|
26
33
|
* Cal.com uses JSON body (not Basic Auth) for token requests.
|
|
27
34
|
*
|
|
28
35
|
* @param context - The Cal.com OAuth context providing client credentials and fetch capabilities.
|
|
29
|
-
* @returns Refreshes an access token using
|
|
36
|
+
* @returns Refreshes an access token using the given refresh token.
|
|
30
37
|
*
|
|
31
38
|
* @see https://cal.com/docs/api-reference/v2/oauth/refresh-an-existing-access-token
|
|
32
39
|
*
|
|
@@ -36,7 +43,7 @@ export interface CalcomOAuthAccessTokenErrorResponse {
|
|
|
36
43
|
* console.log(response.access_token, response.refresh_token);
|
|
37
44
|
* ```
|
|
38
45
|
*/
|
|
39
|
-
export declare function refreshAccessToken(context: CalcomOAuthContext): (input
|
|
46
|
+
export declare function refreshAccessToken(context: CalcomOAuthContext): (input: CalcomOAuthRefreshTokenInput) => Promise<CalcomOAuthTokenResponse>;
|
|
40
47
|
/**
|
|
41
48
|
* Exchanges an OAuth authorization code for access and refresh tokens.
|
|
42
49
|
* Used during the initial OAuth flow when a user authorizes your app.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type Maybe, type WebsiteUrl } from '@dereekb/util';
|
|
2
|
+
import { type CalcomOAuthClientId } from '../calcom.config';
|
|
3
|
+
/**
|
|
4
|
+
* Every granular Cal.com OAuth scope.
|
|
5
|
+
*
|
|
6
|
+
* A runtime list rather than a bare type union, so a configured scope can be validated instead of
|
|
7
|
+
* being passed through to the consent screen and refused there.
|
|
8
|
+
*
|
|
9
|
+
* @see https://cal.com/docs/api-reference/v2/oauth
|
|
10
|
+
*/
|
|
11
|
+
export declare const ALL_CALCOM_OAUTH_SCOPES: readonly ["PROFILE_READ", "PROFILE_WRITE", "BOOKING_READ", "BOOKING_WRITE", "SCHEDULE_READ", "SCHEDULE_WRITE", "EVENT_TYPE_READ", "EVENT_TYPE_WRITE", "APPS_READ", "APPS_WRITE", "WEBHOOK_READ", "WEBHOOK_WRITE"];
|
|
12
|
+
/**
|
|
13
|
+
* A granular Cal.com OAuth scope.
|
|
14
|
+
*/
|
|
15
|
+
export type CalcomOAuthScope = (typeof ALL_CALCOM_OAUTH_SCOPES)[number];
|
|
16
|
+
/**
|
|
17
|
+
* Returns whether the input is a known {@link CalcomOAuthScope}.
|
|
18
|
+
*
|
|
19
|
+
* @param value - The value to check.
|
|
20
|
+
* @returns True when the value is a known Cal.com OAuth scope.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isCalcomOAuthScope(value: string): value is CalcomOAuthScope;
|
|
23
|
+
/**
|
|
24
|
+
* The delimiter used to join scopes in the `scope` query parameter.
|
|
25
|
+
*
|
|
26
|
+
* OAuth2 specifies a space-delimited list. Cal.com's granular scopes are documented without an
|
|
27
|
+
* explicit delimiter, so this is isolated here: if the consent screen rejects the `scope`
|
|
28
|
+
* parameter, this is the only value that needs to change.
|
|
29
|
+
*/
|
|
30
|
+
export declare const CALCOM_OAUTH_SCOPE_DELIMITER = " ";
|
|
31
|
+
/**
|
|
32
|
+
* The `response_type` used by the authorization-code flow.
|
|
33
|
+
*/
|
|
34
|
+
export declare const CALCOM_OAUTH_AUTHORIZE_RESPONSE_TYPE = "code";
|
|
35
|
+
export interface CalcomOAuthAuthorizeUrlFactoryConfig {
|
|
36
|
+
/**
|
|
37
|
+
* The OAuth client id to authorize as.
|
|
38
|
+
*/
|
|
39
|
+
readonly clientId: CalcomOAuthClientId;
|
|
40
|
+
/**
|
|
41
|
+
* The redirect URI to return to after the user consents.
|
|
42
|
+
*
|
|
43
|
+
* Must match the URI registered on the Cal.com OAuth client byte-for-byte, including the port,
|
|
44
|
+
* and must be identical to the `redirectUri` later passed to the token exchange.
|
|
45
|
+
*/
|
|
46
|
+
readonly redirectUri: WebsiteUrl;
|
|
47
|
+
/**
|
|
48
|
+
* The scopes to request.
|
|
49
|
+
*/
|
|
50
|
+
readonly scopes: readonly CalcomOAuthScope[];
|
|
51
|
+
/**
|
|
52
|
+
* Optional override of the authorize URL. Defaults to {@link CALCOM_OAUTH_AUTHORIZE_URL}.
|
|
53
|
+
*/
|
|
54
|
+
readonly authorizeUrl?: Maybe<WebsiteUrl>;
|
|
55
|
+
}
|
|
56
|
+
export interface CalcomOAuthAuthorizeUrlParams {
|
|
57
|
+
/**
|
|
58
|
+
* Opaque state echoed back to the redirect URI.
|
|
59
|
+
*
|
|
60
|
+
* Carries the acting user and is the CSRF defense for the handoff, so it should be signed and
|
|
61
|
+
* short-lived.
|
|
62
|
+
*/
|
|
63
|
+
readonly state?: Maybe<string>;
|
|
64
|
+
}
|
|
65
|
+
export type CalcomOAuthAuthorizeUrlFactory = (params?: Maybe<CalcomOAuthAuthorizeUrlParams>) => WebsiteUrl;
|
|
66
|
+
/**
|
|
67
|
+
* Creates a {@link CalcomOAuthAuthorizeUrlFactory} that composes the Cal.com authorize URL that a
|
|
68
|
+
* user's browser is redirected to in order to begin the authorization-code flow.
|
|
69
|
+
*
|
|
70
|
+
* The client id, redirect URI, and scopes are fixed by the config, since a consumer holds those
|
|
71
|
+
* constant and varies only the per-request `state`.
|
|
72
|
+
*
|
|
73
|
+
* @param config - The client id, redirect URI, and scopes to request.
|
|
74
|
+
* @returns A factory that builds an authorize URL for the given params.
|
|
75
|
+
*
|
|
76
|
+
* @see https://cal.com/docs/api-reference/v2/oauth
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* const authorizeUrlFactory = calcomOAuthAuthorizeUrlFactory({
|
|
81
|
+
* clientId: 'client-id',
|
|
82
|
+
* redirectUri: 'http://localhost:9901/oauth/calcom/callback',
|
|
83
|
+
* scopes: ['PROFILE_READ', 'BOOKING_READ']
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* const url = authorizeUrlFactory({ state: 'signed-state' });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @__NO_SIDE_EFFECTS__
|
|
90
|
+
*/
|
|
91
|
+
export declare function calcomOAuthAuthorizeUrlFactory(config: CalcomOAuthAuthorizeUrlFactoryConfig): CalcomOAuthAuthorizeUrlFactory;
|
|
@@ -3,51 +3,146 @@ import { type ConfiguredFetch, type FetchJsonFunction } from '@dereekb/util/fetc
|
|
|
3
3
|
import { type CalcomApiKey, type CalcomAuthClientIdAndSecretPair, type CalcomRefreshToken } from '../calcom.config';
|
|
4
4
|
import { type CalcomAccessTokenCache, type CalcomAccessTokenFactory } from './oauth';
|
|
5
5
|
/**
|
|
6
|
-
* The Cal.com OAuth
|
|
6
|
+
* The Cal.com OAuth API base URL.
|
|
7
|
+
*
|
|
8
|
+
* Endpoint paths are appended to this base, so it intentionally carries no endpoint segment of
|
|
9
|
+
* its own. This is the single place the OAuth host and prefix are encoded.
|
|
7
10
|
*/
|
|
8
|
-
export declare const
|
|
11
|
+
export declare const CALCOM_OAUTH_API_URL = "https://api.cal.com/v2/auth/oauth2";
|
|
12
|
+
export type CalcomOAuthApiUrl = typeof CALCOM_OAUTH_API_URL;
|
|
13
|
+
/**
|
|
14
|
+
* The Cal.com OAuth token endpoint path, relative to {@link CALCOM_OAUTH_API_URL}.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CALCOM_OAUTH_TOKEN_PATH = "/token";
|
|
9
17
|
/**
|
|
10
18
|
* The Cal.com OAuth authorize URL.
|
|
11
19
|
*/
|
|
12
20
|
export declare const CALCOM_OAUTH_AUTHORIZE_URL = "https://app.cal.com/auth/oauth2/authorize";
|
|
13
21
|
/**
|
|
14
|
-
*
|
|
22
|
+
* Authenticates as the Cal.com user who created the API key.
|
|
23
|
+
*
|
|
24
|
+
* The key IS the bearer token and does not expire, so this credential never reaches the token
|
|
25
|
+
* endpoint and needs no {@link CalcomOAuthConfig.client}.
|
|
15
26
|
*/
|
|
16
|
-
export interface
|
|
27
|
+
export interface CalcomApiKeyCredential {
|
|
28
|
+
readonly apiKey: CalcomApiKey;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Authenticates as the Cal.com user who granted the refresh token.
|
|
32
|
+
*
|
|
33
|
+
* Every exchange is authenticated with {@link CalcomOAuthConfig.client}'s id and secret, so this
|
|
34
|
+
* credential is unusable without one.
|
|
35
|
+
*/
|
|
36
|
+
export interface CalcomRefreshTokenCredential {
|
|
17
37
|
/**
|
|
18
|
-
*
|
|
38
|
+
* The grant's refresh token.
|
|
39
|
+
*
|
|
40
|
+
* Cal.com rotates it on every use. The factory built from this credential tracks the rotation in
|
|
41
|
+
* memory, so this value is read once rather than re-read per refresh.
|
|
19
42
|
*/
|
|
20
|
-
readonly
|
|
43
|
+
readonly refreshToken: CalcomRefreshToken;
|
|
21
44
|
/**
|
|
22
|
-
*
|
|
45
|
+
* Cache for THIS credential's access token.
|
|
46
|
+
*
|
|
47
|
+
* Must be scoped to exactly this grant. Two credentials sharing one cache overwrite each other,
|
|
48
|
+
* and the rotated refresh token rides along inside the cached value — so a shared cache does not
|
|
49
|
+
* merely lose a token, it spends one.
|
|
23
50
|
*/
|
|
51
|
+
readonly accessTokenCache?: Maybe<CalcomAccessTokenCache>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A credential Cal.com calls can be made with.
|
|
55
|
+
*
|
|
56
|
+
* One union for the ambient credential and for any per-user one, because they are the same thing:
|
|
57
|
+
* a way to act as some Cal.com user. Discriminated by the presence of `apiKey`.
|
|
58
|
+
*/
|
|
59
|
+
export type CalcomAuthCredential = CalcomApiKeyCredential | CalcomRefreshTokenCredential;
|
|
60
|
+
/**
|
|
61
|
+
* Returns whether the credential is a {@link CalcomApiKeyCredential}.
|
|
62
|
+
*
|
|
63
|
+
* @param credential - The credential to check.
|
|
64
|
+
* @returns True when the credential carries an api key.
|
|
65
|
+
*
|
|
66
|
+
* @__NO_SIDE_EFFECTS__
|
|
67
|
+
*/
|
|
68
|
+
export declare function isCalcomApiKeyCredential(credential: CalcomAuthCredential): credential is CalcomApiKeyCredential;
|
|
69
|
+
export interface CalcomAuthCredentialValues {
|
|
70
|
+
readonly apiKey?: Maybe<CalcomApiKey>;
|
|
24
71
|
readonly refreshToken?: Maybe<CalcomRefreshToken>;
|
|
72
|
+
readonly accessTokenCache?: Maybe<CalcomAccessTokenCache>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Builds a {@link CalcomAuthCredential} from flat, optional values, as an environment-facing
|
|
76
|
+
* configuration provides them.
|
|
77
|
+
*
|
|
78
|
+
* An api key wins when both are present: it does not expire, so it skips the refresh loop entirely.
|
|
79
|
+
* The cache attaches only to the refresh-token arm, since an api key has no token to cache. Empty
|
|
80
|
+
* strings count as absent, so an unset environment variable read as `''` does not become a
|
|
81
|
+
* credential that sends `Bearer `.
|
|
82
|
+
*
|
|
83
|
+
* @param values - The flat credential values.
|
|
84
|
+
* @returns The equivalent credential, or undefined when neither value is present.
|
|
85
|
+
*
|
|
86
|
+
* @__NO_SIDE_EFFECTS__
|
|
87
|
+
*/
|
|
88
|
+
export declare function calcomAuthCredentialFromValues(values: CalcomAuthCredentialValues): Maybe<CalcomAuthCredential>;
|
|
89
|
+
/**
|
|
90
|
+
* Configuration for CalcomOAuth.
|
|
91
|
+
*
|
|
92
|
+
* `client` is the app's OAuth *registration*. It is sent on EVERY token exchange — the ambient one
|
|
93
|
+
* as much as any per-user one — so it is not "the user half" of anything.
|
|
94
|
+
*
|
|
95
|
+
* `defaultAuth` is the credential used when no specific one is named, and is the only thing
|
|
96
|
+
* `loadAccessToken()` reads. It is not an "app identity" either: an api key acts as the user who
|
|
97
|
+
* created it, and a refresh token here is some user's grant being reused ambiently.
|
|
98
|
+
*
|
|
99
|
+
* An app that only acts for named users needs just `client`. An app that makes ambient calls needs
|
|
100
|
+
* a `defaultAuth`, plus `client` whenever that credential is a refresh token.
|
|
101
|
+
*/
|
|
102
|
+
export interface CalcomOAuthConfig {
|
|
25
103
|
/**
|
|
26
|
-
*
|
|
104
|
+
* The OAuth client registration, required for ANY refresh-token exchange.
|
|
27
105
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
106
|
+
* Both halves of the pair are required together, so a client cannot be half-configured into a state
|
|
107
|
+
* that composes an authorize URL carrying `client_id=undefined`.
|
|
30
108
|
*/
|
|
31
|
-
readonly
|
|
109
|
+
readonly client?: Maybe<CalcomAuthClientIdAndSecretPair>;
|
|
110
|
+
/**
|
|
111
|
+
* The credential used when no specific one is named.
|
|
112
|
+
*/
|
|
113
|
+
readonly defaultAuth?: Maybe<CalcomAuthCredential>;
|
|
32
114
|
}
|
|
33
115
|
export interface CalcomOAuthFetchFactoryInput {
|
|
34
116
|
}
|
|
35
117
|
export type CalcomOAuthFetchFactory = FactoryWithInput<ConfiguredFetch, CalcomOAuthFetchFactoryInput>;
|
|
36
|
-
export type
|
|
37
|
-
readonly refreshToken: CalcomRefreshToken;
|
|
38
|
-
readonly userAccessTokenCache?: Maybe<CalcomAccessTokenCache>;
|
|
39
|
-
};
|
|
40
|
-
export type CalcomOAuthMakeUserAccessTokenFactory = FactoryWithRequiredInput<CalcomAccessTokenFactory, CalcomOAuthMakeUserAccessTokenFactoryInput>;
|
|
118
|
+
export type CalcomOAuthMakeAccessTokenFactory = FactoryWithRequiredInput<CalcomAccessTokenFactory, CalcomAuthCredential>;
|
|
41
119
|
/**
|
|
42
120
|
* Context used for performing fetch() and fetchJson() calls with a configured fetch instance.
|
|
43
121
|
*/
|
|
44
122
|
export interface CalcomOAuthContext {
|
|
45
123
|
readonly fetch: ConfiguredFetch;
|
|
46
124
|
readonly fetchJson: FetchJsonFunction;
|
|
125
|
+
/**
|
|
126
|
+
* Resolves the access token for {@link CalcomOAuthConfig.defaultAuth}.
|
|
127
|
+
*
|
|
128
|
+
* `makeAccessTokenFactory(config.defaultAuth)`, built once at construction so its in-memory token
|
|
129
|
+
* tier and its refresh-token rotation are shared across the whole context.
|
|
130
|
+
*/
|
|
47
131
|
readonly loadAccessToken: CalcomAccessTokenFactory;
|
|
48
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Builds an access token factory for one credential.
|
|
134
|
+
*
|
|
135
|
+
* Each returned factory owns its own rotation and cache tier, so refreshing one credential never
|
|
136
|
+
* spends another's token.
|
|
137
|
+
*/
|
|
138
|
+
readonly makeAccessTokenFactory: CalcomOAuthMakeAccessTokenFactory;
|
|
49
139
|
readonly config: CalcomOAuthConfig;
|
|
50
140
|
}
|
|
51
141
|
export interface CalcomOAuthContextRef {
|
|
52
142
|
readonly oauthContext: CalcomOAuthContext;
|
|
53
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* @deprecated use {@link CALCOM_OAUTH_API_URL} instead. This was previously used as the fetch base
|
|
146
|
+
* URL while the endpoint path `/oauth/token` was also appended, resolving to a doubly-pathed URL.
|
|
147
|
+
*/
|
|
148
|
+
export declare const CALCOM_OAUTH_TOKEN_URL = "https://api.cal.com/v2/auth/oauth2/token";
|
package/src/lib/oauth/oauth.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export interface CalcomAccessToken {
|
|
|
27
27
|
*/
|
|
28
28
|
readonly expiresAt: Date;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* A stable key identifying a cached CalcomAccessToken.
|
|
32
|
+
*
|
|
33
|
+
* Should be owned by the caller (a user/profile id) rather than derived from the refresh token,
|
|
34
|
+
* since Cal.com rotates refresh tokens on every use and a token-derived key changes with them.
|
|
35
|
+
*/
|
|
36
|
+
export type CalcomAccessTokenCacheKey = string;
|
|
30
37
|
/**
|
|
31
38
|
* Used for retrieving and storing CalcomAccessToken values.
|
|
32
39
|
*/
|
|
@@ -1,13 +1,53 @@
|
|
|
1
|
+
import { type FetchHandler } from '@dereekb/util/fetch';
|
|
1
2
|
import { type CalcomOAuthConfig, type CalcomOAuthContextRef, type CalcomOAuthFetchFactory } from './oauth.config';
|
|
3
|
+
import { type CalcomApiKey } from '../calcom.config';
|
|
2
4
|
import { type LogCalcomServerErrorFunction } from '../calcom.error.api';
|
|
3
|
-
import { type CalcomAccessTokenCache, type CalcomAccessTokenFactory, type CalcomAccessTokenRefresher } from './oauth';
|
|
5
|
+
import { type CalcomAccessToken, type CalcomAccessTokenCache, type CalcomAccessTokenFactory, type CalcomAccessTokenRefresher } from './oauth';
|
|
4
6
|
import { type Maybe, type Milliseconds } from '@dereekb/util';
|
|
7
|
+
import { type CalcomOAuthTokenResponse } from './oauth.api';
|
|
5
8
|
export type CalcomOAuth = CalcomOAuthContextRef;
|
|
9
|
+
/**
|
|
10
|
+
* Maps a {@link CalcomOAuthTokenResponse} to a {@link CalcomAccessToken}.
|
|
11
|
+
*
|
|
12
|
+
* Pure: the caller owns the rotated refresh token that comes back on the result, so each token
|
|
13
|
+
* scope (server-level vs per-user) tracks its own rotation instead of sharing one variable.
|
|
14
|
+
*
|
|
15
|
+
* @param response - The token response returned by the Cal.com token endpoint.
|
|
16
|
+
* @returns The equivalent CalcomAccessToken, with `expiresAt` resolved against the current time.
|
|
17
|
+
*
|
|
18
|
+
* @__NO_SIDE_EFFECTS__
|
|
19
|
+
*/
|
|
20
|
+
export declare function calcomAccessTokenFromTokenResponse(response: CalcomOAuthTokenResponse): CalcomAccessToken;
|
|
21
|
+
/**
|
|
22
|
+
* The lifetime given to the synthetic access token an api key is wrapped in.
|
|
23
|
+
*
|
|
24
|
+
* Cal.com api keys do not expire; the value only has to outlive any process holding one, so the
|
|
25
|
+
* token satisfies the same expiration check every other token goes through.
|
|
26
|
+
*/
|
|
27
|
+
export declare const CALCOM_API_KEY_ACCESS_TOKEN_EXPIRATION: Milliseconds;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a {@link CalcomApiKey} as a static {@link CalcomAccessToken}.
|
|
30
|
+
*
|
|
31
|
+
* An api key is already a bearer token acting as the user who created it, so there is nothing to
|
|
32
|
+
* exchange and nothing to refresh.
|
|
33
|
+
*
|
|
34
|
+
* @param apiKey - The Cal.com api key.
|
|
35
|
+
* @returns The equivalent static CalcomAccessToken.
|
|
36
|
+
*
|
|
37
|
+
* @__NO_SIDE_EFFECTS__
|
|
38
|
+
*/
|
|
39
|
+
export declare function calcomAccessTokenFromApiKey(apiKey: CalcomApiKey): CalcomAccessToken;
|
|
6
40
|
export interface CalcomOAuthFactoryConfig {
|
|
7
41
|
/**
|
|
8
42
|
* Creates a new fetch instance to use when making calls.
|
|
9
43
|
*/
|
|
10
44
|
readonly fetchFactory?: CalcomOAuthFetchFactory;
|
|
45
|
+
/**
|
|
46
|
+
* Custom FetchHandler to use with the default fetchFactory.
|
|
47
|
+
*
|
|
48
|
+
* Defaults to a {@link calcomRateLimitedFetchHandler}. Ignored when a `fetchFactory` is provided.
|
|
49
|
+
*/
|
|
50
|
+
readonly fetchHandler?: Maybe<FetchHandler>;
|
|
11
51
|
/**
|
|
12
52
|
* Custom log error function.
|
|
13
53
|
*/
|