@corti/sdk 0.4.0 → 0.5.0-rc
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/README.md +5 -11
- package/dist/cjs/Client.js +2 -2
- package/dist/cjs/custom/CortiClient.d.ts +21 -11
- package/dist/cjs/custom/CortiClient.js +13 -8
- package/dist/cjs/custom/RefreshBearerProvider.d.ts +7 -2
- package/dist/cjs/custom/RefreshBearerProvider.js +38 -11
- package/dist/cjs/custom/utils/decodeToken.d.ts +27 -0
- package/dist/cjs/custom/utils/decodeToken.js +77 -0
- package/dist/cjs/custom/utils/getEnvironmentFromString.d.ts +3 -4
- package/dist/cjs/custom/utils/resolveClientOptions.d.ts +11 -0
- package/dist/cjs/custom/utils/resolveClientOptions.js +109 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Client.mjs +2 -2
- package/dist/esm/custom/CortiClient.d.mts +21 -11
- package/dist/esm/custom/CortiClient.mjs +14 -9
- package/dist/esm/custom/RefreshBearerProvider.d.mts +7 -2
- package/dist/esm/custom/RefreshBearerProvider.mjs +38 -11
- package/dist/esm/custom/utils/decodeToken.d.mts +27 -0
- package/dist/esm/custom/utils/decodeToken.mjs +74 -0
- package/dist/esm/custom/utils/getEnvironmentFromString.d.mts +3 -4
- package/dist/esm/custom/utils/resolveClientOptions.d.mts +11 -0
- package/dist/esm/custom/utils/resolveClientOptions.mjs +73 -0
- package/dist/esm/version.d.mts +1 -1
- package/dist/esm/version.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,29 +38,23 @@ const client = new CortiClient({
|
|
|
38
38
|
|
|
39
39
|
// Or using a bearer token
|
|
40
40
|
const client = new CortiClient({
|
|
41
|
-
environment: CortiEnvironment.Eu,
|
|
42
|
-
tenantName: "YOUR_TENANT_NAME",
|
|
43
41
|
auth: {
|
|
44
|
-
accessToken: "YOUR_ACCESS_TOKEN"
|
|
45
|
-
// Optional: refresh token for automatic token refresh
|
|
46
|
-
refreshToken: "YOUR_REFRESH_TOKEN",
|
|
47
|
-
expiresIn: 3600,
|
|
48
|
-
refreshExpiresIn: 86400,
|
|
42
|
+
accessToken: "YOUR_ACCESS_TOKEN"
|
|
49
43
|
},
|
|
50
44
|
});
|
|
51
45
|
|
|
52
46
|
// Or using just a refresh function (no initial access token needed)
|
|
53
47
|
const client = new CortiClient({
|
|
54
|
-
environment: CortiEnvironment.Eu,
|
|
55
|
-
tenantName: "YOUR_TENANT_NAME",
|
|
56
48
|
auth: {
|
|
49
|
+
// refreshToken will be undefined for the first call, then it will be the refreshToken returned from the previous token request
|
|
57
50
|
refreshAccessToken: async (refreshToken?: string) => {
|
|
58
51
|
// Your custom logic to get a new access token
|
|
59
|
-
const response = await fetch("https://your-auth-server/
|
|
52
|
+
const response = await fetch("https://your-auth-server/token", {
|
|
60
53
|
method: "POST",
|
|
61
54
|
headers: { "Content-Type": "application/json" },
|
|
62
|
-
body: JSON.stringify({ refreshToken
|
|
55
|
+
body: JSON.stringify({ refreshToken }),
|
|
63
56
|
});
|
|
57
|
+
|
|
64
58
|
return response.json();
|
|
65
59
|
},
|
|
66
60
|
},
|
package/dist/cjs/Client.js
CHANGED
|
@@ -64,8 +64,8 @@ class CortiClient {
|
|
|
64
64
|
"Tenant-Name": _options === null || _options === void 0 ? void 0 : _options.tenantName,
|
|
65
65
|
"X-Fern-Language": "JavaScript",
|
|
66
66
|
"X-Fern-SDK-Name": "@corti/sdk",
|
|
67
|
-
"X-Fern-SDK-Version": "0.
|
|
68
|
-
"User-Agent": "@corti/sdk/0.
|
|
67
|
+
"X-Fern-SDK-Version": "0.5.0-rc",
|
|
68
|
+
"User-Agent": "@corti/sdk/0.5.0-rc",
|
|
69
69
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
70
70
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
71
71
|
}, _options === null || _options === void 0 ? void 0 : _options.headers) });
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*
|
|
15
15
|
* All the patches marked with `// Patch: ...` comments.
|
|
16
16
|
*/
|
|
17
|
-
import * as environments from "../environments.js";
|
|
18
17
|
import * as core from "../core/index.js";
|
|
19
18
|
import { Interactions } from "../api/resources/interactions/client/Client.js";
|
|
20
19
|
import { Recordings } from "../api/resources/recordings/client/Client.js";
|
|
@@ -28,37 +27,48 @@ import { Agents } from "../api/resources/agents/client/Client.js";
|
|
|
28
27
|
*/
|
|
29
28
|
import { Stream } from "./CustomStream.js";
|
|
30
29
|
import { Transcribe } from "./CustomTranscribe.js";
|
|
31
|
-
|
|
32
|
-
* Patch: added custom RefreshBearerProvider
|
|
33
|
-
*/
|
|
30
|
+
import { Environment, CortiInternalEnvironment } from "./utils/getEnvironmentFromString.js";
|
|
34
31
|
import { BearerOptions } from "./RefreshBearerProvider.js";
|
|
35
32
|
export declare namespace CortiClient {
|
|
36
33
|
/**
|
|
37
|
-
* Patch: added new public
|
|
34
|
+
* Patch: added new public type for `Options` + internal interfaces to create it
|
|
38
35
|
*/
|
|
39
36
|
interface ClientCredentials {
|
|
40
37
|
clientId: core.Supplier<string>;
|
|
41
38
|
clientSecret: core.Supplier<string>;
|
|
42
39
|
}
|
|
43
|
-
interface
|
|
40
|
+
interface BaseOptions {
|
|
41
|
+
/** Additional headers to include in requests. */
|
|
42
|
+
headers?: Record<string, string | core.Supplier<string | undefined> | undefined>;
|
|
43
|
+
}
|
|
44
|
+
interface OptionsWithClientCredentials extends BaseOptions {
|
|
44
45
|
/**
|
|
45
46
|
* Patch: allow to pass a custom string-based environment
|
|
46
47
|
* */
|
|
47
|
-
environment:
|
|
48
|
+
environment: Environment;
|
|
48
49
|
/** Override the Tenant-Name header */
|
|
49
50
|
tenantName: core.Supplier<string>;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
auth: ClientCredentials;
|
|
52
|
+
}
|
|
53
|
+
interface OptionsWithBearerToken extends BaseOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Patch: allow to pass a custom string-based environment
|
|
56
|
+
* */
|
|
57
|
+
environment?: Environment;
|
|
58
|
+
/** Override the Tenant-Name header */
|
|
59
|
+
tenantName?: core.Supplier<string>;
|
|
60
|
+
auth: BearerOptions;
|
|
53
61
|
}
|
|
62
|
+
type Options = OptionsWithClientCredentials | OptionsWithBearerToken;
|
|
54
63
|
/**
|
|
55
64
|
* Patch:
|
|
56
65
|
* - renamed `Options` to `InternalOptions`
|
|
57
66
|
* - added `token` field to support BearerProvider
|
|
58
67
|
* - made clientId and clientSecret optional
|
|
68
|
+
* - updated environment type to CortiInternalEnvironment
|
|
59
69
|
*/
|
|
60
70
|
interface InternalOptions {
|
|
61
|
-
environment:
|
|
71
|
+
environment: CortiInternalEnvironment;
|
|
62
72
|
/** Specify a custom URL to connect the client to. */
|
|
63
73
|
baseUrl?: core.Supplier<string>;
|
|
64
74
|
clientId?: core.Supplier<string>;
|
|
@@ -78,18 +78,23 @@ const Client_js_7 = require("../api/resources/agents/client/Client.js");
|
|
|
78
78
|
const CustomStream_js_1 = require("./CustomStream.js");
|
|
79
79
|
const CustomTranscribe_js_1 = require("./CustomTranscribe.js");
|
|
80
80
|
/**
|
|
81
|
-
* Patch: added custom
|
|
82
|
-
*/
|
|
83
|
-
const RefreshBearerProvider_js_1 = require("./RefreshBearerProvider.js");
|
|
84
|
-
/**
|
|
85
|
-
* Patch: added SDK_VERSION import
|
|
81
|
+
* Patch: added SDK_VERSION import and custom code imports
|
|
86
82
|
*/
|
|
87
83
|
const version_js_1 = require("../version.js");
|
|
88
84
|
const getEnvironmentFromString_js_1 = require("./utils/getEnvironmentFromString.js");
|
|
85
|
+
const resolveClientOptions_js_1 = require("./utils/resolveClientOptions.js");
|
|
86
|
+
const RefreshBearerProvider_js_1 = require("./RefreshBearerProvider.js");
|
|
89
87
|
class CortiClient {
|
|
90
88
|
constructor(_options) {
|
|
89
|
+
/**
|
|
90
|
+
* Patch: resolve tenantName and environment from options or token
|
|
91
|
+
*/
|
|
92
|
+
const { tenantName, environment, initialTokenResponse } = (0, resolveClientOptions_js_1.resolveClientOptions)(_options);
|
|
93
|
+
/**
|
|
94
|
+
* Patch: redefining options based on new schema
|
|
95
|
+
*/
|
|
91
96
|
this._options = Object.assign(Object.assign({}, _options), { headers: (0, headers_js_1.mergeHeaders)({
|
|
92
|
-
"Tenant-Name":
|
|
97
|
+
"Tenant-Name": tenantName,
|
|
93
98
|
"X-Fern-Language": "JavaScript",
|
|
94
99
|
"X-Fern-SDK-Name": "@corti/sdk",
|
|
95
100
|
/**
|
|
@@ -99,7 +104,7 @@ class CortiClient {
|
|
|
99
104
|
"User-Agent": `@corti/sdk/${version_js_1.SDK_VERSION}`,
|
|
100
105
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
101
106
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
102
|
-
}, _options === null || _options === void 0 ? void 0 : _options.headers), clientId: "clientId" in _options.auth ? _options.auth.clientId : undefined, clientSecret: "clientSecret" in _options.auth ? _options.auth.clientSecret : undefined, token: "accessToken" in _options.auth ? _options.auth.accessToken : undefined, environment: (0, getEnvironmentFromString_js_1.getEnvironment)(
|
|
107
|
+
}, _options === null || _options === void 0 ? void 0 : _options.headers), clientId: "clientId" in _options.auth ? _options.auth.clientId : undefined, clientSecret: "clientSecret" in _options.auth ? _options.auth.clientSecret : undefined, token: "accessToken" in _options.auth ? _options.auth.accessToken : undefined, tenantName, environment: (0, getEnvironmentFromString_js_1.getEnvironment)(environment) });
|
|
103
108
|
/**
|
|
104
109
|
* Patch: if `clientId` is provided, use OAuthTokenProvider, otherwise use BearerProvider
|
|
105
110
|
*/
|
|
@@ -112,7 +117,7 @@ class CortiClient {
|
|
|
112
117
|
*/
|
|
113
118
|
authClient: new CortiAuth_js_1.Auth(this._options),
|
|
114
119
|
}) :
|
|
115
|
-
new RefreshBearerProvider_js_1.RefreshBearerProvider(_options.auth);
|
|
120
|
+
new RefreshBearerProvider_js_1.RefreshBearerProvider(Object.assign(Object.assign({}, _options.auth), { initialTokenResponse }));
|
|
116
121
|
}
|
|
117
122
|
get interactions() {
|
|
118
123
|
var _a;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* RefreshBearerProvider used as a replacement of OAuthTokenProvider, in case when accessToken from outside of library was used instead of Client credentials.
|
|
3
3
|
*/
|
|
4
4
|
import * as api from "../api/index.js";
|
|
5
|
-
type ExpectedTokenResponse = Omit<api.GetTokenResponse,
|
|
5
|
+
export type ExpectedTokenResponse = Omit<api.GetTokenResponse, "tokenType" | "expiresIn"> & {
|
|
6
6
|
tokenType?: string;
|
|
7
|
+
expiresIn?: number;
|
|
7
8
|
};
|
|
8
9
|
type RefreshAccessTokenFunction = (refreshToken?: string) => Promise<ExpectedTokenResponse> | ExpectedTokenResponse;
|
|
9
10
|
export type BearerOptions = Partial<Omit<api.GetTokenResponse, 'accessToken'>> & ({
|
|
@@ -20,9 +21,13 @@ export declare class RefreshBearerProvider {
|
|
|
20
21
|
private _refreshAccessToken;
|
|
21
22
|
private _expiresAt;
|
|
22
23
|
private _refreshExpiresAt;
|
|
23
|
-
|
|
24
|
+
private _initialTokenResponse;
|
|
25
|
+
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, initialTokenResponse, }: BearerOptions & {
|
|
26
|
+
initialTokenResponse?: Promise<ExpectedTokenResponse>;
|
|
27
|
+
});
|
|
24
28
|
getToken(): Promise<string>;
|
|
25
29
|
private refresh;
|
|
26
30
|
private getExpiresAt;
|
|
31
|
+
private parseTokenExpiry;
|
|
27
32
|
}
|
|
28
33
|
export {};
|
|
@@ -47,39 +47,66 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
48
|
exports.RefreshBearerProvider = void 0;
|
|
49
49
|
const core = __importStar(require("../core/index.js"));
|
|
50
|
+
const decodeToken_js_1 = require("./utils/decodeToken.js");
|
|
50
51
|
class RefreshBearerProvider {
|
|
51
|
-
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, }) {
|
|
52
|
+
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, initialTokenResponse, }) {
|
|
52
53
|
this.BUFFER_IN_MINUTES = 2;
|
|
53
|
-
this.
|
|
54
|
-
this._refreshExpiresAt = this.getExpiresAt(refreshExpiresIn, 0);
|
|
55
|
-
this._accessToken = accessToken || 'no_token';
|
|
54
|
+
this._accessToken = accessToken || "no_token";
|
|
56
55
|
this._refreshToken = refreshToken;
|
|
56
|
+
this._initialTokenResponse = initialTokenResponse;
|
|
57
|
+
this._expiresAt = this.getExpiresAt(expiresIn, this._accessToken, this.BUFFER_IN_MINUTES);
|
|
58
|
+
this._refreshExpiresAt = this.getExpiresAt(refreshExpiresIn, this._refreshToken, 0);
|
|
57
59
|
this._refreshAccessToken = refreshAccessToken;
|
|
58
60
|
}
|
|
59
61
|
getToken() {
|
|
60
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
-
if (this._accessToken && this._expiresAt > new Date()) {
|
|
63
|
+
if (this._accessToken && this._accessToken !== "no_token" && this._expiresAt > new Date()) {
|
|
62
64
|
return core.Supplier.get(this._accessToken);
|
|
63
65
|
}
|
|
66
|
+
if (this._initialTokenResponse) {
|
|
67
|
+
const tokenResponse = yield this._initialTokenResponse;
|
|
68
|
+
this._initialTokenResponse = undefined;
|
|
69
|
+
this._accessToken = tokenResponse.accessToken;
|
|
70
|
+
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, tokenResponse.accessToken, this.BUFFER_IN_MINUTES);
|
|
71
|
+
this._refreshToken = tokenResponse.refreshToken;
|
|
72
|
+
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, this._refreshToken, 0);
|
|
73
|
+
return this.getToken();
|
|
74
|
+
}
|
|
64
75
|
return this.refresh();
|
|
65
76
|
});
|
|
66
77
|
}
|
|
67
78
|
refresh() {
|
|
68
79
|
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
-
if (!this._refreshAccessToken || this._refreshToken && this._refreshExpiresAt < new Date()) {
|
|
80
|
+
if (!this._refreshAccessToken || (this._refreshToken && this._refreshExpiresAt < new Date())) {
|
|
70
81
|
return core.Supplier.get(this._accessToken);
|
|
71
82
|
}
|
|
72
83
|
const tokenResponse = yield this._refreshAccessToken(this._refreshToken);
|
|
73
84
|
this._accessToken = tokenResponse.accessToken;
|
|
74
|
-
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, this.BUFFER_IN_MINUTES);
|
|
85
|
+
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, tokenResponse.accessToken, this.BUFFER_IN_MINUTES);
|
|
75
86
|
this._refreshToken = tokenResponse.refreshToken;
|
|
76
|
-
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, 0);
|
|
87
|
+
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, this._refreshToken, 0);
|
|
77
88
|
return this._accessToken;
|
|
78
89
|
});
|
|
79
90
|
}
|
|
80
|
-
getExpiresAt(
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
getExpiresAt(expiresIn, token, bufferInMinutes = this.BUFFER_IN_MINUTES) {
|
|
92
|
+
if (typeof expiresIn === "number") {
|
|
93
|
+
const now = new Date();
|
|
94
|
+
return new Date(now.getTime() + expiresIn * 1000 - bufferInMinutes * 60 * 1000);
|
|
95
|
+
}
|
|
96
|
+
return this.parseTokenExpiry(token, bufferInMinutes) || this.getExpiresAt(0, token, bufferInMinutes);
|
|
97
|
+
}
|
|
98
|
+
parseTokenExpiry(token, bufferInMinutes) {
|
|
99
|
+
if (!token || token === "no_token") {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const decoded = (0, decodeToken_js_1.decodeToken)(token);
|
|
104
|
+
if (decoded && typeof decoded.expiresAt === "number") {
|
|
105
|
+
const ms = decoded.expiresAt * 1000 - bufferInMinutes * 60 * 1000;
|
|
106
|
+
return new Date(ms);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (_a) { }
|
|
83
110
|
}
|
|
84
111
|
}
|
|
85
112
|
exports.RefreshBearerProvider = RefreshBearerProvider;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes a JWT token and extracts environment and tenant details from its issuer URL.
|
|
3
|
+
*
|
|
4
|
+
* This function assumes the JWT token follows the standard header.payload.signature format.
|
|
5
|
+
* It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
|
|
6
|
+
* to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
|
|
7
|
+
* https://keycloak.{environment}.corti.app/realms/{tenant}.
|
|
8
|
+
*
|
|
9
|
+
* @param token - A JSON Web Token (JWT) string.
|
|
10
|
+
* @returns An object containing:
|
|
11
|
+
* - `environment`: The extracted environment from the issuer URL.
|
|
12
|
+
* - `tenant`: The extracted tenant from the issuer URL.
|
|
13
|
+
* - `accessToken`: The original token string.
|
|
14
|
+
* If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
|
|
15
|
+
*
|
|
16
|
+
* @throws Will throw an error if:
|
|
17
|
+
* - The token format is invalid.
|
|
18
|
+
* - The base64 decoding or URI decoding fails.
|
|
19
|
+
* - The JSON payload is invalid.
|
|
20
|
+
* - The token payload does not contain an issuer (iss) field.
|
|
21
|
+
*/
|
|
22
|
+
export declare function decodeToken(token: string): {
|
|
23
|
+
environment: string;
|
|
24
|
+
tenantName: string;
|
|
25
|
+
accessToken: string;
|
|
26
|
+
expiresAt: number | undefined;
|
|
27
|
+
} | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decodeToken = decodeToken;
|
|
4
|
+
/**
|
|
5
|
+
* Decodes a JWT token and extracts environment and tenant details from its issuer URL.
|
|
6
|
+
*
|
|
7
|
+
* This function assumes the JWT token follows the standard header.payload.signature format.
|
|
8
|
+
* It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
|
|
9
|
+
* to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
|
|
10
|
+
* https://keycloak.{environment}.corti.app/realms/{tenant}.
|
|
11
|
+
*
|
|
12
|
+
* @param token - A JSON Web Token (JWT) string.
|
|
13
|
+
* @returns An object containing:
|
|
14
|
+
* - `environment`: The extracted environment from the issuer URL.
|
|
15
|
+
* - `tenant`: The extracted tenant from the issuer URL.
|
|
16
|
+
* - `accessToken`: The original token string.
|
|
17
|
+
* If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
|
|
18
|
+
*
|
|
19
|
+
* @throws Will throw an error if:
|
|
20
|
+
* - The token format is invalid.
|
|
21
|
+
* - The base64 decoding or URI decoding fails.
|
|
22
|
+
* - The JSON payload is invalid.
|
|
23
|
+
* - The token payload does not contain an issuer (iss) field.
|
|
24
|
+
*/
|
|
25
|
+
function decodeToken(token) {
|
|
26
|
+
// Validate the token structure (should contain at least header and payload parts)
|
|
27
|
+
const parts = token ? token.split('.') : '';
|
|
28
|
+
if (parts.length < 2) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
// Retrieve the payload (second part) of the JWT token
|
|
32
|
+
const base64Url = parts[1];
|
|
33
|
+
// Replace URL-safe characters to match standard base64 encoding
|
|
34
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
35
|
+
// Decode the base64 string into a JSON string
|
|
36
|
+
let jsonPayload;
|
|
37
|
+
try {
|
|
38
|
+
jsonPayload = decodeURIComponent(atob(base64)
|
|
39
|
+
.split('')
|
|
40
|
+
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
|
41
|
+
.join(''));
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
// Parse the JSON string to obtain token details
|
|
47
|
+
let tokenDetails;
|
|
48
|
+
try {
|
|
49
|
+
tokenDetails = JSON.parse(jsonPayload);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// Extract the issuer URL from the token details
|
|
55
|
+
const issuerUrl = tokenDetails.iss;
|
|
56
|
+
if (!issuerUrl) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Regex to extract environment and tenant from issuer URL:
|
|
60
|
+
// Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}
|
|
61
|
+
// Note: Unnecessary escapes in character classes have been removed.
|
|
62
|
+
const regex = /^https:\/\/(keycloak|auth)\.([^.]+)\.corti\.app\/realms\/([^/]+)/;
|
|
63
|
+
const match = issuerUrl.match(regex);
|
|
64
|
+
// If the issuer URL matches the expected pattern, return the extracted values along with the token
|
|
65
|
+
if (match) {
|
|
66
|
+
const expiresAt = tokenDetails.exp && typeof tokenDetails.exp === 'number'
|
|
67
|
+
? tokenDetails.exp
|
|
68
|
+
: undefined;
|
|
69
|
+
return {
|
|
70
|
+
environment: match[2],
|
|
71
|
+
tenantName: match[3],
|
|
72
|
+
accessToken: token,
|
|
73
|
+
expiresAt,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as core from "../../core/index.js";
|
|
2
2
|
import * as environments from "../../environments.js";
|
|
3
|
-
type Environment =
|
|
4
|
-
type
|
|
5
|
-
export declare function getEnvironment(environment: Environment):
|
|
6
|
-
export {};
|
|
3
|
+
export type Environment = CortiInternalEnvironment | string;
|
|
4
|
+
export type CortiInternalEnvironment = core.Supplier<environments.CortiEnvironment | environments.CortiEnvironmentUrls>;
|
|
5
|
+
export declare function getEnvironment(environment: Environment): CortiInternalEnvironment;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as core from "../../core/index.js";
|
|
2
|
+
import { CortiClient } from "../CortiClient.js";
|
|
3
|
+
import { Environment } from "./getEnvironmentFromString.js";
|
|
4
|
+
import { ExpectedTokenResponse } from "../RefreshBearerProvider.js";
|
|
5
|
+
type ResolvedClientOptions = {
|
|
6
|
+
environment: Environment;
|
|
7
|
+
tenantName: core.Supplier<string>;
|
|
8
|
+
initialTokenResponse?: Promise<ExpectedTokenResponse>;
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveClientOptions(options: CortiClient.Options): ResolvedClientOptions;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.resolveClientOptions = resolveClientOptions;
|
|
46
|
+
const core = __importStar(require("../../core/index.js"));
|
|
47
|
+
const decodeToken_js_1 = require("./decodeToken.js");
|
|
48
|
+
const index_js_1 = require("../../core/schemas/index.js");
|
|
49
|
+
const getEnvironmentFromString_js_1 = require("./getEnvironmentFromString.js");
|
|
50
|
+
function isClientCredentialsOptions(options) {
|
|
51
|
+
return "clientId" in options.auth;
|
|
52
|
+
}
|
|
53
|
+
function resolveClientOptions(options) {
|
|
54
|
+
if (isClientCredentialsOptions(options)) {
|
|
55
|
+
return {
|
|
56
|
+
tenantName: options.tenantName,
|
|
57
|
+
environment: options.environment,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if ("accessToken" in options.auth && options.auth.accessToken) {
|
|
61
|
+
const decoded = (0, decodeToken_js_1.decodeToken)(options.auth.accessToken);
|
|
62
|
+
if (!decoded) {
|
|
63
|
+
throw new index_js_1.ParseError([
|
|
64
|
+
{
|
|
65
|
+
path: ["auth", "accessToken"],
|
|
66
|
+
message: "Invalid access token format",
|
|
67
|
+
},
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
tenantName: options.tenantName || decoded.tenantName,
|
|
72
|
+
environment: options.environment || decoded.environment,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* This branch -- is when we have "refreshAccessToken" defined but no accessToken.
|
|
77
|
+
* Trying to avoid initial request at all cost
|
|
78
|
+
*/
|
|
79
|
+
if (options.tenantName && options.environment) {
|
|
80
|
+
return {
|
|
81
|
+
tenantName: options.tenantName,
|
|
82
|
+
environment: options.environment,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const tokenResponsePromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const tokenResponse = yield core.Supplier.get(options.auth.refreshAccessToken);
|
|
87
|
+
const decoded = (0, decodeToken_js_1.decodeToken)(tokenResponse.accessToken);
|
|
88
|
+
if (!decoded) {
|
|
89
|
+
throw new index_js_1.ParseError([
|
|
90
|
+
{
|
|
91
|
+
path: ["auth", "refreshAccessToken"],
|
|
92
|
+
message: "Returned invalid access token format",
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
tokenResponse,
|
|
98
|
+
tenantName: decoded.tenantName,
|
|
99
|
+
environment: decoded.environment,
|
|
100
|
+
};
|
|
101
|
+
}))();
|
|
102
|
+
return {
|
|
103
|
+
tenantName: options.tenantName ||
|
|
104
|
+
tokenResponsePromise.then(({ tenantName }) => tenantName),
|
|
105
|
+
environment: options.environment ||
|
|
106
|
+
tokenResponsePromise.then(({ environment }) => core.Supplier.get((0, getEnvironmentFromString_js_1.getEnvironment)(environment))),
|
|
107
|
+
initialTokenResponse: tokenResponsePromise.then((result) => result.tokenResponse),
|
|
108
|
+
};
|
|
109
|
+
}
|
package/dist/cjs/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.
|
|
1
|
+
export declare const SDK_VERSION = "0.5.0-rc";
|
package/dist/cjs/version.js
CHANGED
package/dist/esm/Client.mjs
CHANGED
|
@@ -28,8 +28,8 @@ export class CortiClient {
|
|
|
28
28
|
"Tenant-Name": _options === null || _options === void 0 ? void 0 : _options.tenantName,
|
|
29
29
|
"X-Fern-Language": "JavaScript",
|
|
30
30
|
"X-Fern-SDK-Name": "@corti/sdk",
|
|
31
|
-
"X-Fern-SDK-Version": "0.
|
|
32
|
-
"User-Agent": "@corti/sdk/0.
|
|
31
|
+
"X-Fern-SDK-Version": "0.5.0-rc",
|
|
32
|
+
"User-Agent": "@corti/sdk/0.5.0-rc",
|
|
33
33
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
34
34
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
35
35
|
}, _options === null || _options === void 0 ? void 0 : _options.headers) });
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*
|
|
15
15
|
* All the patches marked with `// Patch: ...` comments.
|
|
16
16
|
*/
|
|
17
|
-
import * as environments from "../environments.mjs";
|
|
18
17
|
import * as core from "../core/index.mjs";
|
|
19
18
|
import { Interactions } from "../api/resources/interactions/client/Client.mjs";
|
|
20
19
|
import { Recordings } from "../api/resources/recordings/client/Client.mjs";
|
|
@@ -28,37 +27,48 @@ import { Agents } from "../api/resources/agents/client/Client.mjs";
|
|
|
28
27
|
*/
|
|
29
28
|
import { Stream } from "./CustomStream.mjs";
|
|
30
29
|
import { Transcribe } from "./CustomTranscribe.mjs";
|
|
31
|
-
|
|
32
|
-
* Patch: added custom RefreshBearerProvider
|
|
33
|
-
*/
|
|
30
|
+
import { Environment, CortiInternalEnvironment } from "./utils/getEnvironmentFromString.mjs";
|
|
34
31
|
import { BearerOptions } from "./RefreshBearerProvider.mjs";
|
|
35
32
|
export declare namespace CortiClient {
|
|
36
33
|
/**
|
|
37
|
-
* Patch: added new public
|
|
34
|
+
* Patch: added new public type for `Options` + internal interfaces to create it
|
|
38
35
|
*/
|
|
39
36
|
interface ClientCredentials {
|
|
40
37
|
clientId: core.Supplier<string>;
|
|
41
38
|
clientSecret: core.Supplier<string>;
|
|
42
39
|
}
|
|
43
|
-
interface
|
|
40
|
+
interface BaseOptions {
|
|
41
|
+
/** Additional headers to include in requests. */
|
|
42
|
+
headers?: Record<string, string | core.Supplier<string | undefined> | undefined>;
|
|
43
|
+
}
|
|
44
|
+
interface OptionsWithClientCredentials extends BaseOptions {
|
|
44
45
|
/**
|
|
45
46
|
* Patch: allow to pass a custom string-based environment
|
|
46
47
|
* */
|
|
47
|
-
environment:
|
|
48
|
+
environment: Environment;
|
|
48
49
|
/** Override the Tenant-Name header */
|
|
49
50
|
tenantName: core.Supplier<string>;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
auth: ClientCredentials;
|
|
52
|
+
}
|
|
53
|
+
interface OptionsWithBearerToken extends BaseOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Patch: allow to pass a custom string-based environment
|
|
56
|
+
* */
|
|
57
|
+
environment?: Environment;
|
|
58
|
+
/** Override the Tenant-Name header */
|
|
59
|
+
tenantName?: core.Supplier<string>;
|
|
60
|
+
auth: BearerOptions;
|
|
53
61
|
}
|
|
62
|
+
type Options = OptionsWithClientCredentials | OptionsWithBearerToken;
|
|
54
63
|
/**
|
|
55
64
|
* Patch:
|
|
56
65
|
* - renamed `Options` to `InternalOptions`
|
|
57
66
|
* - added `token` field to support BearerProvider
|
|
58
67
|
* - made clientId and clientSecret optional
|
|
68
|
+
* - updated environment type to CortiInternalEnvironment
|
|
59
69
|
*/
|
|
60
70
|
interface InternalOptions {
|
|
61
|
-
environment:
|
|
71
|
+
environment: CortiInternalEnvironment;
|
|
62
72
|
/** Specify a custom URL to connect the client to. */
|
|
63
73
|
baseUrl?: core.Supplier<string>;
|
|
64
74
|
clientId?: core.Supplier<string>;
|
|
@@ -42,18 +42,23 @@ import { Agents } from "../api/resources/agents/client/Client.mjs";
|
|
|
42
42
|
import { Stream } from "./CustomStream.mjs";
|
|
43
43
|
import { Transcribe } from "./CustomTranscribe.mjs";
|
|
44
44
|
/**
|
|
45
|
-
* Patch: added custom
|
|
45
|
+
* Patch: added SDK_VERSION import and custom code imports
|
|
46
46
|
*/
|
|
47
|
-
import {
|
|
48
|
-
/**
|
|
49
|
-
* Patch: added SDK_VERSION import
|
|
50
|
-
*/
|
|
51
|
-
import { SDK_VERSION } from '../version.mjs';
|
|
47
|
+
import { SDK_VERSION } from "../version.mjs";
|
|
52
48
|
import { getEnvironment } from "./utils/getEnvironmentFromString.mjs";
|
|
49
|
+
import { resolveClientOptions } from "./utils/resolveClientOptions.mjs";
|
|
50
|
+
import { RefreshBearerProvider } from "./RefreshBearerProvider.mjs";
|
|
53
51
|
export class CortiClient {
|
|
54
52
|
constructor(_options) {
|
|
53
|
+
/**
|
|
54
|
+
* Patch: resolve tenantName and environment from options or token
|
|
55
|
+
*/
|
|
56
|
+
const { tenantName, environment, initialTokenResponse } = resolveClientOptions(_options);
|
|
57
|
+
/**
|
|
58
|
+
* Patch: redefining options based on new schema
|
|
59
|
+
*/
|
|
55
60
|
this._options = Object.assign(Object.assign({}, _options), { headers: mergeHeaders({
|
|
56
|
-
"Tenant-Name":
|
|
61
|
+
"Tenant-Name": tenantName,
|
|
57
62
|
"X-Fern-Language": "JavaScript",
|
|
58
63
|
"X-Fern-SDK-Name": "@corti/sdk",
|
|
59
64
|
/**
|
|
@@ -63,7 +68,7 @@ export class CortiClient {
|
|
|
63
68
|
"User-Agent": `@corti/sdk/${SDK_VERSION}`,
|
|
64
69
|
"X-Fern-Runtime": core.RUNTIME.type,
|
|
65
70
|
"X-Fern-Runtime-Version": core.RUNTIME.version,
|
|
66
|
-
}, _options === null || _options === void 0 ? void 0 : _options.headers), clientId: "clientId" in _options.auth ? _options.auth.clientId : undefined, clientSecret: "clientSecret" in _options.auth ? _options.auth.clientSecret : undefined, token: "accessToken" in _options.auth ? _options.auth.accessToken : undefined, environment: getEnvironment(
|
|
71
|
+
}, _options === null || _options === void 0 ? void 0 : _options.headers), clientId: "clientId" in _options.auth ? _options.auth.clientId : undefined, clientSecret: "clientSecret" in _options.auth ? _options.auth.clientSecret : undefined, token: "accessToken" in _options.auth ? _options.auth.accessToken : undefined, tenantName, environment: getEnvironment(environment) });
|
|
67
72
|
/**
|
|
68
73
|
* Patch: if `clientId` is provided, use OAuthTokenProvider, otherwise use BearerProvider
|
|
69
74
|
*/
|
|
@@ -76,7 +81,7 @@ export class CortiClient {
|
|
|
76
81
|
*/
|
|
77
82
|
authClient: new Auth(this._options),
|
|
78
83
|
}) :
|
|
79
|
-
new RefreshBearerProvider(_options.auth);
|
|
84
|
+
new RefreshBearerProvider(Object.assign(Object.assign({}, _options.auth), { initialTokenResponse }));
|
|
80
85
|
}
|
|
81
86
|
get interactions() {
|
|
82
87
|
var _a;
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* RefreshBearerProvider used as a replacement of OAuthTokenProvider, in case when accessToken from outside of library was used instead of Client credentials.
|
|
3
3
|
*/
|
|
4
4
|
import * as api from "../api/index.mjs";
|
|
5
|
-
type ExpectedTokenResponse = Omit<api.GetTokenResponse,
|
|
5
|
+
export type ExpectedTokenResponse = Omit<api.GetTokenResponse, "tokenType" | "expiresIn"> & {
|
|
6
6
|
tokenType?: string;
|
|
7
|
+
expiresIn?: number;
|
|
7
8
|
};
|
|
8
9
|
type RefreshAccessTokenFunction = (refreshToken?: string) => Promise<ExpectedTokenResponse> | ExpectedTokenResponse;
|
|
9
10
|
export type BearerOptions = Partial<Omit<api.GetTokenResponse, 'accessToken'>> & ({
|
|
@@ -20,9 +21,13 @@ export declare class RefreshBearerProvider {
|
|
|
20
21
|
private _refreshAccessToken;
|
|
21
22
|
private _expiresAt;
|
|
22
23
|
private _refreshExpiresAt;
|
|
23
|
-
|
|
24
|
+
private _initialTokenResponse;
|
|
25
|
+
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, initialTokenResponse, }: BearerOptions & {
|
|
26
|
+
initialTokenResponse?: Promise<ExpectedTokenResponse>;
|
|
27
|
+
});
|
|
24
28
|
getToken(): Promise<string>;
|
|
25
29
|
private refresh;
|
|
26
30
|
private getExpiresAt;
|
|
31
|
+
private parseTokenExpiry;
|
|
27
32
|
}
|
|
28
33
|
export {};
|
|
@@ -11,38 +11,65 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
});
|
|
12
12
|
};
|
|
13
13
|
import * as core from "../core/index.mjs";
|
|
14
|
+
import { decodeToken } from "./utils/decodeToken.mjs";
|
|
14
15
|
export class RefreshBearerProvider {
|
|
15
|
-
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, }) {
|
|
16
|
+
constructor({ accessToken, refreshAccessToken, refreshToken, refreshExpiresIn, expiresIn, initialTokenResponse, }) {
|
|
16
17
|
this.BUFFER_IN_MINUTES = 2;
|
|
17
|
-
this.
|
|
18
|
-
this._refreshExpiresAt = this.getExpiresAt(refreshExpiresIn, 0);
|
|
19
|
-
this._accessToken = accessToken || 'no_token';
|
|
18
|
+
this._accessToken = accessToken || "no_token";
|
|
20
19
|
this._refreshToken = refreshToken;
|
|
20
|
+
this._initialTokenResponse = initialTokenResponse;
|
|
21
|
+
this._expiresAt = this.getExpiresAt(expiresIn, this._accessToken, this.BUFFER_IN_MINUTES);
|
|
22
|
+
this._refreshExpiresAt = this.getExpiresAt(refreshExpiresIn, this._refreshToken, 0);
|
|
21
23
|
this._refreshAccessToken = refreshAccessToken;
|
|
22
24
|
}
|
|
23
25
|
getToken() {
|
|
24
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
-
if (this._accessToken && this._expiresAt > new Date()) {
|
|
27
|
+
if (this._accessToken && this._accessToken !== "no_token" && this._expiresAt > new Date()) {
|
|
26
28
|
return core.Supplier.get(this._accessToken);
|
|
27
29
|
}
|
|
30
|
+
if (this._initialTokenResponse) {
|
|
31
|
+
const tokenResponse = yield this._initialTokenResponse;
|
|
32
|
+
this._initialTokenResponse = undefined;
|
|
33
|
+
this._accessToken = tokenResponse.accessToken;
|
|
34
|
+
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, tokenResponse.accessToken, this.BUFFER_IN_MINUTES);
|
|
35
|
+
this._refreshToken = tokenResponse.refreshToken;
|
|
36
|
+
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, this._refreshToken, 0);
|
|
37
|
+
return this.getToken();
|
|
38
|
+
}
|
|
28
39
|
return this.refresh();
|
|
29
40
|
});
|
|
30
41
|
}
|
|
31
42
|
refresh() {
|
|
32
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
-
if (!this._refreshAccessToken || this._refreshToken && this._refreshExpiresAt < new Date()) {
|
|
44
|
+
if (!this._refreshAccessToken || (this._refreshToken && this._refreshExpiresAt < new Date())) {
|
|
34
45
|
return core.Supplier.get(this._accessToken);
|
|
35
46
|
}
|
|
36
47
|
const tokenResponse = yield this._refreshAccessToken(this._refreshToken);
|
|
37
48
|
this._accessToken = tokenResponse.accessToken;
|
|
38
|
-
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, this.BUFFER_IN_MINUTES);
|
|
49
|
+
this._expiresAt = this.getExpiresAt(tokenResponse.expiresIn, tokenResponse.accessToken, this.BUFFER_IN_MINUTES);
|
|
39
50
|
this._refreshToken = tokenResponse.refreshToken;
|
|
40
|
-
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, 0);
|
|
51
|
+
this._refreshExpiresAt = this.getExpiresAt(tokenResponse.refreshExpiresIn, this._refreshToken, 0);
|
|
41
52
|
return this._accessToken;
|
|
42
53
|
});
|
|
43
54
|
}
|
|
44
|
-
getExpiresAt(
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
getExpiresAt(expiresIn, token, bufferInMinutes = this.BUFFER_IN_MINUTES) {
|
|
56
|
+
if (typeof expiresIn === "number") {
|
|
57
|
+
const now = new Date();
|
|
58
|
+
return new Date(now.getTime() + expiresIn * 1000 - bufferInMinutes * 60 * 1000);
|
|
59
|
+
}
|
|
60
|
+
return this.parseTokenExpiry(token, bufferInMinutes) || this.getExpiresAt(0, token, bufferInMinutes);
|
|
61
|
+
}
|
|
62
|
+
parseTokenExpiry(token, bufferInMinutes) {
|
|
63
|
+
if (!token || token === "no_token") {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const decoded = decodeToken(token);
|
|
68
|
+
if (decoded && typeof decoded.expiresAt === "number") {
|
|
69
|
+
const ms = decoded.expiresAt * 1000 - bufferInMinutes * 60 * 1000;
|
|
70
|
+
return new Date(ms);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (_a) { }
|
|
47
74
|
}
|
|
48
75
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes a JWT token and extracts environment and tenant details from its issuer URL.
|
|
3
|
+
*
|
|
4
|
+
* This function assumes the JWT token follows the standard header.payload.signature format.
|
|
5
|
+
* It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
|
|
6
|
+
* to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
|
|
7
|
+
* https://keycloak.{environment}.corti.app/realms/{tenant}.
|
|
8
|
+
*
|
|
9
|
+
* @param token - A JSON Web Token (JWT) string.
|
|
10
|
+
* @returns An object containing:
|
|
11
|
+
* - `environment`: The extracted environment from the issuer URL.
|
|
12
|
+
* - `tenant`: The extracted tenant from the issuer URL.
|
|
13
|
+
* - `accessToken`: The original token string.
|
|
14
|
+
* If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
|
|
15
|
+
*
|
|
16
|
+
* @throws Will throw an error if:
|
|
17
|
+
* - The token format is invalid.
|
|
18
|
+
* - The base64 decoding or URI decoding fails.
|
|
19
|
+
* - The JSON payload is invalid.
|
|
20
|
+
* - The token payload does not contain an issuer (iss) field.
|
|
21
|
+
*/
|
|
22
|
+
export declare function decodeToken(token: string): {
|
|
23
|
+
environment: string;
|
|
24
|
+
tenantName: string;
|
|
25
|
+
accessToken: string;
|
|
26
|
+
expiresAt: number | undefined;
|
|
27
|
+
} | null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes a JWT token and extracts environment and tenant details from its issuer URL.
|
|
3
|
+
*
|
|
4
|
+
* This function assumes the JWT token follows the standard header.payload.signature format.
|
|
5
|
+
* It decodes the payload from base64 URL format, parses it as JSON, and then uses a regex
|
|
6
|
+
* to extract the `environment` and `tenant` from the issuer URL (iss field) if it matches the pattern:
|
|
7
|
+
* https://keycloak.{environment}.corti.app/realms/{tenant}.
|
|
8
|
+
*
|
|
9
|
+
* @param token - A JSON Web Token (JWT) string.
|
|
10
|
+
* @returns An object containing:
|
|
11
|
+
* - `environment`: The extracted environment from the issuer URL.
|
|
12
|
+
* - `tenant`: The extracted tenant from the issuer URL.
|
|
13
|
+
* - `accessToken`: The original token string.
|
|
14
|
+
* If the issuer URL doesn't match the expected format, the function returns the full decoded token details.
|
|
15
|
+
*
|
|
16
|
+
* @throws Will throw an error if:
|
|
17
|
+
* - The token format is invalid.
|
|
18
|
+
* - The base64 decoding or URI decoding fails.
|
|
19
|
+
* - The JSON payload is invalid.
|
|
20
|
+
* - The token payload does not contain an issuer (iss) field.
|
|
21
|
+
*/
|
|
22
|
+
export function decodeToken(token) {
|
|
23
|
+
// Validate the token structure (should contain at least header and payload parts)
|
|
24
|
+
const parts = token ? token.split('.') : '';
|
|
25
|
+
if (parts.length < 2) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// Retrieve the payload (second part) of the JWT token
|
|
29
|
+
const base64Url = parts[1];
|
|
30
|
+
// Replace URL-safe characters to match standard base64 encoding
|
|
31
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
32
|
+
// Decode the base64 string into a JSON string
|
|
33
|
+
let jsonPayload;
|
|
34
|
+
try {
|
|
35
|
+
jsonPayload = decodeURIComponent(atob(base64)
|
|
36
|
+
.split('')
|
|
37
|
+
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
|
38
|
+
.join(''));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Parse the JSON string to obtain token details
|
|
44
|
+
let tokenDetails;
|
|
45
|
+
try {
|
|
46
|
+
tokenDetails = JSON.parse(jsonPayload);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// Extract the issuer URL from the token details
|
|
52
|
+
const issuerUrl = tokenDetails.iss;
|
|
53
|
+
if (!issuerUrl) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Regex to extract environment and tenant from issuer URL:
|
|
57
|
+
// Expected format: https://keycloak.{environment}.corti.app/realms/{tenant}
|
|
58
|
+
// Note: Unnecessary escapes in character classes have been removed.
|
|
59
|
+
const regex = /^https:\/\/(keycloak|auth)\.([^.]+)\.corti\.app\/realms\/([^/]+)/;
|
|
60
|
+
const match = issuerUrl.match(regex);
|
|
61
|
+
// If the issuer URL matches the expected pattern, return the extracted values along with the token
|
|
62
|
+
if (match) {
|
|
63
|
+
const expiresAt = tokenDetails.exp && typeof tokenDetails.exp === 'number'
|
|
64
|
+
? tokenDetails.exp
|
|
65
|
+
: undefined;
|
|
66
|
+
return {
|
|
67
|
+
environment: match[2],
|
|
68
|
+
tenantName: match[3],
|
|
69
|
+
accessToken: token,
|
|
70
|
+
expiresAt,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as core from "../../core/index.mjs";
|
|
2
2
|
import * as environments from "../../environments.mjs";
|
|
3
|
-
type Environment =
|
|
4
|
-
type
|
|
5
|
-
export declare function getEnvironment(environment: Environment):
|
|
6
|
-
export {};
|
|
3
|
+
export type Environment = CortiInternalEnvironment | string;
|
|
4
|
+
export type CortiInternalEnvironment = core.Supplier<environments.CortiEnvironment | environments.CortiEnvironmentUrls>;
|
|
5
|
+
export declare function getEnvironment(environment: Environment): CortiInternalEnvironment;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as core from "../../core/index.mjs";
|
|
2
|
+
import { CortiClient } from "../CortiClient.mjs";
|
|
3
|
+
import { Environment } from "./getEnvironmentFromString.mjs";
|
|
4
|
+
import { ExpectedTokenResponse } from "../RefreshBearerProvider.mjs";
|
|
5
|
+
type ResolvedClientOptions = {
|
|
6
|
+
environment: Environment;
|
|
7
|
+
tenantName: core.Supplier<string>;
|
|
8
|
+
initialTokenResponse?: Promise<ExpectedTokenResponse>;
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveClientOptions(options: CortiClient.Options): ResolvedClientOptions;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import * as core from "../../core/index.mjs";
|
|
11
|
+
import { decodeToken } from "./decodeToken.mjs";
|
|
12
|
+
import { ParseError } from "../../core/schemas/index.mjs";
|
|
13
|
+
import { getEnvironment } from "./getEnvironmentFromString.mjs";
|
|
14
|
+
function isClientCredentialsOptions(options) {
|
|
15
|
+
return "clientId" in options.auth;
|
|
16
|
+
}
|
|
17
|
+
export function resolveClientOptions(options) {
|
|
18
|
+
if (isClientCredentialsOptions(options)) {
|
|
19
|
+
return {
|
|
20
|
+
tenantName: options.tenantName,
|
|
21
|
+
environment: options.environment,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if ("accessToken" in options.auth && options.auth.accessToken) {
|
|
25
|
+
const decoded = decodeToken(options.auth.accessToken);
|
|
26
|
+
if (!decoded) {
|
|
27
|
+
throw new ParseError([
|
|
28
|
+
{
|
|
29
|
+
path: ["auth", "accessToken"],
|
|
30
|
+
message: "Invalid access token format",
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
tenantName: options.tenantName || decoded.tenantName,
|
|
36
|
+
environment: options.environment || decoded.environment,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* This branch -- is when we have "refreshAccessToken" defined but no accessToken.
|
|
41
|
+
* Trying to avoid initial request at all cost
|
|
42
|
+
*/
|
|
43
|
+
if (options.tenantName && options.environment) {
|
|
44
|
+
return {
|
|
45
|
+
tenantName: options.tenantName,
|
|
46
|
+
environment: options.environment,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const tokenResponsePromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const tokenResponse = yield core.Supplier.get(options.auth.refreshAccessToken);
|
|
51
|
+
const decoded = decodeToken(tokenResponse.accessToken);
|
|
52
|
+
if (!decoded) {
|
|
53
|
+
throw new ParseError([
|
|
54
|
+
{
|
|
55
|
+
path: ["auth", "refreshAccessToken"],
|
|
56
|
+
message: "Returned invalid access token format",
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
tokenResponse,
|
|
62
|
+
tenantName: decoded.tenantName,
|
|
63
|
+
environment: decoded.environment,
|
|
64
|
+
};
|
|
65
|
+
}))();
|
|
66
|
+
return {
|
|
67
|
+
tenantName: options.tenantName ||
|
|
68
|
+
tokenResponsePromise.then(({ tenantName }) => tenantName),
|
|
69
|
+
environment: options.environment ||
|
|
70
|
+
tokenResponsePromise.then(({ environment }) => core.Supplier.get(getEnvironment(environment))),
|
|
71
|
+
initialTokenResponse: tokenResponsePromise.then((result) => result.tokenResponse),
|
|
72
|
+
};
|
|
73
|
+
}
|
package/dist/esm/version.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.
|
|
1
|
+
export declare const SDK_VERSION = "0.5.0-rc";
|
package/dist/esm/version.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = "0.
|
|
1
|
+
export const SDK_VERSION = "0.5.0-rc";
|