diff --git a/packages/open-payments/scripts/generate-types.ts b/packages/open-payments/scripts/generate-types.ts index 27c978a180..608f7d6916 100644 --- a/packages/open-payments/scripts/generate-types.ts +++ b/packages/open-payments/scripts/generate-types.ts @@ -1,16 +1,32 @@ import fs from 'fs' import openapiTS from 'openapi-typescript' import config from '../src/config' + +const generateTypesFromOpenApi = async ( + specUrl: string, + outputFileName: string +) => { + const generatedTypesOutput = await openapiTS(specUrl) + + fs.writeFile(outputFileName, generatedTypesOutput, (error) => { + if (error) { + console.log(`Error when writing types to ${outputFileName}`, { error }) + } + }) +} + ;(async () => { - try { - const output = await openapiTS(config.OPEN_PAYMENTS_OPEN_API_URL) - const fileName = 'src/generated/types.ts' + const rootFolder = `src/generated` - fs.writeFile(fileName, output, (error) => { - if (error) { - console.log(`Error when writing types to ${fileName}`, { error }) - } - }) + try { + await generateTypesFromOpenApi( + config.OPEN_PAYMENTS_RS_OPEN_API_URL, + `${rootFolder}/resource-server-types.ts` + ) + await generateTypesFromOpenApi( + config.OPEN_PAYMENTS_AS_OPEN_API_URL, + `${rootFolder}/auth-server-types.ts` + ) } catch (error) { console.log('Error when generating types', { error diff --git a/packages/open-payments/src/client/grant.test.ts b/packages/open-payments/src/client/grant.test.ts new file mode 100644 index 0000000000..f1706108f1 --- /dev/null +++ b/packages/open-payments/src/client/grant.test.ts @@ -0,0 +1,28 @@ +import { createGrantRoutes } from './grant' +import { OpenAPI, HttpMethod, createOpenAPI } from 'openapi' +import config from '../config' +import { defaultAxiosInstance, silentLogger } from '../test/helpers' + +describe('grant', (): void => { + let openApi: OpenAPI + + beforeAll(async () => { + openApi = await createOpenAPI(config.OPEN_PAYMENTS_AS_OPEN_API_URL) + }) + + const axiosInstance = defaultAxiosInstance + const logger = silentLogger + + describe('createGrantRoutes', (): void => { + test('creates response validator for grant requests', async (): Promise => { + jest.spyOn(openApi, 'createResponseValidator') + + createGrantRoutes({ axiosInstance, openApi, logger }) + expect(openApi.createResponseValidator).toHaveBeenCalledTimes(1) + expect(openApi.createResponseValidator).toHaveBeenCalledWith({ + path: '/', + method: HttpMethod.POST + }) + }) + }) +}) diff --git a/packages/open-payments/src/client/grant.ts b/packages/open-payments/src/client/grant.ts new file mode 100644 index 0000000000..8b6bf2a79f --- /dev/null +++ b/packages/open-payments/src/client/grant.ts @@ -0,0 +1,33 @@ +import { HttpMethod } from 'openapi' +import { RouteDeps } from '.' +import { + getASPath, + InteractiveGrant, + NonInteractiveGrant, + GrantRequest +} from '../types' +import { post } from './requests' + +interface RequestGrantArgs { + url: string + request: GrantRequest +} + +export interface GrantRoutes { + request( + args: RequestGrantArgs + ): Promise +} + +export const createGrantRoutes = (deps: RouteDeps): GrantRoutes => { + const requestGrantValidator = deps.openApi.createResponseValidator< + InteractiveGrant | NonInteractiveGrant + >({ + path: getASPath('/'), + method: HttpMethod.POST + }) + return { + request: (args: RequestGrantArgs) => + post(deps, { url: args.url, body: args.request }, requestGrantValidator) + } +} diff --git a/packages/open-payments/src/client/ilp-stream-connection.test.ts b/packages/open-payments/src/client/ilp-stream-connection.test.ts index 01ae2302bb..699a1799db 100644 --- a/packages/open-payments/src/client/ilp-stream-connection.test.ts +++ b/packages/open-payments/src/client/ilp-stream-connection.test.ts @@ -7,7 +7,7 @@ describe('ilp-stream-connection', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/ilp-stream-connection.ts b/packages/open-payments/src/client/ilp-stream-connection.ts index d03fad0011..6227022128 100644 --- a/packages/open-payments/src/client/ilp-stream-connection.ts +++ b/packages/open-payments/src/client/ilp-stream-connection.ts @@ -1,6 +1,6 @@ import { HttpMethod } from 'openapi' -import { ClientDeps } from '.' -import { getPath, ILPStreamConnection } from '../types' +import { RouteDeps } from '.' +import { getRSPath, ILPStreamConnection } from '../types' import { get } from './requests' interface GetArgs { @@ -12,13 +12,13 @@ export interface ILPStreamConnectionRoutes { } export const createILPStreamConnectionRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): ILPStreamConnectionRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getILPStreamConnectionValidator = openApi.createResponseValidator({ - path: getPath('/connections/{id}'), + path: getRSPath('/connections/{id}'), method: HttpMethod.GET }) diff --git a/packages/open-payments/src/client/incoming-payment.test.ts b/packages/open-payments/src/client/incoming-payment.test.ts index dca95e2ede..80ee4779d5 100644 --- a/packages/open-payments/src/client/incoming-payment.test.ts +++ b/packages/open-payments/src/client/incoming-payment.test.ts @@ -18,7 +18,7 @@ describe('incoming-payment', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/incoming-payment.ts b/packages/open-payments/src/client/incoming-payment.ts index 852cadac35..d160bd4824 100644 --- a/packages/open-payments/src/client/incoming-payment.ts +++ b/packages/open-payments/src/client/incoming-payment.ts @@ -1,6 +1,6 @@ import { HttpMethod, ResponseValidator } from 'openapi' -import { ClientDeps } from '.' -import { IncomingPayment, getPath } from '../types' +import { BaseDeps, RouteDeps } from '.' +import { IncomingPayment, getRSPath } from '../types' import { get } from './requests' interface GetArgs { @@ -13,13 +13,13 @@ export interface IncomingPaymentRoutes { } export const createIncomingPaymentRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): IncomingPaymentRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getIncomingPaymentOpenApiValidator = openApi.createResponseValidator({ - path: getPath('/incoming-payments/{id}'), + path: getRSPath('/incoming-payments/{id}'), method: HttpMethod.GET }) @@ -34,11 +34,11 @@ export const createIncomingPaymentRoutes = ( } export const getIncomingPayment = async ( - clientDeps: Pick, + deps: BaseDeps, args: GetArgs, validateOpenApiResponse: ResponseValidator ) => { - const { axiosInstance, logger } = clientDeps + const { axiosInstance, logger } = deps const { url } = args const incomingPayment = await get( diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 3d3f3730ba..3f43473445 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -16,8 +16,19 @@ import { } from './payment-pointer' import { createAxiosInstance } from './requests' import { AxiosInstance } from 'axios' +import { createGrantRoutes, GrantRoutes } from './grant' -export interface ClientDeps { +export interface BaseDeps { + axiosInstance: AxiosInstance + logger: Logger +} + +interface ClientDeps extends BaseDeps { + resourceServerOpenApi: OpenAPI + authServerOpenApi: OpenAPI +} + +export interface RouteDeps extends BaseDeps { axiosInstance: AxiosInstance openApi: OpenAPI logger: Logger @@ -32,9 +43,19 @@ const createDeps = async ( requestTimeoutMs: args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS }) - const openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + const resourceServerOpenApi = await createOpenAPI( + config.OPEN_PAYMENTS_RS_OPEN_API_URL + ) + const authServerOpenApi = await createOpenAPI( + config.OPEN_PAYMENTS_AS_OPEN_API_URL + ) const logger = args?.logger ?? createLogger() - return { axiosInstance, openApi, logger } + return { + axiosInstance, + resourceServerOpenApi, + authServerOpenApi, + logger + } } export interface CreateUnauthenticatedClientArgs { @@ -50,11 +71,21 @@ export interface UnauthenticatedClient { export const createUnauthenticatedClient = async ( args: CreateUnauthenticatedClientArgs ): Promise => { - const deps = await createDeps(args) + const { axiosInstance, resourceServerOpenApi, logger } = await createDeps( + args + ) return { - ilpStreamConnection: createILPStreamConnectionRoutes(deps), - paymentPointer: createPaymentPointerRoutes(deps) + ilpStreamConnection: createILPStreamConnectionRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + paymentPointer: createPaymentPointerRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }) } } @@ -66,16 +97,35 @@ export interface CreateAuthenticatedClientArgs export interface AuthenticatedClient extends UnauthenticatedClient { incomingPayment: IncomingPaymentRoutes + grant: GrantRoutes } export const createAuthenticatedClient = async ( args: CreateAuthenticatedClientArgs ): Promise => { - const deps = await createDeps(args) + const { axiosInstance, resourceServerOpenApi, authServerOpenApi, logger } = + await createDeps(args) return { - incomingPayment: createIncomingPaymentRoutes(deps), - ilpStreamConnection: createILPStreamConnectionRoutes(deps), - paymentPointer: createPaymentPointerRoutes(deps) + incomingPayment: createIncomingPaymentRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + ilpStreamConnection: createILPStreamConnectionRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + paymentPointer: createPaymentPointerRoutes({ + axiosInstance, + openApi: resourceServerOpenApi, + logger + }), + grant: createGrantRoutes({ + axiosInstance, + openApi: authServerOpenApi, + logger + }) } } diff --git a/packages/open-payments/src/client/payment-pointer.test.ts b/packages/open-payments/src/client/payment-pointer.test.ts index 2915f8bab8..f003f69da4 100644 --- a/packages/open-payments/src/client/payment-pointer.test.ts +++ b/packages/open-payments/src/client/payment-pointer.test.ts @@ -7,7 +7,7 @@ describe('payment-pointer', (): void => { let openApi: OpenAPI beforeAll(async () => { - openApi = await createOpenAPI(config.OPEN_PAYMENTS_OPEN_API_URL) + openApi = await createOpenAPI(config.OPEN_PAYMENTS_RS_OPEN_API_URL) }) const axiosInstance = defaultAxiosInstance diff --git a/packages/open-payments/src/client/payment-pointer.ts b/packages/open-payments/src/client/payment-pointer.ts index d436cf9574..b5b080055a 100644 --- a/packages/open-payments/src/client/payment-pointer.ts +++ b/packages/open-payments/src/client/payment-pointer.ts @@ -1,6 +1,6 @@ import { HttpMethod } from 'openapi' -import { ClientDeps } from '.' -import { PaymentPointer, getPath } from '../types' +import { RouteDeps } from '.' +import { PaymentPointer, getRSPath } from '../types' import { get } from './requests' interface GetArgs { @@ -12,13 +12,13 @@ export interface PaymentPointerRoutes { } export const createPaymentPointerRoutes = ( - clientDeps: ClientDeps + deps: RouteDeps ): PaymentPointerRoutes => { - const { axiosInstance, openApi, logger } = clientDeps + const { axiosInstance, openApi, logger } = deps const getPaymentPaymentValidator = openApi.createResponseValidator({ - path: getPath('/'), + path: getRSPath('/'), method: HttpMethod.GET }) diff --git a/packages/open-payments/src/client/requests.test.ts b/packages/open-payments/src/client/requests.test.ts index 2637bad79a..fffb97fda1 100644 --- a/packages/open-payments/src/client/requests.test.ts +++ b/packages/open-payments/src/client/requests.test.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-empty-function */ -import { createAxiosInstance, get } from './requests' +import { createAxiosInstance, get, post } from './requests' import { generateKeyPairSync } from 'crypto' import nock from 'nock' import { mockOpenApiResponseValidators, silentLogger } from '../test/helpers' @@ -155,4 +155,85 @@ describe('requests', (): void => { ).rejects.toThrow(/Failed to validate OpenApi response/) }) }) + + describe('post', (): void => { + const axiosInstance = createAxiosInstance({ + requestTimeoutMs: 0, + privateKey, + keyId + }) + const baseUrl = 'http://localhost:1000' + const responseValidators = mockOpenApiResponseValidators() + + beforeAll(() => { + jest.spyOn(axiosInstance, 'post') + }) + + test('properly POSTs request', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + + nock(baseUrl).post('/grant', body).reply(status, body) + + await post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.successfulValidator + ) + + expect(axiosInstance.post).toHaveBeenCalledWith(`${baseUrl}/grant`, body) + }) + + test('calls validator function properly', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + + nock(baseUrl).post('/grant', body).reply(status, body) + + const responseValidatorSpy = jest.spyOn( + responseValidators, + 'successfulValidator' + ) + + await post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.successfulValidator + ) + + expect(responseValidatorSpy).toHaveBeenCalledWith({ + body, + status + }) + }) + + test('throws if response validator function fails', async (): Promise => { + const status = 200 + const body = { + id: 'id' + } + nock(baseUrl).post('/grant', body).reply(status, body) + + await expect( + post( + { axiosInstance, logger }, + { + url: `${baseUrl}/grant`, + body + }, + responseValidators.failedValidator + ) + ).rejects.toThrow(/Failed to validate OpenApi response/) + }) + }) }) diff --git a/packages/open-payments/src/client/requests.ts b/packages/open-payments/src/client/requests.ts index 774bf57313..4247cc75b0 100644 --- a/packages/open-payments/src/client/requests.ts +++ b/packages/open-payments/src/client/requests.ts @@ -1,20 +1,24 @@ import axios, { AxiosInstance } from 'axios' import { KeyLike } from 'crypto' import { ResponseValidator } from 'openapi' -import { ClientDeps } from '.' +import { BaseDeps } from '.' import { createSignatureHeaders } from './signatures' interface GetArgs { url: string accessToken?: string } +interface PostArgs { + url: string + body: T +} export const get = async ( - clientDeps: Pick, + deps: BaseDeps, args: GetArgs, openApiResponseValidator: ResponseValidator ): Promise => { - const { axiosInstance, logger } = clientDeps + const { axiosInstance, logger } = deps const { accessToken } = args const requestUrl = new URL(args.url) @@ -63,6 +67,54 @@ export const get = async ( } } +export const post = async ( + deps: BaseDeps, + args: PostArgs, + openApiResponseValidator: ResponseValidator +): Promise => { + const { axiosInstance, logger } = deps + const { body } = args + + const requestUrl = new URL(args.url) + if (process.env.NODE_ENV === 'development') { + requestUrl.protocol = 'http' + } + + const url = requestUrl.href + + try { + const { data, status } = await axiosInstance.post(url, body) + + try { + openApiResponseValidator({ + status, + body: data + }) + } catch (error) { + const errorMessage = 'Failed to validate OpenApi response' + logger.error( + { + data: JSON.stringify(data), + url, + validationError: error?.message + }, + errorMessage + ) + + throw new Error(errorMessage) + } + + return data + } catch (error) { + const errorMessage = `Error when making Open Payments POST request: ${ + error?.message ? error.message : 'Unknown error' + }` + logger.error({ url }, errorMessage) + + throw new Error(errorMessage) + } +} + export const createAxiosInstance = (args: { requestTimeoutMs: number privateKey: KeyLike diff --git a/packages/open-payments/src/config.ts b/packages/open-payments/src/config.ts index d3c58e27b9..feb66e15ea 100644 --- a/packages/open-payments/src/config.ts +++ b/packages/open-payments/src/config.ts @@ -1,5 +1,7 @@ export default { - OPEN_PAYMENTS_OPEN_API_URL: - 'https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/RS/openapi.yaml', + OPEN_PAYMENTS_RS_OPEN_API_URL: + 'https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/resource-server.yaml', + OPEN_PAYMENTS_AS_OPEN_API_URL: + 'https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/auth-server.yaml', DEFAULT_REQUEST_TIMEOUT_MS: 5_000 } diff --git a/packages/open-payments/src/generated/auth-server-types.ts b/packages/open-payments/src/generated/auth-server-types.ts new file mode 100644 index 0000000000..3537d91c5b --- /dev/null +++ b/packages/open-payments/src/generated/auth-server-types.ts @@ -0,0 +1,378 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/": { + /** Make a new grant request */ + post: operations["post-request"]; + parameters: {}; + }; + "/continue/{id}": { + /** Continue a grant request during or after user interaction. */ + post: operations["post-continue"]; + /** Cancel a grant request or delete a grant client side. */ + delete: operations["delete-continue"]; + parameters: { + path: { + id: string; + }; + }; + }; + "/token/{id}": { + /** Management endpoint to rotate access token. */ + post: operations["post-token"]; + /** Management endpoint to revoke access token. */ + delete: operations["delete-token"]; + parameters: { + path: { + id: string; + }; + }; + }; +} + +export interface components { + schemas: { + /** + * client + * @description Describes the client instance that is making this request, including the key that the client instance will use to protect this request and any continuation requests at the AS and any user-facing information about the client instance used in interactions. + * + * When sending a non-continuation request to the AS, the client instance MUST identify itself by including the client field of the request and by signing the request. + */ + client: { + /** @description An object containing additional information that the AS MAY display to the RO during interaction, authorization, and management. */ + display?: { + name?: string; + /** Format: uri */ + uri?: string; + }; + /** @description An identifier string that the AS can use to identify the client software comprising this client instance. */ + client_id?: string; + /** @description The public key of the client instance to be used in this request or a reference to a key. */ + key?: + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["key"] + | string; + }; + /** + * interact + * @description The client instance declares the parameters for interaction methods that it can support using the interact field. + */ + "interact-request": { + /** @description Indicates how the client instance can start an interaction. */ + start: "redirect"[]; + /** @description Indicates how the client instance can receive an indication that interaction has finished at the AS. */ + finish?: { + /** @description The callback method that the AS will use to contact the client instance. */ + method: "redirect"; + /** + * Format: uri + * @description Indicates the URI that the AS will either send the RO to after interaction or send an HTTP POST request. + */ + uri: string; + /** @description Unique value to be used in the calculation of the "hash" query parameter sent to the callback URI, must be sufficiently random to be unguessable by an attacker. MUST be generated by the client instance as a unique value for this request. */ + nonce: string; + }; + }; + /** interact-response */ + "interact-response": { + /** + * Format: uri + * @description The URI to direct the end user to. + */ + redirect: string; + /** @description Unique key to secure the callback. */ + finish: string; + }; + /** + * continue + * @description If the AS determines that the request can be continued with additional requests, it responds with the continue field. + */ + continue: { + /** @description A unique access token for continuing the request, called the "continuation access token". */ + access_token: { + value: string; + }; + /** + * Format: uri + * @description The URI at which the client instance can make continuation requests. + */ + uri: string; + /** @description The amount of time in integer seconds the client instance MUST wait after receiving this request continuation response and calling the continuation URI. */ + wait?: number; + }; + /** + * access_token + * @description A single access token or set of access tokens that the client instance can use to call the RS on behalf of the RO. + */ + access_token: { + /** @description The value of the access token as a string. The value is opaque to the client instance. The value SHOULD be limited to ASCII characters to facilitate transmission over HTTP headers within other protocols without requiring additional encoding. */ + value: string; + /** + * Format: uri + * @description The management URI for this access token. This URI MUST NOT include the access token value and SHOULD be different for each access token issued in a request. + */ + manage: string; + /** @description The number of seconds in which the access will expire. The client instance MUST NOT use the access token past this time. An RS MUST NOT accept an access token past this time. */ + expires_in?: number; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + }; + }; +} + +export interface operations { + /** Make a new grant request */ + "post-request": { + parameters: {}; + responses: { + /** OK */ + 200: { + content: { + "application/json": + | { + interact: components["schemas"]["interact-response"]; + continue: components["schemas"]["continue"]; + } + | { + access_token: components["schemas"]["access_token"]; + continue: components["schemas"]["continue"]; + }; + }; + }; + /** Bad Request */ + 400: unknown; + }; + requestBody: { + content: { + "application/json": { + access_token: { + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access"]; + }; + client: components["schemas"]["client"]; + interact?: components["schemas"]["interact-request"]; + }; + }; + }; + }; + /** Continue a grant request during or after user interaction. */ + "post-continue": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** Success */ + 200: { + content: { + "application/json": { + access_token?: components["schemas"]["access_token"]; + continue: components["schemas"]["continue"]; + }; + }; + }; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + requestBody: { + content: { + "application/json": { + /** + * @description The interaction reference generated for this + * interaction by the AS. + */ + interact_ref: string; + }; + }; + }; + }; + /** Cancel a grant request or delete a grant client side. */ + "delete-continue": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** Accepted */ + 202: unknown; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + }; + /** Management endpoint to rotate access token. */ + "post-token": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** OK */ + 200: { + content: { + "application/json": { + access_token: components["schemas"]["access_token"]; + }; + }; + }; + /** Unauthorized */ + 401: unknown; + /** Not Found */ + 404: unknown; + }; + }; + /** Management endpoint to revoke access token. */ + "delete-token": { + parameters: { + path: { + id: string; + }; + }; + responses: { + /** No Content */ + 204: never; + /** Unauthorized */ + 401: unknown; + }; + }; +} + +export interface external { + "https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml": { + paths: {}; + components: { + schemas: { + /** @description A description of the rights associated with this access token. */ + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; + /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ + "access-item": + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; + /** access-incoming */ + "access-incoming": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "incoming-payment"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ( + | "create" + | "complete" + | "read" + | "read-all" + | "list" + | "list-all" + )[]; + /** + * Format: uri + * @description A string identifier indicating a specific resource at the RS. + */ + identifier?: string; + }; + /** access-outgoing */ + "access-outgoing": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "outgoing-payment"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ("create" | "read" | "read-all" | "list" | "list-all")[]; + /** + * Format: uri + * @description A string identifier indicating a specific resource at the RS. + */ + identifier: string; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + }; + /** access-quote */ + "access-quote": { + /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ + type: "quote"; + /** @description The types of actions the client instance will take at the RS as an array of strings. */ + actions: ("create" | "read" | "read-all")[]; + }; + /** + * amount + * @description All amounts are maxima, i.e. multiple payments can be created under a grant as long as the total amounts of these payments do not exceed the maximum amount per interval as specified in the grant. + */ + amount: { + /** + * Format: uint64 + * @description The value is an unsigned 64-bit integer amount, represented as a string. + */ + value: string; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; + }; + /** + * Asset code + * @description The assetCode is a code that indicates the underlying asset. This SHOULD be an ISO4217 currency code. + */ + assetCode: string; + /** + * Asset scale + * @description The scale of amounts denoted in the corresponding asset code. + */ + assetScale: number; + /** + * Interval + * @description [ISO8601 repeating interval](https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals) + */ + interval: string; + /** + * key + * @description A key presented by value MUST be a public key. + */ + key: { + /** @description The form of proof that the client instance will use when presenting the key. */ + proof: "httpsig"; + /** @description The public key and its properties represented as a JSON Web Key [[RFC7517](https://datatracker.ietf.org/doc/html/rfc7517)]. */ + jwk: { + /** @description The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. */ + alg: "EdDSA"; + /** @description A Key ID can be used to match a specific key. */ + kid: string; + /** @description The Key Type. The only allowed value is `OKP`. */ + kty: "OKP"; + /** @description The intended use of the key. */ + use?: "sig"; + /** @description The cryptographic curve used with the key. The only allowed value is `Ed25519`. */ + crv: "Ed25519"; + /** @description Public key encoded using the `base64url` encoding. */ + x: string; + /** @description Array of allowed operations this key may be used for. */ + key_ops?: ("sign" | "verify")[]; + /** @description UNIX timestamp indicating the earliest this key may be used. */ + nbf?: number; + /** @description UNIX timestamp indicating the latest this key may be used. */ + exp?: number; + /** @description The revocation status of the key. */ + revoked?: boolean; + }; + }; + /** + * limits-outgoing + * @description Open Payments specific property that defines the limits under which outgoing payments can be created. + */ + "limits-outgoing": Partial & { + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; + }; + "list-actions": "list" | "list-all"; + "read-actions": "read" | "read-all"; + /** + * Receiver + * Format: uri + * @description The URL of the incoming payment or ILP STREAM connection that is being paid. + */ + receiver: string; + }; + }; + operations: {}; + }; +} diff --git a/packages/open-payments/src/generated/types.ts b/packages/open-payments/src/generated/resource-server-types.ts similarity index 83% rename from packages/open-payments/src/generated/types.ts rename to packages/open-payments/src/generated/resource-server-types.ts index d3398c3d86..9ae90da8f5 100644 --- a/packages/open-payments/src/generated/types.ts +++ b/packages/open-payments/src/generated/resource-server-types.ts @@ -118,8 +118,8 @@ export interface components { id: string; /** @description A public name for the account. This should be set by the account holder with their provider to provide a hint to counterparties as to the identity of the account holder. */ publicName?: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; /** * Format: uri * @description The URL of the authorization server endpoint for getting grants and access tokens for this payment pointer. @@ -147,8 +147,8 @@ export interface components { ilpAddress: string; /** @description The base64 url-encoded shared secret to use when establishing a STREAM connection. */ sharedSecret: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Incoming Payment @@ -168,9 +168,9 @@ export interface components { /** @description Describes whether the incoming payment has completed receiving fund. */ completed: boolean; /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been paid into the payment pointer under this incoming payment. */ - receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receivedAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments under this incoming payment will no longer be accepted. @@ -232,13 +232,13 @@ export interface components { /** @description Describes whether the payment failed to send its full amount. */ failed?: boolean; /** @description The URL of the incoming payment or ILP STREAM Connection that is being paid. */ - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; /** @description The total amount that should be received by the receiver when this outgoing payment has been paid. */ - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that should be sent when this outgoing payment has been paid. */ - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The total amount that has been sent under this outgoing payment. */ - sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + sentAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description Human readable description of the outgoing payment that will be visible to the account holder and shared with the receiver. */ description?: string; /** @description A reference that can be used by external systems to reconcile this payment with their systems. E.g. An invoice number. (Optional) */ @@ -269,9 +269,9 @@ export interface components { * @description The URL of the payment pointer from which this quote's payment would be sent. */ paymentPointer: string; - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** @description The date and time when the calculated `sendAmount` is no longer valid. */ expiresAt?: string; /** @@ -332,7 +332,7 @@ export interface components { id: string; /** @description The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ signature: string; - /** @description The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** @description The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "signature-input": string; }; } @@ -405,7 +405,7 @@ export interface operations { pagination?: components["schemas"]["pagination"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -433,7 +433,7 @@ export interface operations { "create-incoming-payment": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -458,7 +458,7 @@ export interface operations { content: { "application/json": { /** @description The maximum amount that should be paid into the payment pointer under this incoming payment. */ - incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + incomingAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; /** * Format: date-time * @description The date and time when payments into the incoming payment must no longer be accepted. @@ -480,7 +480,7 @@ export interface operations { pagination?: components["schemas"]["pagination"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -504,7 +504,7 @@ export interface operations { "create-outgoing-payment": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -545,7 +545,7 @@ export interface operations { "create-quote": { parameters: { header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -571,9 +571,9 @@ export interface operations { requestBody: { content: { "application/json": { - receiver: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; + receiver: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; }; }; }; @@ -586,7 +586,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -617,7 +617,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -644,7 +644,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -671,7 +671,7 @@ export interface operations { id: components["parameters"]["id"]; }; header: { - /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization" When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ + /** The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. */ "Signature-Input": components["parameters"]["signature-input"]; /** The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. */ Signature: components["parameters"]["signature"]; @@ -693,17 +693,17 @@ export interface operations { } export interface external { - "https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml": { + "https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml": { paths: {}; components: { schemas: { /** @description A description of the rights associated with this access token. */ - access: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-item"][]; + access: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-item"][]; /** @description The access associated with the access token is described using objects that each contain multiple dimensions of access. */ "access-item": - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-incoming"] - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-outgoing"] - | external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["access-quote"]; + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-incoming"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-outgoing"] + | external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["access-quote"]; /** access-incoming */ "access-incoming": { /** @description The type of resource request as a string. This field defines which other fields are allowed in the request object. */ @@ -734,7 +734,7 @@ export interface external { * @description A string identifier indicating a specific resource at the RS. */ identifier: string; - limits?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; + limits?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["limits-outgoing"]; }; /** access-quote */ "access-quote": { @@ -753,8 +753,8 @@ export interface external { * @description The value is an unsigned 64-bit integer amount, represented as a string. */ value: string; - assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["assetScale"]; + assetCode: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["assetScale"]; }; /** * Asset code @@ -807,10 +807,10 @@ export interface external { * @description Open Payments specific property that defines the limits under which outgoing payments can be created. */ "limits-outgoing": Partial & { - receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["receiver"]; - sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["amount"]; - interval?: external["https://raw.githubusercontent.com/interledger/open-payments/7bb2e6a03d7dfe7ecb0553afb6c70741317bb489/openapi/shared/schemas.yaml"]["components"]["schemas"]["interval"]; + receiver?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["receiver"]; + sendAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + receiveAmount?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["amount"]; + interval?: external["https://raw.githubusercontent.com/interledger/open-payments/0523804eeb21d9b8cd2eb65e4119738a0512a095/openapi/schemas.yaml"]["components"]["schemas"]["interval"]; }; "list-actions": "list" | "list-all"; "read-actions": "read" | "read-all"; diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index 1a656f9a4b..906af96ef1 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -1,4 +1,12 @@ -export { IncomingPayment, ILPStreamConnection } from './types' +export { + IncomingPayment, + ILPStreamConnection, + InteractiveGrant, + NonInteractiveGrant, + isInteractiveGrant, + isNonInteractiveGrant +} from './types' + export { createAuthenticatedClient, createUnauthenticatedClient, diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 113e3b1507..6dcec0ab59 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -1,7 +1,13 @@ import { generateKeyPairSync } from 'crypto' import createLogger from 'pino' import { createAxiosInstance } from '../client/requests' -import { ILPStreamConnection, IncomingPayment } from '../types' +import { + ILPStreamConnection, + IncomingPayment, + InteractiveGrant, + GrantRequest, + NonInteractiveGrant +} from '../types' import base64url from 'base64url' import { v4 as uuid } from 'uuid' import { ResponseValidator } from 'openapi' @@ -60,3 +66,82 @@ export const mockIncomingPayment = ( ilpStreamConnection: mockILPStreamConnection(), ...overrides }) + +export const mockInteractiveGrant = ( + overrides?: Partial +): InteractiveGrant => ({ + interact: { + redirect: 'http://example.com/redirect', + finish: 'EF5C2D8DF0663FD5' + }, + continue: { + access_token: { + value: 'BBBDD7BDD6CB8659' + }, + uri: 'http://example.com/continue', + wait: 5 + }, + ...overrides +}) + +export const mockNonInteractiveGrant = ( + overrides?: Partial +): NonInteractiveGrant => ({ + access_token: { + value: '99C36C2A4DB5BEBC', + manage: 'http://example.com/token/', + access: [ + { + type: 'incoming-payment', + actions: ['create', 'read', 'list', 'complete'] + } + ], + expires_in: 600 + }, + continue: { + access_token: { + value: 'DECCCF3D2229DB48' + }, + uri: 'http://example.com/continue/' + }, + ...overrides +}) + +export const mockGrantRequest = ( + overrides?: Partial +): GrantRequest => ({ + access_token: { + access: [ + { + type: 'quote', + actions: ['create', 'read'] + } + ] + }, + client: { + display: { + name: 'Shoe Shop', + uri: 'https://shoe-shop.com/' + }, + key: { + proof: 'httpsig', + jwk: { + alg: 'EdDSA', + kty: 'OKP', + use: 'sig', + crv: 'Ed25519', + kid: 'http://fynbos/keys/12345', + x: 'jfiusdhvherui4vueurygygb8' + } + } + }, + interact: { + start: ['redirect'], + finish: { + method: 'redirect', + uri: 'http://localhost:3030/mock-idp/fake-client', + nonce: '456' + } + }, + ...overrides +}) diff --git a/packages/open-payments/src/types.test.ts b/packages/open-payments/src/types.test.ts new file mode 100644 index 0000000000..fcf0994d39 --- /dev/null +++ b/packages/open-payments/src/types.test.ts @@ -0,0 +1,36 @@ +import { mockInteractiveGrant, mockNonInteractiveGrant } from './test/helpers' +import { isInteractiveGrant, isNonInteractiveGrant } from './types' + +describe('types', (): void => { + describe('isInteractiveGrant', (): void => { + test('returns true if has interact property', async (): Promise => { + expect(isInteractiveGrant(mockInteractiveGrant())).toBe(true) + }) + + test('returns false if does not have interact property', async (): Promise => { + expect( + isInteractiveGrant( + mockInteractiveGrant({ + interact: undefined + }) + ) + ).toBe(false) + }) + }) + + describe('isNonInteractiveGrant', (): void => { + test('returns true if has access_token property', async (): Promise => { + expect(isNonInteractiveGrant(mockNonInteractiveGrant())).toBe(true) + }) + + test('returns false if does not have access_token property', async (): Promise => { + expect( + isNonInteractiveGrant( + mockNonInteractiveGrant({ + access_token: undefined + }) + ) + ).toBe(false) + }) + }) +}) diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 96198ee03b..c925fd7ba5 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -1,9 +1,44 @@ -import { components, paths as Paths } from './generated/types' +import { + components as RSComponents, + paths as RSPaths +} from './generated/resource-server-types' +import { + components as ASComponents, + paths as ASPaths, + operations as ASOperations +} from './generated/auth-server-types' -export const getPath =

(path: P): string => +export const getRSPath =

(path: P): string => path as string - export type IncomingPayment = - components['schemas']['incoming-payment-with-connection'] -export type ILPStreamConnection = components['schemas']['ilp-stream-connection'] -export type PaymentPointer = components['schemas']['payment-pointer'] + RSComponents['schemas']['incoming-payment-with-connection'] +export type ILPStreamConnection = + RSComponents['schemas']['ilp-stream-connection'] +export type PaymentPointer = RSComponents['schemas']['payment-pointer'] + +export const getASPath =

(path: P): string => + path as string +export type NonInteractiveGrantRequest = { + access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] + client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] +} +export type NonInteractiveGrant = { + access_token: ASComponents['schemas']['access_token'] + continue: ASComponents['schemas']['continue'] +} +export type GrantRequest = { + access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] + client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] + interact: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] +} +export type InteractiveGrant = { + interact: ASComponents['schemas']['interact-response'] + continue: ASComponents['schemas']['continue'] +} +export const isInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is InteractiveGrant => !!(grant as InteractiveGrant).interact + +export const isNonInteractiveGrant = ( + grant: InteractiveGrant | NonInteractiveGrant +): grant is NonInteractiveGrant => !!(grant as NonInteractiveGrant).access_token diff --git a/packages/open-payments/tsconfig.json b/packages/open-payments/tsconfig.json index 700f4d60dc..bc4252bfc2 100644 --- a/packages/open-payments/tsconfig.json +++ b/packages/open-payments/tsconfig.json @@ -7,5 +7,5 @@ "declaration": true }, "include": ["src/**/*"], - "exclude": ["**/*.test.ts", "src/scripts/*", "src/test/*"] + "exclude": ["**/*.test.ts", "src/test/*"] }