diff --git a/examples/pre/nextjs/next-env.d.ts b/examples/pre/nextjs/next-env.d.ts
index 4f11a03d..40c3d680 100644
--- a/examples/pre/nextjs/next-env.d.ts
+++ b/examples/pre/nextjs/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/examples/taco/nextjs/next-env.d.ts b/examples/taco/nextjs/next-env.d.ts
index 4f11a03d..40c3d680 100644
--- a/examples/taco/nextjs/next-env.d.ts
+++ b/examples/taco/nextjs/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/packages/taco/src/conditions/base/index.ts b/packages/taco/src/conditions/base/index.ts
index f116a598..6de843d8 100644
--- a/packages/taco/src/conditions/base/index.ts
+++ b/packages/taco/src/conditions/base/index.ts
@@ -4,5 +4,6 @@
export * as contract from './contract';
export * as jsonApi from './json-api';
export * as jsonRpc from './json-rpc';
+export * as jwt from './jwt';
export * as rpc from './rpc';
export * as time from './time';
diff --git a/packages/taco/src/conditions/base/jwt.ts b/packages/taco/src/conditions/base/jwt.ts
new file mode 100644
index 00000000..92469a4c
--- /dev/null
+++ b/packages/taco/src/conditions/base/jwt.ts
@@ -0,0 +1,23 @@
+import { Condition } from '../condition';
+import {
+ JWTConditionProps,
+ jwtConditionSchema,
+ JWTConditionType,
+} from '../schemas/jwt';
+import { OmitConditionType } from '../shared';
+
+export {
+ JWT_PARAM_DEFAULT,
+ JWTConditionProps,
+ jwtConditionSchema,
+ JWTConditionType,
+} from '../schemas/jwt';
+
+export class JWTCondition extends Condition {
+ constructor(value: OmitConditionType) {
+ super(jwtConditionSchema, {
+ conditionType: JWTConditionType,
+ ...value,
+ });
+ }
+}
diff --git a/packages/taco/src/conditions/condition-factory.ts b/packages/taco/src/conditions/condition-factory.ts
index 3295c243..ab84ae90 100644
--- a/packages/taco/src/conditions/condition-factory.ts
+++ b/packages/taco/src/conditions/condition-factory.ts
@@ -13,6 +13,7 @@ import {
JsonRpcConditionProps,
JsonRpcConditionType,
} from './base/json-rpc';
+import { JWTCondition, JWTConditionProps, JWTConditionType } from './base/jwt';
import { RpcCondition, RpcConditionProps, RpcConditionType } from './base/rpc';
import {
TimeCondition,
@@ -53,6 +54,8 @@ export class ConditionFactory {
return new JsonApiCondition(props as JsonApiConditionProps);
case JsonRpcConditionType:
return new JsonRpcCondition(props as JsonRpcConditionProps);
+ case JWTConditionType:
+ return new JWTCondition(props as JWTConditionProps);
// Logical Conditions
case CompoundConditionType:
return new CompoundCondition(props as CompoundConditionProps);
diff --git a/packages/taco/src/conditions/schemas/jwt.ts b/packages/taco/src/conditions/schemas/jwt.ts
new file mode 100644
index 00000000..34ef9457
--- /dev/null
+++ b/packages/taco/src/conditions/schemas/jwt.ts
@@ -0,0 +1,21 @@
+import { z } from 'zod';
+
+import { baseConditionSchema } from './common';
+import { contextParamSchema } from './context';
+
+export const JWT_PARAM_DEFAULT = ':jwtToken';
+
+export const JWTConditionType = 'jwt';
+
+export const jwtConditionSchema = baseConditionSchema.extend({
+ conditionType: z.literal(JWTConditionType).default(JWTConditionType),
+ publicKey: z.string(),
+ expectedIssuer: z.string().optional(),
+ // TODO see https://github.com/nucypher/taco-web/pull/604#discussion_r1901746814
+ // subject: contextParamSchema.optional(),
+ // expirationWindow: z.number().int().nonnegative().optional(),
+ // issuedWindow: z.number().int().nonnegative().optional(),
+ jwtToken: contextParamSchema.default(JWT_PARAM_DEFAULT),
+});
+
+export type JWTConditionProps = z.infer;
diff --git a/packages/taco/src/conditions/schemas/utils.ts b/packages/taco/src/conditions/schemas/utils.ts
index e9f8f28c..abd14f36 100644
--- a/packages/taco/src/conditions/schemas/utils.ts
+++ b/packages/taco/src/conditions/schemas/utils.ts
@@ -6,6 +6,7 @@ import { contractConditionSchema } from './contract';
import { ifThenElseConditionSchema } from './if-then-else';
import { jsonApiConditionSchema } from './json-api';
import { jsonRpcConditionSchema } from './json-rpc';
+import { jwtConditionSchema } from './jwt';
import { rpcConditionSchema } from './rpc';
import { sequentialConditionSchema } from './sequential';
import { timeConditionSchema } from './time';
@@ -18,6 +19,7 @@ export const anyConditionSchema: z.ZodSchema = z.lazy(() =>
compoundConditionSchema,
jsonApiConditionSchema,
jsonRpcConditionSchema,
+ jwtConditionSchema,
sequentialConditionSchema,
ifThenElseConditionSchema,
]),
diff --git a/packages/taco/test/conditions/base/jwt.test.ts b/packages/taco/test/conditions/base/jwt.test.ts
new file mode 100644
index 00000000..8598e95e
--- /dev/null
+++ b/packages/taco/test/conditions/base/jwt.test.ts
@@ -0,0 +1,40 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { TEST_CONTRACT_ADDR } from '@nucypher/test-utils';
+import { describe, expect, it } from 'vitest';
+
+import {
+ JWTCondition,
+ jwtConditionSchema,
+} from '../../../src/conditions/base/jwt';
+import { testJWTConditionObj } from '../../test-utils';
+
+describe('JWTCondition', () => {
+ describe('validation', () => {
+ it('accepts a valid schema', () => {
+ const result = JWTCondition.validate(
+ jwtConditionSchema,
+ testJWTConditionObj,
+ );
+
+ expect(result.error).toBeUndefined();
+ expect(result.data).toEqual(testJWTConditionObj);
+ });
+
+ it('rejects an invalid schema', () => {
+ const badJWTObj = {
+ ...testJWTConditionObj,
+ jwtToken: TEST_CONTRACT_ADDR,
+ };
+
+ const result = JWTCondition.validate(jwtConditionSchema, badJWTObj);
+
+ expect(result.error).toBeDefined();
+ expect(result.data).toBeUndefined();
+ expect(result.error?.format()).toMatchObject({
+ jwtToken: {
+ _errors: ['Invalid'],
+ },
+ });
+ });
+ });
+});
diff --git a/packages/taco/test/conditions/compound-condition.test.ts b/packages/taco/test/conditions/compound-condition.test.ts
index dabd1e3f..aae71959 100644
--- a/packages/taco/test/conditions/compound-condition.test.ts
+++ b/packages/taco/test/conditions/compound-condition.test.ts
@@ -11,6 +11,7 @@ import {
} from '../../src/conditions/compound-condition';
import {
testContractConditionObj,
+ testJWTConditionObj,
testRpcConditionObj,
testSequentialConditionObj,
testTimeConditionObj,
@@ -167,7 +168,11 @@ describe('validation', () => {
testRpcConditionObj,
{
operator: 'or',
- operands: [testTimeConditionObj, testContractConditionObj],
+ operands: [
+ testTimeConditionObj,
+ testContractConditionObj,
+ testJWTConditionObj,
+ ],
},
testSequentialConditionObj,
],
@@ -187,7 +192,11 @@ describe('validation', () => {
{
conditionType: CompoundConditionType,
operator: 'or',
- operands: [testTimeConditionObj, testContractConditionObj],
+ operands: [
+ testTimeConditionObj,
+ testContractConditionObj,
+ testJWTConditionObj,
+ ],
},
testSequentialConditionObj,
],
diff --git a/packages/taco/test/conditions/lingo.test.ts b/packages/taco/test/conditions/lingo.test.ts
index 8b85a06e..3bbe46b4 100644
--- a/packages/taco/test/conditions/lingo.test.ts
+++ b/packages/taco/test/conditions/lingo.test.ts
@@ -2,15 +2,15 @@ import { TEST_CHAIN_ID } from '@nucypher/test-utils';
import { describe, expect, it } from 'vitest';
import { ConditionExpression } from '../../src/conditions/condition-expr';
+import {
+ testJsonApiConditionObj,
+ testJsonRpcConditionObj,
+ testJWTConditionObj,
+ testRpcConditionObj,
+ testTimeConditionObj,
+} from '../test-utils';
describe('check that valid lingo in python is valid in typescript', () => {
- const timeConditionProps = {
- conditionType: 'time',
- method: 'blocktime',
- chain: TEST_CHAIN_ID,
- returnValueTest: { value: 0, comparator: '>' },
- };
-
const contractConditionProps = {
conditionType: 'contract',
chain: TEST_CHAIN_ID,
@@ -35,50 +35,16 @@ describe('check that valid lingo in python is valid in typescript', () => {
value: true,
},
};
- const rpcConditionProps = {
- conditionType: 'rpc',
- chain: TEST_CHAIN_ID,
- method: 'eth_getBalance',
- parameters: ['0x3d2Bed3259b165EB02A7F0D0753e7a01912A68f8', 'latest'],
- returnValueTest: {
- comparator: '>=',
- value: 10000000000000,
- },
- };
- const jsonApiConditionProps = {
- conditionType: 'json-api',
- endpoint: 'https://api.example.com/data',
- query: '$.store.book[0].price',
- parameters: {
- ids: 'ethereum',
- vs_currencies: 'usd',
- },
- returnValueTest: {
- comparator: '==',
- value: 2,
- },
- };
- const jsonRpcConditionProps = {
- conditionType: 'json-rpc',
- endpoint: 'https://math.example.com/',
- method: 'subtract',
- params: [42, 23],
- query: '$.value',
- returnValueTest: {
- comparator: '==',
- value: 2,
- },
- };
const sequentialConditionProps = {
conditionType: 'sequential',
conditionVariables: [
{
varName: 'timeValue',
- condition: timeConditionProps,
+ condition: testTimeConditionObj,
},
{
varName: 'rpcValue',
- condition: rpcConditionProps,
+ condition: testRpcConditionObj,
},
{
varName: 'contractValue',
@@ -86,15 +52,15 @@ describe('check that valid lingo in python is valid in typescript', () => {
},
{
varName: 'jsonValue',
- condition: jsonApiConditionProps,
+ condition: testJsonApiConditionObj,
},
],
};
const ifThenElseConditionProps = {
conditionType: 'if-then-else',
- ifCondition: jsonRpcConditionProps,
- thenCondition: jsonApiConditionProps,
- elseCondition: timeConditionProps,
+ ifCondition: testJsonRpcConditionObj,
+ thenCondition: testJsonApiConditionObj,
+ elseCondition: testTimeConditionObj,
};
const compoundConditionProps = {
@@ -104,21 +70,22 @@ describe('check that valid lingo in python is valid in typescript', () => {
contractConditionProps,
ifThenElseConditionProps,
sequentialConditionProps,
- rpcConditionProps,
+ testRpcConditionObj,
{
conditionType: 'compound',
operator: 'not',
- operands: [timeConditionProps],
+ operands: [testTimeConditionObj],
},
],
};
it.each([
- rpcConditionProps,
- timeConditionProps,
+ testRpcConditionObj,
+ testTimeConditionObj,
contractConditionProps,
- jsonApiConditionProps,
- jsonRpcConditionProps,
+ testJsonApiConditionObj,
+ testJsonRpcConditionObj,
+ testJWTConditionObj,
compoundConditionProps,
sequentialConditionProps,
ifThenElseConditionProps,
diff --git a/packages/taco/test/test-utils.ts b/packages/taco/test/test-utils.ts
index f1c7b19b..1857142b 100644
--- a/packages/taco/test/test-utils.ts
+++ b/packages/taco/test/test-utils.ts
@@ -31,6 +31,7 @@ import {
fakeTDecFlow,
TEST_CHAIN_ID,
TEST_CONTRACT_ADDR,
+ TEST_ECDSA_PUBLIC_KEY,
} from '@nucypher/test-utils';
import { SpyInstance, vi } from 'vitest';
@@ -43,6 +44,11 @@ import {
JsonApiConditionProps,
JsonApiConditionType,
} from '../src/conditions/base/json-api';
+import {
+ JWT_PARAM_DEFAULT,
+ JWTConditionProps,
+ JWTConditionType,
+} from '../src/conditions/base/jwt';
import {
RpcConditionProps,
RpcConditionType,
@@ -259,6 +265,16 @@ export const testJsonRpcConditionObj: JsonRpcConditionProps = {
returnValueTest: testReturnValueTest,
};
+export const testJWTConditionObj: JWTConditionProps = {
+ conditionType: JWTConditionType,
+ publicKey: TEST_ECDSA_PUBLIC_KEY,
+ expectedIssuer: '0xacbd',
+ // subject: ':userAddress',
+ // expirationWindow: 1800,
+ // issuedWindow: 86400,
+ jwtToken: JWT_PARAM_DEFAULT,
+};
+
export const testRpcConditionObj: RpcConditionProps = {
conditionType: RpcConditionType,
chain: TEST_CHAIN_ID,
diff --git a/packages/test-utils/src/variables.ts b/packages/test-utils/src/variables.ts
index 462f5660..c6f73db2 100644
--- a/packages/test-utils/src/variables.ts
+++ b/packages/test-utils/src/variables.ts
@@ -19,3 +19,8 @@ export const TEST_SIWE_PARAMS = {
domain: 'localhost',
uri: 'http://localhost:3000',
};
+
+export const TEST_ECDSA_PUBLIC_KEY =
+ '-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXHVxB7s5SR7I9cWwry' +
+ '/JkECIReka\nCwG3uOLCYbw5gVzn4dRmwMyYUJFcQWuFSfECRK+uQOOXD0YSEucBq0p5tA==\n-----END PUBLIC ' +
+ 'KEY-----\n ';