diff --git a/.changeset/giant-dots-carry.md b/.changeset/giant-dots-carry.md new file mode 100644 index 0000000..df5ab0f --- /dev/null +++ b/.changeset/giant-dots-carry.md @@ -0,0 +1,5 @@ +--- +'@interledger/open-payments': minor +--- + +New union client type to support directed identity. Exported grant type guards and added new, more explicit grant types. diff --git a/open-payments-specifications b/open-payments-specifications index c1de330..70a3697 160000 --- a/open-payments-specifications +++ b/open-payments-specifications @@ -1 +1 @@ -Subproject commit c1de33071c874f1c481d65f2220d791073a1cb6d +Subproject commit 70a36970f132dd483e691e9d3190741abfdc41fb diff --git a/packages/open-payments/src/index.ts b/packages/open-payments/src/index.ts index c0c979c..eb91e51 100644 --- a/packages/open-payments/src/index.ts +++ b/packages/open-payments/src/index.ts @@ -12,8 +12,12 @@ export { OutgoingPaymentGrantSpentAmounts, PendingGrant, Grant, + GrantWithAccessToken, + GrantWithSubject, isPendingGrant, isFinalizedGrant, + isFinalizedGrantWithAccessToken, + isFinalizedGrantWithSubject, JWK, JWKS, PaginationArgs, @@ -22,7 +26,8 @@ export { AccessAction, AccessToken, AccessItem, - Subject + Subject, + Client } from './types' export { diff --git a/packages/open-payments/src/openapi/generated/auth-server-types.ts b/packages/open-payments/src/openapi/generated/auth-server-types.ts index 7477f96..837b32e 100644 --- a/packages/open-payments/src/openapi/generated/auth-server-types.ts +++ b/packages/open-payments/src/openapi/generated/auth-server-types.ts @@ -167,15 +167,31 @@ export interface components { }; /** * client - * @description Wallet address of the client instance that is making this request. + * @description Client identification for grant requests. * * 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. * + * Can be either: + * - A wallet address string (backwards compatible format) + * - An object with either `jwk` (for directed identity) or `walletAddress` (mutually exclusive) + * + * When using a wallet address string or the `walletAddress` property: * A JSON Web Key Set document, including the public 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, MUST be available at the wallet address + `/jwks.json` url. * + * When using the `jwk` property (directed identity approach): + * The client instance provides its public key directly in the request, eliminating the need for the AS to fetch it from a wallet address. This approach enhances privacy by not requiring the client to expose a persistent wallet address identifier. The `jwk` property can only be used for non-interactive grant requests (i.e.: incoming payments). + * * If sending a grant initiation request that requires RO interaction, the wallet address MUST serve necessary client display information. */ - client: string; + client: string | { + /** + * Format: uri + * @description Wallet address of the client instance that is making this request. + */ + walletAddress: string; + } | { + jwk: components["schemas"]["json-web-key"]; + }; /** * continue * @description If the AS determines that the request can be continued with additional requests, it responds with the continue field. @@ -307,6 +323,29 @@ export interface components { format: "uri"; }[]; }; + /** + * 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`. + * @enum {string} + */ + alg: "EdDSA"; + /** @enum {string} */ + use?: "sig"; + /** @enum {string} */ + kty: "OKP"; + /** + * @description The cryptographic curve used with the key. This parameter identifies the elliptic curve (for EC keys) or the Edwards curve (for OKP keys). The only allowed value is `Ed25519`. + * @enum {string} + */ + crv: "Ed25519"; + /** @description The base64 url-encoded public key. */ + x: string; + }; }; responses: never; parameters: never; 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 144e548..d269a4e 100644 --- a/packages/open-payments/src/openapi/generated/resource-server-types.ts +++ b/packages/open-payments/src/openapi/generated/resource-server-types.ts @@ -894,8 +894,8 @@ export interface operations { }; content: { "application/json": { - spentReceiveAmount?: components["schemas"]["amount"]; - spentDebitAmount?: components["schemas"]["amount"]; + spentReceiveAmount: components["schemas"]["amount"] | null; + spentDebitAmount: components["schemas"]["amount"] | null; }; }; }; diff --git a/packages/open-payments/src/types.ts b/packages/open-payments/src/types.ts index 0331267..371061d 100644 --- a/packages/open-payments/src/types.ts +++ b/packages/open-payments/src/types.ts @@ -85,14 +85,18 @@ export const getASPath =

(path: P): string => path as string export type Subject = ASComponents['schemas']['subject'] +export type Client = + | string + | { walletAddress: string; jwk?: never } + | { jwk: JWK; walletAddress?: never } export type NonInteractiveGrantRequest = { access_token: ASOperations['post-request']['requestBody']['content']['application/json']['access_token'] - client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] + client: Client } type BaseGrantRequest = { - client: ASOperations['post-request']['requestBody']['content']['application/json']['client'] + client: Client interact?: ASOperations['post-request']['requestBody']['content']['application/json']['interact'] } @@ -113,6 +117,12 @@ export type Grant = { continue: ASComponents['schemas']['continue'] subject?: Subject } +export type GrantWithAccessToken = Grant & { + access_token: NonNullable +} +export type GrantWithSubject = Grant & { + subject: NonNullable +} export type GrantContinuation = { continue: ASComponents['schemas']['continue'] } @@ -145,12 +155,11 @@ export const isFinalizedGrant = ( export const isFinalizedGrantWithAccessToken = ( grant: GrantContinuation | Grant -): grant is Grant & { access_token: ASComponents['schemas']['access_token'] } => - !!(grant as Grant).access_token +): grant is GrantWithAccessToken => !!(grant as Grant).access_token export const isFinalizedGrantWithSubject = ( grant: GrantContinuation | Grant -): grant is Grant & { subject: Subject } => !!(grant as Grant).subject +): grant is GrantWithSubject => !!(grant as Grant).subject export type AccessIncomingActions = ASComponents['schemas']['access-incoming']['actions']