@optimizely-opal/opal-tool-ocp-sdk 0.0.0-beta.10 โ 0.0.0-beta.11
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 +43 -9
- package/dist/function/ToolFunction.d.ts +7 -4
- package/dist/function/ToolFunction.d.ts.map +1 -1
- package/dist/function/ToolFunction.js +10 -39
- package/dist/function/ToolFunction.js.map +1 -1
- package/dist/function/ToolFunction.test.js +196 -177
- package/dist/function/ToolFunction.test.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/service/Service.d.ts +7 -7
- package/dist/service/Service.d.ts.map +1 -1
- package/dist/service/Service.js +16 -22
- package/dist/service/Service.js.map +1 -1
- package/dist/service/Service.test.js +3 -8
- 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 +3 -5
- package/src/function/ToolFunction.test.ts +214 -194
- package/src/function/ToolFunction.ts +11 -45
- package/src/index.ts +0 -1
- package/src/service/Service.test.ts +3 -8
- package/src/service/Service.ts +17 -22
- package/src/types/Models.ts +4 -4
- package/dist/auth/TokenVerifier.d.ts +0 -31
- package/dist/auth/TokenVerifier.d.ts.map +0 -1
- package/dist/auth/TokenVerifier.js +0 -127
- package/dist/auth/TokenVerifier.js.map +0 -1
- package/dist/auth/TokenVerifier.test.d.ts +0 -2
- package/dist/auth/TokenVerifier.test.d.ts.map +0 -1
- package/dist/auth/TokenVerifier.test.js +0 -114
- package/dist/auth/TokenVerifier.test.js.map +0 -1
- package/src/auth/TokenVerifier.test.ts +0 -152
- package/src/auth/TokenVerifier.ts +0 -145
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** - Bearer token tool authorization
|
|
14
14
|
- ๐ **Parameter Validation** - Define and validate tool parameters with types
|
|
15
15
|
- ๐งช **Comprehensive Testing** - Fully tested with Jest
|
|
16
16
|
|
|
@@ -115,7 +115,7 @@ export class MyToolFunction extends ToolFunction {
|
|
|
115
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:
|
|
116
116
|
|
|
117
117
|
- **Routes requests** to your registered tools and interactions based on endpoints
|
|
118
|
-
- **Handles authentication** and
|
|
118
|
+
- **Handles authentication** and bearer token validation before calling your methods
|
|
119
119
|
- **Provides discovery** at `/discovery` endpoint for OCP platform integration
|
|
120
120
|
- **Returns proper HTTP responses** with correct status codes and JSON formatting
|
|
121
121
|
|
|
@@ -169,18 +169,29 @@ interface AuthRequirementConfig {
|
|
|
169
169
|
}
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
####
|
|
172
|
+
#### Bearer Token Support
|
|
173
173
|
|
|
174
|
-
The SDK automatically
|
|
174
|
+
The SDK automatically extracts Bearer tokens from the `authorization` header for tool authorization. When users register tools in Opal, they can specify a Bearer token that Opal will include in requests to validate the tool's identity.
|
|
175
175
|
|
|
176
176
|
**Token Validation:**
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
177
|
+
- Bearer tokens are extracted from the `authorization: Bearer <token>` header
|
|
178
|
+
- Empty strings and `undefined` tokens are treated as missing (no validation performed)
|
|
179
|
+
- **Optionally override** the `validateBearerToken(token: string): boolean` method to implement custom validation logic
|
|
180
|
+
- The default implementation returns `true` (accepts all tokens) - override if you need custom authorization
|
|
181
|
+
- If validation fails, returns HTTP 403 Forbidden before reaching your handler methods
|
|
181
182
|
|
|
182
183
|
```typescript
|
|
183
184
|
export class MyToolFunction extends ToolFunction {
|
|
185
|
+
// Optional: Override this method to implement custom bearer token validation
|
|
186
|
+
protected override validateBearerToken(token: string): boolean {
|
|
187
|
+
// If you don't need bearer token validation, don't override this method
|
|
188
|
+
// The default implementation returns true
|
|
189
|
+
|
|
190
|
+
// For custom validation, compare against expected token
|
|
191
|
+
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
192
|
+
return token === expectedToken;
|
|
193
|
+
}
|
|
194
|
+
|
|
184
195
|
@tool({
|
|
185
196
|
name: 'secure_tool',
|
|
186
197
|
description: 'Tool that validates requests from Opal',
|
|
@@ -204,6 +215,15 @@ export class MyToolFunction extends ToolFunction {
|
|
|
204
215
|
}
|
|
205
216
|
```
|
|
206
217
|
|
|
218
|
+
**Bearer Token Usage:**
|
|
219
|
+
- Set by users when registering tools in Opal
|
|
220
|
+
- Used to authorize tool requests from Opal instances
|
|
221
|
+
- Validates that requests are coming from trusted Opal sources
|
|
222
|
+
- **Optionally override** the `validateBearerToken(token: string): boolean` method for custom authorization
|
|
223
|
+
- Default implementation accepts all tokens - override for security
|
|
224
|
+
- If validation fails, returns HTTP 403 Forbidden before reaching your handler methods
|
|
225
|
+
|
|
226
|
+
|
|
207
227
|
## API Reference
|
|
208
228
|
|
|
209
229
|
### Handler Function Signatures
|
|
@@ -220,6 +240,8 @@ async handlerMethod(
|
|
|
220
240
|
- **params**: The input parameters for tools, or interaction data for webhooks
|
|
221
241
|
- **authData**: Available when OptiID user authentication is configured and successful
|
|
222
242
|
|
|
243
|
+
**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.
|
|
244
|
+
|
|
223
245
|
### Decorators
|
|
224
246
|
|
|
225
247
|
#### `@tool(config: ToolConfig)`
|
|
@@ -257,10 +279,11 @@ Abstract base class for OCP functions:
|
|
|
257
279
|
export abstract class ToolFunction extends Function {
|
|
258
280
|
protected ready(): Promise<boolean>;
|
|
259
281
|
public async perform(): Promise<Response>;
|
|
282
|
+
protected validateBearerToken(token: string): boolean;
|
|
260
283
|
}
|
|
261
284
|
```
|
|
262
285
|
|
|
263
|
-
Extend this class and implement your OCP function. The `perform` method automatically routes requests to registered tools.
|
|
286
|
+
Extend this class and implement your OCP function. The `perform` method automatically routes requests to registered tools and handles bearer token validation. **You can optionally override the `validateBearerToken` method** to define custom bearer token authorization for your tools.
|
|
264
287
|
|
|
265
288
|
### Models
|
|
266
289
|
|
|
@@ -384,6 +407,11 @@ yarn lint
|
|
|
384
407
|
import { ToolFunction, tool, interaction, ParameterType, OptiIdAuthData, InteractionResult } from '@optimizely-opal/opal-ocp-sdk';
|
|
385
408
|
|
|
386
409
|
export class AuthenticatedFunction extends ToolFunction {
|
|
410
|
+
// Optional: Override this method for custom bearer token validation
|
|
411
|
+
protected override validateBearerToken(token: string): boolean {
|
|
412
|
+
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
413
|
+
return token === expectedToken;
|
|
414
|
+
}
|
|
387
415
|
|
|
388
416
|
// OptiID authentication example
|
|
389
417
|
@tool({
|
|
@@ -545,6 +573,12 @@ import { ToolFunction } from '@optimizely-opal/opal-ocp-sdk';
|
|
|
545
573
|
import * from './tools';
|
|
546
574
|
|
|
547
575
|
export class MyToolFunction extends ToolFunction {
|
|
576
|
+
|
|
577
|
+
// Optional: Override this method for custom bearer token validation
|
|
578
|
+
protected override validateBearerToken(token: string): boolean {
|
|
579
|
+
const expectedToken = process.env.OPAL_TOOL_TOKEN;
|
|
580
|
+
return token === expectedToken;
|
|
581
|
+
}
|
|
548
582
|
}
|
|
549
583
|
```
|
|
550
584
|
|
|
@@ -18,11 +18,14 @@ export declare abstract class ToolFunction extends Function {
|
|
|
18
18
|
*/
|
|
19
19
|
perform(): Promise<Response>;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Validates the bearer token for authorization.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
23
|
+
* This method provides a default implementation that accepts all tokens.
|
|
24
|
+
* Subclasses can override this method to implement custom bearer token validation logic.
|
|
25
|
+
*
|
|
26
|
+
* @param _bearerToken - The bearer token extracted from the Authorization header
|
|
27
|
+
* @returns true if the token is valid and the request should proceed, false to return 403 Forbidden
|
|
24
28
|
*/
|
|
25
|
-
|
|
26
|
-
private validateAccessToken;
|
|
29
|
+
protected validateBearerToken(_bearerToken: string): boolean;
|
|
27
30
|
}
|
|
28
31
|
//# sourceMappingURL=ToolFunction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolFunction.d.ts","sourceRoot":"","sources":["../../src/function/ToolFunction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"ToolFunction.d.ts","sourceRoot":"","sources":["../../src/function/ToolFunction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAmB,MAAM,mBAAmB,CAAC;AAGxE;;;GAGG;AACH,8BAAsB,YAAa,SAAQ,QAAQ;IAEjD;;;;;OAKG;IACH,SAAS,CAAC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC;;;;OAIG;IACU,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC;IAezC;;;;;;;;OAQG;IACH,SAAS,CAAC,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;CAG7D"}
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ToolFunction = void 0;
|
|
4
4
|
const app_sdk_1 = require("@zaiusinc/app-sdk");
|
|
5
5
|
const Service_1 = require("../service/Service");
|
|
6
|
-
const TokenVerifier_1 = require("../auth/TokenVerifier");
|
|
7
6
|
/**
|
|
8
7
|
* Abstract base class for tool-based function execution
|
|
9
8
|
* Provides a standard interface for processing requests through registered tools
|
|
@@ -25,7 +24,8 @@ class ToolFunction extends app_sdk_1.Function {
|
|
|
25
24
|
*/
|
|
26
25
|
async perform() {
|
|
27
26
|
(0, app_sdk_1.amendLogContext)({ opalThreadId: this.request.headers.get('x-opal-thread-id') || '' });
|
|
28
|
-
|
|
27
|
+
const bearerToken = Service_1.toolsService.extractBearerToken(this.request.headers);
|
|
28
|
+
if (bearerToken && !this.validateBearerToken(bearerToken)) {
|
|
29
29
|
return new app_sdk_1.Response(403, { error: 'Forbidden' });
|
|
30
30
|
}
|
|
31
31
|
if (this.request.path === '/ready') {
|
|
@@ -36,45 +36,16 @@ class ToolFunction extends app_sdk_1.Function {
|
|
|
36
36
|
return Service_1.toolsService.processRequest(this.request, this);
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* Validates the bearer token for authorization.
|
|
40
40
|
*
|
|
41
|
-
*
|
|
41
|
+
* This method provides a default implementation that accepts all tokens.
|
|
42
|
+
* Subclasses can override this method to implement custom bearer token validation logic.
|
|
43
|
+
*
|
|
44
|
+
* @param _bearerToken - The bearer token extracted from the Authorization header
|
|
45
|
+
* @returns true if the token is valid and the request should proceed, false to return 403 Forbidden
|
|
42
46
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
app_sdk_1.logger.debug('Authorizing request:', this.request.bodyJSON);
|
|
48
|
-
const authData = this.request.bodyJSON?.auth;
|
|
49
|
-
const accessToken = authData?.credentials?.access_token;
|
|
50
|
-
if (!accessToken || authData?.provider?.toLowerCase() !== 'optiid') {
|
|
51
|
-
app_sdk_1.logger.error('OptiID token is required but not provided');
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
const customerId = authData.credentials?.customer_id;
|
|
55
|
-
if (!customerId) {
|
|
56
|
-
app_sdk_1.logger.error('Organisation ID is required but not provided');
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
const appOrganisationId = (0, app_sdk_1.getAppContext)().account.organizationId;
|
|
60
|
-
if (customerId !== appOrganisationId) {
|
|
61
|
-
app_sdk_1.logger.error(`Invalid organisation ID: expected ${appOrganisationId}, received ${customerId}`);
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
return await this.validateAccessToken(accessToken);
|
|
65
|
-
}
|
|
66
|
-
async validateAccessToken(accessToken) {
|
|
67
|
-
try {
|
|
68
|
-
if (!accessToken) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
const tokenVerifier = await (0, TokenVerifier_1.getTokenVerifier)();
|
|
72
|
-
return await tokenVerifier.verify(accessToken);
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
app_sdk_1.logger.error('OptiID token validation failed:', error);
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
47
|
+
validateBearerToken(_bearerToken) {
|
|
48
|
+
return true;
|
|
78
49
|
}
|
|
79
50
|
}
|
|
80
51
|
exports.ToolFunction = ToolFunction;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolFunction.js","sourceRoot":"","sources":["../../src/function/ToolFunction.ts"],"names":[],"mappings":";;;AAAA,+
|
|
1
|
+
{"version":3,"file":"ToolFunction.js","sourceRoot":"","sources":["../../src/function/ToolFunction.ts"],"names":[],"mappings":";;;AAAA,+CAAwE;AACxE,gDAAkD;AAElD;;;GAGG;AACH,MAAsB,YAAa,SAAQ,kBAAQ;IAEjD;;;;;OAKG;IACO,KAAK;QACb,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO;QAClB,IAAA,yBAAe,EAAC,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtF,MAAM,WAAW,GAAG,sBAAY,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,WAAW,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,kBAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACnC,OAAO,IAAI,kBAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,4EAA4E;QAC5E,OAAO,sBAAY,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;OAQG;IACO,mBAAmB,CAAC,YAAoB;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AA5CD,oCA4CC"}
|