@optimizely-opal/opal-tool-ocp-sdk 0.0.0-beta.1 โ 0.0.0-beta.10
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 +106 -51
- package/dist/auth/TokenVerifier.d.ts +31 -0
- package/dist/auth/TokenVerifier.d.ts.map +1 -0
- package/dist/auth/TokenVerifier.js +127 -0
- package/dist/auth/TokenVerifier.js.map +1 -0
- package/dist/auth/TokenVerifier.test.d.ts +2 -0
- package/dist/auth/TokenVerifier.test.d.ts.map +1 -0
- package/dist/auth/TokenVerifier.test.js +114 -0
- package/dist/auth/TokenVerifier.test.js.map +1 -0
- package/dist/decorator/Decorator.d.ts +4 -2
- package/dist/decorator/Decorator.d.ts.map +1 -1
- package/dist/decorator/Decorator.js +26 -4
- package/dist/decorator/Decorator.js.map +1 -1
- package/dist/decorator/Decorator.test.js +110 -0
- package/dist/decorator/Decorator.test.js.map +1 -1
- package/dist/function/ToolFunction.d.ts +14 -1
- package/dist/function/ToolFunction.d.ts.map +1 -1
- package/dist/function/ToolFunction.js +59 -3
- package/dist/function/ToolFunction.js.map +1 -1
- package/dist/function/ToolFunction.test.js +229 -104
- package/dist/function/ToolFunction.test.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/service/Service.d.ts +14 -13
- package/dist/service/Service.d.ts.map +1 -1
- package/dist/service/Service.js +25 -19
- package/dist/service/Service.js.map +1 -1
- package/dist/service/Service.test.js +122 -36
- package/dist/service/Service.test.js.map +1 -1
- package/dist/types/Models.d.ts +5 -5
- package/dist/types/Models.d.ts.map +1 -1
- package/dist/types/Models.js +9 -9
- package/dist/types/Models.js.map +1 -1
- package/package.json +10 -3
- package/src/auth/TokenVerifier.test.ts +152 -0
- package/src/auth/TokenVerifier.ts +145 -0
- package/src/decorator/Decorator.test.ts +126 -0
- package/src/decorator/Decorator.ts +32 -4
- package/src/function/ToolFunction.test.ts +259 -109
- package/src/function/ToolFunction.ts +66 -5
- package/src/index.ts +1 -0
- package/src/service/Service.test.ts +139 -28
- package/src/service/Service.ts +31 -24
- package/src/types/Models.ts +4 -4
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ A TypeScript SDK for building Opal tools in Optimizely Connect Platform. This SD
|
|
|
10
10
|
- ๐ง **Type-safe Development** - Full TypeScript support with comprehensive type definitions
|
|
11
11
|
- ๐๏ธ **Abstract Base Classes** - Extend `ToolFunction` for standardized request processing
|
|
12
12
|
- ๐ **Authentication Support** - OptiID authentication
|
|
13
|
-
- ๐ก๏ธ **Authorization Support** -
|
|
13
|
+
- ๐ก๏ธ **Authorization Support** - OptiID token tool authorization
|
|
14
14
|
- ๐ **Parameter Validation** - Define and validate tool parameters with types
|
|
15
15
|
- ๐งช **Comprehensive Testing** - Fully tested with Jest
|
|
16
16
|
|
|
@@ -34,11 +34,6 @@ Create a tool function class by extending `ToolFunction` and registering your to
|
|
|
34
34
|
import { ToolFunction, tool, interaction, ParameterType, InteractionResult, OptiIdAuthData } from '@optimizely-opal/opal-tool-ocp-sdk';
|
|
35
35
|
|
|
36
36
|
export class MyToolFunction extends ToolFunction {
|
|
37
|
-
// Implement required bearer token validation method
|
|
38
|
-
validateBearerToken(token: string): boolean {
|
|
39
|
-
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
40
|
-
return token === expectedToken;
|
|
41
|
-
}
|
|
42
37
|
|
|
43
38
|
// Register a simple tool without authentication
|
|
44
39
|
@tool({
|
|
@@ -120,7 +115,7 @@ export class MyToolFunction extends ToolFunction {
|
|
|
120
115
|
Your function class inherits a `perform()` method from `ToolFunction` that serves as the main entry point for handling all incoming requests. When called, the SDK automatically:
|
|
121
116
|
|
|
122
117
|
- **Routes requests** to your registered tools and interactions based on endpoints
|
|
123
|
-
- **Handles authentication** and
|
|
118
|
+
- **Handles authentication** and OptiID token validation before calling your methods
|
|
124
119
|
- **Provides discovery** at `/discovery` endpoint for OCP platform integration
|
|
125
120
|
- **Returns proper HTTP responses** with correct status codes and JSON formatting
|
|
126
121
|
|
|
@@ -174,27 +169,18 @@ interface AuthRequirementConfig {
|
|
|
174
169
|
}
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
####
|
|
172
|
+
#### OptiID Token Authorization
|
|
178
173
|
|
|
179
|
-
The SDK automatically
|
|
174
|
+
The SDK automatically handles OptiID token validation for tool authorization. OptiID tokens provide both user authentication and authorization for tools, ensuring that only authenticated users with proper permissions can access your tools.
|
|
180
175
|
|
|
181
176
|
**Token Validation:**
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
177
|
+
- The SDK extracts and validates OptiID tokens from the request body
|
|
178
|
+
- Validation includes verifying that requests come from the same organization
|
|
179
|
+
- If validation fails, returns HTTP 403 Unauthorized before reaching your handler methods
|
|
180
|
+
- No additional configuration needed - validation is handled automatically
|
|
185
181
|
|
|
186
182
|
```typescript
|
|
187
183
|
export class MyToolFunction extends ToolFunction {
|
|
188
|
-
// Implement the required bearer token validation method
|
|
189
|
-
validateBearerToken(token: string): boolean {
|
|
190
|
-
// If you don't need bearer token validation, simply return true
|
|
191
|
-
// return true;
|
|
192
|
-
|
|
193
|
-
// For actual validation, compare against expected token
|
|
194
|
-
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
195
|
-
return token === expectedToken;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
184
|
@tool({
|
|
199
185
|
name: 'secure_tool',
|
|
200
186
|
description: 'Tool that validates requests from Opal',
|
|
@@ -218,14 +204,6 @@ export class MyToolFunction extends ToolFunction {
|
|
|
218
204
|
}
|
|
219
205
|
```
|
|
220
206
|
|
|
221
|
-
**Bearer Token Usage:**
|
|
222
|
-
- Set by users when registering tools in Opal
|
|
223
|
-
- Used to authorize tool requests from Opal instances
|
|
224
|
-
- Validates that requests are coming from trusted Opal sources
|
|
225
|
-
- Requires implementing the abstract `validateBearerToken(token: string): boolean` method
|
|
226
|
-
- If validation fails, returns HTTP 403 Forbidden before reaching your handler methods
|
|
227
|
-
|
|
228
|
-
|
|
229
207
|
## API Reference
|
|
230
208
|
|
|
231
209
|
### Handler Function Signatures
|
|
@@ -242,8 +220,6 @@ async handlerMethod(
|
|
|
242
220
|
- **params**: The input parameters for tools, or interaction data for webhooks
|
|
243
221
|
- **authData**: Available when OptiID user authentication is configured and successful
|
|
244
222
|
|
|
245
|
-
**Note**: Bearer token validation is handled automatically by the base class. If a bearer token is present and fails validation, the request is rejected before reaching your handler methods.
|
|
246
|
-
|
|
247
223
|
### Decorators
|
|
248
224
|
|
|
249
225
|
#### `@tool(config: ToolConfig)`
|
|
@@ -279,12 +255,12 @@ Abstract base class for OCP functions:
|
|
|
279
255
|
|
|
280
256
|
```typescript
|
|
281
257
|
export abstract class ToolFunction extends Function {
|
|
258
|
+
protected ready(): Promise<boolean>;
|
|
282
259
|
public async perform(): Promise<Response>;
|
|
283
|
-
protected abstract validateBearerToken(token: string): boolean;
|
|
284
260
|
}
|
|
285
261
|
```
|
|
286
262
|
|
|
287
|
-
Extend this class and implement your OCP function. The `perform` method automatically routes requests to registered tools
|
|
263
|
+
Extend this class and implement your OCP function. The `perform` method automatically routes requests to registered tools.
|
|
288
264
|
|
|
289
265
|
### Models
|
|
290
266
|
|
|
@@ -297,9 +273,13 @@ Key model classes with generic type support:
|
|
|
297
273
|
- `InteractionResult` - Response from interactions
|
|
298
274
|
- `OptiIdAuthData` - OptiID specific authentication data
|
|
299
275
|
|
|
300
|
-
## Discovery
|
|
276
|
+
## Discovery and Ready Endpoints
|
|
277
|
+
|
|
278
|
+
The SDK automatically provides two important endpoints:
|
|
301
279
|
|
|
302
|
-
|
|
280
|
+
### Discovery Endpoint (`/discovery`)
|
|
281
|
+
|
|
282
|
+
Returns all registered tools in the proper OCP format for platform integration:
|
|
303
283
|
|
|
304
284
|
```json
|
|
305
285
|
{
|
|
@@ -349,6 +329,21 @@ The SDK automatically provides a discovery endpoint at `/discovery` that returns
|
|
|
349
329
|
}
|
|
350
330
|
```
|
|
351
331
|
|
|
332
|
+
### Ready Endpoint (`/ready`)
|
|
333
|
+
|
|
334
|
+
Returns the current readiness status of your function:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"ready": true
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
This endpoint calls your function's `ready()` method and returns:
|
|
343
|
+
- `{ready: true}` when the function is ready to process requests
|
|
344
|
+
- `{ready: false}` when the function is not ready (missing configuration, external services unavailable, etc.)
|
|
345
|
+
- HTTP 200 status code regardless of ready state (the ready status is in the response body)
|
|
346
|
+
|
|
352
347
|
## Development
|
|
353
348
|
|
|
354
349
|
### Prerequisites
|
|
@@ -389,15 +384,6 @@ yarn lint
|
|
|
389
384
|
import { ToolFunction, tool, interaction, ParameterType, OptiIdAuthData, InteractionResult } from '@optimizely-opal/opal-ocp-sdk';
|
|
390
385
|
|
|
391
386
|
export class AuthenticatedFunction extends ToolFunction {
|
|
392
|
-
// Implement required bearer token validation
|
|
393
|
-
validateBearerToken(token: string): boolean {
|
|
394
|
-
// If you don't need bearer token validation, simply return true
|
|
395
|
-
// return true;
|
|
396
|
-
|
|
397
|
-
// For actual validation, compare against expected token
|
|
398
|
-
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
399
|
-
return token === expectedToken;
|
|
400
|
-
}
|
|
401
387
|
|
|
402
388
|
// OptiID authentication example
|
|
403
389
|
@tool({
|
|
@@ -559,12 +545,6 @@ import { ToolFunction } from '@optimizely-opal/opal-ocp-sdk';
|
|
|
559
545
|
import * from './tools';
|
|
560
546
|
|
|
561
547
|
export class MyToolFunction extends ToolFunction {
|
|
562
|
-
|
|
563
|
-
// Implement required bearer token validation method
|
|
564
|
-
validateBearerToken(token: string): boolean {
|
|
565
|
-
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
566
|
-
return token === expectedToken;
|
|
567
|
-
}
|
|
568
548
|
}
|
|
569
549
|
```
|
|
570
550
|
|
|
@@ -574,3 +554,78 @@ This approach provides several benefits:
|
|
|
574
554
|
- **Reusability**: Tools can be shared across different ToolFunction classes
|
|
575
555
|
- **Team collaboration**: Different developers can work on different tool files
|
|
576
556
|
- **Testing**: Each tool class can be unit tested independently
|
|
557
|
+
|
|
558
|
+
## Decorator Behavior and Instance Context
|
|
559
|
+
|
|
560
|
+
The `@tool` and `@interaction` decorators provide intelligent instance context management that behaves differently depending on where the decorated methods are defined:
|
|
561
|
+
|
|
562
|
+
### ToolFunction Subclass Context
|
|
563
|
+
|
|
564
|
+
When decorators are used in a class that extends `ToolFunction`, the decorators can reuse the existing ToolFunction instance when called through the `perform()` method:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
export class MyToolFunction extends ToolFunction {
|
|
568
|
+
private secretKey = process.env.SECRET_KEY;
|
|
569
|
+
|
|
570
|
+
@tool({
|
|
571
|
+
name: 'process_data',
|
|
572
|
+
description: 'Processes data using instance context',
|
|
573
|
+
endpoint: '/process-data',
|
|
574
|
+
parameters: [
|
|
575
|
+
{ name: 'data', type: ParameterType.String, description: 'Data to process', required: true }
|
|
576
|
+
]
|
|
577
|
+
})
|
|
578
|
+
async processData(params: { data: string }) {
|
|
579
|
+
// โ
Can access instance properties and methods
|
|
580
|
+
// โ
Can access this.request (inherited from ToolFunction)
|
|
581
|
+
// โ
Shares state with other methods in the same request
|
|
582
|
+
|
|
583
|
+
const userAgent = this.request.headers.get('user-agent');
|
|
584
|
+
return {
|
|
585
|
+
processedData: this.encryptData(params.data),
|
|
586
|
+
userAgent,
|
|
587
|
+
timestamp: Date.now()
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private encryptData(data: string): string {
|
|
592
|
+
// Uses instance property
|
|
593
|
+
return `encrypted_${data}_${this.secretKey}`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Standalone Class Context
|
|
599
|
+
|
|
600
|
+
When decorators are used in classes that don't extend `ToolFunction`, the decorators create new instances for each handler call:
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
export class StandaloneToolService {
|
|
604
|
+
private config = { apiKey: 'standalone-key' };
|
|
605
|
+
|
|
606
|
+
@tool({
|
|
607
|
+
name: 'standalone_operation',
|
|
608
|
+
description: 'Standalone operation without ToolFunction',
|
|
609
|
+
endpoint: '/standalone',
|
|
610
|
+
parameters: [
|
|
611
|
+
{ name: 'input', type: ParameterType.String, description: 'Input data', required: true }
|
|
612
|
+
]
|
|
613
|
+
})
|
|
614
|
+
async standaloneOperation(params: { input: string }) {
|
|
615
|
+
// โ
Can access instance properties and methods
|
|
616
|
+
// โ Cannot access this.request (not inherited from ToolFunction)
|
|
617
|
+
// โ No shared state with ToolFunction lifecycle
|
|
618
|
+
|
|
619
|
+
return {
|
|
620
|
+
result: `${this.helperMethod()}: ${params.input}`,
|
|
621
|
+
source: 'standalone'
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private helperMethod() {
|
|
626
|
+
return this.config.apiKey;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
This behavior ensures that your tools can be both flexible (working in any class) and powerful (leveraging ToolFunction features when available).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare class TokenVerifier {
|
|
2
|
+
private static instance;
|
|
3
|
+
private jwksUri?;
|
|
4
|
+
private issuer?;
|
|
5
|
+
private jwks?;
|
|
6
|
+
private initialized;
|
|
7
|
+
/**
|
|
8
|
+
* Verify the provided Optimizely JWT token string
|
|
9
|
+
* @param token JWT token string to verify
|
|
10
|
+
* @returns boolean true if verification successful, false otherwise
|
|
11
|
+
* @throws Error if token is null, empty, or verifier is not properly configured
|
|
12
|
+
*/
|
|
13
|
+
verify(token: string | undefined): Promise<boolean>;
|
|
14
|
+
private static getInstance;
|
|
15
|
+
/**
|
|
16
|
+
* Get singleton instance of TokenVerifier and ensure it's initialized
|
|
17
|
+
* @returns Promise<TokenVerifier> - initialized singleton instance
|
|
18
|
+
*/
|
|
19
|
+
static getInitializedInstance(): Promise<TokenVerifier>;
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the TokenVerifier with discovery document from well-known endpoint
|
|
22
|
+
*/
|
|
23
|
+
private initialize;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch discovery document from well-known endpoint
|
|
26
|
+
*/
|
|
27
|
+
private fetchDiscoveryDocument;
|
|
28
|
+
private verifyToken;
|
|
29
|
+
}
|
|
30
|
+
export declare const getTokenVerifier: () => Promise<TokenVerifier>;
|
|
31
|
+
//# sourceMappingURL=TokenVerifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenVerifier.d.ts","sourceRoot":"","sources":["../../src/auth/TokenVerifier.ts"],"names":[],"mappings":"AAkCA,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA8B;IACrD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAC,CAAwC;IACrD,OAAO,CAAC,WAAW,CAAkB;IAErC;;;;;OAKG;IACU,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAQhE,OAAO,CAAC,MAAM,CAAC,WAAW;IAO1B;;;OAGG;WACiB,sBAAsB,IAAI,OAAO,CAAC,aAAa,CAAC;IAQpE;;OAEG;YACW,UAAU;IAyBxB;;OAEG;YACW,sBAAsB;YAetB,WAAW;CAsB1B;AAED,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,aAAa,CAA2C,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTokenVerifier = exports.TokenVerifier = void 0;
|
|
4
|
+
const jose_1 = require("jose");
|
|
5
|
+
const app_sdk_1 = require("@zaiusinc/app-sdk");
|
|
6
|
+
/**
|
|
7
|
+
* Default JWKS cache expiration time in milliseconds (1 hour)
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_JWKS_EXPIRES_IN = 60 * 60 * 1000;
|
|
10
|
+
/**
|
|
11
|
+
* Default clock skew tolerance in seconds
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_LEEWAY = 30;
|
|
14
|
+
/**
|
|
15
|
+
* Expected JWT audience for token validation
|
|
16
|
+
*/
|
|
17
|
+
const AUDIENCE = 'api://default';
|
|
18
|
+
/**
|
|
19
|
+
* Prep Base URL for Optimizely OAuth2 endpoints
|
|
20
|
+
*/
|
|
21
|
+
const PREP_BASE_URL = 'https://prep.login.optimizely.com/oauth2/default';
|
|
22
|
+
/**
|
|
23
|
+
* Prod Base URL for Optimizely OAuth2 endpoints
|
|
24
|
+
*/
|
|
25
|
+
const PROD_BASE_URL = 'https://login.optimizely.com/oauth2/default';
|
|
26
|
+
class TokenVerifier {
|
|
27
|
+
static instance = null;
|
|
28
|
+
jwksUri;
|
|
29
|
+
issuer;
|
|
30
|
+
jwks;
|
|
31
|
+
initialized = false;
|
|
32
|
+
/**
|
|
33
|
+
* Verify the provided Optimizely JWT token string
|
|
34
|
+
* @param token JWT token string to verify
|
|
35
|
+
* @returns boolean true if verification successful, false otherwise
|
|
36
|
+
* @throws Error if token is null, empty, or verifier is not properly configured
|
|
37
|
+
*/
|
|
38
|
+
async verify(token) {
|
|
39
|
+
if (!token || token.trim().length === 0) {
|
|
40
|
+
throw new Error('Token cannot be null or empty');
|
|
41
|
+
}
|
|
42
|
+
return this.verifyToken(token);
|
|
43
|
+
}
|
|
44
|
+
static getInstance() {
|
|
45
|
+
if (!TokenVerifier.instance) {
|
|
46
|
+
TokenVerifier.instance = new TokenVerifier();
|
|
47
|
+
}
|
|
48
|
+
return TokenVerifier.instance;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get singleton instance of TokenVerifier and ensure it's initialized
|
|
52
|
+
* @returns Promise<TokenVerifier> - initialized singleton instance
|
|
53
|
+
*/
|
|
54
|
+
static async getInitializedInstance() {
|
|
55
|
+
const instance = TokenVerifier.getInstance();
|
|
56
|
+
if (!instance.initialized) {
|
|
57
|
+
await instance.initialize();
|
|
58
|
+
}
|
|
59
|
+
return instance;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Initialize the TokenVerifier with discovery document from well-known endpoint
|
|
63
|
+
*/
|
|
64
|
+
async initialize() {
|
|
65
|
+
if (this.initialized) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
// Use prep URL when environment variable is set to 'staging', otherwise use prod
|
|
70
|
+
const environment = process.env.environment || 'production';
|
|
71
|
+
const baseUrl = environment === 'staging' ? PREP_BASE_URL : PROD_BASE_URL;
|
|
72
|
+
const discoveryDocument = await this.fetchDiscoveryDocument(baseUrl);
|
|
73
|
+
this.issuer = discoveryDocument.issuer;
|
|
74
|
+
this.jwksUri = discoveryDocument.jwks_uri;
|
|
75
|
+
this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(this.jwksUri), {
|
|
76
|
+
cacheMaxAge: DEFAULT_JWKS_EXPIRES_IN,
|
|
77
|
+
cooldownDuration: DEFAULT_JWKS_EXPIRES_IN
|
|
78
|
+
});
|
|
79
|
+
this.initialized = true;
|
|
80
|
+
app_sdk_1.logger.info(`TokenVerifier initialized with issuer: ${this.issuer} (environment: ${environment})`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
app_sdk_1.logger.error('Failed to initialize TokenVerifier', error);
|
|
84
|
+
// Re-throw the original error to preserve specific error messages for tests
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Fetch discovery document from well-known endpoint
|
|
90
|
+
*/
|
|
91
|
+
async fetchDiscoveryDocument(baseUrl) {
|
|
92
|
+
const wellKnownUrl = `${baseUrl}/.well-known/oauth-authorization-server`;
|
|
93
|
+
const response = await fetch(wellKnownUrl);
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
throw new Error(`Failed to fetch discovery document: ${response.status} ${response.statusText}`);
|
|
96
|
+
}
|
|
97
|
+
const discoveryDocument = await response.json();
|
|
98
|
+
if (!discoveryDocument.issuer || !discoveryDocument.jwks_uri) {
|
|
99
|
+
throw new Error('Invalid discovery document: missing issuer or jwks_uri');
|
|
100
|
+
}
|
|
101
|
+
return discoveryDocument;
|
|
102
|
+
}
|
|
103
|
+
async verifyToken(token) {
|
|
104
|
+
if (!this.initialized) {
|
|
105
|
+
throw new Error('TokenVerifier not initialized. Call initialize() first.');
|
|
106
|
+
}
|
|
107
|
+
if (!this.jwks || !this.issuer) {
|
|
108
|
+
throw new Error('TokenVerifier not properly configured.');
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await (0, jose_1.jwtVerify)(token, this.jwks, {
|
|
112
|
+
issuer: this.issuer,
|
|
113
|
+
audience: AUDIENCE,
|
|
114
|
+
clockTolerance: DEFAULT_LEEWAY,
|
|
115
|
+
});
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
app_sdk_1.logger.error('Token verification failed:', error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.TokenVerifier = TokenVerifier;
|
|
125
|
+
const getTokenVerifier = async () => TokenVerifier.getInitializedInstance();
|
|
126
|
+
exports.getTokenVerifier = getTokenVerifier;
|
|
127
|
+
//# sourceMappingURL=TokenVerifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenVerifier.js","sourceRoot":"","sources":["../../src/auth/TokenVerifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenVerifier.test.d.ts","sourceRoot":"","sources":["../../src/auth/TokenVerifier.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const TokenVerifier_1 = require("./TokenVerifier");
|
|
4
|
+
// Test constants
|
|
5
|
+
const TEST_ISSUER = 'https://prep.login.optimizely.com/oauth2/default';
|
|
6
|
+
const TEST_JWKS_URI = 'https://prep.login.optimizely.com/oauth2/v1/keys';
|
|
7
|
+
// Mock fetch globally
|
|
8
|
+
global.fetch = jest.fn();
|
|
9
|
+
describe('TokenVerifier', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Reset fetch mock
|
|
12
|
+
global.fetch.mockReset();
|
|
13
|
+
// Reset singleton instance for each test
|
|
14
|
+
TokenVerifier_1.TokenVerifier.instance = null;
|
|
15
|
+
});
|
|
16
|
+
describe('getInitializedInstance', () => {
|
|
17
|
+
it('should initialize successfully', async () => {
|
|
18
|
+
// Mock fetch for OAuth2 authorization server discovery
|
|
19
|
+
global.fetch.mockResolvedValue({
|
|
20
|
+
ok: true,
|
|
21
|
+
json: jest.fn().mockResolvedValue({
|
|
22
|
+
issuer: TEST_ISSUER,
|
|
23
|
+
jwks_uri: TEST_JWKS_URI
|
|
24
|
+
})
|
|
25
|
+
});
|
|
26
|
+
const tokenVerifier = await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
27
|
+
expect(tokenVerifier).toBeInstanceOf(TokenVerifier_1.TokenVerifier);
|
|
28
|
+
});
|
|
29
|
+
it('should throw error when discovery document is invalid', async () => {
|
|
30
|
+
global.fetch.mockResolvedValue({
|
|
31
|
+
ok: true,
|
|
32
|
+
json: jest.fn().mockResolvedValue({
|
|
33
|
+
// Missing issuer and jwks_uri
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
await expect(TokenVerifier_1.TokenVerifier.getInitializedInstance()).rejects.toThrow('Invalid discovery document: missing issuer or jwks_uri');
|
|
37
|
+
});
|
|
38
|
+
it('should throw error when discovery endpoint is unreachable', async () => {
|
|
39
|
+
global.fetch.mockResolvedValue({
|
|
40
|
+
ok: false,
|
|
41
|
+
status: 404,
|
|
42
|
+
statusText: 'Not Found'
|
|
43
|
+
});
|
|
44
|
+
await expect(TokenVerifier_1.TokenVerifier.getInitializedInstance()).rejects.toThrow('Failed to fetch discovery document: 404 Not Found');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('token verification', () => {
|
|
48
|
+
let tokenVerifier;
|
|
49
|
+
beforeEach(async () => {
|
|
50
|
+
// Mock successful initialization
|
|
51
|
+
global.fetch.mockResolvedValue({
|
|
52
|
+
ok: true,
|
|
53
|
+
json: jest.fn().mockResolvedValue({
|
|
54
|
+
issuer: TEST_ISSUER,
|
|
55
|
+
jwks_uri: TEST_JWKS_URI
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
tokenVerifier = await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
59
|
+
});
|
|
60
|
+
it('should throw error for empty token', async () => {
|
|
61
|
+
await expect(tokenVerifier.verify('')).rejects.toThrow('Token cannot be null or empty');
|
|
62
|
+
});
|
|
63
|
+
it('should throw error for undefined token', async () => {
|
|
64
|
+
await expect(tokenVerifier.verify(undefined)).rejects.toThrow('Token cannot be null or empty');
|
|
65
|
+
});
|
|
66
|
+
it('should throw error for whitespace-only token', async () => {
|
|
67
|
+
await expect(tokenVerifier.verify(' ')).rejects.toThrow('Token cannot be null or empty');
|
|
68
|
+
});
|
|
69
|
+
it('should return false for invalid token format', async () => {
|
|
70
|
+
const result = await tokenVerifier.verify('invalid.jwt.token');
|
|
71
|
+
expect(result).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('singleton pattern', () => {
|
|
75
|
+
it('should return same instance when called multiple times', async () => {
|
|
76
|
+
// Mock successful initialization
|
|
77
|
+
global.fetch.mockResolvedValue({
|
|
78
|
+
ok: true,
|
|
79
|
+
json: jest.fn().mockResolvedValue({
|
|
80
|
+
issuer: TEST_ISSUER,
|
|
81
|
+
jwks_uri: TEST_JWKS_URI
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
const instance1 = await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
85
|
+
const instance2 = await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
86
|
+
expect(instance1).toBe(instance2);
|
|
87
|
+
});
|
|
88
|
+
it('should call correct prod OAuth2 authorization server discovery URL', async () => {
|
|
89
|
+
const fetchSpy = jest.spyOn(global, 'fetch').mockResolvedValue({
|
|
90
|
+
ok: true,
|
|
91
|
+
json: jest.fn().mockResolvedValue({
|
|
92
|
+
issuer: TEST_ISSUER,
|
|
93
|
+
jwks_uri: TEST_JWKS_URI
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
97
|
+
expect(fetchSpy).toHaveBeenCalledWith('https://login.optimizely.com/oauth2/default/.well-known/oauth-authorization-server');
|
|
98
|
+
});
|
|
99
|
+
it('should call correct prep OAuth2 authorization server discovery URL', async () => {
|
|
100
|
+
// Set environment variable to staging
|
|
101
|
+
process.env.environment = 'staging';
|
|
102
|
+
const fetchSpy = jest.spyOn(global, 'fetch').mockResolvedValue({
|
|
103
|
+
ok: true,
|
|
104
|
+
json: jest.fn().mockResolvedValue({
|
|
105
|
+
issuer: TEST_ISSUER,
|
|
106
|
+
jwks_uri: TEST_JWKS_URI
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
await TokenVerifier_1.TokenVerifier.getInitializedInstance();
|
|
110
|
+
expect(fetchSpy).toHaveBeenCalledWith('https://prep.login.optimizely.com/oauth2/default/.well-known/oauth-authorization-server');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=TokenVerifier.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TokenVerifier.test.js","sourceRoot":"","sources":["../../src/auth/TokenVerifier.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,11 +36,13 @@ export interface InteractionConfig {
|
|
|
36
36
|
/**
|
|
37
37
|
* Decorator for registering tool functions
|
|
38
38
|
* Immediately registers the tool with the global ToolsService
|
|
39
|
+
* The handler will have access to 'this' context when called
|
|
39
40
|
*/
|
|
40
|
-
export declare function tool(config: ToolConfig): (
|
|
41
|
+
export declare function tool(config: ToolConfig): (target: any, _propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
41
42
|
/**
|
|
42
43
|
* Decorator for registering interaction functions
|
|
43
44
|
* Immediately registers the interaction with the global ToolsService
|
|
45
|
+
* The handler will have access to 'this' context when called
|
|
44
46
|
*/
|
|
45
|
-
export declare function interaction(config: InteractionConfig): (
|
|
47
|
+
export declare function interaction(config: InteractionConfig): (target: any, _propertyKey: string, descriptor: PropertyDescriptor) => void;
|
|
46
48
|
//# sourceMappingURL=Decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/Decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG5E;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED
|
|
1
|
+
{"version":3,"file":"Decorator.d.ts","sourceRoot":"","sources":["../../src/decorator/Decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG5E;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,gBAAgB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,UAAU,IACrB,QAAQ,GAAG,EAAE,cAAc,MAAM,EAAE,YAAY,kBAAkB,UAkClF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,IACnC,QAAQ,GAAG,EAAE,cAAc,MAAM,EAAE,YAAY,kBAAkB,UAqBlF"}
|
|
@@ -7,25 +7,47 @@ const Service_1 = require("../service/Service");
|
|
|
7
7
|
/**
|
|
8
8
|
* Decorator for registering tool functions
|
|
9
9
|
* Immediately registers the tool with the global ToolsService
|
|
10
|
+
* The handler will have access to 'this' context when called
|
|
10
11
|
*/
|
|
11
12
|
function tool(config) {
|
|
12
|
-
return function (
|
|
13
|
+
return function (target, _propertyKey, descriptor) {
|
|
13
14
|
// Convert parameter configs to Parameter instances
|
|
14
15
|
const parameters = (config.parameters || []).map((p) => new Models_1.Parameter(p.name, p.type, p.description, p.required));
|
|
15
16
|
// Convert auth requirement configs to AuthRequirement instances
|
|
16
17
|
const authRequirements = (config.authRequirements || []).map((a) => new Models_1.AuthRequirement(a.provider, a.scopeBundle, a.required));
|
|
18
|
+
const originalMethod = descriptor.value;
|
|
19
|
+
const boundHandler = function (functionContext, params, authData) {
|
|
20
|
+
// Check if we're being called from within a ToolFunction instance context
|
|
21
|
+
// If so, use that instance; otherwise create a new one
|
|
22
|
+
const instance = (functionContext && functionContext instanceof target.constructor) ?
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
24
|
+
functionContext : new target.constructor();
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
26
|
+
return originalMethod.call(instance, params, authData);
|
|
27
|
+
};
|
|
17
28
|
// Immediately register with global ToolsService
|
|
18
|
-
Service_1.toolsService.registerTool(config.name, config.description,
|
|
29
|
+
Service_1.toolsService.registerTool(config.name, config.description, boundHandler, parameters, config.endpoint, authRequirements);
|
|
19
30
|
};
|
|
20
31
|
}
|
|
21
32
|
/**
|
|
22
33
|
* Decorator for registering interaction functions
|
|
23
34
|
* Immediately registers the interaction with the global ToolsService
|
|
35
|
+
* The handler will have access to 'this' context when called
|
|
24
36
|
*/
|
|
25
37
|
function interaction(config) {
|
|
26
|
-
return function (
|
|
38
|
+
return function (target, _propertyKey, descriptor) {
|
|
39
|
+
const originalMethod = descriptor.value;
|
|
40
|
+
const boundHandler = function (functionContext, data, authData) {
|
|
41
|
+
// Check if we're being called from within a ToolFunction instance context
|
|
42
|
+
// If so, use that instance; otherwise create a new one
|
|
43
|
+
const instance = (functionContext && functionContext instanceof target.constructor) ?
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
45
|
+
functionContext : new target.constructor();
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
47
|
+
return originalMethod.call(instance, data, authData);
|
|
48
|
+
};
|
|
27
49
|
// Immediately register with global ToolsService
|
|
28
|
-
Service_1.toolsService.registerInteraction(config.name,
|
|
50
|
+
Service_1.toolsService.registerInteraction(config.name, boundHandler, config.endpoint);
|
|
29
51
|
};
|
|
30
52
|
}
|
|
31
53
|
//# sourceMappingURL=Decorator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Decorator.js","sourceRoot":"","sources":["../../src/decorator/Decorator.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"Decorator.js","sourceRoot":"","sources":["../../src/decorator/Decorator.ts"],"names":[],"mappings":";;AA8CA,oBAmCC;AAOD,kCAsBC;AA9GD,4CAA4E;AAC5E,gDAAkD;AAwClD;;;;GAIG;AACH,SAAgB,IAAI,CAAC,MAAkB;IACrC,OAAO,UAAS,MAAW,EAAE,YAAoB,EAAE,UAA8B;QAC/E,mDAAmD;QACnD,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACrD,IAAI,kBAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CACzD,CAAC;QAEF,gEAAgE;QAChE,MAAM,gBAAgB,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACjE,IAAI,wBAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAC3D,CAAC;QAEF,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QAExC,MAAM,YAAY,GAAG,UAAS,eAAoB,EAAE,MAAW,EAAE,QAAa;YAC5E,0EAA0E;YAC1E,uDAAuD;YACvD,MAAM,QAAQ,GAAG,CAAC,eAAe,IAAI,eAAe,YAAY,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;gBACnF,6DAA6D;gBAC7D,eAAe,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YAE7C,6DAA6D;YAC7D,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzD,CAAC,CAAC;QAEF,gDAAgD;QAChD,sBAAY,CAAC,YAAY,CACvB,MAAM,CAAC,IAAI,EACX,MAAM,CAAC,WAAW,EAClB,YAAY,EACZ,UAAU,EACV,MAAM,CAAC,QAAQ,EACf,gBAAgB,CACjB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,MAAyB;IACnD,OAAO,UAAS,MAAW,EAAE,YAAoB,EAAE,UAA8B;QAC/E,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC;QAExC,MAAM,YAAY,GAAG,UAAS,eAAoB,EAAE,IAAS,EAAE,QAAc;YAC3E,0EAA0E;YAC1E,uDAAuD;YACvD,MAAM,QAAQ,GAAG,CAAC,eAAe,IAAI,eAAe,YAAY,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;gBACnF,6DAA6D;gBAC7D,eAAe,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YAE7C,6DAA6D;YAC7D,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC,CAAC;QAEF,gDAAgD;QAChD,sBAAY,CAAC,mBAAmB,CAC9B,MAAM,CAAC,IAAI,EACX,YAAY,EACZ,MAAM,CAAC,QAAQ,CAChB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|