From ec04a61b52d0d5206995091853372671c6ee6c9e Mon Sep 17 00:00:00 2001 From: Adrian Hope-Bailie Date: Fri, 8 Dec 2023 10:42:14 +0200 Subject: [PATCH] feat: extract wallet address from RS (#315) * feat: extract wallet address from RS * fix: address comments from @sabineschaller * fix: validate OpenAPI spec * fix: linting issues * fix: remove spectral config * fix: add code generation * fix: add types and fix mocks * fix: lint and format * fix: update open-payments client * docs: update docs autogen * fix: add changesets * test: add tests for DID Document * fix: accept suggestions --- .changeset/many-snails-punch.md | 5 + .gitignore | 1 + docs/astro.config.mjs | 5 + openapi/resource-server.yaml | 150 ------------- openapi/wallet-address-server.yaml | 201 ++++++++++++++++++ packages/open-payments/package.json | 3 +- packages/open-payments/src/client/index.ts | 19 +- .../src/client/wallet-address.test.ts | 34 ++- .../src/client/wallet-address.ts | 29 ++- .../generated/resource-server-types.ts | 87 -------- .../generated/wallet-address-server-types.ts | 172 +++++++++++++++ .../src/openapi/wallet-address-server.yaml | 1 + packages/open-payments/src/test/helpers.ts | 11 +- packages/open-payments/src/types.ts | 16 +- 14 files changed, 481 insertions(+), 253 deletions(-) create mode 100644 .changeset/many-snails-punch.md create mode 100644 openapi/wallet-address-server.yaml create mode 100644 packages/open-payments/src/openapi/generated/wallet-address-server-types.ts create mode 120000 packages/open-payments/src/openapi/wallet-address-server.yaml diff --git a/.changeset/many-snails-punch.md b/.changeset/many-snails-punch.md new file mode 100644 index 00000000..6177ec91 --- /dev/null +++ b/.changeset/many-snails-punch.md @@ -0,0 +1,5 @@ +--- +'@interledger/open-payments': minor +--- + +Mapped wallet address and jwks get to new stand along Open API spec diff --git a/.gitignore b/.gitignore index 2f866f81..e315a36b 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ dist **/.terraform tmp +.spectral.json diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index e8b7f8e9..70daa595 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -9,6 +9,11 @@ const { starlightOpenAPI } = await generateAPI([ label: 'Open Payments', schema: '../openapi/resource-server.yaml' }, + { + base: 'apis/wallet-address-server', + label: 'Wallet Addresses', + schema: '../openapi/wallet-address-server.yaml' + }, { base: 'apis/auth-server', label: 'Open Payments Authorization Server', diff --git a/openapi/resource-server.yaml b/openapi/resource-server.yaml index 4bb5cf9a..cee5a731 100644 --- a/openapi/resource-server.yaml +++ b/openapi/resource-server.yaml @@ -42,55 +42,6 @@ tags: - name: quote description: quote operations paths: - /: - get: - summary: Get a Wallet Address - tags: - - wallet-address - responses: - '200': - description: Wallet Address Found - content: - application/json: - schema: - $ref: '#/components/schemas/wallet-address' - examples: - Get wallet address for $ilp.rafiki.money/alice: - value: - id: 'https://ilp.rafiki.money/alice' - publicName: Alice - assetCode: USD - assetScale: 2 - authServer: 'https://auth.rafiki.money' - '404': - description: Wallet Address Not Found - operationId: get-wallet-address - description: |- - Retrieve the public information of the Wallet Address. - - This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. - - The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. - security: [] - x-internal: false - /jwks.json: - get: - summary: Get the keys bound to a Wallet Address - tags: - - wallet-address - responses: - '200': - description: JWKS Document Found - content: - application/json: - schema: - $ref: '#/components/schemas/json-web-key-set' - examples: {} - '404': - description: JWKS Document Not Found - operationId: get-wallet-address-keys - description: Retrieve the public keys of the Wallet Address. - security: [] /incoming-payments: post: summary: Create an Incoming Payment @@ -817,62 +768,6 @@ paths: - $ref: '#/components/parameters/id' components: schemas: - wallet-address: - title: Wallet Address - type: object - description: A **wallet address** resource is the root of the API and contains the public details of the financial account represented by the Wallet Address that is also the service endpoint URL. - additionalProperties: true - examples: - - id: 'https://ilp.rafiki.money/alice' - publicName: Alice - assetCode: USD - assetScale: 2 - authServer: 'https://auth.rafiki.money' - properties: - id: - type: string - format: uri - description: The URL identifying the wallet address. - readOnly: true - publicName: - type: 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. - readOnly: true - assetCode: - $ref: ./schemas.yaml#/components/schemas/assetCode - assetScale: - $ref: ./schemas.yaml#/components/schemas/assetScale - authServer: - type: string - format: uri - description: The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. - readOnly: true - required: - - id - - assetCode - - assetScale - - authServer - json-web-key-set: - title: JSON Web Key Set document - type: object - description: 'A JSON Web Key Set document according to [rfc7517](https://datatracker.ietf.org/doc/html/rfc7517) listing the keys associated with this wallet address. These keys are used to sign requests made by this wallet address.' - additionalProperties: false - properties: - keys: - type: array - items: - $ref: '#/components/schemas/json-web-key' - readOnly: true - required: - - keys - examples: - - keys: - - kid: key-1 - alg: EdDSA - use: sig - kty: OKP - crv: Ed25519 - x: 11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo incoming-payment: title: Incoming Payment description: 'An **incoming payment** resource represents a payment that will be, is currently being, or has been received by the account.' @@ -1187,51 +1082,6 @@ components: - hasNextPage - hasPreviousPage additionalProperties: false - json-web-key: - type: object - properties: - kid: - type: string - alg: - type: string - description: 'The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. ' - enum: - - EdDSA - use: - type: string - enum: - - sig - kty: - type: string - enum: - - OKP - crv: - type: string - enum: - - Ed25519 - x: - type: string - pattern: '^[a-zA-Z0-9-_]+$' - description: The base64 url-encoded public key. - required: - - kid - - alg - - kty - - crv - - x - title: Ed25519 Public Key - description: A JWK representation of an Ed25519 Public Key - examples: - - kid: key-1 - use: sig - kty: OKP - crv: Ed25519 - x: 11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo - - kid: '2022-09-02' - use: sig - kty: OKP - crv: Ed25519 - x: oy0L_vTygNE4IogRyn_F5GmHXdqYVjIXkWs2jky7zsI payment-method: type: string enum: diff --git a/openapi/wallet-address-server.yaml b/openapi/wallet-address-server.yaml new file mode 100644 index 00000000..80ba8e01 --- /dev/null +++ b/openapi/wallet-address-server.yaml @@ -0,0 +1,201 @@ +openapi: 3.1.0 +info: + title: Wallet Address API + version: '1.4' + license: + name: Apache 2.0 + identifier: Apache-2.0 + description: |- + The Wallet Address API is a simple REST API to get basic details about a wallet address. + contact: + email: tech@interledger.org +servers: + - url: 'https://rafiki.money/alice' + description: "Server for Alice's wallet address" + - url: 'https://rafiki.money/bob' + description: "Server for Bob's wallet address" +tags: + - name: wallet-address + description: wallet address operations +paths: + /: + get: + summary: Get a Wallet Address + tags: + - wallet-address + responses: + '200': + description: Wallet Address Found + content: + application/json: + schema: + $ref: '#/components/schemas/wallet-address' + examples: + Get wallet address for $rafiki.money/alice: + value: + id: 'https://rafiki.money/alice' + publicName: Alice + assetCode: USD + assetScale: 2 + authServer: 'https://rafiki.money/auth' + resourceServer: 'https://rafiki.money/op' + '404': + description: Wallet Address Not Found + operationId: get-wallet-address + description: |- + Retrieve the public information of the Wallet Address. + + This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. + + The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. + security: [] + x-internal: false + /jwks.json: + get: + summary: Get the keys bound to a Wallet Address + tags: + - wallet-address + responses: + '200': + description: JWKS Document Found + content: + application/json: + schema: + $ref: '#/components/schemas/json-web-key-set' + examples: {} + '404': + description: JWKS Document Not Found + operationId: get-wallet-address-keys + description: Retrieve the public keys of the Wallet Address. + security: [] + /did.json: + get: + summary: Get the DID Document for this wallet + tags: + - wallet-address + responses: + '200': + description: DID Document Found + content: + application/json: + schema: + $ref: '#/components/schemas/did-document' + '500': + description: DID Document not yet implemented + operationId: get-wallet-address-did-document + description: Retrieve the DID Document of the Wallet Address. + security: [] +components: + schemas: + wallet-address: + title: Wallet Address + type: object + description: A **wallet address** resource is the root of the API and contains the public details of the financial account represented by the Wallet Address that is also the service endpoint URL. + additionalProperties: true + examples: + - id: 'https://rafiki.money/alice' + publicName: Alice + assetCode: USD + assetScale: 2 + authServer: 'https://rafiki.money/auth' + resourceServer: 'https://rafiki.money/op' + properties: + id: + type: string + format: uri + description: The URL identifying the wallet address. + readOnly: true + publicName: + type: 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. + readOnly: true + assetCode: + $ref: ./schemas.yaml#/components/schemas/assetCode + assetScale: + $ref: ./schemas.yaml#/components/schemas/assetScale + authServer: + type: string + format: uri + description: The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. + readOnly: true + resourceServer: + type: string + format: uri + description: The URL of the resource server endpoint for performing Open Payments with this wallet address. + readOnly: true + required: + - id + - assetCode + - assetScale + - authServer + - resourceServer + json-web-key-set: + title: JSON Web Key Set document + type: object + description: 'A JSON Web Key Set document according to [rfc7517](https://datatracker.ietf.org/doc/html/rfc7517) listing the keys associated with this wallet address. These keys are used to sign requests made by this wallet address.' + additionalProperties: false + properties: + keys: + type: array + items: + $ref: '#/components/schemas/json-web-key' + readOnly: true + required: + - keys + examples: + - keys: + - kid: key-1 + alg: EdDSA + use: sig + kty: OKP + crv: Ed25519 + x: 11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo + json-web-key: + type: object + properties: + kid: + type: string + alg: + type: string + description: 'The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. ' + enum: + - EdDSA + use: + type: string + enum: + - sig + kty: + type: string + enum: + - OKP + crv: + type: string + enum: + - Ed25519 + x: + type: string + pattern: '^[a-zA-Z0-9-_]+$' + description: The base64 url-encoded public key. + required: + - kid + - alg + - kty + - crv + - x + title: Ed25519 Public Key + description: A JWK representation of an Ed25519 Public Key + examples: + - kid: key-1 + use: sig + kty: OKP + crv: Ed25519 + x: 11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo + - kid: '2022-09-02' + use: sig + kty: OKP + crv: Ed25519 + x: oy0L_vTygNE4IogRyn_F5GmHXdqYVjIXkWs2jky7zsI + did-document: + type: object + title: DID Document + description: A DID Document using JSON encoding diff --git a/packages/open-payments/package.json b/packages/open-payments/package.json index b952e81b..dd6c6c41 100644 --- a/packages/open-payments/package.json +++ b/packages/open-payments/package.json @@ -24,7 +24,8 @@ "copy-files": "cp ./src/openapi/*.yaml ./dist/openapi/", "generate:auth-server-types": "openapi-typescript src/openapi/auth-server.yaml --output src/openapi/generated/auth-server-types.ts", "generate:resource-server-types": "openapi-typescript src/openapi/resource-server.yaml --output src/openapi/generated/resource-server-types.ts", - "generate:types": "pnpm generate:auth-server-types && pnpm generate:resource-server-types", + "generate:wallet-address-server-types": "openapi-typescript src/openapi/wallet-address-server.yaml --output src/openapi/generated/wallet-address-server-types.ts", + "generate:types": "pnpm generate:auth-server-types && pnpm generate:resource-server-types && pnpm generate:wallet-address-server-types", "prepack": "pnpm build", "test": "jest --passWithNoTests" }, diff --git a/packages/open-payments/src/client/index.ts b/packages/open-payments/src/client/index.ts index 15cf9d96..0b8b2e1e 100644 --- a/packages/open-payments/src/client/index.ts +++ b/packages/open-payments/src/client/index.ts @@ -34,6 +34,7 @@ export interface BaseDeps { } interface UnauthenticatedClientDeps extends BaseDeps { + walletAddressServerOpenApi: OpenAPI resourceServerOpenApi: OpenAPI } @@ -135,12 +136,16 @@ const createUnauthenticatedDeps = async ({ args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS }) + const walletAddressServerOpenApi = await createOpenAPI( + path.resolve(__dirname, '../openapi/wallet-address-server.yaml') + ) const resourceServerOpenApi = await createOpenAPI( path.resolve(__dirname, '../openapi/resource-server.yaml') ) return { axiosInstance, + walletAddressServerOpenApi, resourceServerOpenApi, logger, useHttp @@ -177,6 +182,9 @@ const createAuthenticatedClientDeps = async ({ requestTimeoutMs: args?.requestTimeoutMs ?? config.DEFAULT_REQUEST_TIMEOUT_MS }) + const walletAddressServerOpenApi = await createOpenAPI( + path.resolve(__dirname, '../openapi/wallet-address-server.yaml') + ) const resourceServerOpenApi = await createOpenAPI( path.resolve(__dirname, '../openapi/resource-server.yaml') ) @@ -186,6 +194,7 @@ const createAuthenticatedClientDeps = async ({ return { axiosInstance, + walletAddressServerOpenApi, resourceServerOpenApi, authServerOpenApi, logger, @@ -252,8 +261,12 @@ export interface AuthenticatedClient export const createAuthenticatedClient = async ( args: CreateAuthenticatedClientArgs ): Promise => { - const { resourceServerOpenApi, authServerOpenApi, ...baseDeps } = - await createAuthenticatedClientDeps(args) + const { + resourceServerOpenApi, + authServerOpenApi, + walletAddressServerOpenApi, + ...baseDeps + } = await createAuthenticatedClientDeps(args) return { incomingPayment: createIncomingPaymentRoutes({ @@ -266,7 +279,7 @@ export const createAuthenticatedClient = async ( }), walletAddress: createWalletAddressRoutes({ ...baseDeps, - openApi: resourceServerOpenApi + openApi: walletAddressServerOpenApi }), grant: createGrantRoutes({ ...baseDeps, diff --git a/packages/open-payments/src/client/wallet-address.test.ts b/packages/open-payments/src/client/wallet-address.test.ts index 1726d2db..66fbcb91 100644 --- a/packages/open-payments/src/client/wallet-address.test.ts +++ b/packages/open-payments/src/client/wallet-address.test.ts @@ -1,7 +1,12 @@ import { createWalletAddressRoutes } from './wallet-address' import { OpenAPI, HttpMethod, createOpenAPI } from '@interledger/openapi' import path from 'path' -import { createTestDeps, mockJwk, mockWalletAddress } from '../test/helpers' +import { + createTestDeps, + mockDIDDocument, + mockJwk, + mockWalletAddress +} from '../test/helpers' import * as requestors from './requests' jest.mock('./requests', () => { @@ -79,5 +84,32 @@ describe('wallet-address', (): void => { ) }) }) + + describe('getDIDDocument', (): void => { + test('calls get method with correct validator', async (): Promise => { + const mockResponseValidator = ({ path, method }) => + path === '/did.json' && method === HttpMethod.GET + + jest + .spyOn(openApi, 'createResponseValidator') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .mockImplementation(mockResponseValidator as any) + + const getSpy = jest + .spyOn(requestors, 'get') + .mockResolvedValueOnce([mockDIDDocument()]) + + await createWalletAddressRoutes({ + openApi, + ...deps + }).getDIDDocument({ url: walletAddress.id }) + + expect(getSpy).toHaveBeenCalledWith( + deps, + { url: `${walletAddress.id}/did.json` }, + true + ) + }) + }) }) }) diff --git a/packages/open-payments/src/client/wallet-address.ts b/packages/open-payments/src/client/wallet-address.ts index 74c181be..a3f003ee 100644 --- a/packages/open-payments/src/client/wallet-address.ts +++ b/packages/open-payments/src/client/wallet-address.ts @@ -1,11 +1,13 @@ import { HttpMethod } from '@interledger/openapi' import { RouteDeps, UnauthenticatedResourceRequestArgs } from '.' -import { JWKS, WalletAddress, getRSPath } from '../types' +import { JWKS, WalletAddress, DIDDocument, getWAPath } from '../types' import { get } from './requests' export interface WalletAddressRoutes { get(args: UnauthenticatedResourceRequestArgs): Promise getKeys(args: UnauthenticatedResourceRequestArgs): Promise + // TODO: Define schema for DID Document + getDIDDocument(args: UnauthenticatedResourceRequestArgs): Promise } export const createWalletAddressRoutes = ( @@ -13,27 +15,40 @@ export const createWalletAddressRoutes = ( ): WalletAddressRoutes => { const { openApi, ...baseDeps } = deps - const getPaymentPaymentValidator = + const getWalletAddressValidator = openApi.createResponseValidator({ - path: getRSPath('/'), + path: getWAPath('/'), method: HttpMethod.GET }) - const getPaymentPaymentKeysValidator = openApi.createResponseValidator({ - path: getRSPath('/jwks.json'), + const getWalletAddressKeysValidator = openApi.createResponseValidator({ + path: getWAPath('/jwks.json'), + method: HttpMethod.GET + }) + + const getDidDocumentValidator = openApi.createResponseValidator({ + path: getWAPath('/did.json'), method: HttpMethod.GET }) return { get: (args: UnauthenticatedResourceRequestArgs) => - get(baseDeps, { url: args.url }, getPaymentPaymentValidator), + get(baseDeps, args, getWalletAddressValidator), getKeys: (args: UnauthenticatedResourceRequestArgs) => get( baseDeps, { url: `${args.url}/jwks.json` }, - getPaymentPaymentKeysValidator + getWalletAddressKeysValidator + ), + getDIDDocument: (args: UnauthenticatedResourceRequestArgs) => + get( + baseDeps, + { + url: `${args.url}/did.json` + }, + getDidDocumentValidator ) } } diff --git a/packages/open-payments/src/openapi/generated/resource-server-types.ts b/packages/open-payments/src/openapi/generated/resource-server-types.ts index 80e1fb9a..7320e577 100644 --- a/packages/open-payments/src/openapi/generated/resource-server-types.ts +++ b/packages/open-payments/src/openapi/generated/resource-server-types.ts @@ -4,20 +4,6 @@ */ export interface paths { - "/": { - /** - * Retrieve the public information of the Wallet Address. - * - * This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. - * - * The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. - */ - get: operations["get-wallet-address"]; - }; - "/jwks.json": { - /** Retrieve the public keys of the Wallet Address. */ - get: operations["get-wallet-address-keys"]; - }; "/incoming-payments": { /** List all incoming payments on the wallet address */ get: operations["list-incoming-payments"]; @@ -100,33 +86,6 @@ export interface paths { export interface components { schemas: { - /** - * Wallet Address - * @description A **wallet address** resource is the root of the API and contains the public details of the financial account represented by the Wallet Address that is also the service endpoint URL. - */ - "wallet-address": { - /** - * Format: uri - * @description The URL identifying the wallet address. - */ - 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["schemas.yaml"]["components"]["schemas"]["assetCode"]; - assetScale: external["schemas.yaml"]["components"]["schemas"]["assetScale"]; - /** - * Format: uri - * @description The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. - */ - authServer: string; - } & { [key: string]: unknown }; - /** - * JSON Web Key Set document - * @description A JSON Web Key Set document according to [rfc7517](https://datatracker.ietf.org/doc/html/rfc7517) listing the keys associated with this wallet address. These keys are used to sign requests made by this wallet address. - */ - "json-web-key-set": { - keys: components["schemas"]["json-web-key"][]; - }; /** * Incoming Payment * @description An **incoming payment** resource represents a payment that will be, is currently being, or has been received by the account. @@ -269,20 +228,6 @@ export interface components { /** @description Describes whether the data set has previous entries. */ hasPreviousPage: boolean; }; - /** - * Ed25519 Public Key - * @description A JWK representation of an Ed25519 Public Key - */ - "json-web-key": { - kid: string; - /** @description The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. */ - alg: "EdDSA"; - use?: "sig"; - kty: "OKP"; - crv: "Ed25519"; - /** @description The base64 url-encoded public key. */ - x: string; - }; "payment-method": "ilp"; "ilp-payment-method": { type: "ilp"; @@ -321,38 +266,6 @@ export interface components { } export interface operations { - /** - * Retrieve the public information of the Wallet Address. - * - * This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. - * - * The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. - */ - "get-wallet-address": { - responses: { - /** Wallet Address Found */ - 200: { - content: { - "application/json": components["schemas"]["wallet-address"]; - }; - }; - /** Wallet Address Not Found */ - 404: unknown; - }; - }; - /** Retrieve the public keys of the Wallet Address. */ - "get-wallet-address-keys": { - responses: { - /** JWKS Document Found */ - 200: { - content: { - "application/json": components["schemas"]["json-web-key-set"]; - }; - }; - /** JWKS Document Not Found */ - 404: unknown; - }; - }; /** List all incoming payments on the wallet address */ "list-incoming-payments": { parameters: { diff --git a/packages/open-payments/src/openapi/generated/wallet-address-server-types.ts b/packages/open-payments/src/openapi/generated/wallet-address-server-types.ts new file mode 100644 index 00000000..12d1082c --- /dev/null +++ b/packages/open-payments/src/openapi/generated/wallet-address-server-types.ts @@ -0,0 +1,172 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/": { + /** + * Retrieve the public information of the Wallet Address. + * + * This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. + * + * The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. + */ + get: operations["get-wallet-address"]; + }; + "/jwks.json": { + /** Retrieve the public keys of the Wallet Address. */ + get: operations["get-wallet-address-keys"]; + }; + "/did.json": { + /** Retrieve the DID Document of the Wallet Address. */ + get: operations["get-wallet-address-did-document"]; + }; +} + +export interface components { + schemas: { + /** + * Wallet Address + * @description A **wallet address** resource is the root of the API and contains the public details of the financial account represented by the Wallet Address that is also the service endpoint URL. + */ + "wallet-address": { + /** + * Format: uri + * @description The URL identifying the wallet address. + */ + 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["schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["schemas.yaml"]["components"]["schemas"]["assetScale"]; + /** + * Format: uri + * @description The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. + */ + authServer: string; + /** + * Format: uri + * @description The URL of the resource server endpoint for performing Open Payments with this wallet address. + */ + resourceServer: string; + } & { [key: string]: unknown }; + /** + * JSON Web Key Set document + * @description A JSON Web Key Set document according to [rfc7517](https://datatracker.ietf.org/doc/html/rfc7517) listing the keys associated with this wallet address. These keys are used to sign requests made by this wallet address. + */ + "json-web-key-set": { + keys: components["schemas"]["json-web-key"][]; + }; + /** + * Ed25519 Public Key + * @description A JWK representation of an Ed25519 Public Key + */ + "json-web-key": { + kid: string; + /** @description The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. */ + alg: "EdDSA"; + use?: "sig"; + kty: "OKP"; + crv: "Ed25519"; + /** @description The base64 url-encoded public key. */ + x: string; + }; + /** + * DID Document + * @description A DID Document using JSON encoding + */ + "did-document": { [key: string]: unknown }; + }; +} + +export interface operations { + /** + * Retrieve the public information of the Wallet Address. + * + * This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. + * + * The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. + */ + "get-wallet-address": { + responses: { + /** Wallet Address Found */ + 200: { + content: { + "application/json": components["schemas"]["wallet-address"]; + }; + }; + /** Wallet Address Not Found */ + 404: unknown; + }; + }; + /** Retrieve the public keys of the Wallet Address. */ + "get-wallet-address-keys": { + responses: { + /** JWKS Document Found */ + 200: { + content: { + "application/json": components["schemas"]["json-web-key-set"]; + }; + }; + /** JWKS Document Not Found */ + 404: unknown; + }; + }; + /** Retrieve the DID Document of the Wallet Address. */ + "get-wallet-address-did-document": { + responses: { + /** DID Document Found */ + 200: { + content: { + "application/json": components["schemas"]["did-document"]; + }; + }; + /** DID Document not yet implemented */ + 500: unknown; + }; + }; +} + +export interface external { + "schemas.yaml": { + paths: {}; + components: { + schemas: { + /** amount */ + amount: { + /** + * Format: uint64 + * @description The value is an unsigned 64-bit integer amount, represented as a string. + */ + value: string; + assetCode: external["schemas.yaml"]["components"]["schemas"]["assetCode"]; + assetScale: external["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; + /** + * Receiver + * Format: uri + * @description The URL of the incoming payment or ILP STREAM connection that is being paid. + */ + receiver: string; + /** + * Wallet Address + * Format: uri + * @description URL of a wallet address hosted by a Rafiki instance. + */ + walletAddress: string; + }; + }; + operations: {}; + }; +} diff --git a/packages/open-payments/src/openapi/wallet-address-server.yaml b/packages/open-payments/src/openapi/wallet-address-server.yaml new file mode 120000 index 00000000..145b96aa --- /dev/null +++ b/packages/open-payments/src/openapi/wallet-address-server.yaml @@ -0,0 +1 @@ +../../../../openapi/wallet-address-server.yaml \ No newline at end of file diff --git a/packages/open-payments/src/test/helpers.ts b/packages/open-payments/src/test/helpers.ts index 6127f399..50547846 100644 --- a/packages/open-payments/src/test/helpers.ts +++ b/packages/open-payments/src/test/helpers.ts @@ -16,7 +16,8 @@ import { Grant, IncomingPaymentWithPaymentMethods, IlpPaymentMethod, - PublicIncomingPayment + PublicIncomingPayment, + DIDDocument } from '../types' import { v4 as uuid } from 'uuid' import { ResponseValidator } from '@interledger/openapi' @@ -57,11 +58,19 @@ export const mockJwk = (overrides?: Partial): JWK => ({ ...overrides }) +export const mockDIDDocument = ( + overrides?: Partial +): DIDDocument => ({ + // TODO - Flesh this out when we have a more detailed DID Document spec + ...overrides +}) + export const mockWalletAddress = ( overrides?: Partial ): WalletAddress => ({ id: 'https://example.com/.well-known/pay', authServer: 'https://auth.wallet.example/authorize', + resourceServer: 'https://wallet.example/op', assetScale: 2, assetCode: 'USD', ...overrides diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index ad858f7d..c89e2482 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -8,6 +8,18 @@ import { paths as ASPaths, operations as ASOperations } from './openapi/generated/auth-server-types' +import { + components as WAComponents, + paths as WAPaths +} from './openapi/generated/wallet-address-server-types' + +export const getWAPath =

(path: P): string => + path as string + +export type WalletAddress = WAComponents['schemas']['wallet-address'] +export type JWK = WAComponents['schemas']['json-web-key'] +export type JWKS = WAComponents['schemas']['json-web-key-set'] +export type DIDDocument = WAComponents['schemas']['did-document'] export const getRSPath =

(path: P): string => path as string @@ -46,9 +58,7 @@ export type BackwardPagination = Omit & { first?: never } export type PaginationArgs = ForwardPagination | BackwardPagination -export type WalletAddress = RSComponents['schemas']['wallet-address'] -export type JWK = RSComponents['schemas']['json-web-key'] -export type JWKS = RSComponents['schemas']['json-web-key-set'] + export type Quote = RSComponents['schemas']['quote'] type QuoteArgsBase = { walletAddress: RSOperations['create-quote']['requestBody']['content']['application/json']['walletAddress']