@grantex/sdk 0.3.13 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,6 +72,105 @@ const grantex = new Grantex({
72
72
  | `jwksUri` | `string` | `${baseUrl}/.well-known/jwks.json` | URL from which the verifier retrieves signing keys |
73
73
  | `timeout` | `number` | `30000` | Request timeout in milliseconds |
74
74
 
75
+ ## OAuth Agent Profile Client
76
+
77
+ `OAuthAgentClient` implements the client role in the prepared
78
+ `draft-mishra-oauth-agent-grants-03` profile. It discovers and validates RFC
79
+ 8414 metadata, creates an ES256 DPoP key by default, uses PAR and PKCE S256,
80
+ validates `state` and the RFC 9207 `iss` response parameter, rotates refresh
81
+ tokens, performs same-resource RFC 8693 attenuation, and creates DPoP proofs
82
+ for protected-resource requests.
83
+
84
+ ```typescript
85
+ import { OAuthAgentClient } from '@grantex/sdk';
86
+
87
+ const client = await OAuthAgentClient.create({
88
+ issuer: 'https://grantex.dev',
89
+ clientId: 'ag_01J...',
90
+ redirectUri: 'https://agent.example/callback',
91
+ resource: 'https://api.example/resource',
92
+ });
93
+
94
+ const pending = await client.beginAuthorization({
95
+ scopes: ['grantex.resource.read'],
96
+ principalHint: 'principal@example.com',
97
+ });
98
+
99
+ // Redirect the Principal to pending.authorizationUrl. In the callback:
100
+ const tokens = await client.completeAuthorization(callbackUrl);
101
+
102
+ const response = await client.fetch(
103
+ 'https://api.example/resource',
104
+ tokens.access_token,
105
+ );
106
+
107
+ const narrower = await client.attenuate(tokens.access_token, [
108
+ 'grantex.resource.read',
109
+ ]);
110
+ const rotated = await client.refresh(tokens.refresh_token!);
111
+ await client.revoke(rotated.refresh_token!, 'refresh_token');
112
+ ```
113
+
114
+ Persist the generated key securely if an instance must survive process
115
+ restarts. Supply the matching `privateKey` and `publicJwk` to `create`; both are
116
+ required together. `principalHint` is optional account-discovery input and is
117
+ not proof of the Principal's identity; live approval still requires the
118
+ authorization server's passkey authentication. Plain HTTP endpoints are rejected unless
119
+ `allowInsecureLoopback` is enabled for local loopback testing. Revision `-02`
120
+ of the draft family is published as an active individual Internet-Draft;
121
+ revision `-03` is the working candidate implemented here. Neither is an
122
+ IETF-endorsed or independently certified standard.
123
+
124
+ ## Agent prepaid wallets (0.4.0 repository source)
125
+
126
+ `PrepaidWalletAgentClient` uses an `OAuthAgentClient` and DPoP access token to
127
+ list assigned wallets, reserve payments, and request threshold reloads.
128
+ `PrincipalPrepaidWalletClient` uses a short-lived principal-session token to
129
+ create and fund wallets, assign policy, approve reloads, inspect activity, and
130
+ block an assignment, wallet, or all wallets for one agent.
131
+
132
+ The access token must include `wallet:spend` and each action scope used in a
133
+ payment (for example `weather:read`). Agent wallet listings intentionally omit
134
+ custody-provider IDs, wallet addresses, principal IDs, and wallet metadata.
135
+
136
+ ```typescript
137
+ import {
138
+ PrepaidWalletAgentClient,
139
+ PrincipalPrepaidWalletClient,
140
+ } from '@grantex/sdk';
141
+
142
+ const agentWallets = new PrepaidWalletAgentClient({
143
+ oauthClient,
144
+ accessToken,
145
+ });
146
+
147
+ const principalWallets = new PrincipalPrepaidWalletClient({
148
+ baseUrl: 'https://grantex.dev',
149
+ sessionToken,
150
+ });
151
+
152
+ const authorization = await agentWallets.authorizePayment({
153
+ amount: '1000',
154
+ asset: 'USDC',
155
+ network: 'grantex:prepaid',
156
+ recipient: 'merchant:weather-api',
157
+ resource: 'https://merchant.example/weather',
158
+ scope: 'weather:read',
159
+ maxTimeoutSeconds: 120,
160
+ idempotencyKey: crypto.randomUUID(),
161
+ });
162
+ ```
163
+
164
+ Amounts are atomic-unit integer strings. Managed prepaid-wallet APIs are
165
+ available in `@grantex/sdk` 0.4.0 and later.
166
+
167
+ Self-hosted wallet deployments must also provide correct public resource
168
+ routing, migration `091`, durable notification delivery, merchant-side
169
+ idempotency, and any external custody/provider integration. See [Prepaid Wallet
170
+ Production
171
+ Readiness](https://docs.grantex.dev/guides/prepaid-wallet-production);
172
+ installing the SDK alone does not provide those dependencies.
173
+
75
174
  ## Commerce V1 / OACP
76
175
 
77
176
  The SDK includes a `commerce` resource for the Grantex Commerce V1 control
@@ -342,8 +441,10 @@ console.log(token.refreshToken); // for token refresh
342
441
  | `grantToken` | `string` | Signed RS256 JWT — the agent's bearer credential |
343
442
  | `grantId` | `string` | Grant record ID |
344
443
  | `scopes` | `string[]` | Scopes the user approved |
345
- | `expiresAt` | `string` | Token expiry (ISO 8601) |
346
- | `refreshToken` | `string` | Refresh token for obtaining new grant tokens |
444
+ | `expiresAt` | `string` | Underlying grant expiry (ISO 8601) |
445
+ | `refreshToken` | `string` | Refresh token for rotating credentials while the grant remains active |
446
+
447
+ Refresh tokens are single-use and rotate on every accepted refresh. If a refresh response is lost after the server commits rotation, retry the same previous refresh token immediately; Grantex can return the already-rotated token pair for five minutes (300 seconds) while the grant remains active. Refresh does not extend `expiresAt`; after the grant expires, re-authorize.
347
448
 
348
449
  ---
349
450
 
@@ -443,7 +544,7 @@ const { entries } = await grantex.audit.list({
443
544
  #### `grantex.audit.get(entryId)`
444
545
 
445
546
  ```typescript
446
- const entry = await grantex.audit.get('aud_01J...');
547
+ const entry = await grantex.audit.get('alog_01J...');
447
548
  console.log(entry.hash); // SHA-256 hash for tamper evidence
448
549
  console.log(entry.prevHash); // previous entry hash (chain integrity)
449
550
  ```
@@ -801,3 +902,7 @@ const result = await grantex.enforce({ grantToken: token, connector: 'my-crm', t
801
902
  ## License
802
903
 
803
904
  [Apache 2.0](https://github.com/mishrasanjeev/grantex/blob/main/LICENSE)
905
+
906
+ ## Ownership
907
+
908
+ Grantex is owned by Orchestrum Technologies LLP. Inventor and owner: Sanjeev Kumar. Ownership contact: [sanjeev@orchestrum.in](mailto:sanjeev@orchestrum.in) or [mishra.sanjeev@gmail.com](mailto:mishra.sanjeev@gmail.com).
package/dist/index.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  export { Grantex } from './client.js';
2
2
  export { verifyGrantToken } from './verify.js';
3
- export { verifyWebhookSignature } from './webhook.js';
3
+ export { verifyWebhookSignature, verifyWebhook, type VerifyWebhookOptions } from './webhook.js';
4
4
  export { generatePkce, type PkceChallenge } from './pkce.js';
5
+ export { OAuthAgentClient, generateOAuthAgentKey, type OAuthAuthorizationServerMetadata, type OAuthAgentClientOptions, type OAuthAgentKeyPair, type BeginAgentAuthorizationOptions, type PendingAgentAuthorization, type OAuthAgentTokenResponse, } from './oauth-agent.js';
6
+ export { PrepaidWalletAgentClient, PrincipalPrepaidWalletClient, type PrepaidCustodyMode, type PrepaidWalletStatus, type WalletAssignmentStatus, type PrepaidWallet, type AssignedPrepaidWallet, type WalletAssignment, type CreatePrepaidWalletParams, type AssignPrepaidWalletParams, type PrepaidAuthorizationRequest, type PrepaidAuthorizationResponse, type WalletReloadRequest, type PrepaidWalletActivity, } from './prepaid-wallets.js';
5
7
  export { GrantexError, GrantexApiError, GrantexAuthError, GrantexTokenError, GrantexNetworkError, } from './errors.js';
6
8
  export { EventsClient, type GrantexEvent as GrantexStreamEvent, type StreamOptions, type EventHandler } from './resources/events.js';
7
9
  export { PassportsClient, type IssuePassportParams, type IssuedPassportResponse, type GetPassportResponse, type RevokePassportResponse, type ListPassportsParams, } from './resources/passports.js';
8
10
  export { ToolManifest, Permission, permissionCovers, type EnforceOptions, type EnforceResult, type ToolManifestOptions, type WrapToolOptions, type EnforceMiddlewareOptions } from './manifest.js';
9
11
  export { DpdpClient } from './resources/dpdp.js';
10
12
  export { CommerceClient, type CommerceEnvironment, type CommerceProviderKey, type CommercePassportType, type CommercePaymentStatus, type CommerceRecord, type CommerceDataResponse, type CommerceListResponse, type CommerceProfile, type CommerceIdempotentRequest, type CommerceTenantCreateParams, type CommerceTenantUpdateParams, type CommerceDeveloperTenantBindParams, type CommerceMerchantCreateParams, type CommerceMerchantUpdateParams, type CommerceAgentCreateParams, type CommerceAgentUpdateParams, type CommerceCatalogVariantInput, type CommerceCatalogProductCreateParams, type CommerceCatalogProductUpdateParams, type CommerceCatalogProductListParams, type CommerceCatalogSearchParams, type CommerceCartCreateParams, type CommerceConsentRequestCreateParams, type CommerceConsentExchangeParams, type CommercePassportVerifyParams, type CommercePassportRevokeParams, type CommercePolicyCreateParams, type CommercePolicyEvaluateParams, type CommercePaymentIntentCreateParams, type CommercePaymentIntentListParams, type CommerceCheckoutLinkCreateParams, type CommerceProviderCredentialCreateParams, type CommerceProviderCredentialPatchParams, type CommerceProviderCredentialListParams, type CommerceWebhookSourceCreateParams, type CommerceWebhookSourceListParams, type CommerceOpsHealthParams, type CommerceProviderWebhookEventListParams, type CommerceProviderWebhookReplayParams, type CommerceMcpJsonRpcRequest, } from './resources/commerce.js';
11
- export type { RateLimit, GrantexClientOptions, SignupParams, SignupResponse, RotateKeyResponse, Agent, RegisterAgentParams, UpdateAgentParams, ListAgentsResponse, AuthorizeParams, AuthorizationRequest, Grant, ListGrantsParams, ListGrantsResponse, VerifiedGrant, DelegateParams, ExchangeTokenParams, ExchangeTokenResponse, RefreshTokenParams, VerifyTokenResponse, CreatePrincipalSessionParams, PrincipalSessionResponse, LogAuditParams, AuditEntry, ListAuditParams, ListAuditResponse, VerifyGrantTokenOptions, WebhookEventType, CreateWebhookParams, WebhookEndpoint, WebhookEndpointWithSecret, ListWebhooksResponse, SubscriptionStatus, CreateCheckoutParams, CheckoutResponse, CreatePortalParams, PortalResponse, Policy, CreatePolicyParams, UpdatePolicyParams, ListPoliciesResponse, AnomalyType, AnomalySeverity, Anomaly, DetectAnomaliesResponse, ListAnomaliesResponse, ScimEmail, ScimUserMeta, ScimUser, ScimListResponse, CreateScimUserParams, UpdateScimUserParams, ScimToken, ScimTokenWithSecret, CreateScimTokenParams, ListScimTokensResponse, SsoConfig, CreateSsoConfigParams, SsoLoginResponse, SsoCallbackResponse, StoreCredentialParams, StoreCredentialResponse, VaultCredential, ListVaultCredentialsParams, ListVaultCredentialsResponse, ExchangeCredentialParams, ExchangeCredentialResponse, AllocateBudgetParams, BudgetAllocation, DebitBudgetParams, DebitBudgetResponse, BudgetTransaction, BudgetTransactionsResponse, ComplianceSummary, ComplianceExportGrantsParams, ComplianceExportAuditParams, ComplianceGrantsExport, ComplianceAuditExport, EvidencePackParams, EvidencePack, ChainIntegrity, UsageResponse, UsageHistoryEntry, UsageHistoryResponse, CreateDomainParams, CreateDomainResponse, DomainEntry, ListDomainsResponse, VerifyDomainResponse, WebAuthnRegistrationOptions, WebAuthnRegistrationVerifyParams, WebAuthnCredential, ListWebAuthnCredentialsResponse, VerifiableCredentialRecord, ListCredentialsParams, ListCredentialsResponse, VCVerificationResult, SDJWTPresentParams, SDJWTPresentResult, UpdateDeveloperSettingsParams, UpdateDeveloperSettingsResponse, DpdpPurpose, CreateConsentRecordParams, ConsentRecord, ListConsentRecordsResponse, WithdrawConsentParams, WithdrawConsentResponse, PrincipalRecordsResponse, ErasureResponse, CreateConsentNoticeParams, ConsentNotice, FileGrievanceParams, Grievance, DpdpExportType, CreateDpdpExportParams, DpdpExport, } from './types.js';
13
+ export type { RateLimit, GrantexClientOptions, SignupParams, SignupResponse, RotateKeyResponse, Agent, RegisterAgentParams, UpdateAgentParams, ListAgentsResponse, AuthorizeParams, AuthorizationRequest, Grant, ListGrantsParams, ListGrantsResponse, VerifiedGrant, DelegateParams, ExchangeTokenParams, ExchangeTokenResponse, RefreshTokenParams, VerifyTokenResponse, CreatePrincipalSessionParams, PrincipalSessionResponse, LogAuditParams, AuditEntry, AuditCheckpoint, ListAuditParams, ListAuditResponse, VerifyGrantTokenOptions, WebhookEventType, CreateWebhookParams, WebhookEndpoint, WebhookEndpointWithSecret, ListWebhooksResponse, SubscriptionStatus, CreateCheckoutParams, CheckoutResponse, CreatePortalParams, PortalResponse, Policy, CreatePolicyParams, UpdatePolicyParams, ListPoliciesResponse, AnomalyType, AnomalySeverity, Anomaly, DetectAnomaliesResponse, ListAnomaliesResponse, ScimEmail, ScimUserMeta, ScimUser, ScimListResponse, CreateScimUserParams, UpdateScimUserParams, ScimToken, ScimTokenWithSecret, CreateScimTokenParams, ListScimTokensResponse, SsoConfig, CreateSsoConfigParams, SsoLoginResponse, SsoCallbackResponse, StoreCredentialParams, StoreCredentialResponse, VaultCredential, ListVaultCredentialsParams, ListVaultCredentialsResponse, ExchangeCredentialParams, ExchangeCredentialResponse, AllocateBudgetParams, BudgetAllocation, DebitBudgetParams, DebitBudgetResponse, BudgetTransaction, BudgetTransactionsResponse, ComplianceSummary, ComplianceExportGrantsParams, ComplianceExportAuditParams, ComplianceGrantsExport, ComplianceAuditExport, EvidencePackParams, EvidencePack, ChainIntegrity, UsageResponse, UsageHistoryEntry, UsageHistoryResponse, CreateDomainParams, CreateDomainResponse, DomainEntry, ListDomainsResponse, VerifyDomainResponse, WebAuthnRegistrationOptions, WebAuthnRegistrationVerifyParams, WebAuthnCredential, ListWebAuthnCredentialsResponse, VerifiableCredentialRecord, ListCredentialsParams, ListCredentialsResponse, VCVerificationResult, SDJWTPresentParams, SDJWTPresentResult, UpdateDeveloperSettingsParams, UpdateDeveloperSettingsResponse, DpdpPurpose, CreateConsentRecordParams, ConsentRecord, ListConsentRecordsResponse, WithdrawConsentParams, WithdrawConsentResponse, PrincipalRecordsResponse, ErasureResponse, CreateConsentNoticeParams, ConsentNotice, FileGrievanceParams, Grievance, DpdpExportType, CreateDpdpExportParams, DpdpExport, } from './types.js';
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAGtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGhG,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAG7D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,gCAAgC,EACrC,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC7B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,wBAAwB,EACxB,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,IAAI,kBAAkB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrI,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,KAAK,eAAe,EAAE,KAAK,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAGnM,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,EAC/B,KAAK,iCAAiC,EACtC,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EAChC,KAAK,kCAAkC,EACvC,KAAK,kCAAkC,EACvC,KAAK,gCAAgC,EACrC,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,kCAAkC,EACvC,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,4BAA4B,EACjC,KAAK,0BAA0B,EAC/B,KAAK,4BAA4B,EACjC,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,EACpC,KAAK,gCAAgC,EACrC,KAAK,sCAAsC,EAC3C,KAAK,qCAAqC,EAC1C,KAAK,oCAAoC,EACzC,KAAK,iCAAiC,EACtC,KAAK,+BAA+B,EACpC,KAAK,uBAAuB,EAC5B,KAAK,sCAAsC,EAC3C,KAAK,mCAAmC,EACxC,KAAK,yBAAyB,GAC/B,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EACV,SAAS,EACT,oBAAoB,EAEpB,YAAY,EACZ,cAAc,EACd,iBAAiB,EAEjB,KAAK,EACL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAElB,eAAe,EACf,oBAAoB,EAEpB,KAAK,EACL,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,cAAc,EAEd,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EAEnB,4BAA4B,EAC5B,wBAAwB,EAExB,cAAc,EACd,UAAU,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EAEjB,uBAAuB,EAEvB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,yBAAyB,EACzB,oBAAoB,EAEpB,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EAEd,MAAM,EACN,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EAEpB,WAAW,EACX,eAAe,EACf,OAAO,EACP,uBAAuB,EACvB,qBAAqB,EAErB,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EAEtB,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EAEnB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,EAE1B,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,0BAA0B,EAE1B,iBAAiB,EACjB,4BAA4B,EAC5B,2BAA2B,EAC3B,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,YAAY,EACZ,cAAc,EAEd,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EAEpB,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EAEpB,2BAA2B,EAC3B,gCAAgC,EAChC,kBAAkB,EAClB,+BAA+B,EAE/B,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EAEpB,kBAAkB,EAClB,kBAAkB,EAElB,6BAA6B,EAC7B,+BAA+B,EAE/B,WAAW,EACX,yBAAyB,EACzB,aAAa,EACb,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,eAAe,EACf,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,sBAAsB,EACtB,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -3,9 +3,13 @@ export { Grantex } from './client.js';
3
3
  // Standalone token verification (no Grantex account needed)
4
4
  export { verifyGrantToken } from './verify.js';
5
5
  // Webhook signature verification
6
- export { verifyWebhookSignature } from './webhook.js';
6
+ export { verifyWebhookSignature, verifyWebhook } from './webhook.js';
7
7
  // PKCE helper
8
8
  export { generatePkce } from './pkce.js';
9
+ // OAuth agent-grants profile client (PAR + PKCE + RFC 9207 + DPoP)
10
+ export { OAuthAgentClient, generateOAuthAgentKey, } from './oauth-agent.js';
11
+ // Agent prepaid wallets and x402 authorization bridge
12
+ export { PrepaidWalletAgentClient, PrincipalPrepaidWalletClient, } from './prepaid-wallets.js';
9
13
  // Error classes
10
14
  export { GrantexError, GrantexApiError, GrantexAuthError, GrantexTokenError, GrantexNetworkError, } from './errors.js';
11
15
  // Event streaming
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,iCAAiC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,cAAc;AACd,OAAO,EAAE,YAAY,EAAsB,MAAM,WAAW,CAAC;AAE7D,gBAAgB;AAChB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,kBAAkB;AAClB,OAAO,EAAE,YAAY,EAAkF,MAAM,uBAAuB,CAAC;AAErI,iCAAiC;AACjC,OAAO,EACL,eAAe,GAMhB,MAAM,0BAA0B,CAAC;AAElC,oBAAoB;AACpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAA0H,MAAM,eAAe,CAAC;AAEnM,kBAAkB;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,mCAAmC;AACnC,OAAO,EACL,cAAc,GAyCf,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,iCAAiC;AACjC,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAA6B,MAAM,cAAc,CAAC;AAEhG,cAAc;AACd,OAAO,EAAE,YAAY,EAAsB,MAAM,WAAW,CAAC;AAE7D,mEAAmE;AACnE,OAAO,EACL,gBAAgB,EAChB,qBAAqB,GAOtB,MAAM,kBAAkB,CAAC;AAE1B,sDAAsD;AACtD,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAa7B,MAAM,sBAAsB,CAAC;AAE9B,gBAAgB;AAChB,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,kBAAkB;AAClB,OAAO,EAAE,YAAY,EAAkF,MAAM,uBAAuB,CAAC;AAErI,iCAAiC;AACjC,OAAO,EACL,eAAe,GAMhB,MAAM,0BAA0B,CAAC;AAElC,oBAAoB;AACpB,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAA0H,MAAM,eAAe,CAAC;AAEnM,kBAAkB;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,mCAAmC;AACnC,OAAO,EACL,cAAc,GAyCf,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { type CryptoKey, type JWK } from 'jose';
2
+ export interface OAuthAuthorizationServerMetadata {
3
+ issuer: string;
4
+ authorization_endpoint: string;
5
+ token_endpoint: string;
6
+ jwks_uri: string;
7
+ revocation_endpoint: string;
8
+ pushed_authorization_request_endpoint: string;
9
+ require_pushed_authorization_requests: true;
10
+ response_types_supported: string[];
11
+ grant_types_supported: string[];
12
+ code_challenge_methods_supported: string[];
13
+ authorization_response_iss_parameter_supported: true;
14
+ dpop_signing_alg_values_supported: string[];
15
+ token_endpoint_auth_methods_supported: string[];
16
+ revocation_endpoint_auth_methods_supported: string[];
17
+ }
18
+ export interface OAuthAgentClientOptions {
19
+ issuer: string;
20
+ clientId: string;
21
+ redirectUri: string;
22
+ resource: string;
23
+ privateKey?: CryptoKey;
24
+ publicJwk?: JWK;
25
+ allowInsecureLoopback?: boolean;
26
+ }
27
+ export interface OAuthAgentKeyPair {
28
+ privateKey: CryptoKey;
29
+ publicJwk: JWK;
30
+ thumbprint: string;
31
+ }
32
+ export declare function generateOAuthAgentKey(): Promise<OAuthAgentKeyPair>;
33
+ export interface BeginAgentAuthorizationOptions {
34
+ scopes: string[];
35
+ principalHint?: string;
36
+ authorizationDetails?: Array<Record<string, unknown>>;
37
+ }
38
+ export interface PendingAgentAuthorization {
39
+ authorizationUrl: string;
40
+ requestUri: string;
41
+ state: string;
42
+ expiresIn: number;
43
+ }
44
+ export interface OAuthAgentTokenResponse {
45
+ access_token: string;
46
+ token_type: 'DPoP';
47
+ expires_in: number;
48
+ refresh_token?: string;
49
+ scope: string;
50
+ issued_token_type?: string;
51
+ }
52
+ export declare class OAuthAgentClient {
53
+ #private;
54
+ readonly metadata: OAuthAuthorizationServerMetadata;
55
+ readonly clientId: string;
56
+ readonly redirectUri: string;
57
+ readonly resource: string;
58
+ readonly publicJwk: JWK;
59
+ readonly keyThumbprint: string;
60
+ private constructor();
61
+ static create(options: OAuthAgentClientOptions): Promise<OAuthAgentClient>;
62
+ beginAuthorization(options: BeginAgentAuthorizationOptions): Promise<PendingAgentAuthorization>;
63
+ completeAuthorization(callbackUrl: string): Promise<OAuthAgentTokenResponse>;
64
+ refresh(refreshToken: string): Promise<OAuthAgentTokenResponse>;
65
+ attenuate(accessToken: string, scopes: string[]): Promise<OAuthAgentTokenResponse>;
66
+ revoke(token: string, tokenTypeHint?: 'access_token' | 'refresh_token'): Promise<void>;
67
+ fetch(input: string | URL, accessToken: string, init?: RequestInit): Promise<Response>;
68
+ createDpopProof(method: string, targetUri: string, accessToken?: string): Promise<string>;
69
+ tokenRequest(params: URLSearchParams): Promise<OAuthAgentTokenResponse>;
70
+ }
71
+ //# sourceMappingURL=oauth-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-agent.d.ts","sourceRoot":"","sources":["../src/oauth-agent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,359 @@
1
+ import { createHash, randomBytes, randomUUID } from 'node:crypto';
2
+ import { calculateJwkThumbprint, exportJWK, generateKeyPair, importJWK, jwtVerify, SignJWT, } from 'jose';
3
+ import { generatePkce } from './pkce.js';
4
+ const TOKEN_EXCHANGE_GRANT = 'urn:ietf:params:oauth:grant-type:token-exchange';
5
+ const ACCESS_TOKEN_TYPE = 'urn:ietf:params:oauth:token-type:access_token';
6
+ export async function generateOAuthAgentKey() {
7
+ const generated = await generateKeyPair('ES256', { extractable: true });
8
+ const publicJwk = await exportJWK(generated.publicKey);
9
+ return {
10
+ privateKey: generated.privateKey,
11
+ publicJwk,
12
+ thumbprint: await calculateJwkThumbprint(publicJwk, 'sha256'),
13
+ };
14
+ }
15
+ export class OAuthAgentClient {
16
+ metadata;
17
+ clientId;
18
+ redirectUri;
19
+ resource;
20
+ publicJwk;
21
+ keyThumbprint;
22
+ #privateKey;
23
+ #allowInsecureLoopback;
24
+ #pending = new Map();
25
+ constructor(options) {
26
+ this.metadata = options.metadata;
27
+ this.clientId = options.clientId;
28
+ this.redirectUri = options.redirectUri;
29
+ this.resource = options.resource;
30
+ this.#privateKey = options.privateKey;
31
+ this.#allowInsecureLoopback = options.allowInsecureLoopback;
32
+ this.publicJwk = options.publicJwk;
33
+ this.keyThumbprint = options.keyThumbprint;
34
+ }
35
+ static async create(options) {
36
+ const expectedIssuer = options.issuer;
37
+ const allowInsecureLoopback = options.allowInsecureLoopback === true;
38
+ assertSecureEndpoint(expectedIssuer, allowInsecureLoopback);
39
+ const issuerUrl = new URL(expectedIssuer);
40
+ if (issuerUrl.search || issuerUrl.hash) {
41
+ throw new Error('issuer must not contain a query or fragment');
42
+ }
43
+ assertSecureEndpoint(options.redirectUri, allowInsecureLoopback);
44
+ assertSecureEndpoint(options.resource, allowInsecureLoopback);
45
+ if (new URL(options.redirectUri).hash || new URL(options.resource).hash) {
46
+ throw new Error('redirectUri and resource must not contain fragments');
47
+ }
48
+ const metadataUrl = authorizationServerMetadataUrl(expectedIssuer);
49
+ assertSecureEndpoint(metadataUrl, options.allowInsecureLoopback === true);
50
+ const response = await fetch(metadataUrl, { headers: { Accept: 'application/json' } });
51
+ if (!response.ok)
52
+ throw new Error(`Authorization-server metadata request failed with HTTP ${response.status}`);
53
+ const metadata = response.json();
54
+ const validatedMetadata = validateMetadata(await metadata, expectedIssuer, allowInsecureLoopback);
55
+ let privateKey = options.privateKey;
56
+ let publicJwk = options.publicJwk;
57
+ if ((privateKey === undefined) !== (publicJwk === undefined)) {
58
+ throw new Error('privateKey and publicJwk must be provided together');
59
+ }
60
+ if (!privateKey || !publicJwk) {
61
+ const generated = await generateOAuthAgentKey();
62
+ privateKey = generated.privateKey;
63
+ publicJwk = generated.publicJwk;
64
+ }
65
+ await validateAgentKeyPair(privateKey, publicJwk);
66
+ const keyThumbprint = await calculateJwkThumbprint(publicJwk, 'sha256');
67
+ return new OAuthAgentClient({
68
+ metadata: validatedMetadata,
69
+ clientId: options.clientId,
70
+ redirectUri: options.redirectUri,
71
+ resource: options.resource,
72
+ privateKey,
73
+ publicJwk,
74
+ keyThumbprint,
75
+ allowInsecureLoopback,
76
+ });
77
+ }
78
+ async beginAuthorization(options) {
79
+ if (options.scopes.length === 0 || options.scopes.length > 100
80
+ || new Set(options.scopes).size !== options.scopes.length
81
+ || options.scopes.some((scope) => scope.length === 0 || scope.length > 256
82
+ || !/^[\x21\x23-\x5B\x5D-\x7E]+$/.test(scope))) {
83
+ throw new Error('scopes must be a non-empty set of valid OAuth scope tokens');
84
+ }
85
+ if (options.principalHint !== undefined
86
+ && (options.principalHint.length === 0 || options.principalHint.length > 256)) {
87
+ throw new Error('principalHint must contain 1 to 256 characters');
88
+ }
89
+ const state = randomBytes(32).toString('base64url');
90
+ const pkce = generatePkce();
91
+ const params = new URLSearchParams({
92
+ client_id: this.clientId,
93
+ response_type: 'code',
94
+ redirect_uri: this.redirectUri,
95
+ scope: options.scopes.join(' '),
96
+ state,
97
+ code_challenge: pkce.codeChallenge,
98
+ code_challenge_method: pkce.codeChallengeMethod,
99
+ resource: this.resource,
100
+ dpop_jkt: this.keyThumbprint,
101
+ });
102
+ if (options.principalHint !== undefined)
103
+ params.set('login_hint', options.principalHint);
104
+ if (options.authorizationDetails) {
105
+ params.set('authorization_details', JSON.stringify(options.authorizationDetails));
106
+ }
107
+ const proof = await this.createDpopProof('POST', this.metadata.pushed_authorization_request_endpoint);
108
+ const response = await fetch(this.metadata.pushed_authorization_request_endpoint, {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/x-www-form-urlencoded',
112
+ Accept: 'application/json',
113
+ DPoP: proof,
114
+ },
115
+ body: params,
116
+ });
117
+ const result = await oauthJson(response);
118
+ this.#pending.set(state, {
119
+ codeVerifier: pkce.codeVerifier,
120
+ redirectUri: this.redirectUri,
121
+ issuer: this.metadata.issuer,
122
+ consumed: false,
123
+ });
124
+ const authorizationUrl = new URL(this.metadata.authorization_endpoint);
125
+ authorizationUrl.searchParams.set('client_id', this.clientId);
126
+ authorizationUrl.searchParams.set('request_uri', result.request_uri);
127
+ return {
128
+ authorizationUrl: authorizationUrl.toString(),
129
+ requestUri: result.request_uri,
130
+ state,
131
+ expiresIn: result.expires_in,
132
+ };
133
+ }
134
+ async completeAuthorization(callbackUrl) {
135
+ const callback = new URL(callbackUrl);
136
+ const state = callback.searchParams.get('state');
137
+ if (!state)
138
+ throw new Error('Authorization response is missing state');
139
+ const pending = this.#pending.get(state);
140
+ if (!pending || pending.consumed)
141
+ throw new Error('Authorization response state is unknown or already consumed');
142
+ assertRedirectResponse(callback, pending.redirectUri);
143
+ pending.consumed = true;
144
+ this.#pending.delete(state);
145
+ const responseIssuer = callback.searchParams.get('iss');
146
+ if (responseIssuer !== pending.issuer)
147
+ throw new Error('Authorization response issuer does not match the initiating session');
148
+ const error = callback.searchParams.get('error');
149
+ if (error)
150
+ throw new Error(`Authorization failed: ${error}`);
151
+ const code = callback.searchParams.get('code');
152
+ if (!code)
153
+ throw new Error('Authorization response is missing code');
154
+ return this.tokenRequest(new URLSearchParams({
155
+ grant_type: 'authorization_code',
156
+ code,
157
+ client_id: this.clientId,
158
+ redirect_uri: pending.redirectUri,
159
+ code_verifier: pending.codeVerifier,
160
+ }));
161
+ }
162
+ refresh(refreshToken) {
163
+ return this.tokenRequest(new URLSearchParams({
164
+ grant_type: 'refresh_token',
165
+ refresh_token: refreshToken,
166
+ client_id: this.clientId,
167
+ }));
168
+ }
169
+ attenuate(accessToken, scopes) {
170
+ return this.tokenRequest(new URLSearchParams({
171
+ grant_type: TOKEN_EXCHANGE_GRANT,
172
+ client_id: this.clientId,
173
+ subject_token: accessToken,
174
+ subject_token_type: ACCESS_TOKEN_TYPE,
175
+ requested_token_type: ACCESS_TOKEN_TYPE,
176
+ scope: scopes.join(' '),
177
+ resource: this.resource,
178
+ }));
179
+ }
180
+ async revoke(token, tokenTypeHint) {
181
+ const proof = await this.createDpopProof('POST', this.metadata.revocation_endpoint);
182
+ const params = new URLSearchParams({ client_id: this.clientId, token });
183
+ if (tokenTypeHint)
184
+ params.set('token_type_hint', tokenTypeHint);
185
+ const response = await fetch(this.metadata.revocation_endpoint, {
186
+ method: 'POST',
187
+ headers: {
188
+ 'Content-Type': 'application/x-www-form-urlencoded',
189
+ DPoP: proof,
190
+ },
191
+ body: params,
192
+ });
193
+ if (!response.ok)
194
+ await oauthJson(response);
195
+ }
196
+ async fetch(input, accessToken, init = {}) {
197
+ const target = new URL(input);
198
+ const resource = new URL(this.resource);
199
+ if (target.origin !== resource.origin) {
200
+ throw new Error('Protected-resource request origin does not match the configured resource');
201
+ }
202
+ assertSecureEndpoint(target.toString(), this.#allowInsecureLoopback);
203
+ const method = (init.method ?? 'GET').toUpperCase();
204
+ const proof = await this.createDpopProof(method, target.toString(), accessToken);
205
+ const headers = new Headers(init.headers);
206
+ headers.set('Authorization', `DPoP ${accessToken}`);
207
+ headers.set('DPoP', proof);
208
+ return fetch(target, { ...init, method, headers });
209
+ }
210
+ async createDpopProof(method, targetUri, accessToken) {
211
+ const target = new URL(targetUri);
212
+ target.search = '';
213
+ target.hash = '';
214
+ return new SignJWT({
215
+ htm: method.toUpperCase(),
216
+ htu: target.toString(),
217
+ jti: randomUUID(),
218
+ iat: Math.floor(Date.now() / 1000),
219
+ ...(accessToken
220
+ ? { ath: createHash('sha256').update(accessToken, 'ascii').digest('base64url') }
221
+ : {}),
222
+ })
223
+ .setProtectedHeader({ typ: 'dpop+jwt', alg: 'ES256', jwk: this.publicJwk })
224
+ .sign(this.#privateKey);
225
+ }
226
+ async tokenRequest(params) {
227
+ const proof = await this.createDpopProof('POST', this.metadata.token_endpoint);
228
+ const response = await fetch(this.metadata.token_endpoint, {
229
+ method: 'POST',
230
+ headers: {
231
+ 'Content-Type': 'application/x-www-form-urlencoded',
232
+ Accept: 'application/json',
233
+ DPoP: proof,
234
+ },
235
+ body: params,
236
+ });
237
+ const result = await oauthJson(response);
238
+ return validateTokenResponse(result);
239
+ }
240
+ }
241
+ function validateMetadata(value, expectedIssuer, allowInsecureLoopback) {
242
+ if (!value || typeof value !== 'object' || value.issuer !== expectedIssuer) {
243
+ throw new Error('Authorization-server metadata issuer does not match the configured issuer');
244
+ }
245
+ const endpoints = [
246
+ value.authorization_endpoint,
247
+ value.token_endpoint,
248
+ value.jwks_uri,
249
+ value.revocation_endpoint,
250
+ value.pushed_authorization_request_endpoint,
251
+ ];
252
+ if (endpoints.some((endpoint) => typeof endpoint !== 'string')) {
253
+ throw new Error('Authorization-server metadata is missing a required endpoint');
254
+ }
255
+ for (const endpoint of endpoints)
256
+ assertSecureEndpoint(endpoint, allowInsecureLoopback);
257
+ if (value.require_pushed_authorization_requests !== true
258
+ || value.authorization_response_iss_parameter_supported !== true
259
+ || !value.response_types_supported?.includes('code')
260
+ || !value.grant_types_supported?.includes('authorization_code')
261
+ || !value.grant_types_supported?.includes(TOKEN_EXCHANGE_GRANT)
262
+ || !value.grant_types_supported?.includes('refresh_token')
263
+ || !value.code_challenge_methods_supported?.includes('S256')
264
+ || !value.dpop_signing_alg_values_supported?.includes('ES256')
265
+ || !value.token_endpoint_auth_methods_supported?.includes('none')
266
+ || !value.revocation_endpoint_auth_methods_supported?.includes('none')) {
267
+ throw new Error('Authorization server does not advertise the capabilities required by the agent-grants profile');
268
+ }
269
+ return value;
270
+ }
271
+ function assertSecureEndpoint(value, allowInsecureLoopback) {
272
+ const parsed = new URL(value);
273
+ const loopback = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '[::1]';
274
+ if (parsed.protocol !== 'https:' && !(allowInsecureLoopback && loopback && parsed.protocol === 'http:')) {
275
+ throw new Error(`OAuth endpoint must use HTTPS: ${value}`);
276
+ }
277
+ if (parsed.username || parsed.password || parsed.hash)
278
+ throw new Error(`OAuth endpoint is malformed: ${value}`);
279
+ }
280
+ async function validateAgentKeyPair(privateKey, publicJwk) {
281
+ const privateFields = ['d', 'p', 'q', 'dp', 'dq', 'qi', 'oth', 'k'];
282
+ if (privateFields.some((field) => publicJwk[field] !== undefined)) {
283
+ throw new Error('publicJwk must not contain private key material');
284
+ }
285
+ if (publicJwk.kty !== 'EC' || publicJwk.crv !== 'P-256'
286
+ || typeof publicJwk.x !== 'string' || typeof publicJwk.y !== 'string') {
287
+ throw new Error('OAuthAgentClient requires an ES256 P-256 public JWK');
288
+ }
289
+ try {
290
+ const probe = await new SignJWT({ probe: true })
291
+ .setProtectedHeader({ alg: 'ES256' })
292
+ .setIssuedAt()
293
+ .sign(privateKey);
294
+ const publicKey = await importJWK(publicJwk, 'ES256');
295
+ await jwtVerify(probe, publicKey, { algorithms: ['ES256'] });
296
+ }
297
+ catch {
298
+ throw new Error('privateKey does not match publicJwk or cannot sign ES256 proofs');
299
+ }
300
+ }
301
+ function assertRedirectResponse(callback, registeredRedirectUri) {
302
+ const registered = new URL(registeredRedirectUri);
303
+ if (registered.hash || callback.hash || callback.origin !== registered.origin
304
+ || callback.pathname !== registered.pathname
305
+ || callback.username || callback.password) {
306
+ throw new Error('Authorization response URL does not match the registered redirect URI');
307
+ }
308
+ for (const [name, value] of registered.searchParams) {
309
+ if (callback.searchParams.get(name) !== value) {
310
+ throw new Error('Authorization response URL does not match the registered redirect URI');
311
+ }
312
+ }
313
+ const responseParameters = new Set([
314
+ 'code', 'state', 'iss', 'error', 'error_description', 'error_uri',
315
+ ]);
316
+ for (const name of responseParameters) {
317
+ if (callback.searchParams.getAll(name).length > 1) {
318
+ throw new Error(`Authorization response parameter ${name} must occur at most once`);
319
+ }
320
+ }
321
+ for (const name of callback.searchParams.keys()) {
322
+ if (!registered.searchParams.has(name) && !responseParameters.has(name)) {
323
+ throw new Error('Authorization response contains an unexpected redirect parameter');
324
+ }
325
+ }
326
+ }
327
+ function authorizationServerMetadataUrl(issuer) {
328
+ const parsed = new URL(issuer);
329
+ const issuerPath = parsed.pathname === '/' ? '' : parsed.pathname.replace(/\/$/, '');
330
+ parsed.pathname = `/.well-known/oauth-authorization-server${issuerPath}`;
331
+ parsed.search = '';
332
+ parsed.hash = '';
333
+ return parsed.toString();
334
+ }
335
+ function validateTokenResponse(value) {
336
+ if (!value || typeof value !== 'object'
337
+ || typeof value.access_token !== 'string' || value.access_token.length === 0
338
+ || value.token_type !== 'DPoP'
339
+ || typeof value.expires_in !== 'number' || !Number.isSafeInteger(value.expires_in)
340
+ || value.expires_in <= 0
341
+ || typeof value.scope !== 'string' || value.scope.length === 0
342
+ || (value.refresh_token !== undefined
343
+ && (typeof value.refresh_token !== 'string' || value.refresh_token.length === 0))
344
+ || (value.issued_token_type !== undefined
345
+ && value.issued_token_type !== ACCESS_TOKEN_TYPE)) {
346
+ throw new Error('Authorization server returned an invalid DPoP token response');
347
+ }
348
+ return value;
349
+ }
350
+ async function oauthJson(response) {
351
+ const body = await response.json().catch(() => null);
352
+ if (!response.ok) {
353
+ const error = typeof body?.['error'] === 'string' ? body['error'] : `HTTP ${response.status}`;
354
+ const description = typeof body?.['error_description'] === 'string' ? body['error_description'] : '';
355
+ throw new Error(description ? `${error}: ${description}` : error);
356
+ }
357
+ return body;
358
+ }
359
+ //# sourceMappingURL=oauth-agent.js.map