diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index bae69995..a0a77c62 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -126,6 +126,101 @@ "name": "spotify", "source": "./plugins/spotify", "description": "Browse catalog metadata and manage playlists, playback, and saved libraries on Spotify." + }, + { + "name": "authlete", + "source": "./plugins/authlete", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete." + }, + { + "name": "cloudinary-analysis", + "source": "./plugins/cloudinary-analysis", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks." + }, + { + "name": "cloudinary-asset-management", + "source": "./plugins/cloudinary-asset-management", + "description": "Upload, organize, transform, and manage media assets in Cloudinary." + }, + { + "name": "cribl-cloud-management", + "source": "./plugins/cribl-cloud-management", + "description": "Provision and manage Cribl.Cloud organizations and workspaces programmatically." + }, + { + "name": "cribl-control-plane", + "source": "./plugins/cribl-control-plane", + "description": "Configure Cribl Stream and Edge sources, destinations, pipelines, and routes on a Cribl Leader." + }, + { + "name": "dwolla", + "source": "./plugins/dwolla", + "description": "Move money with ACH, RTP, and FedNow transfers, customers, and funding sources on Dwolla." + }, + { + "name": "friendliai", + "source": "./plugins/friendliai", + "description": "Run serverless and dedicated LLM inference, chat completions, and endpoints on FriendliAI." + }, + { + "name": "gbg-go", + "source": "./plugins/gbg-go", + "description": "Run GBG GO identity verification journeys, interactions, and customer checks." + }, + { + "name": "glean-client", + "source": "./plugins/glean-client", + "description": "Search, chat, and act on your company knowledge with the Glean Client API." + }, + { + "name": "glean-indexing", + "source": "./plugins/glean-indexing", + "description": "Index custom datasources, documents, people, and permissions into Glean." + }, + { + "name": "glean-platform", + "source": "./plugins/glean-platform", + "description": "Build on the Glean platform with agents, chat, and enterprise search APIs." + }, + { + "name": "gr4vy", + "source": "./plugins/gr4vy", + "description": "Orchestrate payments, payment methods, buyers, and transactions across PSPs with Gr4vy." + }, + { + "name": "launchdarkly", + "source": "./plugins/launchdarkly", + "description": "Manage LaunchDarkly feature flags, environments, segments, and experiments." + }, + { + "name": "lucid-agent-oracle", + "source": "./plugins/lucid-agent-oracle", + "description": "Query economic intelligence and agent-economy data from the Lucid Agent Oracle." + }, + { + "name": "mollie", + "source": "./plugins/mollie", + "description": "Accept payments, manage refunds, subscriptions, and settlements with Mollie." + }, + { + "name": "pipeshub", + "source": "./plugins/pipeshub", + "description": "Search and chat over workplace knowledge, connectors, and agents with PipesHub." + }, + { + "name": "skylight", + "source": "./plugins/skylight", + "description": "Automate Skylight workflows and data through the Skylight API." + }, + { + "name": "syllable", + "source": "./plugins/syllable", + "description": "Build and operate Syllable voice and chat AI agents, prompts, tools, and channels." + }, + { + "name": "trilogy-oneroster", + "source": "./plugins/trilogy-oneroster", + "description": "Manage OneRoster rosters, classes, enrollments, users, and gradebook results." } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index bae69995..a0a77c62 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -126,6 +126,101 @@ "name": "spotify", "source": "./plugins/spotify", "description": "Browse catalog metadata and manage playlists, playback, and saved libraries on Spotify." + }, + { + "name": "authlete", + "source": "./plugins/authlete", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete." + }, + { + "name": "cloudinary-analysis", + "source": "./plugins/cloudinary-analysis", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks." + }, + { + "name": "cloudinary-asset-management", + "source": "./plugins/cloudinary-asset-management", + "description": "Upload, organize, transform, and manage media assets in Cloudinary." + }, + { + "name": "cribl-cloud-management", + "source": "./plugins/cribl-cloud-management", + "description": "Provision and manage Cribl.Cloud organizations and workspaces programmatically." + }, + { + "name": "cribl-control-plane", + "source": "./plugins/cribl-control-plane", + "description": "Configure Cribl Stream and Edge sources, destinations, pipelines, and routes on a Cribl Leader." + }, + { + "name": "dwolla", + "source": "./plugins/dwolla", + "description": "Move money with ACH, RTP, and FedNow transfers, customers, and funding sources on Dwolla." + }, + { + "name": "friendliai", + "source": "./plugins/friendliai", + "description": "Run serverless and dedicated LLM inference, chat completions, and endpoints on FriendliAI." + }, + { + "name": "gbg-go", + "source": "./plugins/gbg-go", + "description": "Run GBG GO identity verification journeys, interactions, and customer checks." + }, + { + "name": "glean-client", + "source": "./plugins/glean-client", + "description": "Search, chat, and act on your company knowledge with the Glean Client API." + }, + { + "name": "glean-indexing", + "source": "./plugins/glean-indexing", + "description": "Index custom datasources, documents, people, and permissions into Glean." + }, + { + "name": "glean-platform", + "source": "./plugins/glean-platform", + "description": "Build on the Glean platform with agents, chat, and enterprise search APIs." + }, + { + "name": "gr4vy", + "source": "./plugins/gr4vy", + "description": "Orchestrate payments, payment methods, buyers, and transactions across PSPs with Gr4vy." + }, + { + "name": "launchdarkly", + "source": "./plugins/launchdarkly", + "description": "Manage LaunchDarkly feature flags, environments, segments, and experiments." + }, + { + "name": "lucid-agent-oracle", + "source": "./plugins/lucid-agent-oracle", + "description": "Query economic intelligence and agent-economy data from the Lucid Agent Oracle." + }, + { + "name": "mollie", + "source": "./plugins/mollie", + "description": "Accept payments, manage refunds, subscriptions, and settlements with Mollie." + }, + { + "name": "pipeshub", + "source": "./plugins/pipeshub", + "description": "Search and chat over workplace knowledge, connectors, and agents with PipesHub." + }, + { + "name": "skylight", + "source": "./plugins/skylight", + "description": "Automate Skylight workflows and data through the Skylight API." + }, + { + "name": "syllable", + "source": "./plugins/syllable", + "description": "Build and operate Syllable voice and chat AI agents, prompts, tools, and channels." + }, + { + "name": "trilogy-oneroster", + "source": "./plugins/trilogy-oneroster", + "description": "Manage OneRoster rosters, classes, enrollments, users, and gradebook results." } ] } diff --git a/CLAUDE.md b/CLAUDE.md index 0634d4d2..90f6b080 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # Context Plugins Marketplace This repository is a multi-plugin marketplace (`name: context-plugins`) targeting **Claude Code, -Cursor, and VS Code**. It ships **24 plugins** under `plugins/`, one per API. +Cursor, and VS Code**. It ships **43 plugins** under `plugins/`, one per API. Each plugin is a bundle of **skills** β€” Markdown files an agent loads on demand β€” that ground the agent in an APIMatic-generated SDK's real contract instead of model knowledge. There are no MCP diff --git a/README.md b/README.md index 2ba4d9d9..28c4a286 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Coding agents hallucinate APIs. They are trained on public code and documentatio outdated β€” and have no awareness of the API version you are actually on, the current SDK, or the integration workflow the SDK author recommends. -This repository is the **`context-plugins` marketplace**: a collection of **24 plugins**, one per +This repository is the **`context-plugins` marketplace**: a collection of **43 plugins**, one per API, that give your agent authoritative, version-aware, SDK-native context at the moment it's needed. Every plugin is generated from the API's definition and its APIMatic-generated SDKs, so the agent is grounded in the real SDK contract instead of guessing from memory. @@ -73,7 +73,7 @@ Then run `/reload-plugins`, or start a new `claude` session. ## Available plugins -24 plugins, listed in [`.claude-plugin/marketplace.json`](.claude-plugin/marketplace.json). +43 plugins, listed in [`.claude-plugin/marketplace.json`](.claude-plugin/marketplace.json). The **Languages** column is the set of SDK languages that plugin ships skills for. 72 language packs and 696 skills**. See +Across the marketplace that's **91 language packs and 886 skills**. See [`plugins/paypal/README.md`](plugins/paypal/README.md) for a worked example. ## Repository structure diff --git a/plugins/authlete/.claude-plugin/plugin.json b/plugins/authlete/.claude-plugin/plugin.json new file mode 100644 index 00000000..5f1f5af9 --- /dev/null +++ b/plugins/authlete/.claude-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "authlete", + "displayName": "Authlete", + "version": "0.1.0", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/authlete/.codex-plugin/plugin.json b/plugins/authlete/.codex-plugin/plugin.json new file mode 100644 index 00000000..a8869b3f --- /dev/null +++ b/plugins/authlete/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "authlete", + "version": "0.1.0", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete.", + "skills": "./skills", + "interface": { + "displayName": "Authlete", + "developerName": "APIMatic" + } +} diff --git a/plugins/authlete/.cursor-plugin/plugin.json b/plugins/authlete/.cursor-plugin/plugin.json new file mode 100644 index 00000000..5f1f5af9 --- /dev/null +++ b/plugins/authlete/.cursor-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "authlete", + "displayName": "Authlete", + "version": "0.1.0", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/authlete/README.md b/plugins/authlete/README.md new file mode 100644 index 00000000..b69e6975 --- /dev/null +++ b/plugins/authlete/README.md @@ -0,0 +1,11 @@ +# Authlete API SDK Plugin + +A plugin whose skills teach a coding agent to install and use the APIMatic-generated **Authlete API SDK**, in TypeScript. Every SDK fact the skills state is grounded in the SDK's own source and generated documentation, not in what a model remembers about this API. + +## What's inside + +One skill set per language. The entry point is that language's getting-started skill, which carries what is specific to this SDK; the rest are API-agnostic and describe how to use any SDK the same generator produces. + +| Language | Skill prefix | Skills | +| --- | --- | --- | +| TypeScript | `typescript-` | `typescript-authentication`, `typescript-calling-endpoints`, `typescript-client-initialization`, `typescript-configuration-resilience`, `typescript-error-handling`, `typescript-event-streams`, `typescript-getting-started`, `typescript-integrate-authlete-api`, `typescript-models`, `typescript-testing` | diff --git a/plugins/authlete/plugin.json b/plugins/authlete/plugin.json new file mode 100644 index 00000000..5f1f5af9 --- /dev/null +++ b/plugins/authlete/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "authlete", + "displayName": "Authlete", + "version": "0.1.0", + "description": "Implement OAuth 2.0 and OpenID Connect authorization servers backed by Authlete.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/authlete/sdk/typescript/.gitignore b/plugins/authlete/sdk/typescript/.gitignore new file mode 100644 index 00000000..b079e0f3 --- /dev/null +++ b/plugins/authlete/sdk/typescript/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +.tshy/ +.tshy-build/ +*.tsbuildinfo +*.log +.env +.DS_Store diff --git a/plugins/authlete/sdk/typescript/.oxfmtrc.json b/plugins/authlete/sdk/typescript/.oxfmtrc.json new file mode 100644 index 00000000..8e9159d1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 110, + "sortPackageJson": false +} diff --git a/plugins/authlete/sdk/typescript/.oxlintrc.json b/plugins/authlete/sdk/typescript/.oxlintrc.json new file mode 100644 index 00000000..8cdcf059 --- /dev/null +++ b/plugins/authlete/sdk/typescript/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "categories": { + "correctness": "error" + }, + "rules": { + "@typescript-eslint/no-unnecessary-type-assertion": "error" + } +} diff --git a/plugins/authlete/sdk/typescript/LICENSE b/plugins/authlete/sdk/typescript/LICENSE new file mode 100644 index 00000000..5de12eaa --- /dev/null +++ b/plugins/authlete/sdk/typescript/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 APIMatic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/authlete/sdk/typescript/README.md b/plugins/authlete/sdk/typescript/README.md new file mode 100644 index 00000000..919b93de --- /dev/null +++ b/plugins/authlete/sdk/typescript/README.md @@ -0,0 +1,173 @@ +# Authlete API + +[![Built with APIMatic][apimatic-badge]][apimatic-url] [![License: MIT][license-badge]][license-url] + +The Authlete API SDK for TypeScript provides typed access to the Authlete API REST APIs from Node.js and the browser. + +> [!TIP] +> **Looking for a specific signature, request field, model, enum or error type?** This SDK ships a generated, machine-readable **[SDK map](sdk-map.md)** β€” a lookup index of the whole TypeScript surface. Consult it **before** grepping or scanning the source tree; it answers most contract questions directly and, where a source file is genuinely needed, names the exact one to open. Details under [SDK map](#sdk-map). + +Welcome to the **Authlete API documentation**. Authlete is an **API-first service** where every aspect of the platform is configurable via API. This documentation will help you authenticate and integrate with Authlete to build powerful OAuth 2.0 and OpenID Connect servers. + +At a high level, the Authlete API is grouped into two categories: + +- **Management APIs**: Enable you to manage services and clients. +- **Runtime APIs**: Allow you to build your own Authorization Servers or Verifiable Credential (VC) issuers. + +## 🌐 API Servers + +Authlete is a global service with clusters available in multiple regions across the world: + +- πŸ‡ΊπŸ‡Έ **US**: `https://us.authlete.com` +- πŸ‡―πŸ‡΅ **Japan**: `https://jp.authlete.com` +- πŸ‡ͺπŸ‡Ί **Europe**: `https://eu.authlete.com` +- πŸ‡§πŸ‡· **Brazil**: `https://br.authlete.com` + +Our customers can host their data in the region that best meets their requirements. + +## πŸ”‘ Authentication + +All API endpoints are secured using **Bearer token authentication**. You must include an access token in every request: + +``` +Authorization: Bearer YOUR_ACCESS_TOKEN +``` + +### Getting Your Access Token + +Authlete supports two types of access tokens: + +**Service Access Token** - Scoped to a single service (authorization server instance) + +1. Log in to [Authlete Console](https://console.authlete.com) +2. Navigate to your service β†’ **Settings** β†’ **Access Tokens** +3. Click **Create Token** and select permissions (e.g., `service.read`, `client.write`) +4. Copy the generated token + +**Organization Token** - Scoped to your entire organization + +1. Log in to [Authlete Console](https://console.authlete.com) +2. Navigate to **Organization Settings** β†’ **Access Tokens** +3. Click **Create Token** and select org-level permissions +4. Copy the generated token + +> ⚠️ **Important Note**: Tokens inherit the permissions of the account that creates them. Service tokens can only access their specific service, while organization tokens can access all services within your org. + +### Token Security Best Practices + +- **Never commit tokens to version control** - Store in environment variables or secure secret managers +- **Rotate regularly** - Generate new tokens periodically and revoke old ones +- **Scope appropriately** - Request only the permissions your application needs +- **Revoke unused tokens** - Delete tokens you're no longer using from the console + +### Quick Test + +Verify your token works with a simple API call: + +```bash +curl -X GET https://us.authlete.com/api/service/get/list \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +## πŸŽ“ Tutorials + +If you're new to Authlete or want to see sample implementations, these resources will help you get started: + +- [Getting Started with Authlete](https://www.authlete.com/developers/getting_started/) +- [From Sign-Up to the First API Request](https://www.authlete.com/developers/tutorial/signup/) + +## πŸ›  Contact Us + +If you have any questions or need assistance, our team is here to help: + +- [Contact Page](https://www.authlete.com/contact/) + + +--- + +## Installation + +The SDK compiles to `dist/` before it can be referenced β€” run its `build` script once in the SDK folder, then add it to your project by path: + +```bash +npm install +``` + +--- + +## Quick Start + +Create one client and reuse it. Configure its behaviour through [ClientOptions](src/client-options.ts). + +```ts +import { AuthleteApiClient, ServerEnvironment } from "authlete-api"; + +const client = new AuthleteApiClient({ + serverEnvironment: ServerEnvironment.Production, + bearer: "YOUR_BEARER_TOKEN", +}); +``` + +Nothing in `ClientOptions` is required β€” `new AuthleteApiClient()` compiles β€” and each option left out falls back to its default. `serverEnvironment` is spelled out above so the environment a call reaches is visible where the client is built rather than inherited silently. + +### From CommonJS + +The package ships both dialects from a single entry, so `require` works with full types. In a TypeScript CommonJS file use the `import ... = require(...)` form β€” a plain destructuring `require` runs fine but gives you `any`. + +```ts +import sdk = require("authlete-api"); + +const client = new sdk.AuthleteApiClient({ + serverEnvironment: sdk.ServerEnvironment.Production, + bearer: "YOUR_BEARER_TOKEN", +}); +``` + +--- + +## Usage + +For code examples and error responses, see [API Reference](api-reference.md). + +--- + +## SDK map + +This SDK ships a generated **SDK map** β€” [`sdk-map.md`](sdk-map.md) plus the pages under [`map/operations/`](map/operations/service-management.md) β€” a deterministic, lookup-oriented table of contents of the TypeScript surface, generated alongside the SDK. + +**Read it before scanning the source.** Whether you are an AI coding assistant or searching by hand, the map answers "what is the exact …" by lookup for every call-level contract, and for anything it does not carry it names the one file that does: + +- **[`sdk-map.md`](sdk-map.md)** β€” the index: client construction, the one error family, the non-throwing `.asApiResult()` form, servers, environments and auth, the model locator, the runtime facts, and the SDK-wide defaults every operation relies on. +- **[`map/operations/`](map/operations/service-management.md)** β€” one page per resource: the exact signature and return type, the verb and route, the request body and its media type, a **Fields** table giving every request field its channel, and a **Type sources** table naming the file and schema value of every type the operation mentions. + +Model shapes β€” object properties with their wire names, enum member names and wire values, union variants β€” are **not** duplicated in the map, and not in the API reference either. Both name the type and the file to read; take the pair from the operation's **Type sources** table and read the declaring file. That file is the single source of truth and cannot go stale against the code. + +**Each operation block states only what is specific to it.** The SDK-wide defaults are stated once in [`sdk-map.md`](sdk-map.md) β€” the call shape, the base `ApiError`, the default server group β€” and a block departs from one only by saying so, so a block silent on a point is telling you the default applies. Take it and move on rather than opening the source to confirm. + +### Which one to reach for + +The map and the [API reference](api-reference.md) answer different questions, and both are generated from this SDK so they stay in lockstep with the code. + +| Use | For | +| --- | --- | +| **[`sdk-map.md`](sdk-map.md) + [`map/operations/`](map/operations/service-management.md)** | Traversing the SDK and working out its surface β€” locating the operation you need (this SDK exposes **93 operations**), its exact signature, which credential it sends, which channel every request field travels on, which error type it rejects with and how to read it, and the file behind any type. This is the index to consume the SDK from, and the one to reach for first. | +| **[`api-reference.md`](api-reference.md)** | Usage guidance for a single operation once you know which one you want β€” a code sample for each of the two call forms (awaiting it, and the non-throwing `.asApiResult()`), per-parameter descriptions, and the success and error types it resolves or rejects with. | + +--- + +## License + +This SDK is distributed under the [MIT License](LICENSE). + +--- + +## Support + +Refer to the [API reference](api-reference.md) for detailed information on available operations with code samples. + +--- + +[license-url]: LICENSE +[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg +[apimatic-url]: https://www.apimatic.io +[apimatic-badge]: https://www.apimatic.io/hubfs/Built-with-APIMatic-badge.svg diff --git a/plugins/authlete/sdk/typescript/api-reference.md b/plugins/authlete/sdk/typescript/api-reference.md new file mode 100644 index 00000000..c0d8c2a7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/api-reference.md @@ -0,0 +1,9730 @@ +# Reference + +> Source: [AuthleteApiClient](src/client.ts) + +## ServiceManagement + +> Source: [ServiceManagement](src/resources/service-management.ts) + +
+serviceConfigurationApi(request: ServiceManagement.ServiceConfigurationApiRequest, options?: RequestOptions): ApiPromise<Record<string, unknown>, ServiceManagement.ServiceConfigurationApiError> + +
+
+ +### Description + +
+
+ +This API gathers configuration information about a service. + +This API is supposed to be called from within the implementation of the configuration endpoint of the service where the service that supports OpenID Connect and [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) must expose its configuration information in a JSON format. Details about the format are described in "[3. OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)" in OpenID Connect Discovery 1.0. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceConfigurationApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type Record +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceConfigurationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceConfigurationApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Record +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| pretty? | boolean | This boolean value indicates whether the JSON in the response should be formatted or not. If `true`, the JSON in the response is pretty-formatted. The default value is `false`. | +| patch? | string | Get the JSON Patch [RFC 6902 JavaScript Object Notation (JSON) Patch](https://www.rfc-editor.org/rfc/rfc6902) to be applied. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceConfigurationApi(request)` + +- **OnSuccess**: Record<string, unknown> +- **OnError**: throws [ServiceManagement.ServiceConfigurationApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceConfigurationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Record<string, unknown>, ServiceManagement.ServiceConfigurationApiError>, with `result.value` of type Record<string, unknown> +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceCreateApi(request: ServiceManagement.ServiceCreateApiRequest, options?: RequestOptions): ApiPromise<Service, ServiceManagement.ServiceCreateApiError> + +
+
+ +### Description + +
+
+ +Create a new service. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceCreateApi({ + number: 715948317, + serviceName: "My Test Service", + issuer: "https://example.com", + supportedGrantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + supportedResponseTypes: [ResponseType.Code], + }); + // TODO: Handle 'response' of type Service +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceCreateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceCreateApi({ + number: 715948317, + serviceName: "My Test Service", + issuer: "https://example.com", + supportedGrantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + supportedResponseTypes: [ResponseType.Code], +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Service +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| number? | number | The sequential number of the service. The value of this property is assigned by Authlete. | +| serviceName? | string | The name of this service. | +| issuer? | string | The issuer identifier of the service.

A URL that starts with https:// and has no query or fragment component.

The value of this property is used as `iss` claim in an [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and `issuer` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| description? | string | The description about the service. | +| apiKey? | number | The service ID used in Authlete API calls. The value of this property is assigned by Authlete. | +| apiSecret? | string | The API secret of this service. This value is assigned by Authlete and is used for service authentication in API calls. | +| tokenBatchNotificationEndpoint? | string | The endpoint for batch token notifications. This endpoint is called when multiple tokens are issued or revoked in a batch operation. | +| clientAssertionAudRestrictedToIssuer? | boolean | The flag indicating whether the audience of client assertion JWTs must match the issuer identifier of this service. | +| serviceOwnerNumber? | number | The number of the organization that owns this service. This value is assigned by Authlete. | +| clientsPerDeveloper? | number | The maximum number of client applications that a developer can have. | +| developerAuthenticationCallbackEndpoint? | string | The endpoint for developer authentication callbacks. This is used when developers log into the developer portal. | +| developerAuthenticationCallbackApiKey? | string | The API key for basic authentication at the developer authentication callback endpoint. | +| developerAuthenticationCallbackApiSecret? | string | The API secret for basic authentication at the developer authentication callback endpoint. | +| supportedSnses? | [Sns](src/models/sns.ts)[] | Social login services (SNS) that this service supports for end-user authentication. | +| snsCredentials? | [SnsCredentials](src/models/sns-credentials.ts)[] | The credentials for social login services (SNS) that are used for end-user authentication. | +| supportedDeveloperSnses? | [Sns](src/models/sns.ts)[] | Social login services (SNS) that this service supports for developer authentication. | +| developerSnsCredentials? | [SnsCredentials](src/models/sns-credentials.ts)[] | The credentials for social login services (SNS) that are used for developer authentication. | +| clientIdAliasEnabled? | boolean | Deprecated. Always `true`. | +| metadata? | [Pair](src/models/pair.ts)[] | The `metadata` of the service. The content of the returned array depends on contexts. The predefined service metadata is listed in the following table.

\| Key \| Description \|
\| --- \| --- \|
\| `clientCount` \| The number of client applications which belong to this service. \| | +| createdAt? | number | The time at which this service was created. The value is represented as milliseconds since the UNIX epoch (`1970-01-01`). | +| modifiedAt? | number | The time at which this service was last modified. The value is represented as milliseconds since the UNIX epoch (1970-01-01). | +| authenticationCallbackEndpoint? | string | A Web API endpoint for user authentication which is to be prepared on the service side.

The endpoint must be implemented if you do not implement the UI at the authorization endpoint but use the one provided by Authlete.

The user authentication at the authorization endpoint provided by Authlete is performed by making a `POST` request to this endpoint. | +| authenticationCallbackApiKey? | string | API key for basic authentication at the authentication callback endpoint.

If the value is not empty, Authlete generates Authorization header for Basic authentication when making a request to the authentication callback endpoint. | +| authenticationCallbackApiSecret? | string | API secret for `basic` authentication at the authentication callback endpoint. | +| supportedAcrs? | string[] | Values of acrs (authentication context class references) that the service supports.

The value of this property is used as `acr_values_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedGrantTypes? | [GrantType](src/models/grant-type.ts)[] | Values of `grant_type` request parameter that the service supports.

The value of this property is used as `grant_types_supported property` in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedResponseTypes? | [ResponseType](src/models/response-type.ts)[] | Values of `response_type` request parameter that the service supports. Valid values are listed in Response Type.

The value of this property is used as `response_types_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedAuthorizationDetailsTypes? | string[] | The supported data types that can be used as values of the type field in `authorization_details`.

This property corresponds to the `authorization_details_types_supported` metadata. See "OAuth 2.0 Rich Authorization Requests" (RAR) for details. | +| supportedServiceProfiles? | [ServiceProfile](src/models/service-profile.ts)[] | The profiles that this service supports. | +| errorDescriptionOmitted? | boolean | The flag to indicate whether the `error_description` response parameter is omitted.

According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may include the `error_description` response parameter in error responses.

If `true`, Authlete does not embed the `error_description` response parameter in error responses. | +| errorUriOmitted? | boolean | The flag to indicate whether the `error_uri` response parameter is omitted.

According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may include the `error_uri` response parameter in error responses.

If `true`, Authlete does not embed the `error_uri` response parameter in error responses. | +| authorizationEndpoint? | string | The authorization endpoint of the service.

A URL that starts with `https://` and has no fragment component. For example, `https://example.com/auth/authorization`.

The value of this property is used as `authorization_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directAuthorizationEndpointEnabled? | boolean | Deprecated. The direct authorization endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedUiLocales? | string[] | UI locales that the service supports.

Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`.

The value of this property is used as `ui_locales_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedDisplays? | [Display](src/models/display.ts)[] | Values of `display` request parameter that service supports.

The value of this property is used as `display_values_supported` property in the Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| pkceRequired? | boolean | The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required for authorization requests by Authorization Code Flow.

If `true`, `code_challenge` request parameter is always required for authorization requests using Authorization Code Flow.

See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients) for details about `code_challenge` request parameter. | +| pkceS256Required? | boolean | The flag to indicate whether `S256` is always required as the code challenge method whenever [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used.

If this flag is set to `true`, `code_challenge_method=S256` must be included in the authorization request whenever it includes the `code_challenge` request parameter. Neither omission of the `code_challenge_method` request parameter nor use of plain (`code_challenge_method=plain`) is allowed. | +| authorizationResponseDuration? | number | The duration of authorization response JWTs in seconds.

[Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines new values for the `response_mode` request parameter. They are `query.jwt`, `fragment.jwt`, `form_post.jwt` and `jwt`. If one of them is specified as the response mode, response parameters from the authorization endpoint will be packed into a JWT. This property is used to compute the value of the `exp` claim of the JWT. | +| authorizationCodeDuration? | number | The duration of authorization codes in seconds. | +| tokenEndpoint? | string | The [token endpoint](https://tools.ietf.org/html/rfc6749#section-3.2) of the service.

A URL that starts with `https://` and has not fragment component. For example, `https://example.com/auth/token`.

The value of this property is used as `token_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directTokenEndpointEnabled? | boolean | Deprecated. The direct token endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedTokenAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported by the token endpoint of the service.

The value of this property is used as `token_endpoint_auth_methods_supports` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| missingClientIdAllowed? | boolean | The flag to indicate token requests from public clients without the `client_id` request parameter are allowed when the client can be guessed from `authorization_code` or `refresh_token`.

This flag should not be set unless you have special reasons. | +| revocationEndpoint? | string | The [revocation endpoint](https://tools.ietf.org/html/rfc7009) of the service.

A URL that starts with `https://`. For example, `https://example.com/auth/revocation`. | +| directRevocationEndpointEnabled? | boolean | Deprecated. The direct revocation endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedRevocationAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported at the revocation endpoint. | +| introspectionEndpoint? | string | The URI of the introspection endpoint. | +| directIntrospectionEndpointEnabled? | boolean | Deprecated. The direct introspection endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedIntrospectionAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported at the introspection endpoint. | +| pushedAuthReqEndpoint? | string | The URI of the pushed authorization request endpoint.

This property corresponds to the `pushed_authorization_request_endpoint` metadata defined in "[5. Authorization Server Metadata](https://tools.ietf.org/html/draft-lodderstedt-oauth-par#section-5)" of OAuth 2.0 Pushed Authorization Requests. | +| pushedAuthReqDuration? | number | The duration of pushed authorization requests in seconds. | +| parRequired? | boolean | The flag to indicate whether this service requires that clients use the pushed authorization request endpoint.

This property corresponds to the `require_pushed_authorization_requests` server metadata defined in [OAuth 2.0 Pushed Authorization Requests](https://tools.ietf.org/html/draft-lodderstedt-oauth-par). | +| requestObjectRequired? | boolean | The flag to indicate whether this service requires that authorization requests always utilize a request object by using either request or `request_uri` request parameter.

If this flag is set to `true` and the value of `traditionalRequestObjectProcessingApplied` is `false`, the value of `require_signed_request_object` server metadata of this service is reported as `true` in the discovery document. The metadata is defined in JAR (JWT Secured Authorization Request). That `require_signed_request_object` is `true` means that authorization requests which don't conform to the JAR specification are rejected. | +| traditionalRequestObjectProcessingApplied? | boolean | The flag to indicate whether a request object is processed based on rules defined in [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) or JAR (JWT Secured Authorization Request). | +| mutualTlsValidatePkiCertChain? | boolean | The flag to indicate whether this service validates certificate chains during PKI-based client mutual TLS authentication. | +| trustedRootCertificates? | string[] | The list of root certificates trusted by this service for PKI-based client mutual TLS authentication. | +| mtlsEndpointAliases? | [NamedUri](src/models/named-uri.ts)[] | The MTLS endpoint aliases. | +| accessTokenType? | string | The access token type.

This value is used as the value of `token_type` property in access token responses. If this service complies with [RFC 6750](https://tools.ietf.org/html/rfc6750), the value of this property should be `Bearer`.

See [RFC 6749 (OAuth 2.0), 7.1. Access Token Types](https://tools.ietf.org/html/rfc6749#section-7.1) for details. | +| tlsClientCertificateBoundAccessTokens? | boolean | The flag to indicate whether this service supports issuing TLS client certificate bound access tokens. | +| accessTokenDuration? | number | The duration of access tokens in seconds. This value is used as the value of `expires_in` property in access token responses. `expires_in` is defined [RFC 6749, 5.1. Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1). | +| singleAccessTokenPerSubject? | boolean | The flag to indicate whether the number of access tokens per subject (and per client) is at most one or can be more.

If `true`, an attempt to issue a new access token invalidates existing access tokens that are associated with the same subject and the same client.

Note that, however, attempts by [Client Credentials Flow](https://tools.ietf.org/html/rfc6749#section-4.4) do not invalidate existing access tokens because access tokens issued by Client Credentials Flow are not associated with any end-user's subject. Also note that an attempt by [Refresh Token Flow](https://tools.ietf.org/html/rfc6749#section-6) invalidates the coupled access token only and this invalidation is always performed regardless of whether the value of this setting item is `true` or `false`. | +| accessTokenSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| accessTokenSignatureKeyId? | string | The key ID to identify a JWK used for signing access tokens.

A JWK Set can be registered as a property of a service. A JWK Set can contain 0 or more JWKs. Authlete Server has to pick up one JWK for signing from the JWK Set when it generates a JWT-based access token. Authlete Server searches the registered JWK Set for a JWK which satisfies conditions for access token signature. If the number of JWK candidates which satisfy the conditions is 1, there is no problem. On the other hand, if there exist multiple candidates, a Key ID is needed to be specified so that Authlete Server can pick up one JWK from among the JWK candidates. | +| refreshTokenDuration? | number | The duration of refresh tokens in seconds. The related specifications have no requirements on refresh token duration, but Authlete sets expiration for refresh tokens. | +| refreshTokenDurationKept? | boolean | The flag to indicate whether the remaining duration of the used refresh token is taken over to the newly issued refresh token. | +| refreshTokenDurationReset? | boolean | The flag which indicates whether duration of refresh tokens are reset when they are used even if the `refreshTokenKept` property of this service set to is `true` (= even if "Refresh Token Continuous Use" is "Kept").

This flag has no effect when the `refreshTokenKept` property is set to `false`. In other words, if this service issues a new refresh token on every refresh token request, the refresh token will have fresh duration (unless `refreshTokenDurationKept` is set to `true`) and this `refreshTokenDurationReset` property is not referenced. | +| refreshTokenKept? | boolean | The flag to indicate whether a refresh token remains unchanged or gets renewed after its use.

If `true`, a refresh token used to get a new access token remains valid after its use. Otherwise, if `false`, a refresh token is invalidated after its use and a new refresh token is issued.

See [RFC 6749 6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), as to how to get a new access token using a refresh token. | +| supportedScopes? | [Scope](src/models/scope.ts)[] | Scopes supported by the service. | +| scopeRequired? | boolean | The flag to indicate whether requests that request no scope are rejected or not. | +| idTokenDuration? | number | 'The duration of [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)s in seconds. This value is used to calculate the value of `exp` claim in an ID token.' | +| allowableClockSkew? | number | The allowable clock skew between the server and clients in seconds.

The clock skew is taken into consideration when time-related claims in a JWT (e.g. `exp`, `iat`, `nbf`) are verified. | +| supportedClaimTypes? | [ClaimType](src/models/claim-type.ts)[] | Claim types supported by the service. Valid values are listed in Claim Type. Note that Authlete currently doesn't provide any API to help implementations for `AGGREGATED` and `DISTRIBUTED`.

The value of this property is used as `claim_types_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedClaimLocales? | string[] | Claim locales that the service supports. Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`. See [OpenID Connect Core 1.0, 5.2. Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for details.

The value of this property is used as `claims_locales_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedClaims? | string[] | Claim names that the service supports. The standard claim names listed in [OpenID Connect Core 1.0, 5.1. Standard Claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) should be supported. The following is the list of standard claims. | +| claimShortcutRestrictive? | boolean | The flag indicating whether claims specified by shortcut scopes (e.g. `profile`) are included in the issued ID token only when no access token is issued. | +| jwksUri? | string | The URL of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. For example, `http://example.com/auth/jwks`.

Client applications accesses this URL (1) to get the public key of the service to validate the signature of an ID token issued by the service and (2) to get the public key of the service to encrypt an request object of the client application. See [OpenID Connect Core 1.0, 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for details.

The value of this property is used as `jwks_uri` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directJwksEndpointEnabled? | boolean | Deprecated. The direct JWKS endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| jwks? | string | The content of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document.

If this property is not `null` in a `/service/create` request or a `/service/update` request, Authlete hosts the content in the database. This property must not be `null` and must contain pairs of public/private keys if the service wants to support asymmetric signatures for ID tokens and asymmetric encryption for request objects. See [OpenID Connect Core 1.0, 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for details. | +| idTokenSignatureKeyId? | string | The key ID to identify a JWK used for ID token signature using an asymmetric key. | +| userInfoSignatureKeyId? | string | The key ID to identify a JWK used for user info signature using an asymmetric key. | +| authorizationSignatureKeyId? | string | The key ID to identify a JWK used for signing authorization responses using an asymmetric key. | +| userInfoEndpoint? | string | The [user info endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) of the service. A URL that starts with `https://`. For example, `https://example.com/auth/userinfo`.

The value of this property is used as `userinfo_endpoint` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directUserInfoEndpointEnabled? | boolean | Deprecated. The direct userinfo endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| dynamicRegistrationSupported? | boolean | The boolean flag which indicates whether the [OAuth 2.0 Dynamic Client Registration Protocol](https://tools.ietf.org/html/rfc7591) is supported. | +| registrationEndpoint? | string | The [registration endpoint](http://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration) of the service. A URL that starts with `https://`. For example, `https://example.com/auth/registration`.

The value of this property is used as `registration_endpoint` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| registrationManagementEndpoint? | string | The URI of the registration management endpoint. If dynamic client registration is supported, and this is set, this URI will be used as the basis of the client's management endpoint by appending `/clientid}/` to it as a path element. If this is unset, the value of `registrationEndpoint` will be used as the URI base instead. | +| policyUri? | string | The URL of the "Policy" of the service.

The value of this property is used as `op_policy_uri` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| tosUri? | string | The URL of the "Terms Of Service" of the service.

The value of this property is used as `op_tos_uri` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| serviceDocumentation? | string | The URL of a page where documents for developers can be found.

The value of this property is used as `service_documentation` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| backchannelAuthenticationEndpoint? | string | The URI of backchannel authentication endpoint, which is defined in the specification of [CIBA (Client Initiated Backchannel Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). | +| supportedBackchannelTokenDeliveryModes? | [DeliveryMode](src/models/delivery-mode.ts)[] | The supported backchannel token delivery modes. This property corresponds to the `backchannel_token_delivery_modes_supported` metadata.

Backchannel token delivery modes are defined in the specification of [CIBA (Client Initiated Backchannel Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). | +| backchannelAuthReqIdDuration? | number | The duration of backchannel authentication request IDs issued from the backchannel authentication endpoint in seconds. This is used as the value of the `expires_in` property in responses from the backchannel authentication endpoint. | +| backchannelPollingInterval? | number | The minimum interval between polling requests to the token endpoint from client applications in seconds. This is used as the value of the `interval` property in responses from the backchannel authentication endpoint. | +| backchannelUserCodeParameterSupported? | boolean | The boolean flag which indicates whether the `user_code` request parameter is supported at the backchannel authentication endpoint. This property corresponds to the `backchannel_user_code_parameter_supported` metadata. | +| backchannelBindingMessageRequiredInFapi? | boolean | The flag to indicate whether the `binding_message` request parameter is always required whenever a backchannel authentication request is judged as a request for Financial-grade API. | +| deviceAuthorizationEndpoint? | string | The URI of the device authorization endpoint.

Device authorization endpoint is defined in the specification of OAuth 2.0 Device Authorization Grant. | +| deviceVerificationUri? | string | The verification URI for the device flow. This URI is used as the value of the `verification_uri` parameter in responses from the device authorization endpoint. | +| deviceVerificationUriComplete? | string | The verification URI for the device flow with a placeholder for a user code. This URI is used to build the value of the `verification_uri_complete` parameter in responses from the device authorization endpoint. | +| deviceFlowCodeDuration? | number | The duration of device verification codes and end-user verification codes issued from the device authorization endpoint in seconds. This is used as the value of the `expires_in` property in responses from the device authorization endpoint. | +| deviceFlowPollingInterval? | number | The minimum interval between polling requests to the token endpoint from client applications in seconds in device flow. This is used as the value of the `interval` property in responses from the device authorization endpoint. | +| userCodeCharset? | [UserCodeCharset](src/models/user-code-charset.ts) | - | +| userCodeLength? | number | The length of end-user verification codes (`user_code`) for Device Flow. | +| supportedTrustFrameworks? | string[] | Trust frameworks supported by this service. This corresponds to the `trust_frameworks_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedEvidence? | string[] | Evidence supported by this service. This corresponds to the `evidence_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedIdentityDocuments? | string[] | Identity documents supported by this service. This corresponds to the `id_documents_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedVerificationMethods? | string[] | Verification methods supported by this service. This corresponds to the `id_documents_verification_methods_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedVerifiedClaims? | string[] | Verified claims supported by this service. This corresponds to the `claims_in_verified_claims_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| verifiedClaimsValidationSchemaSet? | [VerifiedClaimsValidationSchema](src/models/verified-claims-validation-schema.ts) | The verified claims validation schema set. | +| attributes? | [Pair](src/models/pair.ts)[] | The attributes of this service. | +| nbfOptional? | boolean | The flag indicating whether the nbf claim in the request object is optional even when the authorization request is regarded as a FAPI-Part2 request. | +| issSuppressed? | boolean | The flag indicating whether generation of the iss response parameter is suppressed. | +| supportedCustomClientMetadata? | string[] | custom client metadata supported by this service. | +| tokenExpirationLinked? | boolean | The flag indicating whether the expiration date of an access token never exceeds that of the corresponding refresh token. | +| frontChannelRequestObjectEncryptionRequired? | boolean | The flag indicating whether encryption of request object is required when the request object is passed through the front channel. | +| requestObjectEncryptionAlgMatchRequired? | boolean | The flag indicating whether the JWE alg of encrypted request object must match the `request_object_encryption_alg` client metadata of the client that has sent the request object. | +| requestObjectEncryptionEncMatchRequired? | boolean | The flag indicating whether the JWE `enc` of encrypted request object must match the `request_object_encryption_enc` client metadata of the client that has sent the request object. | +| hsmEnabled? | boolean | The flag indicating whether HSM (Hardware Security Module) support is enabled for this service.

When this flag is `false`, keys managed in HSMs are not used even if they exist. In addition, `/api/hsk/*` APIs reject all requests.

Even if this flag is `true`, HSM-related features do not work if the configuration of the Authlete server you are using does not support HSM. | +| hsks? | [Hsk](src/models/hsk.ts)[] | The information about keys managed on HSMs (Hardware Security Modules).

This `hsks` property is output only, meaning that `hsks` in requests to `/api/service/create` API and `/api/service/update` API do not have any effect. The contents of this property is controlled only by `/api/hsk/*` APIs. | +| grantManagementEndpoint? | string | The URL of the grant management endpoint. | +| grantManagementActionRequired? | boolean | The flag indicating whether every authorization request (and any request serving as an authorization request such as CIBA backchannel authentication request and device authorization request) must include the `grant_management_action` request parameter. | +| unauthorizedOnClientConfigSupported? | boolean | The flag indicating whether Authlete's `/api/client/registration` API uses `UNAUTHORIZED` as a value of the `action` response parameter when appropriate. | +| dcrScopeUsedAsRequestable? | boolean | The flag indicating whether the `scope` request parameter in dynamic client registration and update requests (RFC 7591 and RFC 7592) is used as scopes that the client can request.

Limiting the range of scopes that a client can request is achieved by listing scopes in the `client.extension.requestableScopes` property and setting the `client.extension.requestableScopesEnabled` property to `true`. This feature is called "requestable scopes".

This property affects behaviors of `/api/client/registration` and other family APIs. | +| endSessionEndpoint? | string | The endpoint for clients ending the sessions.

A URL that starts with `https://` and has no fragment component. For example, `https://example.com/auth/endSession`.

The value of this property is used as `end_session_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| challengeEndpoint? | string | The URL of the challenge endpoint, which is defined in OAuth 2.0 Attestation-Based Client Authentication. | +| loopbackRedirectionUriVariable? | boolean | The flag indicating whether the port number component of redirection URIs can be variable when the host component indicates loopback. | +| requestObjectAudienceChecked? | boolean | The flag indicating whether Authlete checks whether the `aud` claim of request objects matches the issuer identifier of this service. | +| accessTokenForExternalAttachmentEmbedded? | boolean | The flag indicating whether Authlete generates access tokens for external attachments and embeds them in ID tokens and userinfo responses. | +| authorityHints? | string[] | Identifiers of entities that can issue entity statements for this service. This property corresponds to the `authority_hints` property that appears in a self-signed entity statement that is defined in OpenID Connect Federation 1.0. | +| federationEnabled? | boolean | flag indicating whether this service supports OpenID Connect Federation 1 | +| federationJwks? | string | JWK Set document containing keys that are used to sign (1) self-signed entity statement of this service and (2) the response from `signed_jwks_uri`. | +| federationSignatureKeyId? | string | A key ID to identify a JWK used to sign the entity configuration and the signed JWK Set. | +| federationConfigurationDuration? | number | The duration of the entity configuration in seconds. | +| federationRegistrationEndpoint? | string | The URI of the federation registration endpoint. This property corresponds to the `federation_registration_endpoint` server metadata that is defined in OpenID Connect Federation 1.0. | +| organizationName? | string | The human-readable name representing the organization that operates this service. This property corresponds to the `organization_name` server metadata that is defined in OpenID Connect Federation 1.0. | +| predefinedTransformedClaims? | string | The transformed claims predefined by this service in JSON format. This property corresponds to the `transformed_claims_predefined` server metadata. | +| refreshTokenIdempotent? | boolean | flag indicating whether refresh token requests with the same refresh token can be made multiple times in quick succession and they can obtain the same renewed refresh token within the short period. | +| signedJwksUri? | string | The URI of the endpoint that returns this service's JWK Set document in the JWT format. This property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect Federation 1.0. | +| supportedAttachments? | [AttachmentType](src/models/attachment-type.ts)[] | Supported attachment types. This property corresponds to the {@code attachments_supported} server metadata which was added by the third implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| supportedDigestAlgorithms? | string[] | Supported algorithms used to compute digest values of external attachments. This property corresponds to the `digest_algorithms_supported` server metadata which was added by the third implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| supportedDocuments? | string[] | Document types supported by this service. This property corresponds to the `documents_supported` server metadata. | +| supportedDocumentsMethods? | string[] | validation and verification processes supported by this service. This property corresponds to the `documents_methods_supported` server metadata.

The third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) renamed the `id_documents_verification_methods_supported` server metadata to `documents_methods_supported`. | +| supportedDocumentsValidationMethods? | string[] | Document validation methods supported by this service. This property corresponds to the `documents_validation_methods_supported` server metadata which was added by the third implementer's draft of | +| supportedDocumentsVerificationMethods? | string[] | Document verification methods supported by this service. This property corresponds to the `documents_verification_methods_supported` server metadata which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) | +| supportedElectronicRecords? | string[] | Electronic record types supported by this service. This property corresponds to the `electronic_records_supported` server metadata which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) | +| supportedClientRegistrationTypes? | [ClientRegistrationType](src/models/client-registration-type.ts)[] | - | +| tokenExchangeByIdentifiableClientsOnly? | boolean | The flag indicating whether to prohibit unidentifiable clients from making token exchange requests. | +| tokenExchangeByConfidentialClientsOnly? | boolean | The flag indicating whether to prohibit public clients from making token exchange requests. | +| tokenExchangeByPermittedClientsOnly? | boolean | The flag indicating whether to prohibit clients that have no explicit permission from making token exchange requests. | +| tokenExchangeEncryptedJwtRejected? | boolean | The flag indicating whether to reject token exchange requests which use encrypted JWTs as input tokens. | +| tokenExchangeUnsignedJwtRejected? | boolean | The flag indicating whether to reject token exchange requests which use unsigned JWTs as input tokens. | +| jwtGrantByIdentifiableClientsOnly? | boolean | The flag indicating whether to prohibit unidentifiable clients from using the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| jwtGrantEncryptedJwtRejected? | boolean | The flag indicating whether to reject token requests that use an encrypted JWT as an authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| jwtGrantUnsignedJwtRejected? | boolean | The flag indicating whether to reject token requests that use an unsigned JWT as an authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| dcrDuplicateSoftwareIdBlocked? | boolean | The flag indicating whether to block DCR (Dynamic Client Registration) requests whose "software_id" has already been used previously. | +| trustAnchors? | [TrustAnchor](src/models/trust-anchor.ts)[] | The trust anchors that are referenced when this service resolves trust chains of relying parties.

If this property is empty, client registration fails regardless of whether its type is `automatic` or `explicit`. It means that OpenID Connect Federation 1.0 does not work. | +| openidDroppedOnRefreshWithoutOfflineAccess? | boolean | The flag indicating whether the openid scope should be dropped from scopes list assigned to access token issued when a refresh token grant is used. | +| idTokenDroppedOnIssueWithoutOpenid? | boolean | The flag indicating whether to refrain from issuing an ID token when the `openid` scope has been removed from the scopes to be granted by the `scopes` request parameter of the `/auth/authorization/issue` API.

Flows whose `response_type` contains `id_token`, and flows that requested Native SSO, are not affected, because both require an ID token. | +| supportedDocumentsCheckMethods? | string[] | Supported document check methods. This property corresponds to the `documents_check_methods_supported` server metadata which was added by the fourth implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| rsResponseSigned? | boolean | The flag indicating whether this service signs responses from the resource server. | +| cnonceDuration? | number | The duration of `c_nonce`. | +| dpopNonceRequired? | boolean | Whether to require DPoP proof JWTs to include the `nonce` claim whenever they are presented. | +| verifiableCredentialsEnabled? | boolean | Get the flag indicating whether the feature of Verifiable Credentials for this service is enabled or not. | +| credentialJwksUri? | string | The URL at which the JWK Set document of the credential issuer is exposed. | +| credentialOfferDuration? | number | The default duration of credential offers in seconds. | +| dpopNonceDuration? | number | The duration of nonce values for DPoP proof JWTs in seconds. | +| preAuthorizedGrantAnonymousAccessSupported? | boolean | The flag indicating whether token requests using the pre-authorized code grant flow by unidentifiable clients are allowed. | +| credentialTransactionDuration? | number | The duration of transaction ID in seconds that may be issued as a result of a credential request or a batch credential request. | +| introspectionSignatureKeyId? | string | The key ID of the key for signing introspection responses. | +| resourceSignatureKeyId? | string | The key ID of the key for signing introspection responses. | +| userPinLength? | number | The default length of user PINs. | +| supportedPromptValues? | [Prompt](src/models/prompt.ts)[] | The supported `prompt` values. | +| idTokenReissuable? | boolean | The flag indicating whether to enable the feature of ID token reissuance in the refresh token flow. | +| credentialJwks? | string | The JWK Set document containing private keys that are used to sign verifiable credentials. | +| fapiModes? | [FapiMode](src/models/fapi-mode.ts)[] | FAPI modes for this service.

When the value of this property is not `null`, Authlete always processes requests to this service based on the specified FAPI modes if the FAPI feature is enabled in Authlete and the FAPI profile is supported by this service.

For instance, when this property is set to an array containing `FAPI1_ADVANCED` only, Authlete always processes requests to this service based on "Financial-grade API Security Profile 1.0 - Part 2: Advanced" if the FAPI feature is enabled in Authlete and the FAPI profile is supported by this service. | +| credentialDuration? | number | The default duration of verifiable credentials in seconds. | +| credentialIssuerMetadata? | [CredentialIssuerMetadata](src/models/credential-issuer-metadata.ts) | - | +| idTokenAudType? | string | The type of the `aud` claim in ID tokens. | +| nativeSsoSupported? | boolean | Flag that enables the [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (β€œNative SSO”). When this property is **not** `true`, Native SSO specific parameters are ignored or treated as errors. For example:

* The `device_sso` scope has no special meaning (Authlete does not embed the `sid` claim in ID tokens).
* The `urn:openid:params:token-type:device-secret` token type is treated as unknown and results in an error.

When set to `true`, the server metadata advertises `"native_sso_supported": true`. See [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) and [RFC 8414 Β§2](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for background. Native SSO is available in Authlete 3.0 and later. | +| oid4VciVersion? | string | Version of the [OpenID for Verifiable Credential Issuance](https://www.authlete.com/developers/oid4vci/) (OID4VCI) specification to support.

Accepted values are:

* `null` or `"1.0-ID1"` β†’ Implementer’s Draft 1.
* `"1.0"` or `"1.0-Final"` β†’ Final 1.0 specification.

Choose the value that matches the OID4VCI behaviour your service should expose. See the OID4VCI documentation for details. | +| cimdMetadataPolicyEnabled? | boolean | Flag that controls whether the CIMD metadata policy is applied to client metadata obtained through the Client ID Metadata Document (CIMD) mechanism. | +| clientIdMetadataDocumentSupported? | boolean | Indicates whether the Client ID Metadata Document (CIMD) mechanism is supported. When `true`, the service will attempt to retrieve client metadata via CIMD where applicable. | +| cimdAllowlistEnabled? | boolean | Enables the allowlist for CIMD. When `true`, only CIMD endpoints that are on the allowlist are used. | +| cimdAllowlist? | string[] | The allowlist of CIMD endpoints (hosts/URIs) that may be used when retrieving client metadata via Client ID Metadata Documents. | +| cimdAlwaysRetrieved? | boolean | If `true`, CIMD retrieval is always attempted for clients, regardless of other conditions. | +| cimdHttpPermitted? | boolean | Allows CIMD retrieval over plain HTTP. When `false`, only HTTPS CIMD endpoints are allowed. | +| cimdQueryPermitted? | boolean | Allows the use of query parameters when retrieving CIMD metadata. When `false`, query parameters are disallowed for CIMD requests. | +| cimdMetadataPolicy? | string | The metadata policy applied to client metadata obtained through the CIMD mechanism. The value must follow the metadata policy grammar defined in [OpenID Federation 1.0 Β§6.1 Metadata Policy](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy). | +| httpAliasProhibited? | boolean | When `true`, client ID aliases starting with `https://` or `http://` are prohibited. | +| attestationChallengeTimeWindow? | number | The time window of attestation challenges in seconds. This is used for OAuth 2.0 Attestation-Based Client Authentication. | +| clientAttesterRootsEnabled? | boolean | The flag indicating whether the attester roots for Client Attestation JWT x5c chain validation are enabled. | +| clientAttesterRootsOnly? | boolean | The flag indicating whether only client authentication validated via attester roots is accepted. | +| keyAttesterRootsEnabled? | boolean | The flag indicating whether the attester roots for Key Attestation JWT x5c chain validation are enabled. | +| keyAttesterRootsOnly? | boolean | The flag indicating whether only key attestation validated via attester roots is accepted. | +| clientAttesterRoots? | string[] | The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Client Attestation JWTs. | +| keyAttesterRoots? | string[] | The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Key Attestation JWTs. | +| backchannelLogoutSupported? | boolean | The flag indicating whether this service supports OpenID Connect Back-Channel Logout 1.0. | +| backchannelLogoutSessionSupported? | boolean | The flag indicating whether this service includes a `sid` (session ID) claim in ID tokens, supporting per-session backchannel logout. | +| haipVersion? | string | The HAIP version to comply with. Valid values for this property are as follows: `"1.0"`. If this property is set, requests to this service undergo additional validation based on the specified version of the [HAIP](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) specification. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceCreateApi(request)` + +- **OnSuccess**: [Service](src/models/service.ts) +- **OnError**: throws [ServiceManagement.ServiceCreateApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceCreateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Service, ServiceManagement.ServiceCreateApiError>, with `result.value` of type [Service](src/models/service.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceCreateIdpApi(request: ServiceManagement.ServiceCreateIdpApiRequest, options?: RequestOptions): ApiPromise<Service, ServiceManagement.ServiceCreateIdpApiError> + +
+
+ +### Description + +
+
+ +Create a new service on the API server, belonging to the specified organization. A service can be created with an organization token or by a user with the CREATE_SERVICE role. + +This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the regional API clusters. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceCreateIdpApi({ + body: { + apiServerId: 76281, + organizationId: 123456789012345, + service: { serviceName: "My service", issuer: "https://my-service.example.com" }, + }, + }); + // TODO: Handle 'response' of type Service +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceCreateIdpApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceCreateIdpApi({ + body: { + apiServerId: 76281, + organizationId: 123456789012345, + service: { serviceName: "My service", issuer: "https://my-service.example.com" }, + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Service +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [ServiceCreateIdpRequest](src/models/service-create-idp-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceCreateIdpApi(request)` + +- **OnSuccess**: [Service](src/models/service.ts) +- **OnError**: throws [ServiceManagement.ServiceCreateIdpApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceCreateIdpApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Service, ServiceManagement.ServiceCreateIdpApiError>, with `result.value` of type [Service](src/models/service.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceDeleteApi(request: ServiceManagement.ServiceDeleteApiRequest, options?: RequestOptions): ApiPromise<undefined, ServiceManagement.ServiceDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete a service. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.serviceManagement.serviceDeleteApi({ serviceId: "some example string" }); +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceDeleteApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceDeleteApi(request)` + +- **OnSuccess**: undefined +- **OnError**: throws [ServiceManagement.ServiceDeleteApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, ServiceManagement.ServiceDeleteApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceGetApi(request: ServiceManagement.ServiceGetApiRequest, options?: RequestOptions): ApiPromise<Service, ServiceManagement.ServiceGetApiError> + +
+
+ +### Description + +
+
+ +Get a service. + +If the access token can only view or modify clients underneath this service, but does not have access to view this service directly, a limited view of the service will be returned. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceGetApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type Service +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceGetApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Service +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceGetApi(request)` + +- **OnSuccess**: [Service](src/models/service.ts) +- **OnError**: throws [ServiceManagement.ServiceGetApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Service, ServiceManagement.ServiceGetApiError>, with `result.value` of type [Service](src/models/service.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceGetListApi(request: ServiceManagement.ServiceGetListApiRequest, options?: RequestOptions): ApiPromise<ServiceGetListResponse, ServiceManagement.ServiceGetListApiError> + +
+
+ +### Description + +
+
+ +Get a list of services. + +If the access token can only view or modify clients underneath a service, but does not have access to view that service directly, a limited view of the service will be returned. Otherwise, all properties of the service are returned. + +If the access token is an administrative token, this returns a list of all services on the Authlete instance. Otherwise, all services that the access token can view, even in a limited fashion, are returned. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceGetListApi(); + // TODO: Handle 'response' of type ServiceGetListResponse +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceGetListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceGetListApi().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ServiceGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| start? | number | Start index (inclusive) of the result set. The default value is 0. Must not be a negative number. | +| end? | number | End index (exclusive) of the result set. The default value is 5. Must not be a negative number. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceGetListApi(request)` + +- **OnSuccess**: [ServiceGetListResponse](src/models/service-get-list-response.ts) +- **OnError**: throws [ServiceManagement.ServiceGetListApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceGetListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ServiceGetListResponse, ServiceManagement.ServiceGetListApiError>, with `result.value` of type [ServiceGetListResponse](src/models/service-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceRemoveIdpApi(request: ServiceManagement.ServiceRemoveIdpApiRequest, options?: RequestOptions): ApiPromise<undefined, ServiceManagement.ServiceRemoveIdpApiError> + +
+
+ +### Description + +
+
+ +Delete a service from the API server. A service can be deleted with an organization token or by a user with the MODIFY_SERVICE role. + +This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the regional API clusters. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.serviceManagement.serviceRemoveIdpApi({ + body: { apiServerId: 76281, organizationId: 123456789012345, serviceId: 21653835348762 }, + }); +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceRemoveIdpApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceRemoveIdpApi({ + body: { apiServerId: 76281, organizationId: 123456789012345, serviceId: 21653835348762 }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [ServiceRemoveIdpRequest](src/models/service-remove-idp-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceRemoveIdpApi(request)` + +- **OnSuccess**: undefined +- **OnError**: throws [ServiceManagement.ServiceRemoveIdpApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceRemoveIdpApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, ServiceManagement.ServiceRemoveIdpApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+serviceUpdateApi(request: ServiceManagement.ServiceUpdateApiRequest, options?: RequestOptions): ApiPromise<Service, ServiceManagement.ServiceUpdateApiError> + +
+
+ +### Description + +
+
+ +Update a service. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.serviceManagement.serviceUpdateApi({ + serviceId: "some example string", + number: 715948317, + serviceName: "My Test Service", + issuer: "https://example.com", + supportedGrantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + supportedResponseTypes: [ResponseType.Code], + }); + // TODO: Handle 'response' of type Service +} catch (err) { + // TODO: Handle 'err' of type ServiceManagement.ServiceUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.serviceManagement.serviceUpdateApi({ + serviceId: "some example string", + number: 715948317, + serviceName: "My Test Service", + issuer: "https://example.com", + supportedGrantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + supportedResponseTypes: [ResponseType.Code], +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Service +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| number? | number | The sequential number of the service. The value of this property is assigned by Authlete. | +| serviceName? | string | The name of this service. | +| issuer? | string | The issuer identifier of the service.

A URL that starts with https:// and has no query or fragment component.

The value of this property is used as `iss` claim in an [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and `issuer` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| description? | string | The description about the service. | +| apiKey? | number | The service ID used in Authlete API calls. The value of this property is assigned by Authlete. | +| apiSecret? | string | The API secret of this service. This value is assigned by Authlete and is used for service authentication in API calls. | +| tokenBatchNotificationEndpoint? | string | The endpoint for batch token notifications. This endpoint is called when multiple tokens are issued or revoked in a batch operation. | +| clientAssertionAudRestrictedToIssuer? | boolean | The flag indicating whether the audience of client assertion JWTs must match the issuer identifier of this service. | +| serviceOwnerNumber? | number | The number of the organization that owns this service. This value is assigned by Authlete. | +| clientsPerDeveloper? | number | The maximum number of client applications that a developer can have. | +| developerAuthenticationCallbackEndpoint? | string | The endpoint for developer authentication callbacks. This is used when developers log into the developer portal. | +| developerAuthenticationCallbackApiKey? | string | The API key for basic authentication at the developer authentication callback endpoint. | +| developerAuthenticationCallbackApiSecret? | string | The API secret for basic authentication at the developer authentication callback endpoint. | +| supportedSnses? | [Sns](src/models/sns.ts)[] | Social login services (SNS) that this service supports for end-user authentication. | +| snsCredentials? | [SnsCredentials](src/models/sns-credentials.ts)[] | The credentials for social login services (SNS) that are used for end-user authentication. | +| supportedDeveloperSnses? | [Sns](src/models/sns.ts)[] | Social login services (SNS) that this service supports for developer authentication. | +| developerSnsCredentials? | [SnsCredentials](src/models/sns-credentials.ts)[] | The credentials for social login services (SNS) that are used for developer authentication. | +| clientIdAliasEnabled? | boolean | Deprecated. Always `true`. | +| metadata? | [Pair](src/models/pair.ts)[] | The `metadata` of the service. The content of the returned array depends on contexts. The predefined service metadata is listed in the following table.

\| Key \| Description \|
\| --- \| --- \|
\| `clientCount` \| The number of client applications which belong to this service. \| | +| createdAt? | number | The time at which this service was created. The value is represented as milliseconds since the UNIX epoch (`1970-01-01`). | +| modifiedAt? | number | The time at which this service was last modified. The value is represented as milliseconds since the UNIX epoch (1970-01-01). | +| authenticationCallbackEndpoint? | string | A Web API endpoint for user authentication which is to be prepared on the service side.

The endpoint must be implemented if you do not implement the UI at the authorization endpoint but use the one provided by Authlete.

The user authentication at the authorization endpoint provided by Authlete is performed by making a `POST` request to this endpoint. | +| authenticationCallbackApiKey? | string | API key for basic authentication at the authentication callback endpoint.

If the value is not empty, Authlete generates Authorization header for Basic authentication when making a request to the authentication callback endpoint. | +| authenticationCallbackApiSecret? | string | API secret for `basic` authentication at the authentication callback endpoint. | +| supportedAcrs? | string[] | Values of acrs (authentication context class references) that the service supports.

The value of this property is used as `acr_values_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedGrantTypes? | [GrantType](src/models/grant-type.ts)[] | Values of `grant_type` request parameter that the service supports.

The value of this property is used as `grant_types_supported property` in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedResponseTypes? | [ResponseType](src/models/response-type.ts)[] | Values of `response_type` request parameter that the service supports. Valid values are listed in Response Type.

The value of this property is used as `response_types_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedAuthorizationDetailsTypes? | string[] | The supported data types that can be used as values of the type field in `authorization_details`.

This property corresponds to the `authorization_details_types_supported` metadata. See "OAuth 2.0 Rich Authorization Requests" (RAR) for details. | +| supportedServiceProfiles? | [ServiceProfile](src/models/service-profile.ts)[] | The profiles that this service supports. | +| errorDescriptionOmitted? | boolean | The flag to indicate whether the `error_description` response parameter is omitted.

According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may include the `error_description` response parameter in error responses.

If `true`, Authlete does not embed the `error_description` response parameter in error responses. | +| errorUriOmitted? | boolean | The flag to indicate whether the `error_uri` response parameter is omitted.

According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may include the `error_uri` response parameter in error responses.

If `true`, Authlete does not embed the `error_uri` response parameter in error responses. | +| authorizationEndpoint? | string | The authorization endpoint of the service.

A URL that starts with `https://` and has no fragment component. For example, `https://example.com/auth/authorization`.

The value of this property is used as `authorization_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directAuthorizationEndpointEnabled? | boolean | Deprecated. The direct authorization endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedUiLocales? | string[] | UI locales that the service supports.

Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`.

The value of this property is used as `ui_locales_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedDisplays? | [Display](src/models/display.ts)[] | Values of `display` request parameter that service supports.

The value of this property is used as `display_values_supported` property in the Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| pkceRequired? | boolean | The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required for authorization requests by Authorization Code Flow.

If `true`, `code_challenge` request parameter is always required for authorization requests using Authorization Code Flow.

See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients) for details about `code_challenge` request parameter. | +| pkceS256Required? | boolean | The flag to indicate whether `S256` is always required as the code challenge method whenever [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used.

If this flag is set to `true`, `code_challenge_method=S256` must be included in the authorization request whenever it includes the `code_challenge` request parameter. Neither omission of the `code_challenge_method` request parameter nor use of plain (`code_challenge_method=plain`) is allowed. | +| authorizationResponseDuration? | number | The duration of authorization response JWTs in seconds.

[Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines new values for the `response_mode` request parameter. They are `query.jwt`, `fragment.jwt`, `form_post.jwt` and `jwt`. If one of them is specified as the response mode, response parameters from the authorization endpoint will be packed into a JWT. This property is used to compute the value of the `exp` claim of the JWT. | +| authorizationCodeDuration? | number | The duration of authorization codes in seconds. | +| tokenEndpoint? | string | The [token endpoint](https://tools.ietf.org/html/rfc6749#section-3.2) of the service.

A URL that starts with `https://` and has not fragment component. For example, `https://example.com/auth/token`.

The value of this property is used as `token_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directTokenEndpointEnabled? | boolean | Deprecated. The direct token endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedTokenAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported by the token endpoint of the service.

The value of this property is used as `token_endpoint_auth_methods_supports` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| missingClientIdAllowed? | boolean | The flag to indicate token requests from public clients without the `client_id` request parameter are allowed when the client can be guessed from `authorization_code` or `refresh_token`.

This flag should not be set unless you have special reasons. | +| revocationEndpoint? | string | The [revocation endpoint](https://tools.ietf.org/html/rfc7009) of the service.

A URL that starts with `https://`. For example, `https://example.com/auth/revocation`. | +| directRevocationEndpointEnabled? | boolean | Deprecated. The direct revocation endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedRevocationAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported at the revocation endpoint. | +| introspectionEndpoint? | string | The URI of the introspection endpoint. | +| directIntrospectionEndpointEnabled? | boolean | Deprecated. The direct introspection endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| supportedIntrospectionAuthMethods? | [ClientAuthMethod](src/models/client-auth-method.ts)[] | Client authentication methods supported at the introspection endpoint. | +| pushedAuthReqEndpoint? | string | The URI of the pushed authorization request endpoint.

This property corresponds to the `pushed_authorization_request_endpoint` metadata defined in "[5. Authorization Server Metadata](https://tools.ietf.org/html/draft-lodderstedt-oauth-par#section-5)" of OAuth 2.0 Pushed Authorization Requests. | +| pushedAuthReqDuration? | number | The duration of pushed authorization requests in seconds. | +| parRequired? | boolean | The flag to indicate whether this service requires that clients use the pushed authorization request endpoint.

This property corresponds to the `require_pushed_authorization_requests` server metadata defined in [OAuth 2.0 Pushed Authorization Requests](https://tools.ietf.org/html/draft-lodderstedt-oauth-par). | +| requestObjectRequired? | boolean | The flag to indicate whether this service requires that authorization requests always utilize a request object by using either request or `request_uri` request parameter.

If this flag is set to `true` and the value of `traditionalRequestObjectProcessingApplied` is `false`, the value of `require_signed_request_object` server metadata of this service is reported as `true` in the discovery document. The metadata is defined in JAR (JWT Secured Authorization Request). That `require_signed_request_object` is `true` means that authorization requests which don't conform to the JAR specification are rejected. | +| traditionalRequestObjectProcessingApplied? | boolean | The flag to indicate whether a request object is processed based on rules defined in [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) or JAR (JWT Secured Authorization Request). | +| mutualTlsValidatePkiCertChain? | boolean | The flag to indicate whether this service validates certificate chains during PKI-based client mutual TLS authentication. | +| trustedRootCertificates? | string[] | The list of root certificates trusted by this service for PKI-based client mutual TLS authentication. | +| mtlsEndpointAliases? | [NamedUri](src/models/named-uri.ts)[] | The MTLS endpoint aliases. | +| accessTokenType? | string | The access token type.

This value is used as the value of `token_type` property in access token responses. If this service complies with [RFC 6750](https://tools.ietf.org/html/rfc6750), the value of this property should be `Bearer`.

See [RFC 6749 (OAuth 2.0), 7.1. Access Token Types](https://tools.ietf.org/html/rfc6749#section-7.1) for details. | +| tlsClientCertificateBoundAccessTokens? | boolean | The flag to indicate whether this service supports issuing TLS client certificate bound access tokens. | +| accessTokenDuration? | number | The duration of access tokens in seconds. This value is used as the value of `expires_in` property in access token responses. `expires_in` is defined [RFC 6749, 5.1. Successful Response](https://tools.ietf.org/html/rfc6749#section-5.1). | +| singleAccessTokenPerSubject? | boolean | The flag to indicate whether the number of access tokens per subject (and per client) is at most one or can be more.

If `true`, an attempt to issue a new access token invalidates existing access tokens that are associated with the same subject and the same client.

Note that, however, attempts by [Client Credentials Flow](https://tools.ietf.org/html/rfc6749#section-4.4) do not invalidate existing access tokens because access tokens issued by Client Credentials Flow are not associated with any end-user's subject. Also note that an attempt by [Refresh Token Flow](https://tools.ietf.org/html/rfc6749#section-6) invalidates the coupled access token only and this invalidation is always performed regardless of whether the value of this setting item is `true` or `false`. | +| accessTokenSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| accessTokenSignatureKeyId? | string | The key ID to identify a JWK used for signing access tokens.

A JWK Set can be registered as a property of a service. A JWK Set can contain 0 or more JWKs. Authlete Server has to pick up one JWK for signing from the JWK Set when it generates a JWT-based access token. Authlete Server searches the registered JWK Set for a JWK which satisfies conditions for access token signature. If the number of JWK candidates which satisfy the conditions is 1, there is no problem. On the other hand, if there exist multiple candidates, a Key ID is needed to be specified so that Authlete Server can pick up one JWK from among the JWK candidates. | +| refreshTokenDuration? | number | The duration of refresh tokens in seconds. The related specifications have no requirements on refresh token duration, but Authlete sets expiration for refresh tokens. | +| refreshTokenDurationKept? | boolean | The flag to indicate whether the remaining duration of the used refresh token is taken over to the newly issued refresh token. | +| refreshTokenDurationReset? | boolean | The flag which indicates whether duration of refresh tokens are reset when they are used even if the `refreshTokenKept` property of this service set to is `true` (= even if "Refresh Token Continuous Use" is "Kept").

This flag has no effect when the `refreshTokenKept` property is set to `false`. In other words, if this service issues a new refresh token on every refresh token request, the refresh token will have fresh duration (unless `refreshTokenDurationKept` is set to `true`) and this `refreshTokenDurationReset` property is not referenced. | +| refreshTokenKept? | boolean | The flag to indicate whether a refresh token remains unchanged or gets renewed after its use.

If `true`, a refresh token used to get a new access token remains valid after its use. Otherwise, if `false`, a refresh token is invalidated after its use and a new refresh token is issued.

See [RFC 6749 6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), as to how to get a new access token using a refresh token. | +| supportedScopes? | [Scope](src/models/scope.ts)[] | Scopes supported by the service. | +| scopeRequired? | boolean | The flag to indicate whether requests that request no scope are rejected or not. | +| idTokenDuration? | number | 'The duration of [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)s in seconds. This value is used to calculate the value of `exp` claim in an ID token.' | +| allowableClockSkew? | number | The allowable clock skew between the server and clients in seconds.

The clock skew is taken into consideration when time-related claims in a JWT (e.g. `exp`, `iat`, `nbf`) are verified. | +| supportedClaimTypes? | [ClaimType](src/models/claim-type.ts)[] | Claim types supported by the service. Valid values are listed in Claim Type. Note that Authlete currently doesn't provide any API to help implementations for `AGGREGATED` and `DISTRIBUTED`.

The value of this property is used as `claim_types_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedClaimLocales? | string[] | Claim locales that the service supports. Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`. See [OpenID Connect Core 1.0, 5.2. Languages and Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for details.

The value of this property is used as `claims_locales_supported` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| supportedClaims? | string[] | Claim names that the service supports. The standard claim names listed in [OpenID Connect Core 1.0, 5.1. Standard Claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) should be supported. The following is the list of standard claims. | +| claimShortcutRestrictive? | boolean | The flag indicating whether claims specified by shortcut scopes (e.g. `profile`) are included in the issued ID token only when no access token is issued. | +| jwksUri? | string | The URL of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. For example, `http://example.com/auth/jwks`.

Client applications accesses this URL (1) to get the public key of the service to validate the signature of an ID token issued by the service and (2) to get the public key of the service to encrypt an request object of the client application. See [OpenID Connect Core 1.0, 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for details.

The value of this property is used as `jwks_uri` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directJwksEndpointEnabled? | boolean | Deprecated. The direct JWKS endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| jwks? | string | The content of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document.

If this property is not `null` in a `/service/create` request or a `/service/update` request, Authlete hosts the content in the database. This property must not be `null` and must contain pairs of public/private keys if the service wants to support asymmetric signatures for ID tokens and asymmetric encryption for request objects. See [OpenID Connect Core 1.0, 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for details. | +| idTokenSignatureKeyId? | string | The key ID to identify a JWK used for ID token signature using an asymmetric key. | +| userInfoSignatureKeyId? | string | The key ID to identify a JWK used for user info signature using an asymmetric key. | +| authorizationSignatureKeyId? | string | The key ID to identify a JWK used for signing authorization responses using an asymmetric key. | +| userInfoEndpoint? | string | The [user info endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) of the service. A URL that starts with `https://`. For example, `https://example.com/auth/userinfo`.

The value of this property is used as `userinfo_endpoint` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| directUserInfoEndpointEnabled? | boolean | Deprecated. The direct userinfo endpoint was removed in Authlete 3.0.15, so this flag has no effect and is always returned as `false`. | +| dynamicRegistrationSupported? | boolean | The boolean flag which indicates whether the [OAuth 2.0 Dynamic Client Registration Protocol](https://tools.ietf.org/html/rfc7591) is supported. | +| registrationEndpoint? | string | The [registration endpoint](http://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration) of the service. A URL that starts with `https://`. For example, `https://example.com/auth/registration`.

The value of this property is used as `registration_endpoint` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| registrationManagementEndpoint? | string | The URI of the registration management endpoint. If dynamic client registration is supported, and this is set, this URI will be used as the basis of the client's management endpoint by appending `/clientid}/` to it as a path element. If this is unset, the value of `registrationEndpoint` will be used as the URI base instead. | +| policyUri? | string | The URL of the "Policy" of the service.

The value of this property is used as `op_policy_uri` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| tosUri? | string | The URL of the "Terms Of Service" of the service.

The value of this property is used as `op_tos_uri` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| serviceDocumentation? | string | The URL of a page where documents for developers can be found.

The value of this property is used as `service_documentation` property in the [OpenID Provider Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| backchannelAuthenticationEndpoint? | string | The URI of backchannel authentication endpoint, which is defined in the specification of [CIBA (Client Initiated Backchannel Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). | +| supportedBackchannelTokenDeliveryModes? | [DeliveryMode](src/models/delivery-mode.ts)[] | The supported backchannel token delivery modes. This property corresponds to the `backchannel_token_delivery_modes_supported` metadata.

Backchannel token delivery modes are defined in the specification of [CIBA (Client Initiated Backchannel Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). | +| backchannelAuthReqIdDuration? | number | The duration of backchannel authentication request IDs issued from the backchannel authentication endpoint in seconds. This is used as the value of the `expires_in` property in responses from the backchannel authentication endpoint. | +| backchannelPollingInterval? | number | The minimum interval between polling requests to the token endpoint from client applications in seconds. This is used as the value of the `interval` property in responses from the backchannel authentication endpoint. | +| backchannelUserCodeParameterSupported? | boolean | The boolean flag which indicates whether the `user_code` request parameter is supported at the backchannel authentication endpoint. This property corresponds to the `backchannel_user_code_parameter_supported` metadata. | +| backchannelBindingMessageRequiredInFapi? | boolean | The flag to indicate whether the `binding_message` request parameter is always required whenever a backchannel authentication request is judged as a request for Financial-grade API. | +| deviceAuthorizationEndpoint? | string | The URI of the device authorization endpoint.

Device authorization endpoint is defined in the specification of OAuth 2.0 Device Authorization Grant. | +| deviceVerificationUri? | string | The verification URI for the device flow. This URI is used as the value of the `verification_uri` parameter in responses from the device authorization endpoint. | +| deviceVerificationUriComplete? | string | The verification URI for the device flow with a placeholder for a user code. This URI is used to build the value of the `verification_uri_complete` parameter in responses from the device authorization endpoint. | +| deviceFlowCodeDuration? | number | The duration of device verification codes and end-user verification codes issued from the device authorization endpoint in seconds. This is used as the value of the `expires_in` property in responses from the device authorization endpoint. | +| deviceFlowPollingInterval? | number | The minimum interval between polling requests to the token endpoint from client applications in seconds in device flow. This is used as the value of the `interval` property in responses from the device authorization endpoint. | +| userCodeCharset? | [UserCodeCharset](src/models/user-code-charset.ts) | - | +| userCodeLength? | number | The length of end-user verification codes (`user_code`) for Device Flow. | +| supportedTrustFrameworks? | string[] | Trust frameworks supported by this service. This corresponds to the `trust_frameworks_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedEvidence? | string[] | Evidence supported by this service. This corresponds to the `evidence_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedIdentityDocuments? | string[] | Identity documents supported by this service. This corresponds to the `id_documents_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedVerificationMethods? | string[] | Verification methods supported by this service. This corresponds to the `id_documents_verification_methods_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| supportedVerifiedClaims? | string[] | Verified claims supported by this service. This corresponds to the `claims_in_verified_claims_supported` [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). | +| verifiedClaimsValidationSchemaSet? | [VerifiedClaimsValidationSchema](src/models/verified-claims-validation-schema.ts) | The verified claims validation schema set. | +| attributes? | [Pair](src/models/pair.ts)[] | The attributes of this service. | +| nbfOptional? | boolean | The flag indicating whether the nbf claim in the request object is optional even when the authorization request is regarded as a FAPI-Part2 request. | +| issSuppressed? | boolean | The flag indicating whether generation of the iss response parameter is suppressed. | +| supportedCustomClientMetadata? | string[] | custom client metadata supported by this service. | +| tokenExpirationLinked? | boolean | The flag indicating whether the expiration date of an access token never exceeds that of the corresponding refresh token. | +| frontChannelRequestObjectEncryptionRequired? | boolean | The flag indicating whether encryption of request object is required when the request object is passed through the front channel. | +| requestObjectEncryptionAlgMatchRequired? | boolean | The flag indicating whether the JWE alg of encrypted request object must match the `request_object_encryption_alg` client metadata of the client that has sent the request object. | +| requestObjectEncryptionEncMatchRequired? | boolean | The flag indicating whether the JWE `enc` of encrypted request object must match the `request_object_encryption_enc` client metadata of the client that has sent the request object. | +| hsmEnabled? | boolean | The flag indicating whether HSM (Hardware Security Module) support is enabled for this service.

When this flag is `false`, keys managed in HSMs are not used even if they exist. In addition, `/api/hsk/*` APIs reject all requests.

Even if this flag is `true`, HSM-related features do not work if the configuration of the Authlete server you are using does not support HSM. | +| hsks? | [Hsk](src/models/hsk.ts)[] | The information about keys managed on HSMs (Hardware Security Modules).

This `hsks` property is output only, meaning that `hsks` in requests to `/api/service/create` API and `/api/service/update` API do not have any effect. The contents of this property is controlled only by `/api/hsk/*` APIs. | +| grantManagementEndpoint? | string | The URL of the grant management endpoint. | +| grantManagementActionRequired? | boolean | The flag indicating whether every authorization request (and any request serving as an authorization request such as CIBA backchannel authentication request and device authorization request) must include the `grant_management_action` request parameter. | +| unauthorizedOnClientConfigSupported? | boolean | The flag indicating whether Authlete's `/api/client/registration` API uses `UNAUTHORIZED` as a value of the `action` response parameter when appropriate. | +| dcrScopeUsedAsRequestable? | boolean | The flag indicating whether the `scope` request parameter in dynamic client registration and update requests (RFC 7591 and RFC 7592) is used as scopes that the client can request.

Limiting the range of scopes that a client can request is achieved by listing scopes in the `client.extension.requestableScopes` property and setting the `client.extension.requestableScopesEnabled` property to `true`. This feature is called "requestable scopes".

This property affects behaviors of `/api/client/registration` and other family APIs. | +| endSessionEndpoint? | string | The endpoint for clients ending the sessions.

A URL that starts with `https://` and has no fragment component. For example, `https://example.com/auth/endSession`.

The value of this property is used as `end_session_endpoint` property in the [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). | +| challengeEndpoint? | string | The URL of the challenge endpoint, which is defined in OAuth 2.0 Attestation-Based Client Authentication. | +| loopbackRedirectionUriVariable? | boolean | The flag indicating whether the port number component of redirection URIs can be variable when the host component indicates loopback. | +| requestObjectAudienceChecked? | boolean | The flag indicating whether Authlete checks whether the `aud` claim of request objects matches the issuer identifier of this service. | +| accessTokenForExternalAttachmentEmbedded? | boolean | The flag indicating whether Authlete generates access tokens for external attachments and embeds them in ID tokens and userinfo responses. | +| authorityHints? | string[] | Identifiers of entities that can issue entity statements for this service. This property corresponds to the `authority_hints` property that appears in a self-signed entity statement that is defined in OpenID Connect Federation 1.0. | +| federationEnabled? | boolean | flag indicating whether this service supports OpenID Connect Federation 1 | +| federationJwks? | string | JWK Set document containing keys that are used to sign (1) self-signed entity statement of this service and (2) the response from `signed_jwks_uri`. | +| federationSignatureKeyId? | string | A key ID to identify a JWK used to sign the entity configuration and the signed JWK Set. | +| federationConfigurationDuration? | number | The duration of the entity configuration in seconds. | +| federationRegistrationEndpoint? | string | The URI of the federation registration endpoint. This property corresponds to the `federation_registration_endpoint` server metadata that is defined in OpenID Connect Federation 1.0. | +| organizationName? | string | The human-readable name representing the organization that operates this service. This property corresponds to the `organization_name` server metadata that is defined in OpenID Connect Federation 1.0. | +| predefinedTransformedClaims? | string | The transformed claims predefined by this service in JSON format. This property corresponds to the `transformed_claims_predefined` server metadata. | +| refreshTokenIdempotent? | boolean | flag indicating whether refresh token requests with the same refresh token can be made multiple times in quick succession and they can obtain the same renewed refresh token within the short period. | +| signedJwksUri? | string | The URI of the endpoint that returns this service's JWK Set document in the JWT format. This property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect Federation 1.0. | +| supportedAttachments? | [AttachmentType](src/models/attachment-type.ts)[] | Supported attachment types. This property corresponds to the {@code attachments_supported} server metadata which was added by the third implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| supportedDigestAlgorithms? | string[] | Supported algorithms used to compute digest values of external attachments. This property corresponds to the `digest_algorithms_supported` server metadata which was added by the third implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| supportedDocuments? | string[] | Document types supported by this service. This property corresponds to the `documents_supported` server metadata. | +| supportedDocumentsMethods? | string[] | validation and verification processes supported by this service. This property corresponds to the `documents_methods_supported` server metadata.

The third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) renamed the `id_documents_verification_methods_supported` server metadata to `documents_methods_supported`. | +| supportedDocumentsValidationMethods? | string[] | Document validation methods supported by this service. This property corresponds to the `documents_validation_methods_supported` server metadata which was added by the third implementer's draft of | +| supportedDocumentsVerificationMethods? | string[] | Document verification methods supported by this service. This property corresponds to the `documents_verification_methods_supported` server metadata which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) | +| supportedElectronicRecords? | string[] | Electronic record types supported by this service. This property corresponds to the `electronic_records_supported` server metadata which was added by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) | +| supportedClientRegistrationTypes? | [ClientRegistrationType](src/models/client-registration-type.ts)[] | - | +| tokenExchangeByIdentifiableClientsOnly? | boolean | The flag indicating whether to prohibit unidentifiable clients from making token exchange requests. | +| tokenExchangeByConfidentialClientsOnly? | boolean | The flag indicating whether to prohibit public clients from making token exchange requests. | +| tokenExchangeByPermittedClientsOnly? | boolean | The flag indicating whether to prohibit clients that have no explicit permission from making token exchange requests. | +| tokenExchangeEncryptedJwtRejected? | boolean | The flag indicating whether to reject token exchange requests which use encrypted JWTs as input tokens. | +| tokenExchangeUnsignedJwtRejected? | boolean | The flag indicating whether to reject token exchange requests which use unsigned JWTs as input tokens. | +| jwtGrantByIdentifiableClientsOnly? | boolean | The flag indicating whether to prohibit unidentifiable clients from using the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| jwtGrantEncryptedJwtRejected? | boolean | The flag indicating whether to reject token requests that use an encrypted JWT as an authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| jwtGrantUnsignedJwtRejected? | boolean | The flag indicating whether to reject token requests that use an unsigned JWT as an authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". | +| dcrDuplicateSoftwareIdBlocked? | boolean | The flag indicating whether to block DCR (Dynamic Client Registration) requests whose "software_id" has already been used previously. | +| trustAnchors? | [TrustAnchor](src/models/trust-anchor.ts)[] | The trust anchors that are referenced when this service resolves trust chains of relying parties.

If this property is empty, client registration fails regardless of whether its type is `automatic` or `explicit`. It means that OpenID Connect Federation 1.0 does not work. | +| openidDroppedOnRefreshWithoutOfflineAccess? | boolean | The flag indicating whether the openid scope should be dropped from scopes list assigned to access token issued when a refresh token grant is used. | +| idTokenDroppedOnIssueWithoutOpenid? | boolean | The flag indicating whether to refrain from issuing an ID token when the `openid` scope has been removed from the scopes to be granted by the `scopes` request parameter of the `/auth/authorization/issue` API.

Flows whose `response_type` contains `id_token`, and flows that requested Native SSO, are not affected, because both require an ID token. | +| supportedDocumentsCheckMethods? | string[] | Supported document check methods. This property corresponds to the `documents_check_methods_supported` server metadata which was added by the fourth implementer's draft of OpenID Connect for Identity Assurance 1.0. | +| rsResponseSigned? | boolean | The flag indicating whether this service signs responses from the resource server. | +| cnonceDuration? | number | The duration of `c_nonce`. | +| dpopNonceRequired? | boolean | Whether to require DPoP proof JWTs to include the `nonce` claim whenever they are presented. | +| verifiableCredentialsEnabled? | boolean | Get the flag indicating whether the feature of Verifiable Credentials for this service is enabled or not. | +| credentialJwksUri? | string | The URL at which the JWK Set document of the credential issuer is exposed. | +| credentialOfferDuration? | number | The default duration of credential offers in seconds. | +| dpopNonceDuration? | number | The duration of nonce values for DPoP proof JWTs in seconds. | +| preAuthorizedGrantAnonymousAccessSupported? | boolean | The flag indicating whether token requests using the pre-authorized code grant flow by unidentifiable clients are allowed. | +| credentialTransactionDuration? | number | The duration of transaction ID in seconds that may be issued as a result of a credential request or a batch credential request. | +| introspectionSignatureKeyId? | string | The key ID of the key for signing introspection responses. | +| resourceSignatureKeyId? | string | The key ID of the key for signing introspection responses. | +| userPinLength? | number | The default length of user PINs. | +| supportedPromptValues? | [Prompt](src/models/prompt.ts)[] | The supported `prompt` values. | +| idTokenReissuable? | boolean | The flag indicating whether to enable the feature of ID token reissuance in the refresh token flow. | +| credentialJwks? | string | The JWK Set document containing private keys that are used to sign verifiable credentials. | +| fapiModes? | [FapiMode](src/models/fapi-mode.ts)[] | FAPI modes for this service.

When the value of this property is not `null`, Authlete always processes requests to this service based on the specified FAPI modes if the FAPI feature is enabled in Authlete and the FAPI profile is supported by this service.

For instance, when this property is set to an array containing `FAPI1_ADVANCED` only, Authlete always processes requests to this service based on "Financial-grade API Security Profile 1.0 - Part 2: Advanced" if the FAPI feature is enabled in Authlete and the FAPI profile is supported by this service. | +| credentialDuration? | number | The default duration of verifiable credentials in seconds. | +| credentialIssuerMetadata? | [CredentialIssuerMetadata](src/models/credential-issuer-metadata.ts) | - | +| idTokenAudType? | string | The type of the `aud` claim in ID tokens. | +| nativeSsoSupported? | boolean | Flag that enables the [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (β€œNative SSO”). When this property is **not** `true`, Native SSO specific parameters are ignored or treated as errors. For example:

* The `device_sso` scope has no special meaning (Authlete does not embed the `sid` claim in ID tokens).
* The `urn:openid:params:token-type:device-secret` token type is treated as unknown and results in an error.

When set to `true`, the server metadata advertises `"native_sso_supported": true`. See [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) and [RFC 8414 Β§2](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for background. Native SSO is available in Authlete 3.0 and later. | +| oid4VciVersion? | string | Version of the [OpenID for Verifiable Credential Issuance](https://www.authlete.com/developers/oid4vci/) (OID4VCI) specification to support.

Accepted values are:

* `null` or `"1.0-ID1"` β†’ Implementer’s Draft 1.
* `"1.0"` or `"1.0-Final"` β†’ Final 1.0 specification.

Choose the value that matches the OID4VCI behaviour your service should expose. See the OID4VCI documentation for details. | +| cimdMetadataPolicyEnabled? | boolean | Flag that controls whether the CIMD metadata policy is applied to client metadata obtained through the Client ID Metadata Document (CIMD) mechanism. | +| clientIdMetadataDocumentSupported? | boolean | Indicates whether the Client ID Metadata Document (CIMD) mechanism is supported. When `true`, the service will attempt to retrieve client metadata via CIMD where applicable. | +| cimdAllowlistEnabled? | boolean | Enables the allowlist for CIMD. When `true`, only CIMD endpoints that are on the allowlist are used. | +| cimdAllowlist? | string[] | The allowlist of CIMD endpoints (hosts/URIs) that may be used when retrieving client metadata via Client ID Metadata Documents. | +| cimdAlwaysRetrieved? | boolean | If `true`, CIMD retrieval is always attempted for clients, regardless of other conditions. | +| cimdHttpPermitted? | boolean | Allows CIMD retrieval over plain HTTP. When `false`, only HTTPS CIMD endpoints are allowed. | +| cimdQueryPermitted? | boolean | Allows the use of query parameters when retrieving CIMD metadata. When `false`, query parameters are disallowed for CIMD requests. | +| cimdMetadataPolicy? | string | The metadata policy applied to client metadata obtained through the CIMD mechanism. The value must follow the metadata policy grammar defined in [OpenID Federation 1.0 Β§6.1 Metadata Policy](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy). | +| httpAliasProhibited? | boolean | When `true`, client ID aliases starting with `https://` or `http://` are prohibited. | +| attestationChallengeTimeWindow? | number | The time window of attestation challenges in seconds. This is used for OAuth 2.0 Attestation-Based Client Authentication. | +| clientAttesterRootsEnabled? | boolean | The flag indicating whether the attester roots for Client Attestation JWT x5c chain validation are enabled. | +| clientAttesterRootsOnly? | boolean | The flag indicating whether only client authentication validated via attester roots is accepted. | +| keyAttesterRootsEnabled? | boolean | The flag indicating whether the attester roots for Key Attestation JWT x5c chain validation are enabled. | +| keyAttesterRootsOnly? | boolean | The flag indicating whether only key attestation validated via attester roots is accepted. | +| clientAttesterRoots? | string[] | The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Client Attestation JWTs. | +| keyAttesterRoots? | string[] | The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Key Attestation JWTs. | +| backchannelLogoutSupported? | boolean | The flag indicating whether this service supports OpenID Connect Back-Channel Logout 1.0. | +| backchannelLogoutSessionSupported? | boolean | The flag indicating whether this service includes a `sid` (session ID) claim in ID tokens, supporting per-session backchannel logout. | +| haipVersion? | string | The HAIP version to comply with. Valid values for this property are as follows: `"1.0"`. If this property is set, requests to this service undergo additional validation based on the specified version of the [HAIP](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) specification. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.serviceManagement.serviceUpdateApi(request)` + +- **OnSuccess**: [Service](src/models/service.ts) +- **OnError**: throws [ServiceManagement.ServiceUpdateApiError](src/resources/service-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.serviceManagement.serviceUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Service, ServiceManagement.ServiceUpdateApiError>, with `result.value` of type [Service](src/models/service.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## ClientManagement + +> Source: [ClientManagement](src/resources/client-management.ts) + +
+clientAuthorizationDeleteApi(request: ClientManagement.ClientAuthorizationDeleteApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete all existing access tokens issued to a client application by an end-user. + +The subject parameter is required and must be provided as a query parameter. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationDeleteApi(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationDeleteApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteApiError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationDeleteApiPost(request: ClientManagement.ClientAuthorizationDeleteApiPostRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteApiPostError> + +
+
+ +### Description + +
+
+ +Delete all existing access tokens issued to a client application by an end-user. + +The subject parameter is required. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationDeleteApiPost({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationDeleteApiPostError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationDeleteApiPost({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationDeleteApiPost(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationDeleteApiPostError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationDeleteApiPost(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteApiPostError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationDeleteBySubjectApi(request: ClientManagement.ClientAuthorizationDeleteBySubjectApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteBySubjectApiError> + +
+
+ +### Description + +
+
+ +Delete all existing access tokens issued to a client application by an end-user. In this variant, the subject is provided in the path. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationDeleteBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationDeleteBySubjectApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationDeleteBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationDeleteBySubjectApi(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationDeleteBySubjectApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationDeleteBySubjectApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientAuthorizationDeleteBySubjectApiError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationGetListApi(request: ClientManagement.ClientAuthorizationGetListApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListApiError> + +
+
+ +### Description + +
+
+ +Get a list of client applications that an end-user has authorized. + +The subject parameter is required and can be provided as a query parameter. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationGetListApi({ + serviceId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationGetListResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationGetListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationGetListApi({ + serviceId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| subject | string | Unique user ID of an end-user. | +| developer? | string | Unique ID of a client developer. | +| start? | number | Start index of search results (inclusive). The default value is 0. | +| end? | number | End index of search results (exclusive). The default value is 5. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationGetListApi(request)` + +- **OnSuccess**: [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationGetListApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationGetListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListApiError>, with `result.value` of type [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationGetListApiPost(request: ClientManagement.ClientAuthorizationGetListApiPostRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListApiPostError> + +
+
+ +### Description + +
+
+ +Get a list of client applications that an end-user has authorized. + +The subject parameter is required. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationGetListApiPost({ + serviceId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationGetListResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationGetListApiPostError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationGetListApiPost({ + serviceId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| subject | string | Unique user ID of an end-user. | +| developer? | string | Unique ID of a client developer. | +| start? | number | Start index of search results (inclusive). | +| end? | number | End index of search results (exclusive). | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationGetListApiPost(request)` + +- **OnSuccess**: [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationGetListApiPostError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationGetListApiPost(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListApiPostError>, with `result.value` of type [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationGetListBySubjectApi(request: ClientManagement.ClientAuthorizationGetListBySubjectApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListBySubjectApiError> + +
+
+ +### Description + +
+
+ +Get a list of client applications that an end-user has authorized. In this variant, the subject is provided in the path. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationGetListBySubjectApi({ + serviceId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationGetListResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationGetListBySubjectApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationGetListBySubjectApi({ + serviceId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| subject | string | Unique user ID of an end-user. | +| developer? | string | Unique ID of a client developer. | +| start? | number | Start index of search results (inclusive). The default value is 0. | +| end? | number | End index of search results (exclusive). The default value is 5. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationGetListBySubjectApi(request)` + +- **OnSuccess**: [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationGetListBySubjectApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationGetListBySubjectApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationGetListResponse, ClientManagement.ClientAuthorizationGetListBySubjectApiError>, with `result.value` of type [ClientAuthorizationGetListResponse](src/models/client-authorization-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientAuthorizationUpdateApi(request: ClientManagement.ClientAuthorizationUpdateApiRequest, options?: RequestOptions): ApiPromise<Result, ClientManagement.ClientAuthorizationUpdateApiError> + +
+
+ +### Description + +
+
+ +Update attributes of all existing access tokens given to a client application. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientAuthorizationUpdateApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type Result +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientAuthorizationUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientAuthorizationUpdateApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Result +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | The subject (= unique identifier) of the end-user who has granted authorization to the client application. | +| scopes? | string[] | An array of new scopes. Optional. If a non-null value is given, the new scopes are set to all existing access tokens. If an API call is made using `"Content-Type: application/x-www-form-urlencoded"`, scope names listed in this request parameter should be delimited by spaces (after form encoding, spaces are converted to `+`). | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientAuthorizationUpdateApi(request)` + +- **OnSuccess**: [Result](src/models/result.ts) +- **OnError**: throws [ClientManagement.ClientAuthorizationUpdateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientAuthorizationUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Result, ClientManagement.ClientAuthorizationUpdateApiError>, with `result.value` of type [Result](src/models/result.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientCreateApi(request: ClientManagement.ClientCreateApiRequest, options?: RequestOptions): ApiPromise<Client, ClientManagement.ClientCreateApiError> + +
+
+ +### Description + +
+
+ +Create a new client. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientCreateApi({ + serviceId: "some example string", + body: { + clientName: "My Client", + clientIdAlias: "my-client", + clientType: ClientType.Confidential, + applicationType: ApplicationType.Web, + grantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + responseTypes: [ResponseType.Code, ResponseType.Token], + redirectUris: ["https://my-client.example.com/cb1", "https://my-client.example.com/cb2"], + tokenAuthMethod: ClientAuthMethod.ClientSecretBasic, + attributes: [ + { key: "attribute1-key", value: "attribute1-value" }, + { key: "attribute2-key", value: "attribute2-value" }, + ], + }, + }); + // TODO: Handle 'response' of type Client +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientCreateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientCreateApi({ + serviceId: "some example string", + body: { + clientName: "My Client", + clientIdAlias: "my-client", + clientType: ClientType.Confidential, + applicationType: ApplicationType.Web, + grantTypes: [GrantType.AuthorizationCode, GrantType.RefreshToken], + responseTypes: [ResponseType.Code, ResponseType.Token], + redirectUris: ["https://my-client.example.com/cb1", "https://my-client.example.com/cb2"], + tokenAuthMethod: ClientAuthMethod.ClientSecretBasic, + attributes: [ + { key: "attribute1-key", value: "attribute1-value" }, + { key: "attribute2-key", value: "attribute2-value" }, + ], + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Client +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body? | [Client](src/models/client.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientCreateApi(request)` + +- **OnSuccess**: [Client](src/models/client.ts) +- **OnError**: throws [ClientManagement.ClientCreateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientCreateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Client, ClientManagement.ClientCreateApiError>, with `result.value` of type [Client](src/models/client.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientCursorListApi(request: ClientManagement.ClientCursorListApiRequest, options?: RequestOptions): ApiPromise<ClientCursorListResponse, ClientManagement.ClientCursorListApiError> + +
+
+ +### Description + +
+
+ +Retrieve the clients in the requested service via cursor based iteration. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientCursorListApi({ serviceId: 1 }); + // TODO: Handle 'response' of type ClientCursorListResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientCursorListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientCursorListApi({ serviceId: 1 }).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientCursorListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | number | The ID of the service to retrieve a list of clients from. | +| cursor? | string | This should be set to the value of `nextCursor` from a previous call to this endpoint. Which will retrieve the next set of clients available and receive the next available set. If this is not provided the list operation will start from the beginning. | +| limit? | number | The maximum amount of results to return from this request. This value will be clamped between 1 and the allowed maximum. The default will be used if no value is provided. | +| includeTotalCount? | boolean | Indicates whether the `totalCount` property should be calculated and included in the response, otherwise it will be unset. For some large datasets this can be expensive and is not always required to calculate. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientCursorListApi(request)` + +- **OnSuccess**: [ClientCursorListResponse](src/models/client-cursor-list-response.ts) +- **OnError**: throws [ClientManagement.ClientCursorListApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientCursorListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientCursorListResponse, ClientManagement.ClientCursorListApiError>, with `result.value` of type [ClientCursorListResponse](src/models/client-cursor-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientDeleteApi(request: ClientManagement.ClientDeleteApiRequest, options?: RequestOptions): ApiPromise<undefined, ClientManagement.ClientDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete a client. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.clientManagement.clientDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + }); +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientDeleteApi({ + serviceId: "some example string", + clientId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | The client ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientDeleteApi(request)` + +- **OnSuccess**: undefined +- **OnError**: throws [ClientManagement.ClientDeleteApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, ClientManagement.ClientDeleteApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientExtensionRequestablesScopesDeleteApi(request: ClientManagement.ClientExtensionRequestablesScopesDeleteApiRequest, options?: RequestOptions): ApiPromise<undefined, ClientManagement.ClientExtensionRequestablesScopesDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete requestable scopes of a client + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.clientManagement.clientExtensionRequestablesScopesDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + }); +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientExtensionRequestablesScopesDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientExtensionRequestablesScopesDeleteApi({ + serviceId: "some example string", + clientId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientExtensionRequestablesScopesDeleteApi(request)` + +- **OnSuccess**: undefined +- **OnError**: throws [ClientManagement.ClientExtensionRequestablesScopesDeleteApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientExtensionRequestablesScopesDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, ClientManagement.ClientExtensionRequestablesScopesDeleteApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientExtensionRequestablesScopesGetApi(request: ClientManagement.ClientExtensionRequestablesScopesGetApiRequest, options?: RequestOptions): ApiPromise<ClientExtensionRequestableScopesGetResponse, ClientManagement.ClientExtensionRequestablesScopesGetApiError> + +
+
+ +### Description + +
+
+ +Get the requestable scopes per client + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientExtensionRequestablesScopesGetApi({ + serviceId: "some example string", + clientId: "some example string", + }); + // TODO: Handle 'response' of type ClientExtensionRequestableScopesGetResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientExtensionRequestablesScopesGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientExtensionRequestablesScopesGetApi({ + serviceId: "some example string", + clientId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientExtensionRequestableScopesGetResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientExtensionRequestablesScopesGetApi(request)` + +- **OnSuccess**: [ClientExtensionRequestableScopesGetResponse](src/models/client-extension-requestable-scopes-get-response.ts) +- **OnError**: throws [ClientManagement.ClientExtensionRequestablesScopesGetApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientExtensionRequestablesScopesGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientExtensionRequestableScopesGetResponse, ClientManagement.ClientExtensionRequestablesScopesGetApiError>, with `result.value` of type [ClientExtensionRequestableScopesGetResponse](src/models/client-extension-requestable-scopes-get-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientExtensionRequestablesScopesUpdateApi(request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiRequest, options?: RequestOptions): ApiPromise<ClientExtensionRequestableScopesUpdateResponse, ClientManagement.ClientExtensionRequestablesScopesUpdateApiError> + +
+
+ +### Description + +
+
+ +Update requestable scopes of a client + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientExtensionRequestablesScopesUpdateApi({ + serviceId: "some example string", + clientId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type ClientExtensionRequestableScopesUpdateResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientExtensionRequestablesScopesUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientExtensionRequestablesScopesUpdateApi({ + serviceId: "some example string", + clientId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientExtensionRequestableScopesUpdateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| body | [ClientExtensionRequestableScopesUpdateRequest](src/models/client-extension-requestable-scopes-update-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientExtensionRequestablesScopesUpdateApi(request)` + +- **OnSuccess**: [ClientExtensionRequestableScopesUpdateResponse](src/models/client-extension-requestable-scopes-update-response.ts) +- **OnError**: throws [ClientManagement.ClientExtensionRequestablesScopesUpdateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientExtensionRequestablesScopesUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientExtensionRequestableScopesUpdateResponse, ClientManagement.ClientExtensionRequestablesScopesUpdateApiError>, with `result.value` of type [ClientExtensionRequestableScopesUpdateResponse](src/models/client-extension-requestable-scopes-update-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientExtensionRequestablesScopesUpdateApiPost(request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostRequest, options?: RequestOptions): ApiPromise<ClientExtensionRequestableScopesUpdateResponse, ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError> + +
+
+ +### Description + +
+
+ +Update requestable scopes of a client + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientExtensionRequestablesScopesUpdateApiPost({ + serviceId: "some example string", + clientId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type ClientExtensionRequestableScopesUpdateResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientExtensionRequestablesScopesUpdateApiPost({ + serviceId: "some example string", + clientId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientExtensionRequestableScopesUpdateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| body | [ClientExtensionRequestableScopesUpdateRequest](src/models/client-extension-requestable-scopes-update-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientExtensionRequestablesScopesUpdateApiPost(request)` + +- **OnSuccess**: [ClientExtensionRequestableScopesUpdateResponse](src/models/client-extension-requestable-scopes-update-response.ts) +- **OnError**: throws [ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientExtensionRequestablesScopesUpdateApiPost(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientExtensionRequestableScopesUpdateResponse, ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError>, with `result.value` of type [ClientExtensionRequestableScopesUpdateResponse](src/models/client-extension-requestable-scopes-update-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientFlagUpdateApi(request: ClientManagement.ClientFlagUpdateApiRequest, options?: RequestOptions): ApiPromise<ClientFlagUpdateResponse, ClientManagement.ClientFlagUpdateApiError> + +
+
+ +### Description + +
+
+ +Lock and unlock a client + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientFlagUpdateApi({ + serviceId: "some example string", + clientIdentifier: "some example string", + clientLocked: true, + }); + // TODO: Handle 'response' of type ClientFlagUpdateResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientFlagUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientFlagUpdateApi({ + serviceId: "some example string", + clientIdentifier: "some example string", + clientLocked: true, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientFlagUpdateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientIdentifier | string | A client ID. | +| clientLocked | boolean | The flag value to be set | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientFlagUpdateApi(request)` + +- **OnSuccess**: [ClientFlagUpdateResponse](src/models/client-flag-update-response.ts) +- **OnError**: throws [ClientManagement.ClientFlagUpdateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientFlagUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientFlagUpdateResponse, ClientManagement.ClientFlagUpdateApiError>, with `result.value` of type [ClientFlagUpdateResponse](src/models/client-flag-update-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGetApi(request: ClientManagement.ClientGetApiRequest, options?: RequestOptions): ApiPromise<Client, ClientManagement.ClientGetApiError> + +
+
+ +### Description + +
+
+ +Get a client. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGetApi({ + serviceId: "some example string", + clientId: "some example string", + }); + // TODO: Handle 'response' of type Client +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGetApi({ + serviceId: "some example string", + clientId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Client +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGetApi(request)` + +- **OnSuccess**: [Client](src/models/client.ts) +- **OnError**: throws [ClientManagement.ClientGetApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Client, ClientManagement.ClientGetApiError>, with `result.value` of type [Client](src/models/client.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGetListApi(request: ClientManagement.ClientGetListApiRequest, options?: RequestOptions): ApiPromise<ClientGetListResponse, ClientManagement.ClientGetListApiError> + +
+
+ +### Description + +
+
+ +Get a list of clients on a service. + +If the access token can view a full service (including an admin), all clients within the service are returned. Otherwise, only clients that the access token can view within the service are returned. +- ViewClient: [] + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGetListApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type ClientGetListResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGetListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGetListApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| developer? | string | The developer of client applications. The default value is null. If this parameter is not set to `null`, client application of the specified developer are returned. Otherwise, all client applications that belong to the service are returned. | +| start? | number | Start index (inclusive) of the result set. The default value is 0. Must not be a negative number. | +| end? | number | End index (exclusive) of the result set. The default value is 5. Must not be a negative number. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGetListApi(request)` + +- **OnSuccess**: [ClientGetListResponse](src/models/client-get-list-response.ts) +- **OnError**: throws [ClientManagement.ClientGetListApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGetListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientGetListResponse, ClientManagement.ClientGetListApiError>, with `result.value` of type [ClientGetListResponse](src/models/client-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGrantedScopesDeleteApi(request: ClientManagement.ClientGrantedScopesDeleteApiRequest, options?: RequestOptions): ApiPromise<Result, ClientManagement.ClientGrantedScopesDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete the set of scopes that an end-user has granted to a client application. + +Even if records about granted scopes are deleted by calling this API, existing access tokens are not deleted and scopes of existing access tokens are not changed. The subject parameter is required and must be provided as a query parameter. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGrantedScopesDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type Result +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGrantedScopesDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGrantedScopesDeleteApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Result +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGrantedScopesDeleteApi(request)` + +- **OnSuccess**: [Result](src/models/result.ts) +- **OnError**: throws [ClientManagement.ClientGrantedScopesDeleteApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGrantedScopesDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Result, ClientManagement.ClientGrantedScopesDeleteApiError>, with `result.value` of type [Result](src/models/result.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGrantedScopesDeleteBySubjectApi(request: ClientManagement.ClientGrantedScopesDeleteBySubjectApiRequest, options?: RequestOptions): ApiPromise<Result, ClientManagement.ClientGrantedScopesDeleteBySubjectApiError> + +
+
+ +### Description + +
+
+ +Delete the set of scopes that an end-user has granted to a client application. In this variant, the subject is provided in the path. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGrantedScopesDeleteBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type Result +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGrantedScopesDeleteBySubjectApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGrantedScopesDeleteBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Result +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGrantedScopesDeleteBySubjectApi(request)` + +- **OnSuccess**: [Result](src/models/result.ts) +- **OnError**: throws [ClientManagement.ClientGrantedScopesDeleteBySubjectApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGrantedScopesDeleteBySubjectApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Result, ClientManagement.ClientGrantedScopesDeleteBySubjectApiError>, with `result.value` of type [Result](src/models/result.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGrantedScopesGetApi(request: ClientManagement.ClientGrantedScopesGetApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetApiError> + +
+
+ +### Description + +
+
+ +Get the set of scopes that a user has granted to a client application. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGrantedScopesGetApi({ + serviceId: "715948317", + clientId: "1140735077", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGrantedScopesGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGrantedScopesGetApi({ + serviceId: "715948317", + clientId: "1140735077", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGrantedScopesGetApi(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientGrantedScopesGetApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGrantedScopesGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetApiError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGrantedScopesGetApiPost(request: ClientManagement.ClientGrantedScopesGetApiPostRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetApiPostError> + +
+
+ +### Description + +
+
+ +Get the set of scopes that a user has granted to a client application. + +The subject parameter is required. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGrantedScopesGetApiPost({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGrantedScopesGetApiPostError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGrantedScopesGetApiPost({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGrantedScopesGetApiPost(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientGrantedScopesGetApiPostError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGrantedScopesGetApiPost(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetApiPostError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientGrantedScopesGetBySubjectApi(request: ClientManagement.ClientGrantedScopesGetBySubjectApiRequest, options?: RequestOptions): ApiPromise<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetBySubjectApiError> + +
+
+ +### Description + +
+
+ +Get the set of scopes that a user has granted to a client application. In this variant, the subject is provided in the path. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientGrantedScopesGetBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type ClientAuthorizationDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientGrantedScopesGetBySubjectApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientGrantedScopesGetBySubjectApi({ + serviceId: "some example string", + clientId: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientAuthorizationDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientId | string | A client ID. | +| subject | string | Unique user ID of an end-user. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientGrantedScopesGetBySubjectApi(request)` + +- **OnSuccess**: [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: throws [ClientManagement.ClientGrantedScopesGetBySubjectApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientGrantedScopesGetBySubjectApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientAuthorizationDeleteResponse, ClientManagement.ClientGrantedScopesGetBySubjectApiError>, with `result.value` of type [ClientAuthorizationDeleteResponse](src/models/client-authorization-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientSearchApi(request: ClientManagement.ClientSearchApiRequest, options?: RequestOptions): ApiPromise<ClientSearchResponse, ClientManagement.ClientSearchApiError> + +
+
+ +### Description + +
+
+ +Search the clients in the requested service that match the condition provided in the `q` query parameter. Results are returned via cursor based iteration. To iterate through all clients without a search condition, use the `/api/{serviceId}/client` API instead. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientSearchApi({ serviceId: 1 }); + // TODO: Handle 'response' of type ClientSearchResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientSearchApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientSearchApi({ serviceId: 1 }).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientSearchResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | number | The ID of the service to retrieve a list of clients from. | +| q? | string | The search query. See the description of this API for the supported syntax and fields.

This parameter is required unless `cursor` is provided. When `cursor` is provided, this parameter is ignored and the search query stored in the cursor is used instead. | +| cursor? | string | This should be set to the value of `nextCursor` from a previous call to this endpoint, which will retrieve the next set of clients matching the original search query. If this is not provided the search will start from the beginning. | +| limit? | number | The maximum amount of results to return from this request. This value will be clamped between 1 and the allowed maximum. The default will be used if no value is provided. | +| includeTotalCount? | boolean | Indicates whether the `totalCount` property should be calculated and included in the response, otherwise it will be unset.

**Warning:** Calculating the total count requires an additional query and reduces the performance and throughput of this API. Only set this to `true` when the total count is actually required, rather than on every request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientSearchApi(request)` + +- **OnSuccess**: [ClientSearchResponse](src/models/client-search-response.ts) +- **OnError**: throws [ClientManagement.ClientSearchApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientSearchApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientSearchResponse, ClientManagement.ClientSearchApiError>, with `result.value` of type [ClientSearchResponse](src/models/client-search-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientSecretRefreshApi(request: ClientManagement.ClientSecretRefreshApiRequest, options?: RequestOptions): ApiPromise<ClientSecretRefreshResponse, ClientManagement.ClientSecretRefreshApiError> + +
+
+ +### Description + +
+
+ +Refresh the client secret of a client. A new value of the client secret will be generated by the Authlete server. + +If you want to specify a new value, use `/api/client/secret/update` API. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientSecretRefreshApi({ + serviceId: "some example string", + clientIdentifier: "some example string", + }); + // TODO: Handle 'response' of type ClientSecretRefreshResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientSecretRefreshApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientSecretRefreshApi({ + serviceId: "some example string", + clientIdentifier: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientSecretRefreshResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientIdentifier | string | The client ID or the client ID alias of a client. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientSecretRefreshApi(request)` + +- **OnSuccess**: [ClientSecretRefreshResponse](src/models/client-secret-refresh-response.ts) +- **OnError**: throws [ClientManagement.ClientSecretRefreshApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientSecretRefreshApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientSecretRefreshResponse, ClientManagement.ClientSecretRefreshApiError>, with `result.value` of type [ClientSecretRefreshResponse](src/models/client-secret-refresh-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientSecretUpdateApi(request: ClientManagement.ClientSecretUpdateApiRequest, options?: RequestOptions): ApiPromise<ClientSecretRefreshResponse, ClientManagement.ClientSecretUpdateApiError> + +
+
+ +### Description + +
+
+ +Update the client secret of a client. + +If you want to have the Authlete server generate a new value of the client secret, use `/api/client/secret/refresh` API. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientSecretUpdateApi({ + serviceId: "some example string", + clientIdentifier: "some example string", + clientSecret: "some example string", + }); + // TODO: Handle 'response' of type ClientSecretRefreshResponse +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientSecretUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientSecretUpdateApi({ + serviceId: "some example string", + clientIdentifier: "some example string", + clientSecret: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientSecretRefreshResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientIdentifier | string | The client ID or the client ID alias of a client. | +| clientSecret | string | The new value of the client secret. Valid characters for a client secret are `A-Z`, `a-z`, `0-9`, `-`, and `_`. The maximum length of a client secret is 86. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientSecretUpdateApi(request)` + +- **OnSuccess**: [ClientSecretRefreshResponse](src/models/client-secret-refresh-response.ts) +- **OnError**: throws [ClientManagement.ClientSecretUpdateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientSecretUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientSecretRefreshResponse, ClientManagement.ClientSecretUpdateApiError>, with `result.value` of type [ClientSecretRefreshResponse](src/models/client-secret-refresh-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientUpdateApi(request: ClientManagement.ClientUpdateApiRequest, options?: RequestOptions): ApiPromise<Client, ClientManagement.ClientUpdateApiError> + +
+
+ +### Description + +
+
+ +Update a client. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.clientManagement.clientUpdateApi({ + serviceId: "some example string", + clientIdTemplate: "some example string", + number: 1140735077, + serviceNumber: 715948317, + clientName: "My Test Client", + clientId: 1140735077, + clientSecret: "gXz97ISgLs4HuXwOZWch8GEmgL4YMvUJwu3er_kDVVGcA0UOhA9avLPbEmoeZdagi9yC_-tEiT2BdRyH9dbrQQ", + clientType: ClientType.Public, + grantTypes: [GrantType.AuthorizationCode], + responseTypes: [ResponseType.Code], + redirectUris: ["https://example.com/callback"], + }); + // TODO: Handle 'response' of type Client +} catch (err) { + // TODO: Handle 'err' of type ClientManagement.ClientUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.clientManagement.clientUpdateApi({ + serviceId: "some example string", + clientIdTemplate: "some example string", + number: 1140735077, + serviceNumber: 715948317, + clientName: "My Test Client", + clientId: 1140735077, + clientSecret: "gXz97ISgLs4HuXwOZWch8GEmgL4YMvUJwu3er_kDVVGcA0UOhA9avLPbEmoeZdagi9yC_-tEiT2BdRyH9dbrQQ", + clientType: ClientType.Public, + grantTypes: [GrantType.AuthorizationCode], + responseTypes: [ResponseType.Code], + redirectUris: ["https://example.com/callback"], +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type Client +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientIdTemplate | string | A client ID. | +| number? | number | The sequential number of the client. The value of this property is assigned by Authlete. | +| serviceNumber? | number | The sequential number of the service of the client application. The value of this property is assigned by Authlete. | +| clientName? | string | The name of the client application. This property corresponds to `client_name` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| clientNames? | [TaggedValue](src/models/tagged-value.ts)[] | Client names with language tags. If the client application has different names for different languages, this property can be used to register the names. | +| description? | string | The description about the client application. | +| descriptions? | [TaggedValue](src/models/tagged-value.ts)[] | Descriptions about the client application with language tags. If the client application has different descriptions for different languages, this property can be used to register the descriptions. | +| clientId? | number | The client identifier used in Authlete API calls. The value of this property is assigned by Authlete. | +| clientSecret? | string | The client secret. A random 512-bit value encoded by base64url (86 letters). The value of this property is assigned by Authlete. | +| clientIdAlias? | string | The value of the client's `client_id` property used in OAuth and OpenID Connect calls. By default, this is a string version of the `clientId` property. | +| clientIdAliasEnabled? | boolean | Deprecated. Always set to `true`. | +| clientType? | [ClientType](src/models/client-type.ts) | The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. | +| applicationType? | [ApplicationType](src/models/application-type.ts) | The application type. The value of this property affects the validation steps for a redirect URI. See the description about `redirectUris` property for more details. | +| logoUri? | string | The URL pointing to the logo image of the client application.

This property corresponds to `logo_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| logoUris? | [TaggedValue](src/models/tagged-value.ts)[] | Logo image URLs with language tags. If the client application has different logo images for different languages, this property can be used to register URLs of the images. | +| contacts? | string[] | An array of email addresses of people responsible for the client application.

This property corresponds to contacts in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| tlsClientCertificateBoundAccessTokens? | boolean | The flag to indicate whether this client use TLS client certificate bound access tokens. | +| dynamicallyRegistered? | boolean | The flag to indicate whether this client has been registered dynamically. For more details, see [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). | +| softwareId? | string | The unique identifier string assigned by the client developer or software publisher used by registration endpoints to identify the client software to be dynamically registered.

This property corresponds to the `software_id metadata` defined in [2. Client Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). | +| developer? | string | The unique identifier of the developer who created this client application. | +| softwareVersion? | string | The version identifier string for the client software identified by the software ID.

This property corresponds to the software_version metadata defined in [2. Client Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). | +| registrationAccessTokenHash? | string | The hash of the registration access token for this client. | +| createdAt? | number | The time at which this client was created. The value is represented as milliseconds since the UNIX epoch (1970-01-01). | +| modifiedAt? | number | The time at which this client was last modified. The value is represented as milliseconds since the UNIX epoch (1970-01-01). | +| grantTypes? | [GrantType](src/models/grant-type.ts)[] | A string array of grant types which the client application declares that it will restrict itself to using. This property corresponds to `grant_types` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| responseTypes? | [ResponseType](src/models/response-type.ts)[] | A string array of response types which the client application declares that it will restrict itself to using. This property corresponds to `response_types` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| redirectUris? | string[] | Redirect URIs that the client application uses to receive a response from the authorization endpoint. Requirements for a redirect URI are as follows. | +| authorizationSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| authorizationEncryptionAlg? | [JweAlg](src/models/jwe-alg.ts) | this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this refers to key transport scheme to be used by the client and by the server. For instance:
- as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for transporting they keys on JARM objects
- as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm that server expect from client when encrypting a Request Object
- as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key transport of id_tokens

**Please note that some of the algorithms are more secure than others, some are not supported very well cross platforms and some (like RSA1_5) is known to be weak**. | +| authorizationEncryptionEnc? | [JweEnc](src/models/jwe-enc.ts) | This is the encryption algorithm to be used when encrypting a JWT on client or server side. Depending upon the context, this refers to encryption done by the client or by the server. For instance:
- as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server when creating a JARM response
- as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the client when encrypting a Request Object
- as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt id_tokens | +| tokenAuthMethod? | [ClientAuthMethod](src/models/client-auth-method.ts) | The client authentication method that the client application declares that it uses at the token endpoint. This property corresponds to `token_endpoint_auth_method` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| tokenAuthSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| selfSignedCertificateKeyId? | string | The key ID of a JWK containing a self-signed certificate of this client. | +| tlsClientAuthSubjectDn? | string | The string representation of the expected subject distinguished name of the certificate this client will use in mutual TLS authentication.

See `tls_client_auth_subject_dn` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client Registration" for details. | +| tlsClientAuthSanDns? | string | The string representation of the expected DNS subject alternative name of the certificate this client will use in mutual TLS authentication.

See `tls_client_auth_san_dns` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client Registration" for details. | +| tlsClientAuthSanUri? | string | The string representation of the expected URI subject alternative name of the certificate this client will use in mutual TLS authentication.

See `tls_client_auth_san_uri` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client Registration" for details. | +| tlsClientAuthSanIp? | string | The string representation of the expected IP address subject alternative name of the certificate this client will use in mutual TLS authentication.

See `tls_client_auth_san_ip` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client Registration" for details. | +| tlsClientAuthSanEmail? | string | The string representation of the expected email address subject alternative name of the certificate this client will use in mutual TLS authentication.

See `tls_client_auth_san_email` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client Registration" for details. | +| parRequired? | boolean | The flag to indicate whether this client is required to use the pushed authorization request endpoint. This property corresponds to the `require_pushed_authorization_requests` client metadata defined in "OAuth 2.0 Pushed Authorization Requests". | +| requestObjectRequired? | boolean | The flag to indicate whether authorization requests from this client are always required to utilize a request object by using either `request` or `request_uri` request parameter.

If this flag is set to `true` and the service's `traditionalRequestObjectProcessingApplied` is set to `false`, authorization requests from this client are processed as if `require_signed_request_object` client metadata of this client is `true`. The metadata is defined in "JAR (JWT Secured Authorization Request)". | +| requestSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| requestEncryptionAlg? | [JweAlg](src/models/jwe-alg.ts) | this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this refers to key transport scheme to be used by the client and by the server. For instance:
- as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for transporting they keys on JARM objects
- as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm that server expect from client when encrypting a Request Object
- as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key transport of id_tokens

**Please note that some of the algorithms are more secure than others, some are not supported very well cross platforms and some (like RSA1_5) is known to be weak**. | +| requestEncryptionEnc? | [JweEnc](src/models/jwe-enc.ts) | This is the encryption algorithm to be used when encrypting a JWT on client or server side. Depending upon the context, this refers to encryption done by the client or by the server. For instance:
- as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server when creating a JARM response
- as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the client when encrypting a Request Object
- as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt id_tokens | +| requestUris? | string[] | An array of URLs each of which points to a request object.

Authlete requires that URLs used as values for `request_uri` request parameter be pre-registered. This property is used for the pre-registration. See [OpenID Connect Core 1.0, 6.2. Passing a Request Object by Reference](https://openid.net/specs/openid-connect-core-1_0.html#RequestUriParameter) for details. | +| defaultMaxAge? | number | The default maximum authentication age in seconds. This value is used when an authorization request from the client application does not have `max_age` request parameter.

This property corresponds to `default_max_age` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| defaultAcrs? | string[] | The default ACRs (Authentication Context Class References). This value is used when an authorization request from the client application has neither `acr_values` request parameter nor `acr` claim in claims request parameter. | +| idTokenSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| idTokenEncryptionAlg? | [JweAlg](src/models/jwe-alg.ts) | this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this refers to key transport scheme to be used by the client and by the server. For instance:
- as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for transporting they keys on JARM objects
- as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm that server expect from client when encrypting a Request Object
- as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key transport of id_tokens

**Please note that some of the algorithms are more secure than others, some are not supported very well cross platforms and some (like RSA1_5) is known to be weak**. | +| idTokenEncryptionEnc? | [JweEnc](src/models/jwe-enc.ts) | This is the encryption algorithm to be used when encrypting a JWT on client or server side. Depending upon the context, this refers to encryption done by the client or by the server. For instance:
- as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server when creating a JARM response
- as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the client when encrypting a Request Object
- as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt id_tokens | +| authTimeRequired? | boolean | The flag to indicate whether this client requires `auth_time` claim to be embedded in the ID token.

This property corresponds to `require_auth_time` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| subjectType? | [SubjectType](src/models/subject-type.ts) | The subject type that the client application requests. Details about the subject type are described in [OpenID Connect Core 1.0, 8. Subjct Identifier Types](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes).

This property corresponds to `subject_type` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| sectorIdentifierUri? | string | The value of the sector identifier URI. This represents the `sector_identifier_uri` client metadata which is defined in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata) | +| derivedSectorIdentifier? | string | The sector identifier host component as derived from either the `sector_identifier_uri` or the registered redirect URI. If no `sector_identifier_uri` is registered and multiple redirect URIs are also registered, the value of this property is `null`. | +| jwksUri? | string | The URL pointing to the JWK Set of the client application. The content pointed to by the URL is JSON which complies with the format described in [JSON Web Key (JWK), 5. JWK Set Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The JWK Set must not include private keys of the client application. | +| jwks? | string | The content of the JWK Set of the client application. The format is described in [JSON Web Key (JWK), 5. JWK Set Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The JWK Set must not include private keys of the client application. | +| userInfoSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| userInfoEncryptionAlg? | [JweAlg](src/models/jwe-alg.ts) | this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this refers to key transport scheme to be used by the client and by the server. For instance:
- as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for transporting they keys on JARM objects
- as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm that server expect from client when encrypting a Request Object
- as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key transport of id_tokens

**Please note that some of the algorithms are more secure than others, some are not supported very well cross platforms and some (like RSA1_5) is known to be weak**. | +| userInfoEncryptionEnc? | [JweEnc](src/models/jwe-enc.ts) | This is the encryption algorithm to be used when encrypting a JWT on client or server side. Depending upon the context, this refers to encryption done by the client or by the server. For instance:
- as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server when creating a JARM response
- as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the client when encrypting a Request Object
- as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt id_tokens | +| loginUri? | string | The URL which a third party can use to initiate a login by the client application.

This property corresponds to `initiate_login_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| tosUri? | string | The URL pointing to the "Terms Of Service" page.

This property corresponds to `tos_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| tosUris? | [TaggedValue](src/models/tagged-value.ts)[] | URLs of "Terms Of Service" pages with language tags.

If the client application has different "Terms Of Service" pages for different languages, this property can be used to register the URLs. | +| policyUri? | string | The URL pointing to the page which describes the policy as to how end-user's profile data is used.

This property corresponds to `policy_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| policyUris? | [TaggedValue](src/models/tagged-value.ts)[] | URLs of policy pages with language tags. If the client application has different policy pages for different languages, this property can be used to register the URLs. | +| clientUri? | string | The URL pointing to the home page of the client application.

This property corresponds to `client_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). | +| clientUris? | [TaggedValue](src/models/tagged-value.ts)[] | Home page URLs with language tags. If the client application has different home pages for different languages, this property can be used to register the URLs. | +| bcDeliveryMode? | string | The backchannel token delivery mode.

This property corresponds to the `backchannel_token_delivery_mode` metadata. The backchannel token delivery mode is defined in the specification of "CIBA (Client Initiated Backchannel Authentication)". | +| bcNotificationEndpoint? | string | The backchannel client notification endpoint.

This property corresponds to the `backchannel_client_notification_endpoint` metadata. The backchannel token delivery mode is defined in the specification of "CIBA (Client Initiated Backchannel Authentication)". | +| bcRequestSignAlg? | [JwsAlg](src/models/jws-alg.ts) | The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of JWT.

it's semantics depends upon where is this defined, for instance:
- as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm used to sign it. Check your [KB article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token).
- as client authorizationSignAlg value, it represents the signature algorithm used when [creating a JARM response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm).
- or as client requestSignAlg value, it specifies which is the expected signature used by [client on a Request Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). | +| bcUserCodeRequired? | boolean | The boolean flag to indicate whether a user code is required when this client makes a backchannel authentication request.

This property corresponds to the `backchannel_user_code_parameter` metadata. | +| backchannelLogoutUri? | string | The backchannel logout URI for this client. Used by the service to deliver logout tokens when OpenID Connect Back-Channel Logout 1.0 is triggered. | +| backchannelLogoutSessionRequired? | boolean | The flag indicating whether the client requires that a `sid` (session ID) claim be included in the logout token sent to `backchannelLogoutUri`. | +| attributes? | [Pair](src/models/pair.ts)[] | The attributes of this client. | +| extension? | [ClientExtension](src/models/client-extension.ts) | - | +| authorizationDetailsTypes? | string[] | The authorization details types that this client may use as values of the `type` field in `authorization_details`.

This property corresponds to the `authorization_details_types` metadata. See [OAuth 2.0 Rich Authorization Requests (RAR)](https://datatracker.ietf.org/doc/draft-ietf-oauth-rar/) for details.

Note that the property name was renamed from authorizationDataTypes to authorizationDetailsTypes to align with the change made by the 5th draft of the RAR specification. | +| customMetadata? | string | The custom client metadata in JSON format. | +| frontChannelRequestObjectEncryptionRequired? | boolean | The flag indicating whether encryption of request object is required when the request object is passed through the front channel. | +| requestObjectEncryptionAlgMatchRequired? | boolean | The flag indicating whether the JWE alg of encrypted request object must match the `request_object_encryption_alg` client metadata. | +| requestObjectEncryptionEncMatchRequired? | boolean | The flag indicating whether the JWE enc of encrypted request object must match the `request_object_encryption_enc` client metadata. | +| digestAlgorithm? | string | The digest algorithm that this client requests the server to use when it computes digest values of [external attachments](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#name-external-attachments), which may be referenced from within ID tokens or userinfo responses (or any place that can have the `verified_claims` claim). Possible values are listed in the [Hash Algorithm Registry](https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg) of IANA (Internet Assigned Numbers Authority), but the server does not necessarily support all the values there. When this property is omitted, `sha-256` is used as the default algorithm. This property corresponds to the `digest_algorithm` client metadata which was defined by the third implementer's draft of [OpenID Connect for Identity Assurance 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html). | +| singleAccessTokenPerSubject? | boolean | If `Enabled` is selected, an attempt to issue a new access token invalidates existing access tokens that are associated with the same combination of subject and client.

Note that, however, attempts by Client Credentials Flow do not invalidate existing access tokens because access tokens issued by Client Credentials Flow are not associated with any end-user's subject.

Even if `Disabled` is selected here, single access token per subject is effective if `singleAccessTokenPerSubject` of the `Service` this client belongs to is Enabled. | +| pkceRequired? | boolean | The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required for authorization requests by Authorization Code Flow.

If `true`, `code_challenge` request parameter is always required for authorization requests using Authorization Code Flow.

See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth Public Clients) for details about `code_challenge` request parameter. | +| pkceS256Required? | boolean | The flag to indicate whether `S256` is always required as the code challenge method whenever [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used.

If this flag is set to `true`, `code_challenge_method=S256` must be included in the authorization request whenever it includes the `code_challenge` request parameter. Neither omission of the `code_challenge_method` request parameter nor use of plain (`code_challenge_method=plain`) is allowed. | +| dpopRequired? | boolean | If the DPoP is required for this client | +| automaticallyRegistered? | boolean | The flag indicating whether this client was registered by the "automatic" client registration of OIDC Federation. | +| explicitlyRegistered? | boolean | The flag indicating whether this client was registered by the "explicit" client registration of OIDC Federation. | +| rsRequestSigned? | boolean | The flag indicating whether this service signs responses from the resource server. | +| rsSignedRequestKeyId? | string | The key ID of a JWK containing the public key used by this client to sign requests to the resource server. | +| clientRegistrationTypes? | [ClientRegistrationType](src/models/client-registration-type.ts)[] | The client registration types that the client has declared it may use. | +| organizationName? | string | The human-readable name representing the organization that manages this client. This property corresponds to the organization_name client metadata that is defined in OpenID Connect Federation 1.0. | +| signedJwksUri? | string | The URI of the endpoint that returns this client's JWK Set document in the JWT format. This property corresponds to the `signed_jwks_uri` client metadata defined in OpenID Connect Federation 1.0. | +| entityId? | string | the entity ID of this client. | +| trustAnchorId? | string | The entity ID of the trust anchor of the trust chain that was used when this client was registered or updated by the mechanism defined in OpenID Connect Federation 1.0 | +| trustChain? | string[] | The trust chain that was used when this client was registered or updated by the mechanism defined in OpenID Connect Federation 1.0 | +| trustChainExpiresAt? | number | the expiration time of the trust chain that was used when this client was registered or updated by the mechanism defined in OpenID Connect Federation 1.0. The value is represented as milliseconds elapsed since the Unix epoch (1970-01-01). | +| trustChainUpdatedAt? | number | the time at which the trust chain was updated by the mechanism defined in OpenID Connect Federation 1.0 | +| locked? | boolean | The flag which indicates whether this client is locked. | +| credentialOfferEndpoint? | string | The URL of the credential offer endpoint at which this client (wallet) receives a credential offer from the credential issuer. | +| fapiModes? | [FapiMode](src/models/fapi-mode.ts)[] | The FAPI modes for this client. | +| responseModes? | [ResponseMode](src/models/response-mode.ts)[] | The response modes that this client may use. | +| credentialResponseEncryptionRequired? | boolean | True if credential responses to this client must be always encrypted. | +| mtlsEndpointAliasesUsed? | boolean | The flag indicating whether the client intends to prefer mutual TLS endpoints over non-MTLS endpoints.

This property corresponds to the `use_mtls_endpoint_aliases` client metadata that is defined in [FAPI 2.0 Security Profile, 8.1.1. use_mtls_endpoint_aliases](https://openid.bitbucket.io/fapi/fapi-2_0-security-profile.html#section-8.1.1). | +| inScopeForTokenMigration? | boolean | The flag indicating whether this client is in scope for token migration operations. | +| metadataDocumentLocation? | string | Location of the Client ID Metadata Document that was used for this client. | +| metadataDocumentExpiresAt? | number | Expiration time of the metadata document (UNIX time in milliseconds). | +| metadataDocumentUpdatedAt? | number | Last-updated time of the metadata document (UNIX time in milliseconds). | +| discoveredByMetadataDocument? | boolean | Indicates whether this client was discovered via a Client ID Metadata Document. | +| clientSource? | [ClientSource](src/models/client-source.ts) | Source of this client record. | +| spiffeId? | string | The SPIFFE ID of the client. Used for SPIFFE-based client authentication (`SPIFFE_JWT`). Corresponds to the `spiffe_id` client metadata parameter. | +| spiffeBundleEndpoint? | string | The endpoint URL of the SPIFFE bundle for this client. Used to fetch the SPIFFE trust bundle for validating JWT-SVIDs. Corresponds to the `spiffe_bundle_endpoint` client metadata parameter. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.clientManagement.clientUpdateApi(request)` + +- **OnSuccess**: [Client](src/models/client.ts) +- **OnError**: throws [ClientManagement.ClientUpdateApiError](src/resources/client-management.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.clientManagement.clientUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<Client, ClientManagement.ClientUpdateApiError>, with `result.value` of type [Client](src/models/client.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## AuthorizationEndpoint + +> Source: [AuthorizationEndpoint](src/resources/authorization-endpoint.ts) + +
+authAuthorizationApi(request: AuthorizationEndpoint.AuthAuthorizationApiRequest, options?: RequestOptions): ApiPromise<AuthorizationResponse, AuthorizationEndpoint.AuthAuthorizationApiError> + +
+
+ +### Description + +
+
+ +This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.authorizationEndpoint.authAuthorizationApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type AuthorizationResponse +} catch (err) { + // TODO: Handle 'err' of type AuthorizationEndpoint.AuthAuthorizationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.authorizationEndpoint.authAuthorizationApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuthorizationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | OAuth 2.0 authorization request parameters which are the request parameters that the OAuth 2.0 authorization endpoint of the authorization server implementation received from the client application.

The value of parameters is either (1) the entire query string when the HTTP method of the request from the client application is `GET` or (2) the entire entity body (which is formatted in `application/x-www-form-urlencoded`) when the HTTP method of the request from the client application is `POST`. | +| context? | string | The arbitrary text to be attached to the ticket that will be issued from the `/auth/authorization` API.

The text can be retrieved later by the `/auth/authorization/ticket/info` API and can be updated by the `/auth/authorization/ticket/update` API.

The text will be compressed and encrypted when it is saved in the Authlete database. | +| cimdOptions? | [CimdOptions](src/models/cimd-options.ts) | Options for [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD).

These options allow per-request control over CIMD behavior, taking precedence over service-level configuration when provided. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.authorizationEndpoint.authAuthorizationApi(request)` + +- **OnSuccess**: [AuthorizationResponse](src/models/authorization-response.ts) +- **OnError**: throws [AuthorizationEndpoint.AuthAuthorizationApiError](src/resources/authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.authorizationEndpoint.authAuthorizationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuthorizationResponse, AuthorizationEndpoint.AuthAuthorizationApiError>, with `result.value` of type [AuthorizationResponse](src/models/authorization-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authAuthorizationFailApi(request: AuthorizationEndpoint.AuthAuthorizationFailApiRequest, options?: RequestOptions): ApiPromise<AuthorizationFailResponse, AuthorizationEndpoint.AuthAuthorizationFailApiError> + +
+
+ +### Description + +
+
+ +This API generates a content of an error authorization response that the authorization server implementation returns to the client application. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.authorizationEndpoint.authAuthorizationFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason.Unknown, + }); + // TODO: Handle 'response' of type AuthorizationFailResponse +} catch (err) { + // TODO: Handle 'err' of type AuthorizationEndpoint.AuthAuthorizationFailApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.authorizationEndpoint.authAuthorizationFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason.Unknown, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuthorizationFailResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued from Authlete `/auth/authorization` API. | +| reason | [Reason](src/models/reason.ts) | The reason of the failure of the authorization request. For more details, see [NO_INTERACTION] in the description of `/auth/authorization` API. | +| description? | string | The custom description about the authorization failure. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.authorizationEndpoint.authAuthorizationFailApi(request)` + +- **OnSuccess**: [AuthorizationFailResponse](src/models/authorization-fail-response.ts) +- **OnError**: throws [AuthorizationEndpoint.AuthAuthorizationFailApiError](src/resources/authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.authorizationEndpoint.authAuthorizationFailApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuthorizationFailResponse, AuthorizationEndpoint.AuthAuthorizationFailApiError>, with `result.value` of type [AuthorizationFailResponse](src/models/authorization-fail-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authAuthorizationIssueApi(request: AuthorizationEndpoint.AuthAuthorizationIssueApiRequest, options?: RequestOptions): ApiPromise<AuthorizationIssueResponse, AuthorizationEndpoint.AuthAuthorizationIssueApiError> + +
+
+ +### Description + +
+
+ +This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.authorizationEndpoint.authAuthorizationIssueApi({ + serviceId: "some example string", + ticket: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type AuthorizationIssueResponse +} catch (err) { + // TODO: Handle 'err' of type AuthorizationEndpoint.AuthAuthorizationIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.authorizationEndpoint.authAuthorizationIssueApi({ + serviceId: "some example string", + ticket: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuthorizationIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued from Authlete `/auth/authorization` API. | +| subject | string | The subject (= a user account managed by the service) who has granted authorization to the client application. | +| authTime? | number | The time when the authentication of the end-user occurred. Its value is the number of seconds from `1970-01-01`. | +| acr? | string | The Authentication Context Class Reference performed for the end-user authentication. | +| claims? | string | The claims of the end-user (= pieces of information about the end-user) in JSON format. See [OpenID Connect Core 1.0, 5.1. Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) for details about the format. | +| properties? | [Property](src/models/property.ts)[] | Extra properties to associate with an access token and/or an authorization code. | +| scopes? | string[] | Scopes to associate with an access token and/or an authorization code. If a non-empty string array is given, it replaces the scopes specified by the original authorization request. | +| sub? | string | The value of the `sub` claim to embed in an ID token. If this request parameter is `null` or empty, the value of the `subject` request parameter is used as the value of the `sub` claim. | +| idtHeaderParams? | string | JSON that represents additional JWS header parameters for ID tokens that may be issued based on the authorization request. | +| claimsForTx? | string | Claim key-value pairs that are used to compute transformed claims. | +| consentedClaims? | string[] | the claims that the user has consented for the client application to know. | +| authorizationDetails? | [AuthzDetails](src/models/authz-details.ts) | The authorization details. This represents the value of the `authorization_details` request parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich Authorization Requests". | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| accessToken? | string | The representation of an access token that may be issued as a result of the Authlete API call. | +| accessTokenDuration? | number | The duration (in seconds) of the access token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the access token in. In other cases, this request parameter is ignored. | +| sessionId? | string | The session ID of the user's authentication session. The specified value will be embedded in the ID token as the value of the `sid` claim. This parameter needs to be provided only if you want to support the [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (a.k.a. "Native SSO"). To enable support for the Native SSO specification, the `nativeSsoSupported` property of your service must be set to `true`. | +| idTokenAudType? | string | The type of the `aud` claim of the ID token being issued. Valid values are as follows.

\| Value \| Description \|
\| ----- \| ----------- \|
\| "array" \| The type of the aud claim is always an array of strings. \|
\| "string" \| The type of the aud claim is always a single string. \|
\| null \| The type of the aud claim remains the same as before. \|

This request parameter takes precedence over the `idTokenAudType` property of the service. | +| verifiedClaimsForTx? | string[] | Values of verified claims requested indirectly by "transformed claims". | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.authorizationEndpoint.authAuthorizationIssueApi(request)` + +- **OnSuccess**: [AuthorizationIssueResponse](src/models/authorization-issue-response.ts) +- **OnError**: throws [AuthorizationEndpoint.AuthAuthorizationIssueApiError](src/resources/authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.authorizationEndpoint.authAuthorizationIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuthorizationIssueResponse, AuthorizationEndpoint.AuthAuthorizationIssueApiError>, with `result.value` of type [AuthorizationIssueResponse](src/models/authorization-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+getAuthorizationTicketInfo(request: AuthorizationEndpoint.GetAuthorizationTicketInfoRequest, options?: RequestOptions): ApiPromise<AuthorizationTicketInfoResponse, AuthorizationEndpoint.GetAuthorizationTicketInfoError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.authorizationEndpoint.getAuthorizationTicketInfo({ + serviceId: "some example string", + ticket: "some example string", + }); + // TODO: Handle 'response' of type AuthorizationTicketInfoResponse +} catch (err) { + // TODO: Handle 'err' of type AuthorizationEndpoint.GetAuthorizationTicketInfoError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.authorizationEndpoint.getAuthorizationTicketInfo({ + serviceId: "some example string", + ticket: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuthorizationTicketInfoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket that has been issued from the `/auth/authorization` API. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.authorizationEndpoint.getAuthorizationTicketInfo(request)` + +- **OnSuccess**: [AuthorizationTicketInfoResponse](src/models/authorization-ticket-info-response.ts) +- **OnError**: throws [AuthorizationEndpoint.GetAuthorizationTicketInfoError](src/resources/authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.authorizationEndpoint.getAuthorizationTicketInfo(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuthorizationTicketInfoResponse, AuthorizationEndpoint.GetAuthorizationTicketInfoError>, with `result.value` of type [AuthorizationTicketInfoResponse](src/models/authorization-ticket-info-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+updateAuthorizationTicket(request: AuthorizationEndpoint.UpdateAuthorizationTicketRequest, options?: RequestOptions): ApiPromise<AuthorizationTicketUpdateResponse, AuthorizationEndpoint.UpdateAuthorizationTicketError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.authorizationEndpoint.updateAuthorizationTicket({ + serviceId: "some example string", + ticket: "some example string", + info: {}, + }); + // TODO: Handle 'response' of type AuthorizationTicketUpdateResponse +} catch (err) { + // TODO: Handle 'err' of type AuthorizationEndpoint.UpdateAuthorizationTicketError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.authorizationEndpoint.updateAuthorizationTicket({ + serviceId: "some example string", + ticket: "some example string", + info: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuthorizationTicketUpdateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket. | +| info | [AuthorizationTicketInfo](src/models/authorization-ticket-info.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.authorizationEndpoint.updateAuthorizationTicket(request)` + +- **OnSuccess**: [AuthorizationTicketUpdateResponse](src/models/authorization-ticket-update-response.ts) +- **OnError**: throws [AuthorizationEndpoint.UpdateAuthorizationTicketError](src/resources/authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.authorizationEndpoint.updateAuthorizationTicket(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuthorizationTicketUpdateResponse, AuthorizationEndpoint.UpdateAuthorizationTicketError>, with `result.value` of type [AuthorizationTicketUpdateResponse](src/models/authorization-ticket-update-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## PushedAuthorizationEndpoint + +> Source: [PushedAuthorizationEndpoint](src/resources/pushed-authorization-endpoint.ts) + +
+pushedAuthReqApi(request: PushedAuthorizationEndpoint.PushedAuthReqApiRequest, options?: RequestOptions): ApiPromise<PushedAuthorizationResponse, PushedAuthorizationEndpoint.PushedAuthReqApiError> + +
+
+ +### Description + +
+
+ +This API creates a pushed request authorization. It authenticates the client and creates a authorization_uri to be returned by the authorization server. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.pushedAuthorizationEndpoint.pushedAuthReqApi({ + serviceId: "some example string", + parameters: + "response_type=code%20id_token&client_id=5921531358155430&redirect_uri=https%3A%2F%2Fserver.example.com%2Fcb&state=SOME_VALUE_ABLE_TO_PREVENT_CSRF&scope=openid&nonce=SOME_VALUE_ABLE_TO_PREVENT_REPLAY_ATTACK&code_challenge=5ZWDQJiryK3eaLtSeFV8y1XySMCWtyITxICLaTwvK8g&code_challenge_method=S256", + clientId: "5921531358155430", + clientSecret: "P_FouxWlI7zcOep_9vBwR9qMAVJQiCiUiK1HrAP4GziOyezHQpqY0f5dHXK4JT4tnvI51OkbWVoEM9GnOyJViA", + }); + // TODO: Handle 'response' of type PushedAuthorizationResponse +} catch (err) { + // TODO: Handle 'err' of type PushedAuthorizationEndpoint.PushedAuthReqApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.pushedAuthorizationEndpoint.pushedAuthReqApi({ + serviceId: "some example string", + parameters: + "response_type=code%20id_token&client_id=5921531358155430&redirect_uri=https%3A%2F%2Fserver.example.com%2Fcb&state=SOME_VALUE_ABLE_TO_PREVENT_CSRF&scope=openid&nonce=SOME_VALUE_ABLE_TO_PREVENT_REPLAY_ATTACK&code_challenge=5ZWDQJiryK3eaLtSeFV8y1XySMCWtyITxICLaTwvK8g&code_challenge_method=S256", + clientId: "5921531358155430", + clientSecret: "P_FouxWlI7zcOep_9vBwR9qMAVJQiCiUiK1HrAP4GziOyezHQpqY0f5dHXK4JT4tnvI51OkbWVoEM9GnOyJViA", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type PushedAuthorizationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | The pushed authorization request body received from the client application.

The value of parameters is the entire entity body (which is formatted in `application/x-www-form-urlencoded`) of the request from the client application. | +| clientId? | string | The client ID extracted from `Authorization` header of the pushed request from the client application. | +| clientSecret? | string | The client secret extracted from `Authorization` header of the pushed authorization request from the client application. | +| clientCertificate? | string | The client certificate from the MTLS connection to pushed authorization endpoint from the client application. | +| clientCertificatePath? | string[] | The certificate path presented by the client during client authentication. These certificates are strings in PEM format. | +| dpop? | string | DPoP Header | +| htm? | string | HTTP Method (for DPoP validation). | +| htu? | string | HTTP URL base (for DPoP validation). | +| oauthClientAttestation? | string | The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| oauthClientAttestationPop? | string | The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| dpopNonceRequired? | boolean | The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even if the service's `dpopNonceRequired` property is `false`, calling the `/pushed_auth_req` API with this `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the DPoP proof JWT includes the expected `nonce` value. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.pushedAuthorizationEndpoint.pushedAuthReqApi(request)` + +- **OnSuccess**: [PushedAuthorizationResponse](src/models/pushed-authorization-response.ts) +- **OnError**: throws [PushedAuthorizationEndpoint.PushedAuthReqApiError](src/resources/pushed-authorization-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.pushedAuthorizationEndpoint.pushedAuthReqApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<PushedAuthorizationResponse, PushedAuthorizationEndpoint.PushedAuthReqApiError>, with `result.value` of type [PushedAuthorizationResponse](src/models/pushed-authorization-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## TokenEndpoint + +> Source: [TokenEndpoint](src/resources/token-endpoint.ts) + +
+authTokenApi(request: TokenEndpoint.AuthTokenApiRequest, options?: RequestOptions): ApiPromise<TokenResponse, TokenEndpoint.AuthTokenApiError> + +
+
+ +### Description + +
+
+ +This API parses request parameters of an authorization request and returns necessary data for the authorization server implementation to process the authorization request further. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenEndpoint.authTokenApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type TokenResponse +} catch (err) { + // TODO: Handle 'err' of type TokenEndpoint.AuthTokenApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenEndpoint.authTokenApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | OAuth 2.0 token request parameters which are the request parameters that the OAuth 2.0 token endpoint of the authorization server implementation received from the client application.

The value of parameters is the entire entity body (which is formatted in `application/x-www-form-urlencoded`) of the request from the client application. | +| clientId? | string | The client ID extracted from `Authorization` header of the token request from the client application.

If the token endpoint of the authorization server implementation supports basic authentication as a means of client authentication, and the request from the client application contained its client ID in `Authorization` header, the value should be extracted and set to this parameter. | +| clientSecret? | string | The client secret extracted from `Authorization` header of the token request from the client application.

If the token endpoint of the authorization server implementation supports basic authentication as a means of client authentication, and the request from the client application contained its client secret in `Authorization` header, the value should be extracted and set to this parameter. | +| clientCertificate? | string | The client certificate from the MTLS of the token request from the client application. | +| clientCertificatePath? | string[] | The certificate path presented by the client during client authentication. These certificates are strings in PEM format. | +| properties? | [Property](src/models/property.ts)[] | Extra properties to associate with an access token. See [Extra Properties](https://www.authlete.com/developers/definitive_guide/extra_properties/) for details. | +| dpop? | string | `DPoP` header presented by the client during the request to the token endpoint.

The header contains a signed JWT which includes the public key that is paired with the private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| htm? | string | HTTP method of the token request. This field is used to validate the `DPoP` header.

In normal cases, the value is `POST`. When this parameter is omitted, `POST` is used as the default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| htu? | string | URL of the token endpoint. This field is used to validate the `DPoP` header.

If this parameter is omitted, the `tokenEndpoint` property of the Service is used as the default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| accessToken? | string | The representation of an access token that may be issued as a result of the Authlete API call. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token.

Effective only in the client_credentials grant flow (and the Native SSO branch of Token Exchange). For other grant flows, set additional JWT claims via the corresponding API: /auth/authorization/issue (authorization_code), /auth/backchannel/authentication/complete (CIBA), /auth/device/complete (device_code). The parameter is silently ignored when sent with other grants. | +| oauthClientAttestation? | string | The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| oauthClientAttestationPop? | string | The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| accessTokenDuration? | number | The duration (in seconds) of the access token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the access token in. In other cases, this request parameter is ignored. | +| refreshTokenDuration? | number | The duration (in seconds) of the refresh token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the refresh token in. In other cases, this request parameter is ignored. | +| dpopNonceRequired? | boolean | The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even if the service's `dpopNonceRequired` property is `false`, calling the `/auth/token` API with this `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the DPoP proof JWT includes the expected `nonce` value. | +| cimdOptions? | [CimdOptions](src/models/cimd-options.ts) | Options for [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD).

These options allow per-request control over CIMD behavior, taking precedence over service-level configuration when provided. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenEndpoint.authTokenApi(request)` + +- **OnSuccess**: [TokenResponse](src/models/token-response.ts) +- **OnError**: throws [TokenEndpoint.AuthTokenApiError](src/resources/token-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenEndpoint.authTokenApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenResponse, TokenEndpoint.AuthTokenApiError>, with `result.value` of type [TokenResponse](src/models/token-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenFailApi(request: TokenEndpoint.AuthTokenFailApiRequest, options?: RequestOptions): ApiPromise<TokenFailResponse, TokenEndpoint.AuthTokenFailApiError> + +
+
+ +### Description + +
+
+ +This API generates a content of an error token response that the authorization server implementation returns to the client application. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenEndpoint.authTokenFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason1.Unknown, + }); + // TODO: Handle 'response' of type TokenFailResponse +} catch (err) { + // TODO: Handle 'err' of type TokenEndpoint.AuthTokenFailApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenEndpoint.authTokenFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason1.Unknown, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenFailResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued from Authlete `/auth/token` API. | +| reason | [Reason1](src/models/reason1.ts) | The reason of the failure of the token request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenEndpoint.authTokenFailApi(request)` + +- **OnSuccess**: [TokenFailResponse](src/models/token-fail-response.ts) +- **OnError**: throws [TokenEndpoint.AuthTokenFailApiError](src/resources/token-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenEndpoint.authTokenFailApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenFailResponse, TokenEndpoint.AuthTokenFailApiError>, with `result.value` of type [TokenFailResponse](src/models/token-fail-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenIssueApi(request: TokenEndpoint.AuthTokenIssueApiRequest, options?: RequestOptions): ApiPromise<TokenIssueResponse, TokenEndpoint.AuthTokenIssueApiError> + +
+
+ +### Description + +
+
+ +This API generates a content of a successful token response that the authorization server implementation returns to the client application. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenEndpoint.authTokenIssueApi({ + serviceId: "some example string", + ticket: "some example string", + subject: "some example string", + }); + // TODO: Handle 'response' of type TokenIssueResponse +} catch (err) { + // TODO: Handle 'err' of type TokenEndpoint.AuthTokenIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenEndpoint.authTokenIssueApi({ + serviceId: "some example string", + ticket: "some example string", + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued from Authlete `/auth/token` API. | +| subject | string | The subject (= unique identifier) of the authenticated user. | +| properties? | [Property](src/models/property.ts)[] | Extra properties to associate with a newly created access token. Note that properties parameter is accepted only when `Content-Type` of the request is `application/json`, so don't use `application/x-www-form-urlencoded` if you want to specify properties. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| accessToken? | string | The representation of an access token that may be issued as a result of the Authlete API call. | +| accessTokenDuration? | number | The duration (in seconds) of the access token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the access token in. In other cases, this request parameter is ignored. | +| refreshTokenDuration? | number | The duration (in seconds) of the refresh token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the refresh token in. In other cases, this request parameter is ignored. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenEndpoint.authTokenIssueApi(request)` + +- **OnSuccess**: [TokenIssueResponse](src/models/token-issue-response.ts) +- **OnError**: throws [TokenEndpoint.AuthTokenIssueApiError](src/resources/token-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenEndpoint.authTokenIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenIssueResponse, TokenEndpoint.AuthTokenIssueApiError>, with `result.value` of type [TokenIssueResponse](src/models/token-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+idtokenReissueApi(request: TokenEndpoint.IdtokenReissueApiRequest, options?: RequestOptions): ApiPromise<IdtokenReissueResponse, TokenEndpoint.IdtokenReissueApiError> + +
+
+ +### Description + +
+
+ +The API is expected to be called only when the value of the `action` parameter in a response from the `/auth/token` API is [ID_TOKEN_REISSUABLE](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.Action.html#ID_TOKEN_REISSUABLE). The purpose of the `/idtoken/reissue` API is to generate a token response that includes a new ID token together with a new access token and a refresh token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenEndpoint.idtokenReissueApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type IdtokenReissueResponse +} catch (err) { + // TODO: Handle 'err' of type TokenEndpoint.IdtokenReissueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenEndpoint.idtokenReissueApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type IdtokenReissueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body? | [IdtokenReissueRequest](src/models/idtoken-reissue-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenEndpoint.idtokenReissueApi(request)` + +- **OnSuccess**: [IdtokenReissueResponse](src/models/idtoken-reissue-response.ts) +- **OnError**: throws [TokenEndpoint.IdtokenReissueApiError](src/resources/token-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenEndpoint.idtokenReissueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<IdtokenReissueResponse, TokenEndpoint.IdtokenReissueApiError>, with `result.value` of type [IdtokenReissueResponse](src/models/idtoken-reissue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## TokenOperations + +> Source: [TokenOperations](src/resources/token-operations.ts) + +
+authTokenCreateApi(request: TokenOperations.AuthTokenCreateApiRequest, options?: RequestOptions): ApiPromise<TokenCreateResponse, TokenOperations.AuthTokenCreateApiError> + +
+
+ +### Description + +
+
+ +Create an access token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenOperations.authTokenCreateApi({ + serviceId: "some example string", + grantType: GrantType.AuthorizationCode, + }); + // TODO: Handle 'response' of type TokenCreateResponse +} catch (err) { + // TODO: Handle 'err' of type TokenOperations.AuthTokenCreateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenOperations.authTokenCreateApi({ + serviceId: "some example string", + grantType: GrantType.AuthorizationCode, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenCreateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| grantType | [GrantType](src/models/grant-type.ts) | The grant type of the access token when the access token was created. | +| clientId? | number | The ID of the client application which will be associated with a newly created access token. | +| subject? | string | The subject (= unique identifier) of the user who will be associated with a newly created access token. This parameter is required unless the grant type is `CLIENT_CREDENTIALS`. The value must consist of only ASCII characters and its length must not exceed 100. | +| scopes? | string[] | The scopes which will be associated with a newly created access token. Scopes that are not supported by the service cannot be specified and requesting them will cause an error. | +| accessTokenDuration? | number | The duration of a newly created access token in seconds. If the value is 0, the duration is determined according to the settings of the service. | +| refreshTokenDuration? | number | The duration of a newly created refresh token in seconds. If the value is 0, the duration is determined according to the settings of the service.

A refresh token is not created (1) if the service does not support `REFRESH_TOKEN`, or (2) if the specified grant type is either `IMPLICIT`or `CLIENT_CREDENTIALS`. | +| properties? | [Property](src/models/property.ts)[] | Extra properties to associate with a newly created access token. Note that properties parameter is accepted only when the HTTP method of the request is POST and Content-Type of the request is `application/json`, so don't use `GET` method or `application/x-www-form-urlencoded` if you want to specify properties. | +| clientIdAliasUsed? | boolean | A boolean request parameter which indicates whether to emulate that the client ID alias is used instead of the original numeric client ID when a new access token is created. | +| accessToken? | string | The value of the new access token. | +| refreshToken? | string | The value of the new refresh token. | +| accessTokenPersistent? | boolean | Get whether the access token expires or not. By default, all access tokens expire after a period of time determined by their service.

If this request parameter is `true`, then the access token will not automatically expire and must be revoked or deleted manually at the service. If this request parameter is true, the `accessTokenDuration` request parameter is ignored. | +| certificateThumbprint? | string | The thumbprint of the MTLS certificate bound to this token. If this property is set, a certificate with the corresponding value MUST be presented with the access token when it is used by a client. The value of this property must be a SHA256 certificate thumbprint, base64url encoded. | +| dpopKeyThumbprint? | string | The thumbprint of the public key used for DPoP presentation of this token. If this property is set, a DPoP proof signed with the corresponding private key MUST be presented with the access token when it is used by a client. Additionally, the token's `token_type` will be set to 'DPoP'. | +| authorizationDetails? | [AuthzDetails](src/models/authz-details.ts) | The authorization details. This represents the value of the `authorization_details` request parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich Authorization Requests". | +| resources? | string[] | The value of the resources to associate with the token. This property represents the value of one or more `resource` request parameters which is defined in "RFC8707 Resource Indicators for OAuth 2.0". | +| forExternalAttachment? | boolean | the flag which indicates whether the access token is for an external attachment. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| acr? | string | The Authentication Context Class Reference of the user authentication that the authorization server performed during the course of issuing the access token. | +| authTime? | number | The time when the user authentication was performed during the course of issuing the access token. | +| clientEntityIdUsed? | boolean | Flag which indicates whether the entity ID of the client was used when the request for the access token was made. | +| clientIdentifier? | string | The client Identifier associated with the newly issued access token. | +| sessionId? | string | The session ID, which is the ID of the user's authentication session, associated with a newly created access token. | +| metadataDocumentUsed? | boolean | Flag indicating whether a metadata document was used to resolve client metadata for this request.

When `true`, the client metadata was retrieved via the [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD) mechanism rather than from the Authlete database. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenOperations.authTokenCreateApi(request)` + +- **OnSuccess**: [TokenCreateResponse](src/models/token-create-response.ts) +- **OnError**: throws [TokenOperations.AuthTokenCreateApiError](src/resources/token-operations.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenOperations.authTokenCreateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenCreateResponse, TokenOperations.AuthTokenCreateApiError>, with `result.value` of type [TokenCreateResponse](src/models/token-create-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenDeleteApi(request: TokenOperations.AuthTokenDeleteApiRequest, options?: RequestOptions): ApiPromise<undefined, TokenOperations.AuthTokenDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete an access token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.tokenOperations.authTokenDeleteApi({ + serviceId: "some example string", + accessTokenIdentifier: "some example string", + }); +} catch (err) { + // TODO: Handle 'err' of type TokenOperations.AuthTokenDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenOperations.authTokenDeleteApi({ + serviceId: "some example string", + accessTokenIdentifier: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessTokenIdentifier | string | The identifier of an existing access token. The identifier is the value of the access token or the value of the hash of the access token. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenOperations.authTokenDeleteApi(request)` + +- **OnSuccess**: undefined +- **OnError**: throws [TokenOperations.AuthTokenDeleteApiError](src/resources/token-operations.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenOperations.authTokenDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, TokenOperations.AuthTokenDeleteApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenGetListApi(request: TokenOperations.AuthTokenGetListApiRequest, options?: RequestOptions): ApiPromise<TokenGetListResponse, TokenOperations.AuthTokenGetListApiError> + +
+
+ +### Description + +
+
+ +Get the list of access tokens that are associated with the service. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenOperations.authTokenGetListApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type TokenGetListResponse +} catch (err) { + // TODO: Handle 'err' of type TokenOperations.AuthTokenGetListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenOperations.authTokenGetListApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| clientIdentifier? | string | Client Identifier (client ID or client ID alias). | +| subject? | string | Unique user ID. | +| start? | number | Start index of search results (inclusive). The default value is 0. | +| end? | number | End index of search results (exclusive). The default value is 5. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenOperations.authTokenGetListApi(request)` + +- **OnSuccess**: [TokenGetListResponse](src/models/token-get-list-response.ts) +- **OnError**: throws [TokenOperations.AuthTokenGetListApiError](src/resources/token-operations.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenOperations.authTokenGetListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenGetListResponse, TokenOperations.AuthTokenGetListApiError>, with `result.value` of type [TokenGetListResponse](src/models/token-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenRevokeApi(request: TokenOperations.AuthTokenRevokeApiRequest, options?: RequestOptions): ApiPromise<TokenRevokeResponse, TokenOperations.AuthTokenRevokeApiError> + +
+
+ +### Description + +
+
+ +Revoke an access token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenOperations.authTokenRevokeApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type TokenRevokeResponse +} catch (err) { + // TODO: Handle 'err' of type TokenOperations.AuthTokenRevokeApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenOperations.authTokenRevokeApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenRevokeResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessTokenIdentifier? | string | The identifier of an access token to revoke

The hash of an access token is recognized as an identifier as well as the access token itself. | +| refreshTokenIdentifier? | string | The identifier of a refresh token to revoke.

The hash of a refresh token is recognized as an identifier as well as the refresh token itself. | +| clientIdentifier? | string | The client ID of the access token to be revoked.

Both the numeric client ID and the alias are recognized as an identifier of a client.

Bulk revocation with `clientIdentifier` only or `clientIdentifier` + `subject` deletes at most **20 tokens per request** (the default of `token.revoke.count.max` in `ServerConfiguration.java`). If the target has more than 20 tokens, the response `count` will be 20 and the remainder is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0. | +| subject? | string | The subject of a resource owner.

Bulk revocation with `clientIdentifier` + `subject` or `subject` only deletes at most **20 tokens per request** (the default of `token.revoke.count.max` in `ServerConfiguration.java`). If the target has more than 20 tokens, the response `count` will be 20 and the remainder is left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenOperations.authTokenRevokeApi(request)` + +- **OnSuccess**: [TokenRevokeResponse](src/models/token-revoke-response.ts) +- **OnError**: throws [TokenOperations.AuthTokenRevokeApiError](src/resources/token-operations.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenOperations.authTokenRevokeApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenRevokeResponse, TokenOperations.AuthTokenRevokeApiError>, with `result.value` of type [TokenRevokeResponse](src/models/token-revoke-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authTokenUpdateApi(request: TokenOperations.AuthTokenUpdateApiRequest, options?: RequestOptions): ApiPromise<TokenUpdateResponse, TokenOperations.AuthTokenUpdateApiError> + +
+
+ +### Description + +
+
+ +Update an access token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tokenOperations.authTokenUpdateApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type TokenUpdateResponse +} catch (err) { + // TODO: Handle 'err' of type TokenOperations.AuthTokenUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tokenOperations.authTokenUpdateApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type TokenUpdateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessToken? | string | An access token. | +| accessTokenExpiresAt? | number | A new date at which the access token will expire in milliseconds since the Unix epoch (1970-01-01). If the `accessTokenExpiresAt` request parameter is not included in a request or its value is 0 (or negative), the expiration date of the access token is not changed. | +| scopes? | string[] | A new set of scopes assigned to the access token. Scopes that are not supported by the service and those that the client application associated with the access token is not allowed to request are ignored on the server side. If the `scopes` request parameter is not included in a request or its value is `null`, the scopes of the access token are not changed. Note that `properties` parameter is accepted only when `Content-Type` of the request is `application/json`, so don't use `application/x-www-form-urlencoded` if you want to specify `properties`. | +| properties? | [Property](src/models/property.ts)[] | A new set of properties assigned to the access token. If the `properties` request parameter is not included in a request or its value is null, the properties of the access token are not changed. | +| accessTokenExpiresAtUpdatedOnScopeUpdate? | boolean | A boolean request parameter which indicates whether the API attempts to update the expiration date of the access token when the scopes linked to the access token are changed by this request. | +| accessTokenHash? | string | The hash of the access token value. Used when the hash of the token is known (perhaps from lookup) but the value of the token itself is not. The value of the `accessToken` parameter takes precedence. | +| accessTokenValueUpdated? | boolean | A boolean request parameter which indicates whether to update the value of the access token in the data store. If this parameter is set to `true` then a new access token value is generated by the server and returned in the response. | +| accessTokenPersistent? | boolean | The flag which indicates whether the access token expires or not. By default, all access tokens expire after a period of time determined by their service. If this request parameter is `true` then the access token will not automatically expire and must be revoked or deleted manually at the service.

If this request parameter is `true`, the `accessTokenExpiresAt` request parameter is ignored. If this request parameter is `false`, the `accessTokenExpiresAt` request parameter is processed normally. | +| certificateThumbprint? | string | The thumbprint of the MTLS certificate bound to this token. If this property is set, a certificate with the corresponding value MUST be presented with the access token when it is used by a client. The value of this property must be a SHA256 certificate thumbprint, base64url encoded. | +| dpopKeyThumbprint? | string | The thumbprint of the public key used for DPoP presentation of this token. If this property is set, a DPoP proof signed with the corresponding private key MUST be presented with the access token when it is used by a client. Additionally, the token's `token_type` will be set to 'DPoP'. | +| authorizationDetails? | [AuthzDetails](src/models/authz-details.ts) | The authorization details. This represents the value of the `authorization_details` request parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich Authorization Requests". | +| forExternalAttachment? | boolean | the flag which indicates whether the access token is for an external attachment. | +| refreshTokenExpiresAt? | number | A new date at which the access token will expire in milliseconds since the Unix epoch (1970-01-01). If the `refreshTokenExpiresAt` request parameter is not included in a request or its value is 0 (or negative), the expiration date of the refresh token is not changed. | +| refreshTokenExpiresAtUpdatedOnScopeUpdate? | boolean | A boolean request parameter which indicates whether the API attempts to update the expiration date of the refresh token when the scopes linked to the refresh token are changed by this request. | +| tokenId? | string | The token identifier. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tokenOperations.authTokenUpdateApi(request)` + +- **OnSuccess**: [TokenUpdateResponse](src/models/token-update-response.ts) +- **OnError**: throws [TokenOperations.AuthTokenUpdateApiError](src/resources/token-operations.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tokenOperations.authTokenUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<TokenUpdateResponse, TokenOperations.AuthTokenUpdateApiError>, with `result.value` of type [TokenUpdateResponse](src/models/token-update-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## IntrospectionEndpoint + +> Source: [IntrospectionEndpoint](src/resources/introspection-endpoint.ts) + +
+authIntrospectionApi(request: IntrospectionEndpoint.AuthIntrospectionApiRequest, options?: RequestOptions): ApiPromise<IntrospectionResponse, IntrospectionEndpoint.AuthIntrospectionApiError> + +
+
+ +### Description + +
+
+ +This API gathers information about an access token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.introspectionEndpoint.authIntrospectionApi({ + serviceId: "some example string", + token: "some example string", + }); + // TODO: Handle 'response' of type IntrospectionResponse +} catch (err) { + // TODO: Handle 'err' of type IntrospectionEndpoint.AuthIntrospectionApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.introspectionEndpoint.authIntrospectionApi({ + serviceId: "some example string", + token: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type IntrospectionResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| token | string | An access token to introspect. | +| scopes? | string[] | A string array listing names of scopes which the caller (= a protected resource endpoint of the service) requires. When the content type of the request from the service is `application/x-www-form-urlencoded`, the format of `scopes` is a space-separated list of scope names.

If this parameter is a non-empty array and if it contains a scope which is not covered by the access token,`action=FORBIDDEN` with `error=insufficient_scope` is returned from Authlete. | +| subject? | string | A subject (= a user account managed by the service) whom the caller (= a protected resource endpoint of the service) requires.

If this parameter is not `null` and if the value does not match the subject who is associated with the access token, `action=FORBIDDEN` with `error=invalid_request` is returned from Authlete. | +| clientCertificate? | string | Client certificate in PEM format, used to validate binding against access tokens using the TLS client certificate confirmation method. | +| dpop? | string | `DPoP` header presented by the client during the request to the resource server.

The header contains a signed JWT which includes the public key that is paired with the private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| htm? | string | HTTP method of the request from the client to the protected resource endpoint. This field is used to validate the `DPoP` header.

See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| htu? | string | URL of the protected resource endpoint. This field is used to validate the `DPoP` header.

See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| resources? | string[] | The resources specified by the `resource` request parameters in the token request. See "Resource Indicators for OAuth 2.0" for details. | +| acrValues? | string[] | Authentication Context Class Reference values one of which the user authentication performed during the course of issuing the access token must satisfy. | +| maxAge? | number | The maximum authentication age which is the maximum allowable elapsed time since the user authentication was performed during the course of issuing the access token. | +| requiredComponents? | string[] | HTTP Message Components required to be in the signature. If absent, defaults to [ "@method", "@target-uri", "authorization" ]. | +| uri? | string | The full URL of the userinfo endpoint. | +| message? | string | The HTTP message body of the request, if present. | +| headers? | [Pair](src/models/pair.ts)[] | HTTP headers to be included in processing the signature. If this is a signed request, this must include the Signature and Signature-Input headers, as well as any additional headers covered by the signature. | +| targetUri? | string | The target URI of the resource request, including the query part, if any. | +| dpopNonceRequired? | boolean | The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` value.

If this request parameter is set to `true` or if the service's `dpopNonceRequired` property is set to `true`, the `/auth/introspection` API checks if the DPoP proof JWT includes the expected `nonce` value. In this case, the response from the `/auth/introspection` API will include the `dpopNonce` response parameter, which should be used as the value of the DPoP-Nonce HTTP header. | +| requestBodyContained? | boolean | The flag indicating whether the resource request contains a request body.

When the resource request must comply with the HTTP message signing requirements defined in the FAPI 2.0 Message Signing specification, the `"content-digest"` component identifier must be included in the signature base of the HTTP message signature (see [RFC 9421 HTTP Message Signatures](https://www.rfc-editor.org/rfc/rfc9421.html)) if the resource request contains a request body.

When this `requestBodyContained` parameter is set to `true`, Authlete checks whether `"content-digest"` is included in the signature base, if the FAPI profile applies to the resource request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.introspectionEndpoint.authIntrospectionApi(request)` + +- **OnSuccess**: [IntrospectionResponse](src/models/introspection-response.ts) +- **OnError**: throws [IntrospectionEndpoint.AuthIntrospectionApiError](src/resources/introspection-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.introspectionEndpoint.authIntrospectionApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<IntrospectionResponse, IntrospectionEndpoint.AuthIntrospectionApiError>, with `result.value` of type [IntrospectionResponse](src/models/introspection-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authIntrospectionStandardApi(request: IntrospectionEndpoint.AuthIntrospectionStandardApiRequest, options?: RequestOptions): ApiPromise<StandardIntrospectionResponse, IntrospectionEndpoint.AuthIntrospectionStandardApiError> + +
+
+ +### Description + +
+
+ +This API exists to help your authorization server provide its own introspection API which complies with [RFC 7662](https://tools.ietf.org/html/rfc7662) (OAuth 2.0 Token Introspection). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.introspectionEndpoint.authIntrospectionStandardApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type StandardIntrospectionResponse +} catch (err) { + // TODO: Handle 'err' of type IntrospectionEndpoint.AuthIntrospectionStandardApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.introspectionEndpoint.authIntrospectionStandardApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type StandardIntrospectionResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | Request parameters which comply with the introspection request defined in "[2.1. Introspection Request](https://datatracker.ietf.org/doc/html/rfc7662#section-2.1)" in RFC 7662.

The implementation of the introspection endpoint of your authorization server will receive an HTTP POST [[RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231)] request with parameters in the `application/x-www-form-urlencoded` format. It is the entity body of the request that Authlete's `/api/auth/introspection/standard` API expects as the value of `parameters`. | +| withHiddenProperties? | boolean | Flag indicating whether to include hidden properties in the output.

Authlete has a mechanism whereby to associate arbitrary key-value pairs with an access token. Each key-value pair has a hidden attribute. By default, key-value pairs whose hidden attribute is set to `true` are not embedded in the standard introspection output.

If the `withHiddenProperties` request parameter is given and its value is `true`, `/api/auth/introspection/standard API includes all the associated key-value pairs into the output regardless of the value of the hidden attribute. | +| rsUri? | string | The URI of the resource server making the introspection request.

If the `rsUri` request parameter is given and the token has audience values, Authlete checks if the value of the `rsUri` request parameter is contained in the audience values. If not contained, Authlete generates an introspection response with the `active` property set to `false`.

The `rsUri` request parameter is required when the resource server requests a JWT introspection response, i.e., when the value of the `httpAcceptHeader` request parameter is set to `"application/token-introspection+jwt"`. | +| httpAcceptHeader? | string | The value of the `HTTP Accept` header in the introspection request.

If the value of the `httpAcceptHeader` request parameter is `"application/token-introspection+jwt"`, Authlete generates a JWT introspection response. See "[4. Requesting a JWT Response](https://www.rfc-editor.org/rfc/rfc9701.html#section-4)" of "[RFC 9701: JWT Response for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)" for more details. | +| introspectionSignAlg? | string | The JWS `alg` algorithm for signing the introspection response. This parameter corresponds to `introspection_signed_response_alg` defined in "[6. Client Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)".

The default value is `RS256`. | +| introspectionEncryptionAlg? | string | The JWE `alg` algorithm for encrypting the introspection response. This parameter corresponds to `introspection_encrypted_response_alg` defined in "[6. Client Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)".

If the `introspectionEncryptionAlg` request parameter is specified, Authlete generates a JWT introspection response encrypted with the algorithm by this property and the algorithm specified by the `introspectionEncryptionEnc` request parameter. | +| introspectionEncryptionEnc? | string | The JWE `enc` algorithm for encrypting the introspection response. This parameter corresponds to `introspection_encrypted_response_enc` defined in "[6. Client Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)".

The default value is `A128CBC_HS256`. | +| sharedKeyForSign? | string | The shared key for signing the introspection response with a symmetric algorithm.

The `sharedKeyForSign` request parameter is required when the introspection response is requested to be signed with a symmetric algorithm. | +| sharedKeyForEncryption? | string | The shared key for encrypting the introspection response with a symmetric algorithm.

The `sharedKeyForEncryption` request parameter is required when the introspection response is requested to be encrypted with a symmetric algorithm. | +| publicKeyForEncryption? | string | The public key for signing the introspection response with an asymmetric algorithm.

The `publicKeyForEncryption` request parameter is required when the introspection response is requested to be encrypted with an asymmetric algorithm. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.introspectionEndpoint.authIntrospectionStandardApi(request)` + +- **OnSuccess**: [StandardIntrospectionResponse](src/models/standard-introspection-response.ts) +- **OnError**: throws [IntrospectionEndpoint.AuthIntrospectionStandardApiError](src/resources/introspection-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.introspectionEndpoint.authIntrospectionStandardApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<StandardIntrospectionResponse, IntrospectionEndpoint.AuthIntrospectionStandardApiError>, with `result.value` of type [StandardIntrospectionResponse](src/models/standard-introspection-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## RevocationEndpoint + +> Source: [RevocationEndpoint](src/resources/revocation-endpoint.ts) + +
+authRevocationApi(request: RevocationEndpoint.AuthRevocationApiRequest, options?: RequestOptions): ApiPromise<RevocationResponse, RevocationEndpoint.AuthRevocationApiError> + +
+
+ +### Description + +
+
+ +This API revokes access tokens and refresh tokens. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.revocationEndpoint.authRevocationApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type RevocationResponse +} catch (err) { + // TODO: Handle 'err' of type RevocationEndpoint.AuthRevocationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.revocationEndpoint.authRevocationApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type RevocationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | OAuth 2.0 token revocation request parameters which are the request parameters that the OAuth 2.0 token revocation endpoint ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)) of the authorization server implementation received from the client application.

The value of parameters is the entire entity body (which is formatted in `application/x-www-form-urlencoded`) of the request from the client application. | +| clientId? | string | The client ID extracted from `Authorization` header of the revocation request from the client application.

If the revocation endpoint of the authorization server implementation supports Basic Authentication as a means of client authentication, and the request from the client application contains its client ID in `Authorization` header, the value should be extracted and set to this parameter. | +| clientSecret? | string | The client secret extracted from `Authorization` header of the revocation request from the client application.

If the revocation endpoint of the authorization server implementation supports basic authentication as a means of client authentication, and the request from the client application contained its client secret in `Authorization` header, the value should be extracted and set to this parameter. | +| clientCertificate? | string | The client certificate used in the TLS connection between the client application and the revocation endpoint. | +| clientCertificatePath? | string[] | The certificate path presented by the client during client authentication. | +| oauthClientAttestation? | string | The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| oauthClientAttestationPop? | string | The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.revocationEndpoint.authRevocationApi(request)` + +- **OnSuccess**: [RevocationResponse](src/models/revocation-response.ts) +- **OnError**: throws [RevocationEndpoint.AuthRevocationApiError](src/resources/revocation-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.revocationEndpoint.authRevocationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<RevocationResponse, RevocationEndpoint.AuthRevocationApiError>, with `result.value` of type [RevocationResponse](src/models/revocation-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## UserInfoEndpoint + +> Source: [UserInfoEndpoint](src/resources/user-info-endpoint.ts) + +
+authUserinfoApi(request: UserInfoEndpoint.AuthUserinfoApiRequest, options?: RequestOptions): ApiPromise<UserinfoResponse, UserInfoEndpoint.AuthUserinfoApiError> + +
+
+ +### Description + +
+
+ +This API gathers information about a user. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.userInfoEndpoint.authUserinfoApi({ + serviceId: "some example string", + token: "some example string", + }); + // TODO: Handle 'response' of type UserinfoResponse +} catch (err) { + // TODO: Handle 'err' of type UserInfoEndpoint.AuthUserinfoApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.userInfoEndpoint.authUserinfoApi({ + serviceId: "some example string", + token: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type UserinfoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| token | string | An access token. | +| clientCertificate? | string | Client certificate used in the TLS connection established between the client application and the userinfo endpoint.

The value of this request parameter is referred to when the access token given to the userinfo endpoint was bound to a client certificate when it was issued. See [OAuth 2.0 Mutual TLS Client Authentication and Certificate-Bound Access Tokens] (https://datatracker.ietf.org/doc/rfc8705/) for details about the specification of certificate-bound access tokens. | +| dpop? | string | `DPoP` header presented by the client during the request to the user info endpoint.

The header contains a signed JWT which includes the public key that is paired with the private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| htm? | string | HTTP method of the user info request. This field is used to validate the DPoP header. In normal cases, the value is either `GET` or `POST`. | +| htu? | string | URL of the user info endpoint. This field is used to validate the DPoP header.

If this parameter is omitted, the `userInfoEndpoint` property of the service is used as the default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. | +| uri? | string | The full URL of the userinfo endpoint. | +| message? | string | The HTTP message body of the request, if present. | +| headers? | [Pair](src/models/pair.ts)[] | HTTP headers to be included in processing the signature. If this is a signed request, this must include the Signature and Signature-Input headers, as well as any additional headers covered by the signature. | +| targetUri? | string | The target URI of the userinfo request, including the query part, if any. | +| dpopNonceRequired? | boolean | The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` value.

If this request parameter is set to `true` or if the service's `dpopNonceRequired` property is set to `true`, the `/auth/userinfo` API checks if the DPoP proof JWT includes the expected `nonce` value. In this case, the response from the `/auth/userinfo` API will include the `dpopNonce` response parameter, which should be used as the value of the DPoP-Nonce HTTP header. | +| requestBodyContained? | boolean | The flag indicating whether the userinfo request contains a request body. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.userInfoEndpoint.authUserinfoApi(request)` + +- **OnSuccess**: [UserinfoResponse](src/models/userinfo-response.ts) +- **OnError**: throws [UserInfoEndpoint.AuthUserinfoApiError](src/resources/user-info-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.userInfoEndpoint.authUserinfoApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<UserinfoResponse, UserInfoEndpoint.AuthUserinfoApiError>, with `result.value` of type [UserinfoResponse](src/models/userinfo-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+authUserinfoIssueApi(request: UserInfoEndpoint.AuthUserinfoIssueApiRequest, options?: RequestOptions): ApiPromise<UserinfoIssueResponse, UserInfoEndpoint.AuthUserinfoIssueApiError> + +
+
+ +### Description + +
+
+ +This API generates an ID token. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.userInfoEndpoint.authUserinfoIssueApi({ + serviceId: "some example string", + token: "some example string", + }); + // TODO: Handle 'response' of type UserinfoIssueResponse +} catch (err) { + // TODO: Handle 'err' of type UserInfoEndpoint.AuthUserinfoIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.userInfoEndpoint.authUserinfoIssueApi({ + serviceId: "some example string", + token: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type UserinfoIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| token | string | The access token that has been passed to the userinfo endpoint by the client application. In other words, the access token which was contained in the userinfo request. | +| claims? | string | Claims in JSON format. As for the format, see [OpenID Connect Core 1.0, 5.1. Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). | +| sub? | string | The value of the `sub` claim. If the value of this request parameter is not empty, it is used as the value of the `sub` claim. Otherwise, the value of the subject associated with the access token is used. | +| claimsForTx? | string | Claim key-value pairs that are used to compute transformed claims. | +| requestSignature? | string | The Signature header value from the request. | +| headers? | [Pair](src/models/pair.ts)[] | HTTP headers to be included in processing the signature. If this is a signed request, this must include the Signature and Signature-Input headers, as well as any additional headers covered by the signature. | +| verifiedClaimsForTx? | string[] | Values of verified claims requested indirectly by "transformed claims". | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.userInfoEndpoint.authUserinfoIssueApi(request)` + +- **OnSuccess**: [UserinfoIssueResponse](src/models/userinfo-issue-response.ts) +- **OnError**: throws [UserInfoEndpoint.AuthUserinfoIssueApiError](src/resources/user-info-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.userInfoEndpoint.authUserinfoIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<UserinfoIssueResponse, UserInfoEndpoint.AuthUserinfoIssueApiError>, with `result.value` of type [UserinfoIssueResponse](src/models/userinfo-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## JwkSetEndpoint + +> Source: [JwkSetEndpoint](src/resources/jwk-set-endpoint.ts) + +
+serviceJwksGetApi(request: JwkSetEndpoint.ServiceJwksGetApiRequest, options?: RequestOptions): ApiPromise<ServiceJwksGetResponse, JwkSetEndpoint.ServiceJwksGetApiError> + +
+
+ +### Description + +
+
+ +This API gathers JWK Set information for a service so that its client applications can verify signatures by the service and encrypt their requests to the service. + +This API is supposed to be called from within the implementation of the jwk set endpoint of the service where the service that supports OpenID Connect must expose its JWK Set information so that client applications can verify signatures by the service and encrypt their requests to the service. The URI of the endpoint can be found as the value of `jwks_uri` in [OpenID Provider Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) if the service supports [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.jwkSetEndpoint.serviceJwksGetApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type ServiceJwksGetResponse +} catch (err) { + // TODO: Handle 'err' of type JwkSetEndpoint.ServiceJwksGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.jwkSetEndpoint.serviceJwksGetApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ServiceJwksGetResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| includePrivateKeys? | boolean | The boolean value that indicates whether the response should include the private keys associated with the service or not. If `true`, the private keys are included in the response. The default value is `false`. | +| pretty? | boolean | This boolean value indicates whether the JSON in the response should be formatted or not. If `true`, the JSON in the response is pretty-formatted. The default value is `false`. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.jwkSetEndpoint.serviceJwksGetApi(request)` + +- **OnSuccess**: [ServiceJwksGetResponse](src/models/service-jwks-get-response.ts) +- **OnError**: throws [JwkSetEndpoint.ServiceJwksGetApiError](src/resources/jwk-set-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.jwkSetEndpoint.serviceJwksGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ServiceJwksGetResponse, JwkSetEndpoint.ServiceJwksGetApiError>, with `result.value` of type [ServiceJwksGetResponse](src/models/service-jwks-get-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## DynamicClientRegistration + +> Source: [DynamicClientRegistration](src/resources/dynamic-client-registration.ts) + +
+clientRegistrationApi(request: DynamicClientRegistration.ClientRegistrationApiRequest, options?: RequestOptions): ApiPromise<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationApiError> + +
+
+ +### Description + +
+
+ +Register a client. This API is supposed to be used to implement a client registration endpoint that complies with [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration Protocol). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.dynamicClientRegistration.clientRegistrationApi({ + serviceId: "some example string", + body: { json: '{ "client_name": "My Dynamic Client" }' }, + }); + // TODO: Handle 'response' of type ClientRegistrationResponse +} catch (err) { + // TODO: Handle 'err' of type DynamicClientRegistration.ClientRegistrationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.dynamicClientRegistration.clientRegistrationApi({ + serviceId: "some example string", + body: { json: '{ "client_name": "My Dynamic Client" }' }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientRegistrationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [ApiClientRegistrationRequest](src/models/api-client-registration-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.dynamicClientRegistration.clientRegistrationApi(request)` + +- **OnSuccess**: [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: throws [DynamicClientRegistration.ClientRegistrationApiError](src/resources/dynamic-client-registration.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.dynamicClientRegistration.clientRegistrationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationApiError>, with `result.value` of type [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientRegistrationDeleteApi(request: DynamicClientRegistration.ClientRegistrationDeleteApiRequest, options?: RequestOptions): ApiPromise<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationDeleteApiError> + +
+
+ +### Description + +
+
+ +Delete a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.dynamicClientRegistration.clientRegistrationDeleteApi({ + serviceId: "some example string", + body: { token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", clientId: "26837717140341" }, + }); + // TODO: Handle 'response' of type ClientRegistrationResponse +} catch (err) { + // TODO: Handle 'err' of type DynamicClientRegistration.ClientRegistrationDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.dynamicClientRegistration.clientRegistrationDeleteApi({ + serviceId: "some example string", + body: { token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", clientId: "26837717140341" }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientRegistrationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [ApiClientRegistrationDeleteRequest](src/models/api-client-registration-delete-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.dynamicClientRegistration.clientRegistrationDeleteApi(request)` + +- **OnSuccess**: [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: throws [DynamicClientRegistration.ClientRegistrationDeleteApiError](src/resources/dynamic-client-registration.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.dynamicClientRegistration.clientRegistrationDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationDeleteApiError>, with `result.value` of type [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientRegistrationGetApi(request: DynamicClientRegistration.ClientRegistrationGetApiRequest, options?: RequestOptions): ApiPromise<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationGetApiError> + +
+
+ +### Description + +
+
+ +Get a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.dynamicClientRegistration.clientRegistrationGetApi({ + serviceId: "some example string", + body: { token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", clientId: "26837717140341" }, + }); + // TODO: Handle 'response' of type ClientRegistrationResponse +} catch (err) { + // TODO: Handle 'err' of type DynamicClientRegistration.ClientRegistrationGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.dynamicClientRegistration.clientRegistrationGetApi({ + serviceId: "some example string", + body: { token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", clientId: "26837717140341" }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientRegistrationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [ApiClientRegistrationGetRequest](src/models/api-client-registration-get-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.dynamicClientRegistration.clientRegistrationGetApi(request)` + +- **OnSuccess**: [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: throws [DynamicClientRegistration.ClientRegistrationGetApiError](src/resources/dynamic-client-registration.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.dynamicClientRegistration.clientRegistrationGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationGetApiError>, with `result.value` of type [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+clientRegistrationUpdateApi(request: DynamicClientRegistration.ClientRegistrationUpdateApiRequest, options?: RequestOptions): ApiPromise<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationUpdateApiError> + +
+
+ +### Description + +
+
+ +Update a dynamically registered client. This API is supposed to be used to implement a client registration management endpoint that complies with [RFC 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration Management). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.dynamicClientRegistration.clientRegistrationUpdateApi({ + serviceId: "some example string", + body: { + json: '{"client_name":"My Updated Dynamic Client","default_max_age":0,"registration_client_uri":"https://my-service.example.com/dcr/register/26837717140341","client_id":"26837717140341","token_endpoint_auth_method":"client_secret_basic","require_pushed_authorization_requests":false,"backchannel_user_code_parameter":false,"client_secret":"bMsjvZm2FE1_mqJgxhmYj_Wr8rA0Pia_A_j-V076qQm6-P1edKB055W579GBe7MSbOdxZ3dJKsKinCtdIFwxpw","tls_client_certificate_bound_access_tokens":false,"id_token_signed_response_alg":"RS256","subject_type":"public","require_signed_request_object":false}', + token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", + clientId: "26837717140341", + }, + }); + // TODO: Handle 'response' of type ClientRegistrationResponse +} catch (err) { + // TODO: Handle 'err' of type DynamicClientRegistration.ClientRegistrationUpdateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.dynamicClientRegistration.clientRegistrationUpdateApi({ + serviceId: "some example string", + body: { + json: '{"client_name":"My Updated Dynamic Client","default_max_age":0,"registration_client_uri":"https://my-service.example.com/dcr/register/26837717140341","client_id":"26837717140341","token_endpoint_auth_method":"client_secret_basic","require_pushed_authorization_requests":false,"backchannel_user_code_parameter":false,"client_secret":"bMsjvZm2FE1_mqJgxhmYj_Wr8rA0Pia_A_j-V076qQm6-P1edKB055W579GBe7MSbOdxZ3dJKsKinCtdIFwxpw","tls_client_certificate_bound_access_tokens":false,"id_token_signed_response_alg":"RS256","subject_type":"public","require_signed_request_object":false}', + token: "qs4Tu5TV7qqDYT93bFs6ISyhTByMF9o-54GY4JU5vTA", + clientId: "26837717140341", + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type ClientRegistrationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [ApiClientRegistrationUpdateRequest](src/models/api-client-registration-update-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.dynamicClientRegistration.clientRegistrationUpdateApi(request)` + +- **OnSuccess**: [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: throws [DynamicClientRegistration.ClientRegistrationUpdateApiError](src/resources/dynamic-client-registration.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.dynamicClientRegistration.clientRegistrationUpdateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<ClientRegistrationResponse, DynamicClientRegistration.ClientRegistrationUpdateApiError>, with `result.value` of type [ClientRegistrationResponse](src/models/client-registration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## Ciba + +> Source: [Ciba](src/resources/ciba.ts) + +
+backchannelAuthenticationApi(request: Ciba.BackchannelAuthenticationApiRequest, options?: RequestOptions): ApiPromise<BackchannelAuthenticationResponse, Ciba.BackchannelAuthenticationApiError> + +
+
+ +### Description + +
+
+ +This API parses request parameters of a [backchannel authentication request](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_request) and returns necessary data for the authorization server implementation to process the backchannel authentication request further. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.ciba.backchannelAuthenticationApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type BackchannelAuthenticationResponse +} catch (err) { + // TODO: Handle 'err' of type Ciba.BackchannelAuthenticationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.ciba.backchannelAuthenticationApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type BackchannelAuthenticationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | Parameters of a backchannel authentication request which are the request parameters that the backchannel authentication endpoint of the OpenID provider implementation received from the client application.

The value of `parameters` is the entire entity body (which is formatted in `application/x-www-form-urlencoded`) of the request from the client application. | +| clientId? | string | The client ID extracted from Authorization header of the backchannel authentication request from the client application.

If the backchannel authentication endpoint of the OpenID provider implementation supports Basic Authentication as a means of client authentication, and the request from the client application contained its client ID in Authorization header, the value should be extracted and set to this parameter. | +| clientSecret? | string | The client secret extracted from Authorization header of the backchannel authentication request from the client application.

If the backchannel authentication endpoint of the OpenID provider implementation supports Basic Authentication as a means of client authentication, and the request from the client application contained its client secret in Authorization header, the value should be extracted and set to this parameter. | +| clientCertificate? | string | The client certification used in the TLS connection between the client application and the backchannel authentication endpoint of the OpenID provider. | +| clientCertificatePath? | string[] | The client certificate path presented by the client during client authentication. Each element is a string in PEM format. | +| oauthClientAttestation? | string | The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| oauthClientAttestationPop? | string | The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| cimdOptions? | [CimdOptions](src/models/cimd-options.ts) | Options for [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD).

These options allow per-request control over CIMD behavior, taking precedence over service-level configuration when provided. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.ciba.backchannelAuthenticationApi(request)` + +- **OnSuccess**: [BackchannelAuthenticationResponse](src/models/backchannel-authentication-response.ts) +- **OnError**: throws [Ciba.BackchannelAuthenticationApiError](src/resources/ciba.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.ciba.backchannelAuthenticationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<BackchannelAuthenticationResponse, Ciba.BackchannelAuthenticationApiError>, with `result.value` of type [BackchannelAuthenticationResponse](src/models/backchannel-authentication-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+backchannelAuthenticationCompleteApi(request: Ciba.BackchannelAuthenticationCompleteApiRequest, options?: RequestOptions): ApiPromise<BackchannelAuthenticationCompleteResponse, Ciba.BackchannelAuthenticationCompleteApiError> + +
+
+ +### Description + +
+
+ +This API returns information about what action the authorization server should take after it receives the result of end-user's decision about whether the end-user has approved or rejected a client application's request on the authentication device. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.ciba.backchannelAuthenticationCompleteApi({ + serviceId: "some example string", + ticket: "some example string", + result: Result1.TransactionFailed, + subject: "some example string", + }); + // TODO: Handle 'response' of type BackchannelAuthenticationCompleteResponse +} catch (err) { + // TODO: Handle 'err' of type Ciba.BackchannelAuthenticationCompleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.ciba.backchannelAuthenticationCompleteApi({ + serviceId: "some example string", + ticket: "some example string", + result: Result1.TransactionFailed, + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type BackchannelAuthenticationCompleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued by Authlete's `/backchannel/authentication` API. | +| result | [Result1](src/models/result1.ts) | The result of the end-user authentication and authorization. One of the following. Details are described in the description. | +| subject | string | The subject (= unique identifier) of the end-user. | +| sub? | string | The value of the sub claim that should be used in the ID token. | +| authTime? | number | The time at which the end-user was authenticated. Its value is the number of seconds from `1970-01-01`. | +| acr? | string | The reference of the authentication context class which the end-user authentication satisfied. | +| claims? | string | Additional claims which will be embedded in the ID token. | +| properties? | [Property](src/models/property.ts)[] | The extra properties associated with the access token. | +| scopes? | string[] | Scopes to replace the scopes specified in the original backchannel authentication request with. When nothing is specified for this parameter, replacement is not performed. | +| idtHeaderParams? | string | JSON that represents additional JWS header parameters for ID tokens. | +| errorDescription? | string | The description of the error. If this optional request parameter is given, its value is used as the value of the `error_description` property, but it is used only when the result is not `AUTHORIZED`. To comply with the specification strictly, the description must not include characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. | +| errorUri? | string | The URI of a document which describes the error in detail. This corresponds to the `error_uri` property in the response to the client. | +| consentedClaims? | string[] | the claims that the user has consented for the client application to know. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| accessToken? | string | The representation of an access token that may be issued as a result of the Authlete API call. | +| accessTokenDuration? | number | The duration (in seconds) of the access token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the access token in. In other cases, this request parameter is ignored. | +| refreshTokenDuration? | number | The duration (in seconds) of the refresh token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the refresh token in. In other cases, this request parameter is ignored. | +| idTokenAudType? | string | The type of the `aud` claim of the ID token being issued. Valid values are as follows.

\| Value \| Description \|
\| ----- \| ----------- \|
\| "array" \| The type of the aud claim is always an array of strings. \|
\| "string" \| The type of the aud claim is always a single string. \|
\| null \| The type of the aud claim remains the same as before. \|

This request parameter takes precedence over the `idTokenAudType` property of the service. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.ciba.backchannelAuthenticationCompleteApi(request)` + +- **OnSuccess**: [BackchannelAuthenticationCompleteResponse](src/models/backchannel-authentication-complete-response.ts) +- **OnError**: throws [Ciba.BackchannelAuthenticationCompleteApiError](src/resources/ciba.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.ciba.backchannelAuthenticationCompleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<BackchannelAuthenticationCompleteResponse, Ciba.BackchannelAuthenticationCompleteApiError>, with `result.value` of type [BackchannelAuthenticationCompleteResponse](src/models/backchannel-authentication-complete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+backchannelAuthenticationFailApi(request: Ciba.BackchannelAuthenticationFailApiRequest, options?: RequestOptions): ApiPromise<BackchannelAuthenticationFailResponse, Ciba.BackchannelAuthenticationFailApiError> + +
+
+ +### Description + +
+
+ +The API prepares JSON that contains an error. The JSON should be used as the response body of the response which is returned to the client from the [backchannel authentication endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.ciba.backchannelAuthenticationFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason2.AccessDenied, + }); + // TODO: Handle 'response' of type BackchannelAuthenticationFailResponse +} catch (err) { + // TODO: Handle 'err' of type Ciba.BackchannelAuthenticationFailApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.ciba.backchannelAuthenticationFailApi({ + serviceId: "some example string", + ticket: "some example string", + reason: Reason2.AccessDenied, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type BackchannelAuthenticationFailResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket which should be deleted on a call of Authlete's `/backchannel/authentication/fail` API. This request parameter is not mandatory but optional. If this request parameter is given and the ticket belongs to the service, the specified ticket is deleted from the database. Giving this parameter is recommended to clean up the storage area for the service. | +| reason | [Reason2](src/models/reason2.ts) | The reason of the failure of the backchannel authentication request. This request parameter is not mandatory but optional. However, giving this parameter is recommended. If omitted, `SERVER_ERROR` is used as a reason. | +| errorDescription? | string | The description of the error. This corresponds to the `error_description` property in the response to the client. | +| errorUri? | string | The URI of a document which describes the error in detail. If this optional request parameter is given, its value is used as the value of the `error_uri` property. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.ciba.backchannelAuthenticationFailApi(request)` + +- **OnSuccess**: [BackchannelAuthenticationFailResponse](src/models/backchannel-authentication-fail-response.ts) +- **OnError**: throws [Ciba.BackchannelAuthenticationFailApiError](src/resources/ciba.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.ciba.backchannelAuthenticationFailApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<BackchannelAuthenticationFailResponse, Ciba.BackchannelAuthenticationFailApiError>, with `result.value` of type [BackchannelAuthenticationFailResponse](src/models/backchannel-authentication-fail-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+backchannelAuthenticationIssueApi(request: Ciba.BackchannelAuthenticationIssueApiRequest, options?: RequestOptions): ApiPromise<BackchannelAuthenticationIssueResponse, Ciba.BackchannelAuthenticationIssueApiError> + +
+
+ +### Description + +
+
+ +This API prepares JSON that contains an `auth_req_id`. The JSON should be used as the response body of the response which is returned to the client from the [backchannel authentication endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint) + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.ciba.backchannelAuthenticationIssueApi({ + serviceId: "some example string", + ticket: "some example string", + }); + // TODO: Handle 'response' of type BackchannelAuthenticationIssueResponse +} catch (err) { + // TODO: Handle 'err' of type Ciba.BackchannelAuthenticationIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.ciba.backchannelAuthenticationIssueApi({ + serviceId: "some example string", + ticket: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type BackchannelAuthenticationIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| ticket | string | The ticket issued from Authlete's `/backchannel/authentication` API. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.ciba.backchannelAuthenticationIssueApi(request)` + +- **OnSuccess**: [BackchannelAuthenticationIssueResponse](src/models/backchannel-authentication-issue-response.ts) +- **OnError**: throws [Ciba.BackchannelAuthenticationIssueApiError](src/resources/ciba.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.ciba.backchannelAuthenticationIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<BackchannelAuthenticationIssueResponse, Ciba.BackchannelAuthenticationIssueApiError>, with `result.value` of type [BackchannelAuthenticationIssueResponse](src/models/backchannel-authentication-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## GrantManagementEndpoint + +> Source: [GrantManagementEndpoint](src/resources/grant-management-endpoint.ts) + +
+grantMApi(request: GrantManagementEndpoint.GrantMApiRequest, options?: RequestOptions): ApiPromise<GmResponse, GrantManagementEndpoint.GrantMApiError> + +
+
+ +### Description + +
+
+ +The API is for the implementation of the grant management endpoint which is defined in "[Grant Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)". + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.grantManagementEndpoint.grantMApi({ + serviceId: "some example string", + body: { + accessToken: + "eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE1NTk4MTE3NTAsImlzcyI6IjU3Mjk3NDA4ODY3In0K.csmdholMVcmjqHe59YWgLGNvm7I5Whp4phQCoGxyrlRGMnTgsfxtwyxBgMXQqEPD5q5k9FaEWNk37K8uAtSwrA", + gmAction: GrantManagementAction.Revoke, + grantId: "57297408867", + }, + }); + // TODO: Handle 'response' of type GmResponse +} catch (err) { + // TODO: Handle 'err' of type GrantManagementEndpoint.GrantMApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.grantManagementEndpoint.grantMApi({ + serviceId: "some example string", + body: { + accessToken: + "eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE1NTk4MTE3NTAsImlzcyI6IjU3Mjk3NDA4ODY3In0K.csmdholMVcmjqHe59YWgLGNvm7I5Whp4phQCoGxyrlRGMnTgsfxtwyxBgMXQqEPD5q5k9FaEWNk37K8uAtSwrA", + gmAction: GrantManagementAction.Revoke, + grantId: "57297408867", + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type GmResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [GmRequest](src/models/gm-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.grantManagementEndpoint.grantMApi(request)` + +- **OnSuccess**: [GmResponse](src/models/gm-response.ts) +- **OnError**: throws [GrantManagementEndpoint.GrantMApiError](src/resources/grant-management-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.grantManagementEndpoint.grantMApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<GmResponse, GrantManagementEndpoint.GrantMApiError>, with `result.value` of type [GmResponse](src/models/gm-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## JoseObject + +> Source: [JoseObject](src/resources/jose-object.ts) + +
+joseVerifyApi(request: JoseObject.JoseVerifyApiRequest, options?: RequestOptions): ApiPromise<JoseVerifyResponse, JoseObject.JoseVerifyApiError> + +
+
+ +### Description + +
+
+ +This API verifies a JOSE object. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.joseObject.joseVerifyApi({ + serviceId: "some example string", + jose: "some example string", + }); + // TODO: Handle 'response' of type JoseVerifyResponse +} catch (err) { + // TODO: Handle 'err' of type JoseObject.JoseVerifyApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.joseObject.joseVerifyApi({ + serviceId: "some example string", + jose: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type JoseVerifyResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| jose | string | A JOSE object. | +| mandatoryClaims? | string[] | Mandatory claims that are required to be included in the JOSE object. | +| clockSkew? | number | Allowable clock skew in seconds. | +| clientIdentifier? | string | The identifier of the client application whose keys are required for verification of the JOSE object. | +| signedByClient? | boolean | The flag which indicates whether the signature of the JOSE object has been signed by a client application with the client's private key or a shared symmetric key. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.joseObject.joseVerifyApi(request)` + +- **OnSuccess**: [JoseVerifyResponse](src/models/jose-verify-response.ts) +- **OnError**: throws [JoseObject.JoseVerifyApiError](src/resources/jose-object.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.joseObject.joseVerifyApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<JoseVerifyResponse, JoseObject.JoseVerifyApiError>, with `result.value` of type [JoseVerifyResponse](src/models/jose-verify-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## DeviceFlow + +> Source: [DeviceFlow](src/resources/device-flow.ts) + +
+deviceAuthorizationApi(request: DeviceFlow.DeviceAuthorizationApiRequest, options?: RequestOptions): ApiPromise<DeviceAuthorizationResponse, DeviceFlow.DeviceAuthorizationApiError> + +
+
+ +### Description + +
+
+ +This API parses request parameters of a [device authorization request](https://datatracker.ietf.org/doc/html/rfc8628#section-3.1) and returns necessary data for the authorization server implementation to process the device authorization request further. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.deviceFlow.deviceAuthorizationApi({ + serviceId: "some example string", + parameters: "some example string", + }); + // TODO: Handle 'response' of type DeviceAuthorizationResponse +} catch (err) { + // TODO: Handle 'err' of type DeviceFlow.DeviceAuthorizationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.deviceFlow.deviceAuthorizationApi({ + serviceId: "some example string", + parameters: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type DeviceAuthorizationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| parameters | string | Parameters of a device authorization request which are the request parameters that the device authorization endpoint of the authorization server implementation received from the client application.

The value of `parameters` is the entire entity body (which is formatted in `application/x-www-form-urlencoded`) of the request from the client application. | +| clientId? | string | The client ID extracted from Authorization header of the device authorization request from the client application.

If the device authorization endpoint of the authorization server implementation supports Basic `Authentication` as a means of client authentication, and the request from the client application contained its client ID in `Authorization` header, the value should be extracted and set to this parameter. | +| clientSecret? | string | The client secret extracted from `Authorization` header of the device authorization request from the client application.

If the device authorization endpoint of the authorization server implementation supports Basic Authentication as a means of client authentication, and the request from the client application contained its client secret in `Authorization` header, the value should be extracted and set to this parameter. | +| clientCertificate? | string | The client certificate used in the TLS connection between the client application and the device authorization endpoint of the authorization server. | +| clientCertificatePath? | string[] | The client certificate path presented by the client during client authentication. Each element is a string in PEM format. | +| oauthClientAttestation? | string | The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| oauthClientAttestationPop? | string | The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the specification of [OAuth 2.0 Attestation-Based Client Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). | +| cimdOptions? | [CimdOptions](src/models/cimd-options.ts) | Options for [OAuth Client ID Metadata Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD).

These options allow per-request control over CIMD behavior, taking precedence over service-level configuration when provided. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.deviceFlow.deviceAuthorizationApi(request)` + +- **OnSuccess**: [DeviceAuthorizationResponse](src/models/device-authorization-response.ts) +- **OnError**: throws [DeviceFlow.DeviceAuthorizationApiError](src/resources/device-flow.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.deviceFlow.deviceAuthorizationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<DeviceAuthorizationResponse, DeviceFlow.DeviceAuthorizationApiError>, with `result.value` of type [DeviceAuthorizationResponse](src/models/device-authorization-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+deviceCompleteApi(request: DeviceFlow.DeviceCompleteApiRequest, options?: RequestOptions): ApiPromise<DeviceCompleteResponse, DeviceFlow.DeviceCompleteApiError> + +
+
+ +### Description + +
+
+ +This API returns information about what action the authorization server should take after it receives the result of end-user's decision about whether the end-user has approved or rejected a client application's request. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.deviceFlow.deviceCompleteApi({ + serviceId: "some example string", + userCode: "some example string", + result: Result1.TransactionFailed, + subject: "some example string", + }); + // TODO: Handle 'response' of type DeviceCompleteResponse +} catch (err) { + // TODO: Handle 'err' of type DeviceFlow.DeviceCompleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.deviceFlow.deviceCompleteApi({ + serviceId: "some example string", + userCode: "some example string", + result: Result1.TransactionFailed, + subject: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type DeviceCompleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| userCode | string | A user code. | +| result | [Result1](src/models/result1.ts) | The result of the end-user authentication and authorization. One of the following. Details are described in the description. | +| subject | string | The subject (= unique identifier) of the end-user. | +| sub? | string | The value of the sub claim that should be used in the ID token. | +| authTime? | number | The time at which the end-user was authenticated. Its value is the number of seconds from `1970-01-01`. | +| acr? | string | The reference of the authentication context class which the end-user authentication satisfied. | +| claims? | string | Additional claims which will be embedded in the ID token. | +| properties? | [Property](src/models/property.ts)[] | The extra properties associated with the access token. | +| scopes? | string[] | Scopes to replace the scopes specified in the original device authorization request with. When nothing is specified for this parameter, replacement is not performed. | +| errorDescription? | string | The description of the error. If this optional request parameter is given, its value is used as the value of the `error_description` property, but it is used only when the result is not `AUTHORIZED`. To comply with the specification strictly, the description must not include characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. | +| errorUri? | string | The URI of a document which describes the error in detail. This corresponds to the `error_uri` property in the response to the client. | +| idtHeaderParams? | string | JSON that represents additional JWS header parameters for ID tokens. | +| consentedClaims? | string[] | the claims that the user has consented for the client application to know. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| accessTokenDuration? | number | The duration (in seconds) of the access token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the access token in. In other cases, this request parameter is ignored. | +| refreshTokenDuration? | number | The duration (in seconds) of the refresh token that may be issued as a result of the Authlete API call.

When this request parameter holds a positive integer, it is used as the duration of the refresh token in. In other cases, this request parameter is ignored. | +| idTokenAudType? | string | The type of the `aud` claim of the ID token being issued. Valid values are as follows.

\| Value \| Description \|
\| ----- \| ----------- \|
\| "array" \| The type of the aud claim is always an array of strings. \|
\| "string" \| The type of the aud claim is always a single string. \|
\| null \| The type of the aud claim remains the same as before. \|

This request parameter takes precedence over the `idTokenAudType` property of the service. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.deviceFlow.deviceCompleteApi(request)` + +- **OnSuccess**: [DeviceCompleteResponse](src/models/device-complete-response.ts) +- **OnError**: throws [DeviceFlow.DeviceCompleteApiError](src/resources/device-flow.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.deviceFlow.deviceCompleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<DeviceCompleteResponse, DeviceFlow.DeviceCompleteApiError>, with `result.value` of type [DeviceCompleteResponse](src/models/device-complete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+deviceVerificationApi(request: DeviceFlow.DeviceVerificationApiRequest, options?: RequestOptions): ApiPromise<DeviceVerificationResponse, DeviceFlow.DeviceVerificationApiError> + +
+
+ +### Description + +
+
+ +The API returns information associated with a user code. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.deviceFlow.deviceVerificationApi({ + serviceId: "some example string", + userCode: "some example string", + }); + // TODO: Handle 'response' of type DeviceVerificationResponse +} catch (err) { + // TODO: Handle 'err' of type DeviceFlow.DeviceVerificationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.deviceFlow.deviceVerificationApi({ + serviceId: "some example string", + userCode: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type DeviceVerificationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| userCode | string | A user code. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.deviceFlow.deviceVerificationApi(request)` + +- **OnSuccess**: [DeviceVerificationResponse](src/models/device-verification-response.ts) +- **OnError**: throws [DeviceFlow.DeviceVerificationApiError](src/resources/device-flow.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.deviceFlow.deviceVerificationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<DeviceVerificationResponse, DeviceFlow.DeviceVerificationApiError>, with `result.value` of type [DeviceVerificationResponse](src/models/device-verification-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## FederationEndpoint + +> Source: [FederationEndpoint](src/resources/federation-endpoint.ts) + +
+federationConfigurationApi(request: FederationEndpoint.FederationConfigurationApiRequest, options?: RequestOptions): ApiPromise<FederationConfigurationResponse, FederationEndpoint.FederationConfigurationApiError> + +
+
+ +### Description + +
+
+ +This API gathers the federation configuration about a service. The authorization server implementation should retrieve the value of the `action` response parameter from the API response and take the following steps according to the value. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.federationEndpoint.federationConfigurationApi({ + serviceId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type FederationConfigurationResponse +} catch (err) { + // TODO: Handle 'err' of type FederationEndpoint.FederationConfigurationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.federationEndpoint.federationConfigurationApi({ + serviceId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type FederationConfigurationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [ApiFederationConfigurationRequest](src/models/api-federation-configuration-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.federationEndpoint.federationConfigurationApi(request)` + +- **OnSuccess**: [FederationConfigurationResponse](src/models/federation-configuration-response.ts) +- **OnError**: throws [FederationEndpoint.FederationConfigurationApiError](src/resources/federation-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.federationEndpoint.federationConfigurationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<FederationConfigurationResponse, FederationEndpoint.FederationConfigurationApiError>, with `result.value` of type [FederationConfigurationResponse](src/models/federation-configuration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+federationRegistrationApi(request: FederationEndpoint.FederationRegistrationApiRequest, options?: RequestOptions): ApiPromise<FederationRegistrationResponse, FederationEndpoint.FederationRegistrationApiError> + +
+
+ +### Description + +
+
+ +The Authlete API is for implementations of the **federation registration endpoint** that accepts "explicit client registration". Its details are defined in [OpenID Connect Federation 1.0](https://openid.net/specs/openid-connect-federation-1_0.html). The endpoint accepts `POST` requests whose `Content-Type` is either of the following. +1. `application/entity-statement+jwt`- `application/trust-chain+json` When the `Content-Type` of a request is `application/entity-statement+jwt`, the content of the request is the entity configuration of a relying party that is to be registered. In this case, the implementation of the federation registration endpoint should call Authlete's `/federation/registration` API with the entity configuration set to the `entityConfiguration` request parameter. On the other hand, when the `Content-Type` of a request is `application/trust-chain+json`, the content of the request is a JSON array that contains entity statements in JWT format. The sequence of the entity statements composes the trust chain of a relying party that is to be registered. In this case, the implementation of the federation registration endpoint should call Authlete's `/federation/registration` API with the trust chain set to the `trustChain` request parameter. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.federationEndpoint.federationRegistrationApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type FederationRegistrationResponse +} catch (err) { + // TODO: Handle 'err' of type FederationEndpoint.FederationRegistrationApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.federationEndpoint.federationRegistrationApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type FederationRegistrationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| entityConfiguration? | string | The entity configuration of a relying party. | +| trustChain? | string | The trust chain of a relying party. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.federationEndpoint.federationRegistrationApi(request)` + +- **OnSuccess**: [FederationRegistrationResponse](src/models/federation-registration-response.ts) +- **OnError**: throws [FederationEndpoint.FederationRegistrationApiError](src/resources/federation-endpoint.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.federationEndpoint.federationRegistrationApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<FederationRegistrationResponse, FederationEndpoint.FederationRegistrationApiError>, with `result.value` of type [FederationRegistrationResponse](src/models/federation-registration-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## VerifiableCredentialIssuer + +> Source: [VerifiableCredentialIssuer](src/resources/verifiable-credential-issuer.ts) + +
+vciBatchIssueApi(request: VerifiableCredentialIssuer.VciBatchIssueApiRequest, options?: RequestOptions): ApiPromise<VciBatchIssueResponse, VerifiableCredentialIssuer.VciBatchIssueApiError> + +
+
+ +### Description + +
+
+ +Issue multiple verifiable credentials in batch + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciBatchIssueApi({ + serviceId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type VciBatchIssueResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciBatchIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciBatchIssueApi({ + serviceId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciBatchIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [VciBatchIssueRequest](src/models/vci-batch-issue-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciBatchIssueApi(request)` + +- **OnSuccess**: [VciBatchIssueResponse](src/models/vci-batch-issue-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciBatchIssueApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciBatchIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciBatchIssueResponse, VerifiableCredentialIssuer.VciBatchIssueApiError>, with `result.value` of type [VciBatchIssueResponse](src/models/vci-batch-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciBatchParseApi(request: VerifiableCredentialIssuer.VciBatchParseApiRequest, options?: RequestOptions): ApiPromise<VciBatchParseResponse, VerifiableCredentialIssuer.VciBatchParseApiError> + +
+
+ +### Description + +
+
+ +Parse multiple verifiable credentials in batch + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciBatchParseApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type VciBatchParseResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciBatchParseApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciBatchParseApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciBatchParseResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessToken? | string | The access token that came along with the credential request. | +| requestContent? | string | The message body of the batch credential request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciBatchParseApi(request)` + +- **OnSuccess**: [VciBatchParseResponse](src/models/vci-batch-parse-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciBatchParseApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciBatchParseApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciBatchParseResponse, VerifiableCredentialIssuer.VciBatchParseApiError>, with `result.value` of type [VciBatchParseResponse](src/models/vci-batch-parse-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciDeferredIssueApi(request: VerifiableCredentialIssuer.VciDeferredIssueApiRequest, options?: RequestOptions): ApiPromise<VciDeferredIssueResponse, VerifiableCredentialIssuer.VciDeferredIssueApiError> + +
+
+ +### Description + +
+
+ +Issue a deferred verifiable credential + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciDeferredIssueApi({ + serviceId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type VciDeferredIssueResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciDeferredIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciDeferredIssueApi({ + serviceId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciDeferredIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [VciDeferredIssueRequest](src/models/vci-deferred-issue-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciDeferredIssueApi(request)` + +- **OnSuccess**: [VciDeferredIssueResponse](src/models/vci-deferred-issue-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciDeferredIssueApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciDeferredIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciDeferredIssueResponse, VerifiableCredentialIssuer.VciDeferredIssueApiError>, with `result.value` of type [VciDeferredIssueResponse](src/models/vci-deferred-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciDeferredParseApi(request: VerifiableCredentialIssuer.VciDeferredParseApiRequest, options?: RequestOptions): ApiPromise<VciDeferredParseResponse, VerifiableCredentialIssuer.VciDeferredParseApiError> + +
+
+ +### Description + +
+
+ +Parse a deferred verifiable credential + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciDeferredParseApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type VciDeferredParseResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciDeferredParseApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciDeferredParseApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciDeferredParseResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessToken? | string | The access token that came along with the deferred credential request. | +| requestContent? | string | The message body of the deferred credential request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciDeferredParseApi(request)` + +- **OnSuccess**: [VciDeferredParseResponse](src/models/vci-deferred-parse-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciDeferredParseApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciDeferredParseApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciDeferredParseResponse, VerifiableCredentialIssuer.VciDeferredParseApiError>, with `result.value` of type [VciDeferredParseResponse](src/models/vci-deferred-parse-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciJwksApi(request: VerifiableCredentialIssuer.VciJwksApiRequest, options?: RequestOptions): ApiPromise<VciJwksResponse, VerifiableCredentialIssuer.VciJwksApiError> + +
+
+ +### Description + +
+
+ +Get JSON Web Key Set for VCI + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciJwksApi({ + serviceId: "some example string", + pretty: true, + }); + // TODO: Handle 'response' of type VciJwksResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciJwksApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciJwksApi({ + serviceId: "some example string", + pretty: true, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciJwksResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| pretty | boolean | The flag indicating whether the metadata is written in the pretty format or not. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciJwksApi(request)` + +- **OnSuccess**: [VciJwksResponse](src/models/vci-jwks-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciJwksApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciJwksApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciJwksResponse, VerifiableCredentialIssuer.VciJwksApiError>, with `result.value` of type [VciJwksResponse](src/models/vci-jwks-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciJwtissuerApi(request: VerifiableCredentialIssuer.VciJwtissuerApiRequest, options?: RequestOptions): ApiPromise<VciJwtissuerResponse, VerifiableCredentialIssuer.VciJwtissuerApiError> + +
+
+ +### Description + +
+
+ +Get JWT issuer information for VCI + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciJwtissuerApi({ + serviceId: "some example string", + pretty: true, + }); + // TODO: Handle 'response' of type VciJwtissuerResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciJwtissuerApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciJwtissuerApi({ + serviceId: "some example string", + pretty: true, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciJwtissuerResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| pretty | boolean | The flag indicating whether the metadata is written in the pretty format or not. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciJwtissuerApi(request)` + +- **OnSuccess**: [VciJwtissuerResponse](src/models/vci-jwtissuer-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciJwtissuerApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciJwtissuerApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciJwtissuerResponse, VerifiableCredentialIssuer.VciJwtissuerApiError>, with `result.value` of type [VciJwtissuerResponse](src/models/vci-jwtissuer-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciMetadataApi(request: VerifiableCredentialIssuer.VciMetadataApiRequest, options?: RequestOptions): ApiPromise<VciMetadataResponse, VerifiableCredentialIssuer.VciMetadataApiError> + +
+
+ +### Description + +
+
+ +Get verifiable credential issuer metadata + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciMetadataApi({ + serviceId: "some example string", + pretty: true, + }); + // TODO: Handle 'response' of type VciMetadataResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciMetadataApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciMetadataApi({ + serviceId: "some example string", + pretty: true, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciMetadataResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| pretty | boolean | The flag indicating whether the metadata is written in the pretty format or not. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciMetadataApi(request)` + +- **OnSuccess**: [VciMetadataResponse](src/models/vci-metadata-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciMetadataApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciMetadataApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciMetadataResponse, VerifiableCredentialIssuer.VciMetadataApiError>, with `result.value` of type [VciMetadataResponse](src/models/vci-metadata-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciOfferCreateApi(request: VerifiableCredentialIssuer.VciOfferCreateApiRequest, options?: RequestOptions): ApiPromise<VciOfferCreateResponse, VerifiableCredentialIssuer.VciOfferCreateApiError> + +
+
+ +### Description + +
+
+ +Create a verifiable credential offer + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciOfferCreateApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type VciOfferCreateResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciOfferCreateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciOfferCreateApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciOfferCreateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| authorizationCodeGrantIncluded? | boolean | The flag indicating whether the `authorization_code` object is included in the `grants` object. | +| issuerStateIncluded? | boolean | The flag indicating whether the `issuer_state` property is included in the `authorization_code` object in the `grants` object. | +| preAuthorizedCodeGrantIncluded? | boolean | The flag to include the `urn:ietf:params:oauth:grant-type:pre-authorized_code` object in the `grants` object. | +| subject? | string | The subject associated with the credential offer. | +| duration? | number | The duration of the credential offer. | +| context? | string | The general-purpose arbitrary string. | +| properties? | [Property](src/models/property.ts)[] | Extra properties to associate with the credential offer. | +| jwtAtClaims? | string | Additional claims that are added to the payload part of the JWT access token. | +| authTime? | number | The time at which the user authentication was performed during the course of issuing the credential offer. | +| acr? | string | The Authentication Context Class Reference of the user authentication performed during the course of issuing the credential offer. | +| credentialConfigurationIds? | string[] | The value of the `credential_configuration_ids` array. | +| txCode? | string | The transaction code that should be associated with the credential offer. | +| txCodeInputMode? | string | The input mode of the transaction code. | +| txCodeDescription? | string | The description of the transaction code. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciOfferCreateApi(request)` + +- **OnSuccess**: [VciOfferCreateResponse](src/models/vci-offer-create-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciOfferCreateApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciOfferCreateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciOfferCreateResponse, VerifiableCredentialIssuer.VciOfferCreateApiError>, with `result.value` of type [VciOfferCreateResponse](src/models/vci-offer-create-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciOfferInfoApi(request: VerifiableCredentialIssuer.VciOfferInfoApiRequest, options?: RequestOptions): ApiPromise<VciOfferInfoResponse, VerifiableCredentialIssuer.VciOfferInfoApiError> + +
+
+ +### Description + +
+
+ +Get information about a verifiable credential offer + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciOfferInfoApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type VciOfferInfoResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciOfferInfoApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciOfferInfoApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciOfferInfoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| identifier? | string | The identifier of the credential offer. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciOfferInfoApi(request)` + +- **OnSuccess**: [VciOfferInfoResponse](src/models/vci-offer-info-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciOfferInfoApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciOfferInfoApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciOfferInfoResponse, VerifiableCredentialIssuer.VciOfferInfoApiError>, with `result.value` of type [VciOfferInfoResponse](src/models/vci-offer-info-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciSingleIssueApi(request: VerifiableCredentialIssuer.VciSingleIssueApiRequest, options?: RequestOptions): ApiPromise<VciSingleIssueResponse, VerifiableCredentialIssuer.VciSingleIssueApiError> + +
+
+ +### Description + +
+
+ +Issue a single verifiable credential + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciSingleIssueApi({ + serviceId: "some example string", + body: {}, + }); + // TODO: Handle 'response' of type VciSingleIssueResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciSingleIssueApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciSingleIssueApi({ + serviceId: "some example string", + body: {}, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciSingleIssueResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [VciSingleIssueRequest](src/models/vci-single-issue-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciSingleIssueApi(request)` + +- **OnSuccess**: [VciSingleIssueResponse](src/models/vci-single-issue-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciSingleIssueApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciSingleIssueApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciSingleIssueResponse, VerifiableCredentialIssuer.VciSingleIssueApiError>, with `result.value` of type [VciSingleIssueResponse](src/models/vci-single-issue-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+vciSingleParseApi(request: VerifiableCredentialIssuer.VciSingleParseApiRequest, options?: RequestOptions): ApiPromise<VciSingleParseResponse, VerifiableCredentialIssuer.VciSingleParseApiError> + +
+
+ +### Description + +
+
+ +Parse a single verifiable credential + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.verifiableCredentialIssuer.vciSingleParseApi({ + serviceId: "some example string", + }); + // TODO: Handle 'response' of type VciSingleParseResponse +} catch (err) { + // TODO: Handle 'err' of type VerifiableCredentialIssuer.VciSingleParseApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.verifiableCredentialIssuer.vciSingleParseApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type VciSingleParseResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| accessToken? | string | The access token that came along with the credential request. | +| requestContent? | string | The message body of the credential request. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.verifiableCredentialIssuer.vciSingleParseApi(request)` + +- **OnSuccess**: [VciSingleParseResponse](src/models/vci-single-parse-response.ts) +- **OnError**: throws [VerifiableCredentialIssuer.VciSingleParseApiError](src/resources/verifiable-credential-issuer.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.verifiableCredentialIssuer.vciSingleParseApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<VciSingleParseResponse, VerifiableCredentialIssuer.VciSingleParseApiError>, with `result.value` of type [VciSingleParseResponse](src/models/vci-single-parse-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## HardwareSecurityKey + +> Source: [HardwareSecurityKey](src/resources/hardware-security-key.ts) + +
+hskCreateApi(request: HardwareSecurityKey.HskCreateApiRequest, options?: RequestOptions): ApiPromise<HskCreateResponse, HardwareSecurityKey.HskCreateApiError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.hardwareSecurityKey.hskCreateApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type HskCreateResponse +} catch (err) { + // TODO: Handle 'err' of type HardwareSecurityKey.HskCreateApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.hardwareSecurityKey.hskCreateApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type HskCreateResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| kty? | string | The key type (EC or RSA) | +| use? | string | The key on the HSM. When the key use is "sig" (signature), the private key on the HSM is used to sign data and the corresponding public key is used to verify the signature. When the key use is "enc" (encryption), the private key on the HSM is used to decrypt encrypted data which have been encrypted with the corresponding public key | +| kid? | string | Key ID for the key on the HSM. | +| hsmName? | string | The name of the HSM. The identifier for the HSM that sits behind the Authlete server. For example, "google". | +| alg? | string | The algorithm of the key on the HSM. When the key use is `"sig"`, the algorithm represents a signing algorithm such as `"ES256"`. When the key use is `"enc"`, the algorithm represents an encryption algorithm such as `"RSA-OAEP-256"`.

It is rare that HSMs support all the algorithms listed in [RFC 7518 JSON Web Algorithms (JWA)](https://www.rfc-editor.org/rfc/rfc7518.html). When the specified algorithm is not supported by the HSM, the request to the `/hsk/create` API fails. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.hardwareSecurityKey.hskCreateApi(request)` + +- **OnSuccess**: [HskCreateResponse](src/models/hsk-create-response.ts) +- **OnError**: throws [HardwareSecurityKey.HskCreateApiError](src/resources/hardware-security-key.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.hardwareSecurityKey.hskCreateApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<HskCreateResponse, HardwareSecurityKey.HskCreateApiError>, with `result.value` of type [HskCreateResponse](src/models/hsk-create-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+hskDeleteApi(request: HardwareSecurityKey.HskDeleteApiRequest, options?: RequestOptions): ApiPromise<HskDeleteResponse, HardwareSecurityKey.HskDeleteApiError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.hardwareSecurityKey.hskDeleteApi({ + serviceId: "some example string", + handle: "some example string", + }); + // TODO: Handle 'response' of type HskDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type HardwareSecurityKey.HskDeleteApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.hardwareSecurityKey.hskDeleteApi({ + serviceId: "some example string", + handle: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type HskDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| handle | string | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.hardwareSecurityKey.hskDeleteApi(request)` + +- **OnSuccess**: [HskDeleteResponse](src/models/hsk-delete-response.ts) +- **OnError**: throws [HardwareSecurityKey.HskDeleteApiError](src/resources/hardware-security-key.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.hardwareSecurityKey.hskDeleteApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<HskDeleteResponse, HardwareSecurityKey.HskDeleteApiError>, with `result.value` of type [HskDeleteResponse](src/models/hsk-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+hskGetApi(request: HardwareSecurityKey.HskGetApiRequest, options?: RequestOptions): ApiPromise<HskDeleteResponse, HardwareSecurityKey.HskGetApiError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.hardwareSecurityKey.hskGetApi({ + serviceId: "some example string", + handle: "some example string", + }); + // TODO: Handle 'response' of type HskDeleteResponse +} catch (err) { + // TODO: Handle 'err' of type HardwareSecurityKey.HskGetApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.hardwareSecurityKey.hskGetApi({ + serviceId: "some example string", + handle: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type HskDeleteResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| handle | string | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.hardwareSecurityKey.hskGetApi(request)` + +- **OnSuccess**: [HskDeleteResponse](src/models/hsk-delete-response.ts) +- **OnError**: throws [HardwareSecurityKey.HskGetApiError](src/resources/hardware-security-key.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.hardwareSecurityKey.hskGetApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<HskDeleteResponse, HardwareSecurityKey.HskGetApiError>, with `result.value` of type [HskDeleteResponse](src/models/hsk-delete-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+hskGetListApi(request: HardwareSecurityKey.HskGetListApiRequest, options?: RequestOptions): ApiPromise<HskGetListResponse, HardwareSecurityKey.HskGetListApiError> + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.hardwareSecurityKey.hskGetListApi({ serviceId: "some example string" }); + // TODO: Handle 'response' of type HskGetListResponse +} catch (err) { + // TODO: Handle 'err' of type HardwareSecurityKey.HskGetListApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.hardwareSecurityKey.hskGetListApi({ + serviceId: "some example string", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type HskGetListResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.hardwareSecurityKey.hskGetListApi(request)` + +- **OnSuccess**: [HskGetListResponse](src/models/hsk-get-list-response.ts) +- **OnError**: throws [HardwareSecurityKey.HskGetListApiError](src/resources/hardware-security-key.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.hardwareSecurityKey.hskGetListApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<HskGetListResponse, HardwareSecurityKey.HskGetListApiError>, with `result.value` of type [HskGetListResponse](src/models/hsk-get-list-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## UtilityEndpoints + +> Source: [UtilityEndpoints](src/resources/utility-endpoints.ts) + +
+infoApi(options?: RequestOptions): ApiPromise<InfoResponse, UtilityEndpoints.InfoApiError> + +
+
+ +### Description + +
+
+ +get the server version and enabled features + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.utilityEndpoints.infoApi(); + // TODO: Handle 'response' of type InfoResponse +} catch (err) { + // TODO: Handle 'err' of type UtilityEndpoints.InfoApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.utilityEndpoints.infoApi().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type InfoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Response + +
+
+ +**Direct**: `await client.utilityEndpoints.infoApi()` + +- **OnSuccess**: [InfoResponse](src/models/info-response.ts) +- **OnError**: throws [UtilityEndpoints.InfoApiError](src/resources/utility-endpoints.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.utilityEndpoints.infoApi().asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<InfoResponse, UtilityEndpoints.InfoApiError>, with `result.value` of type [InfoResponse](src/models/info-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+miscEchoApi(options?: RequestOptions): ApiPromise<undefined, ApiError> + +
+
+ +### Description + +
+
+ +Echo test endpoint. Will return all path parameters in the request + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + await client.utilityEndpoints.miscEchoApi(); +} catch (err) { + // TODO: Handle 'err' of type ApiError, where 'err.payload' is always the Undeclared arm +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.utilityEndpoints.miscEchoApi().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: The call succeeded and resolves to no body +} else { + // TODO: Handle 'result', where 'result.payload' is always the Undeclared arm +} +``` + +
+
+ +### Response + +
+
+ +**Direct**: `await client.utilityEndpoints.miscEchoApi()` + +- **OnSuccess**: undefined +- **OnError**: throws [ApiError](src/core/api-error.ts), with `err.payload` always the [Undeclared](src/core/api-error.ts) arm + +**As ApiResult**: `await client.utilityEndpoints.miscEchoApi().asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<undefined, ApiError>, with `result.value` of type undefined +- **OnError**: `result.payload` always the [Undeclared](src/core/api-error.ts) arm, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## NativeSso + +> Source: [NativeSso](src/resources/native-sso.ts) + +
+nativeSsoApi(request: NativeSso.NativeSsoApiRequest, options?: RequestOptions): ApiPromise<NativeSsoResponse, NativeSso.NativeSsoApiError> + +
+
+ +### Description + +
+
+ +This API should be called by the implementation of a token endpoint to generate the ID token and token response that comply with [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO) when Authlete’s `/auth/token` response indicates `action = NATIVE_SSO` (after you validate the session id and verify or generate the device secret as required by the flow). The token endpoint implementation should retrieve the value of `action` from the response and take the following steps according to the value. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.nativeSso.nativeSsoApi({ + serviceId: "715948317", + body: { + accessToken: "_kh1aygxZ5NKLYKCJRM8M_AYvDg2wCWoprQDjfO87ZWq", + refreshToken: "kHUGSt_d3LSgiCQzH7wa5TpwIHWgjAZGw14zZV7hRqw", + claims: '{"given_name":"John","family_name":"Brown","email":"test@example.com"}', + deviceSecret: "my-ds", + }, + }); + // TODO: Handle 'response' of type NativeSsoResponse +} catch (err) { + // TODO: Handle 'err' of type NativeSso.NativeSsoApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.nativeSso.nativeSsoApi({ + serviceId: "715948317", + body: { + accessToken: "_kh1aygxZ5NKLYKCJRM8M_AYvDg2wCWoprQDjfO87ZWq", + refreshToken: "kHUGSt_d3LSgiCQzH7wa5TpwIHWgjAZGw14zZV7hRqw", + claims: '{"given_name":"John","family_name":"Brown","email":"test@example.com"}', + deviceSecret: "my-ds", + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type NativeSsoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [NativeSsoRequest](src/models/native-sso-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.nativeSso.nativeSsoApi(request)` + +- **OnSuccess**: [NativeSsoResponse](src/models/native-sso-response.ts) +- **OnError**: throws [NativeSso.NativeSsoApiError](src/resources/native-sso.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.nativeSso.nativeSsoApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<NativeSsoResponse, NativeSso.NativeSsoApiError>, with `result.value` of type [NativeSsoResponse](src/models/native-sso-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+nativeSsoLogoutApi(request: NativeSso.NativeSsoLogoutApiRequest, options?: RequestOptions): ApiPromise<NativeSsoLogoutResponse, NativeSso.NativeSsoLogoutApiError> + +
+
+ +### Description + +
+
+ +The `/nativesso/logout` API is supposed to be used to support the concept of "logout from all applications" in the context of [OpenID Connect Native SSO for Mobile Apps 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO). This is accomplished by deleting access/refresh token records associated with the specified session ID. In Authlete's implementation, access/refresh token records can be associated with a session ID only through the mechanism introduced by Native SSO. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.nativeSso.nativeSsoLogoutApi({ + serviceId: "some example string", + body: { sessionId: "my-sid" }, + }); + // TODO: Handle 'response' of type NativeSsoLogoutResponse +} catch (err) { + // TODO: Handle 'err' of type NativeSso.NativeSsoLogoutApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.nativeSso.nativeSsoLogoutApi({ + serviceId: "some example string", + body: { sessionId: "my-sid" }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type NativeSsoLogoutResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [NativeSsoLogoutRequest](src/models/native-sso-logout-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.nativeSso.nativeSsoLogoutApi(request)` + +- **OnSuccess**: [NativeSsoLogoutResponse](src/models/native-sso-logout-response.ts) +- **OnError**: throws [NativeSso.NativeSsoLogoutApiError](src/resources/native-sso.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.nativeSso.nativeSsoLogoutApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<NativeSsoLogoutResponse, NativeSso.NativeSsoLogoutApiError>, with `result.value` of type [NativeSsoLogoutResponse](src/models/native-sso-logout-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## AuditLogs + +> Source: [AuditLogs](src/resources/audit-logs.ts) + +
+auditEntriesGetIdpApi(request: AuditLogs.AuditEntriesGetIdpApiRequest, options?: RequestOptions): ApiPromise<AuditEntriesGetResponse, AuditLogs.AuditEntriesGetIdpApiError> + +
+
+ +### Description + +
+
+ +Retrieve audit logs as a cursor-paginated list. Results can be filtered by time range, event type, and organization. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.auditLogs.auditEntriesGetIdpApi(); + // TODO: Handle 'response' of type AuditEntriesGetResponse +} catch (err) { + // TODO: Handle 'err' of type AuditLogs.AuditEntriesGetIdpApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.auditLogs.auditEntriesGetIdpApi().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AuditEntriesGetResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| after? | Date (date-time) | Return logs after this timestamp (ISO 8601). | +| before? | Date (date-time) | Return logs before this timestamp (ISO 8601). | +| types? | string[] | Filter by event type (e.g. `service.create`, `security.login`, `client.create`). Can be specified multiple times. The set of available event types is environment-specific and includes server-side types; retrieve the full list from the `/api/audit/types` endpoint. | +| organizationId? | number | The organization to retrieve audit logs for. Not required when authenticating with an organization token β€” the token's own organization is used, and a differing value is rejected. Required for user access tokens, unless the user is an Authlete administrator (administrators may omit it to query across all organizations). | +| limit? | number | The maximum number of entries to return per page. Values outside the allowed range are clamped; the effective value is echoed back in the response.
**Default**: 100 | +| cursor? | string | An opaque cursor returned as `nextCursor` by a previous response. When provided, it takes precedence over the `before` parameter. | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.auditLogs.auditEntriesGetIdpApi(request)` + +- **OnSuccess**: [AuditEntriesGetResponse](src/models/audit-entries-get-response.ts) +- **OnError**: throws [AuditLogs.AuditEntriesGetIdpApiError](src/resources/audit-logs.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.auditLogs.auditEntriesGetIdpApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AuditEntriesGetResponse, AuditLogs.AuditEntriesGetIdpApiError>, with `result.value` of type [AuditEntriesGetResponse](src/models/audit-entries-get-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+auditTypesGetIdpApi(options?: RequestOptions): ApiPromise<string[], AuditLogs.AuditTypesGetIdpApiError> + +
+
+ +### Description + +
+
+ +Returns the list of available audit log event types for this environment. + +Accessible with a user access token or an organization token that has the `view_audit_log` permission (e.g. a token created with the `admin` or `audit_reader` preset). + +This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the regional API clusters. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.auditLogs.auditTypesGetIdpApi(); + // TODO: Handle 'response' of type string[] +} catch (err) { + // TODO: Handle 'err' of type AuditLogs.AuditTypesGetIdpApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.auditLogs.auditTypesGetIdpApi().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type string[] +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Response + +
+
+ +**Direct**: `await client.auditLogs.auditTypesGetIdpApi()` + +- **OnSuccess**: string[] +- **OnError**: throws [AuditLogs.AuditTypesGetIdpApiError](src/resources/audit-logs.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.auditLogs.auditTypesGetIdpApi().asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<string[], AuditLogs.AuditTypesGetIdpApiError>, with `result.value` of type string[] +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## BackChannelLogout + +> Source: [BackChannelLogout](src/resources/back-channel-logout.ts) + +
+backchannelLogoutTokenApi(request: BackChannelLogout.BackchannelLogoutTokenApiRequest, options?: RequestOptions): ApiPromise<BackchannelLogoutTokenResponse, BackChannelLogout.BackchannelLogoutTokenApiError> + +
+
+ +### Description + +
+
+ +The `/backchannel/logout/token` API issues a logout token for a client application in the context of [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html). + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.backChannelLogout.backchannelLogoutTokenApi({ + serviceId: "some example string", + body: { clientIdentifier: "1140735077", subject: "user123", sessionId: "my-sid" }, + }); + // TODO: Handle 'response' of type BackchannelLogoutTokenResponse +} catch (err) { + // TODO: Handle 'err' of type BackChannelLogout.BackchannelLogoutTokenApiError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.backChannelLogout.backchannelLogoutTokenApi({ + serviceId: "some example string", + body: { clientIdentifier: "1140735077", subject: "user123", sessionId: "my-sid" }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type BackchannelLogoutTokenResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| serviceId | string | A service ID. | +| body | [BackchannelLogoutTokenRequest](src/models/backchannel-logout-token-request.ts) | - | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.backChannelLogout.backchannelLogoutTokenApi(request)` + +- **OnSuccess**: [BackchannelLogoutTokenResponse](src/models/backchannel-logout-token-response.ts) +- **OnError**: throws [BackChannelLogout.BackchannelLogoutTokenApiError](src/resources/back-channel-logout.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.backChannelLogout.backchannelLogoutTokenApi(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<BackchannelLogoutTokenResponse, BackChannelLogout.BackchannelLogoutTokenApiError>, with `result.value` of type [BackchannelLogoutTokenResponse](src/models/backchannel-logout-token-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## Lifecycle + +> Source: [Lifecycle](src/resources/lifecycle.ts) + +
+healthCheck(request: Lifecycle.HealthCheckRequest, options?: RequestOptions): ApiPromise<string, Lifecycle.HealthCheckError> + +
+
+ +### Description + +
+
+ +Perform a health check of the server. + + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.lifecycle.healthCheck(); + // TODO: Handle 'response' of type string +} catch (err) { + // TODO: Handle 'err' of type Lifecycle.HealthCheckError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.lifecycle.healthCheck().asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type string +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| extended? | boolean | If `true`, perform extended health checks (e.g. database connectivity). | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.lifecycle.healthCheck(request)` + +- **OnSuccess**: string +- **OnError**: throws [Lifecycle.HealthCheckError](src/resources/lifecycle.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.lifecycle.healthCheck(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<string, Lifecycle.HealthCheckError>, with `result.value` of type string +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AuthleteApiError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ diff --git a/plugins/authlete/sdk/typescript/map/operations/audit-logs.md b/plugins/authlete/sdk/typescript/map/operations/audit-logs.md new file mode 100644 index 00000000..5e81744c --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/audit-logs.md @@ -0,0 +1,48 @@ + + +# AuditLogs β€” operations + +Accessor: `client.auditLogs` Β· Source: `src/resources/audit-logs.ts` Β· 2 operations Β· Request and error types: namespace `AuditLogs` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### auditEntriesGetIdpApi + +- **Signature**: `auditEntriesGetIdpApi(request: AuditLogs.AuditEntriesGetIdpApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/audit/entries` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `AuditEntriesGetResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuditLogs.AuditEntriesGetIdpApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"idpError"` [400] `IdpError` Β· `"idpError2"` [401] `IdpError` Β· `"idpError3"` [403] `IdpError` Β· `"idpError4"` [500] `IdpError` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuditLogs.AuditEntriesGetIdpApiRequest` (6): + +| Field | Channel | Type | Req | Default | +| --- | --- | --- | --- | --- | +| `after` | `query` | `Date` (date-time) | no | β€” | +| `before` | `query` | `Date` (date-time) | no | β€” | +| `types` | `query` | `string[]` | no | β€” | +| `organizationId` | `query` | `number` | no | β€” | +| `limit` | `query` | `number` | no | `100` | +| `cursor` | `query` | `string` | no | β€” | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AuditEntriesGetResponse` | `auditEntriesGetResponseSchema` | `src/models/audit-entries-get-response.ts` | +| `IdpError` | `idpErrorSchema` | `src/models/idp-error.ts` | + +### auditTypesGetIdpApi + +- **Signature**: `auditTypesGetIdpApi(options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/audit/types` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `string[]` β€” a bare `application/json` array; the success type *is* the array, not a wrapper model +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuditLogs.AuditTypesGetIdpApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"idpError"` [400] `IdpError` Β· `"idpError2"` [401] `IdpError` Β· `"idpError3"` [403] `IdpError` Β· `"idpError4"` [500] `IdpError` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +| Type | Schema value | Source | +| --- | --- | --- | +| `IdpError` | `idpErrorSchema` | `src/models/idp-error.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/authorization-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/authorization-endpoint.md new file mode 100644 index 00000000..d3d61139 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/authorization-endpoint.md @@ -0,0 +1,150 @@ + + +# AuthorizationEndpoint β€” operations + +Accessor: `client.authorizationEndpoint` Β· Source: `src/resources/authorization-endpoint.ts` Β· 5 operations Β· Request and error types: namespace `AuthorizationEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authAuthorizationApi + +- **Signature**: `authAuthorizationApi(request: AuthorizationEndpoint.AuthAuthorizationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/authorization` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AuthorizationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuthorizationEndpoint.AuthAuthorizationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuthorizationEndpoint.AuthAuthorizationApiRequest` (4): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `context` | `form` | `string` | no | +| `cimdOptions` | `form` | `CimdOptions` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `CimdOptions` | `cimdOptionsSchema` | `src/models/cimd-options.ts` | +| `AuthorizationResponse` | `authorizationResponseSchema` | `src/models/authorization-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authAuthorizationFailApi + +- **Signature**: `authAuthorizationFailApi(request: AuthorizationEndpoint.AuthAuthorizationFailApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/authorization/fail` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AuthorizationFailResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuthorizationEndpoint.AuthAuthorizationFailApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuthorizationEndpoint.AuthAuthorizationFailApiRequest` (4): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `reason` | `form` | `Reason` | yes | +| `description` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Reason` | `reasonSchema` | `src/models/reason.ts` | +| `AuthorizationFailResponse` | `authorizationFailResponseSchema` | `src/models/authorization-fail-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authAuthorizationIssueApi + +- **Signature**: `authAuthorizationIssueApi(request: AuthorizationEndpoint.AuthAuthorizationIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/authorization/issue` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AuthorizationIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuthorizationEndpoint.AuthAuthorizationIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuthorizationEndpoint.AuthAuthorizationIssueApiRequest` (19): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `subject` | `form` | `string` | yes | +| `authTime` | `form` | `number` | no | +| `acr` | `form` | `string` | no | +| `claims` | `form` | `string` | no | +| `properties` | `form` | `Property[]` | no | +| `scopes` | `form` | `string[]` | no | +| `sub` | `form` | `string` | no | +| `idtHeaderParams` | `form` | `string` | no | +| `claimsForTx` | `form` | `string` | no | +| `consentedClaims` | `form` | `string[]` | no | +| `authorizationDetails` | `form` | `AuthzDetails` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `accessToken` | `form` | `string` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `sessionId` | `form` | `string` | no | +| `idTokenAudType` | `form` | `string` | no | +| `verifiedClaimsForTx` | `form` | `string[]` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `AuthzDetails` | `authzDetailsSchema` | `src/models/authz-details.ts` | +| `AuthorizationIssueResponse` | `authorizationIssueResponseSchema` | `src/models/authorization-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### getAuthorizationTicketInfo + +- **Signature**: `getAuthorizationTicketInfo(request: AuthorizationEndpoint.GetAuthorizationTicketInfoRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/authorization/ticket/info` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AuthorizationTicketInfoResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuthorizationEndpoint.GetAuthorizationTicketInfoError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuthorizationEndpoint.GetAuthorizationTicketInfoRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AuthorizationTicketInfoResponse` | `authorizationTicketInfoResponseSchema` | `src/models/authorization-ticket-info-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### updateAuthorizationTicket + +- **Signature**: `updateAuthorizationTicket(request: AuthorizationEndpoint.UpdateAuthorizationTicketRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/authorization/ticket/update` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AuthorizationTicketUpdateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `AuthorizationEndpoint.UpdateAuthorizationTicketError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `AuthorizationEndpoint.UpdateAuthorizationTicketRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `info` | `form` | `AuthorizationTicketInfo` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AuthorizationTicketInfo` | `authorizationTicketInfoSchema` | `src/models/authorization-ticket-info.ts` | +| `AuthorizationTicketUpdateResponse` | `authorizationTicketUpdateResponseSchema` | `src/models/authorization-ticket-update-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/back-channel-logout.md b/plugins/authlete/sdk/typescript/map/operations/back-channel-logout.md new file mode 100644 index 00000000..4d5305e7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/back-channel-logout.md @@ -0,0 +1,32 @@ + + +# BackChannelLogout β€” operations + +Accessor: `client.backChannelLogout` Β· Source: `src/resources/back-channel-logout.ts` Β· 1 operation Β· Request and error types: namespace `BackChannelLogout` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### backchannelLogoutTokenApi + +- **Signature**: `backchannelLogoutTokenApi(request: BackChannelLogout.BackchannelLogoutTokenApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/backchannel/logout/token` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `BackchannelLogoutTokenResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `BackChannelLogout.BackchannelLogoutTokenApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `BackChannelLogout.BackchannelLogoutTokenApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `BackchannelLogoutTokenRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BackchannelLogoutTokenRequest` | `backchannelLogoutTokenRequestSchema` | `src/models/backchannel-logout-token-request.ts` | +| `BackchannelLogoutTokenResponse` | `backchannelLogoutTokenResponseSchema` | `src/models/backchannel-logout-token-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/ciba.md b/plugins/authlete/sdk/typescript/map/operations/ciba.md new file mode 100644 index 00000000..5d475a38 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/ciba.md @@ -0,0 +1,131 @@ + + +# Ciba β€” operations + +Accessor: `client.ciba` Β· Source: `src/resources/ciba.ts` Β· 4 operations Β· Request and error types: namespace `Ciba` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### backchannelAuthenticationApi + +- **Signature**: `backchannelAuthenticationApi(request: Ciba.BackchannelAuthenticationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/backchannel/authentication` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `BackchannelAuthenticationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `Ciba.BackchannelAuthenticationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Ciba.BackchannelAuthenticationApiRequest` (9): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `clientId` | `form` | `string` | no | +| `clientSecret` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `clientCertificatePath` | `form` | `string[]` | no | +| `oauthClientAttestation` | `form` | `string` | no | +| `oauthClientAttestationPop` | `form` | `string` | no | +| `cimdOptions` | `form` | `CimdOptions` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `CimdOptions` | `cimdOptionsSchema` | `src/models/cimd-options.ts` | +| `BackchannelAuthenticationResponse` | `backchannelAuthenticationResponseSchema` | `src/models/backchannel-authentication-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### backchannelAuthenticationCompleteApi + +- **Signature**: `backchannelAuthenticationCompleteApi(request: Ciba.BackchannelAuthenticationCompleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/backchannel/authentication/complete` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `BackchannelAuthenticationCompleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `Ciba.BackchannelAuthenticationCompleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Ciba.BackchannelAuthenticationCompleteApiRequest` (19): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `result` | `form` | `Result1` | yes | +| `subject` | `form` | `string` | yes | +| `sub` | `form` | `string` | no | +| `authTime` | `form` | `number` | no | +| `acr` | `form` | `string` | no | +| `claims` | `form` | `string` | no | +| `properties` | `form` | `Property[]` | no | +| `scopes` | `form` | `string[]` | no | +| `idtHeaderParams` | `form` | `string` | no | +| `errorDescription` | `form` | `string` | no | +| `errorUri` | `form` | `string` | no | +| `consentedClaims` | `form` | `string[]` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `accessToken` | `form` | `string` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `refreshTokenDuration` | `form` | `number` | no | +| `idTokenAudType` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result1` | `result1Schema` | `src/models/result1.ts` | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `BackchannelAuthenticationCompleteResponse` | `backchannelAuthenticationCompleteResponseSchema` | `src/models/backchannel-authentication-complete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### backchannelAuthenticationFailApi + +- **Signature**: `backchannelAuthenticationFailApi(request: Ciba.BackchannelAuthenticationFailApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/backchannel/authentication/fail` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `BackchannelAuthenticationFailResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `Ciba.BackchannelAuthenticationFailApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Ciba.BackchannelAuthenticationFailApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `reason` | `form` | `Reason2` | yes | +| `errorDescription` | `form` | `string` | no | +| `errorUri` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Reason2` | `reason2Schema` | `src/models/reason2.ts` | +| `BackchannelAuthenticationFailResponse` | `backchannelAuthenticationFailResponseSchema` | `src/models/backchannel-authentication-fail-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### backchannelAuthenticationIssueApi + +- **Signature**: `backchannelAuthenticationIssueApi(request: Ciba.BackchannelAuthenticationIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/backchannel/authentication/issue` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `BackchannelAuthenticationIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `Ciba.BackchannelAuthenticationIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Ciba.BackchannelAuthenticationIssueApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BackchannelAuthenticationIssueResponse` | `backchannelAuthenticationIssueResponseSchema` | `src/models/backchannel-authentication-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/client-management.md b/plugins/authlete/sdk/typescript/map/operations/client-management.md new file mode 100644 index 00000000..5872d429 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/client-management.md @@ -0,0 +1,747 @@ + + +# ClientManagement β€” operations + +Accessor: `client.clientManagement` Β· Source: `src/resources/client-management.ts` Β· 26 operations Β· Request and error types: namespace `ClientManagement` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### clientAuthorizationDeleteApi + +- **Signature**: `clientAuthorizationDeleteApi(request: ClientManagement.ClientAuthorizationDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/authorization/delete/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationDeleteApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `query` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationDeleteApiPost + +- **Signature**: `clientAuthorizationDeleteApiPost(request: ClientManagement.ClientAuthorizationDeleteApiPostRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/authorization/delete/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationDeleteApiPostError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [429] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationDeleteApiPostRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationDeleteBySubjectApi + +- **Signature**: `clientAuthorizationDeleteBySubjectApi(request: ClientManagement.ClientAuthorizationDeleteBySubjectApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/authorization/delete/{clientId}/{subject}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationDeleteBySubjectApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationDeleteBySubjectApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationGetListApi + +- **Signature**: `clientAuthorizationGetListApi(request: ClientManagement.ClientAuthorizationGetListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/authorization/get/list` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientAuthorizationGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationGetListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationGetListApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `subject` | `query` | `string` | yes | +| `developer` | `query` | `string` | no | +| `start` | `query` | `number` | no | +| `end` | `query` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationGetListResponse` | `clientAuthorizationGetListResponseSchema` | `src/models/client-authorization-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationGetListApiPost + +- **Signature**: `clientAuthorizationGetListApiPost(request: ClientManagement.ClientAuthorizationGetListApiPostRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/authorization/get/list` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientAuthorizationGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationGetListApiPostError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationGetListApiPostRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `subject` | `form` | `string` | yes | +| `developer` | `form` | `string` | no | +| `start` | `form` | `number` | no | +| `end` | `form` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationGetListResponse` | `clientAuthorizationGetListResponseSchema` | `src/models/client-authorization-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationGetListBySubjectApi + +- **Signature**: `clientAuthorizationGetListBySubjectApi(request: ClientManagement.ClientAuthorizationGetListBySubjectApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/authorization/get/list/{subject}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientAuthorizationGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationGetListBySubjectApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationGetListBySubjectApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `subject` | `path` | `string` | yes | +| `developer` | `query` | `string` | no | +| `start` | `query` | `number` | no | +| `end` | `query` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationGetListResponse` | `clientAuthorizationGetListResponseSchema` | `src/models/client-authorization-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientAuthorizationUpdateApi + +- **Signature**: `clientAuthorizationUpdateApi(request: ClientManagement.ClientAuthorizationUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/authorization/update/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Result` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientAuthorizationUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"error404"` [404] no body Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientAuthorizationUpdateApiRequest` (4): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `form` | `string` | yes | +| `scopes` | `form` | `string[]` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientCreateApi + +- **Signature**: `clientCreateApi(request: ClientManagement.ClientCreateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/create` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field. **Optional**: omit it and the request carries no body and no `Content-Type` header at all +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Client` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientCreateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientCreateApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `Client` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Client` | `clientSchema` | `src/models/client.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientCursorListApi + +- **Signature**: `clientCursorListApi(request: ClientManagement.ClientCursorListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientCursorListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientCursorListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientCursorListApiRequest` (4): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `number` | yes | +| `cursor` | `query` | `string` | no | +| `limit` | `query` | `number` | no | +| `includeTotalCount` | `query` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientCursorListResponse` | `clientCursorListResponseSchema` | `src/models/client-cursor-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientDeleteApi + +- **Signature**: `clientDeleteApi(request: ClientManagement.ClientDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/delete/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"error404"` [404] no body Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientDeleteApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientExtensionRequestablesScopesDeleteApi + +- **Signature**: `clientExtensionRequestablesScopesDeleteApi(request: ClientManagement.ClientExtensionRequestablesScopesDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/extension/requestable_scopes/delete/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientExtensionRequestablesScopesDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientExtensionRequestablesScopesDeleteApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientExtensionRequestablesScopesGetApi + +- **Signature**: `clientExtensionRequestablesScopesGetApi(request: ClientManagement.ClientExtensionRequestablesScopesGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/extension/requestable_scopes/get/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientExtensionRequestableScopesGetResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientExtensionRequestablesScopesGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientExtensionRequestablesScopesGetApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientExtensionRequestableScopesGetResponse` | `clientExtensionRequestableScopesGetResponseSchema` | `src/models/client-extension-requestable-scopes-get-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientExtensionRequestablesScopesUpdateApi + +- **Signature**: `clientExtensionRequestablesScopesUpdateApi(request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `PUT /api/{serviceId}/client/extension/requestable_scopes/update/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientExtensionRequestableScopesUpdateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientExtensionRequestablesScopesUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientExtensionRequestablesScopesUpdateApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `body` | `body` | `ClientExtensionRequestableScopesUpdateRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientExtensionRequestableScopesUpdateRequest` | `clientExtensionRequestableScopesUpdateRequestSchema` | `src/models/client-extension-requestable-scopes-update-request.ts` | +| `ClientExtensionRequestableScopesUpdateResponse` | `clientExtensionRequestableScopesUpdateResponseSchema` | `src/models/client-extension-requestable-scopes-update-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientExtensionRequestablesScopesUpdateApiPost + +- **Signature**: `clientExtensionRequestablesScopesUpdateApiPost(request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/extension/requestable_scopes/update/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientExtensionRequestableScopesUpdateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `body` | `body` | `ClientExtensionRequestableScopesUpdateRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientExtensionRequestableScopesUpdateRequest` | `clientExtensionRequestableScopesUpdateRequestSchema` | `src/models/client-extension-requestable-scopes-update-request.ts` | +| `ClientExtensionRequestableScopesUpdateResponse` | `clientExtensionRequestableScopesUpdateResponseSchema` | `src/models/client-extension-requestable-scopes-update-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientFlagUpdateApi + +- **Signature**: `clientFlagUpdateApi(request: ClientManagement.ClientFlagUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/lock_flag/update/{clientIdentifier}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientFlagUpdateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientFlagUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientFlagUpdateApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientIdentifier` | `path` | `string` | yes | +| `clientLocked` | `form` | `boolean` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientFlagUpdateResponse` | `clientFlagUpdateResponseSchema` | `src/models/client-flag-update-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGetApi + +- **Signature**: `clientGetApi(request: ClientManagement.ClientGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/get/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `Client` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [404] `Result` Β· `"result5"` [429] `Result` Β· `"result6"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGetApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Client` | `clientSchema` | `src/models/client.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGetListApi + +- **Signature**: `clientGetListApi(request: ClientManagement.ClientGetListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/get/list` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGetListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGetListApiRequest` (4): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `developer` | `query` | `string` | no | +| `start` | `query` | `number` | no | +| `end` | `query` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientGetListResponse` | `clientGetListResponseSchema` | `src/models/client-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGrantedScopesDeleteApi + +- **Signature**: `clientGrantedScopesDeleteApi(request: ClientManagement.ClientGrantedScopesDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/granted_scopes/delete/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Result` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGrantedScopesDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGrantedScopesDeleteApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `query` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGrantedScopesDeleteBySubjectApi + +- **Signature**: `clientGrantedScopesDeleteBySubjectApi(request: ClientManagement.ClientGrantedScopesDeleteBySubjectApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/client/granted_scopes/delete/{clientId}/{subject}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Result` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGrantedScopesDeleteBySubjectApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGrantedScopesDeleteBySubjectApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGrantedScopesGetApi + +- **Signature**: `clientGrantedScopesGetApi(request: ClientManagement.ClientGrantedScopesGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/granted_scopes/get/{clientId}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGrantedScopesGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGrantedScopesGetApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `query` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGrantedScopesGetApiPost + +- **Signature**: `clientGrantedScopesGetApiPost(request: ClientManagement.ClientGrantedScopesGetApiPostRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/granted_scopes/get/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGrantedScopesGetApiPostError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [429] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGrantedScopesGetApiPostRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientGrantedScopesGetBySubjectApi + +- **Signature**: `clientGrantedScopesGetBySubjectApi(request: ClientManagement.ClientGrantedScopesGetBySubjectApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/granted_scopes/get/{clientId}/{subject}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientAuthorizationDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientGrantedScopesGetBySubjectApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientGrantedScopesGetBySubjectApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientId` | `path` | `string` | yes | +| `subject` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientAuthorizationDeleteResponse` | `clientAuthorizationDeleteResponseSchema` | `src/models/client-authorization-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientSearchApi + +- **Signature**: `clientSearchApi(request: ClientManagement.ClientSearchApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/search` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientSearchResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientSearchApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientSearchApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `number` | yes | +| `q` | `query` | `string` | no | +| `cursor` | `query` | `string` | no | +| `limit` | `query` | `number` | no | +| `includeTotalCount` | `query` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientSearchResponse` | `clientSearchResponseSchema` | `src/models/client-search-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientSecretRefreshApi + +- **Signature**: `clientSecretRefreshApi(request: ClientManagement.ClientSecretRefreshApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/client/secret/refresh/{clientIdentifier}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ClientSecretRefreshResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientSecretRefreshApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientSecretRefreshApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientIdentifier` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientSecretRefreshResponse` | `clientSecretRefreshResponseSchema` | `src/models/client-secret-refresh-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientSecretUpdateApi + +- **Signature**: `clientSecretUpdateApi(request: ClientManagement.ClientSecretUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/secret/update/{clientIdentifier}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientSecretRefreshResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientSecretUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientSecretUpdateApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientIdentifier` | `path` | `string` | yes | +| `clientSecret` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ClientSecretRefreshResponse` | `clientSecretRefreshResponseSchema` | `src/models/client-secret-refresh-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientUpdateApi + +- **Signature**: `clientUpdateApi(request: ClientManagement.ClientUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/update/{clientId}` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Client` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ClientManagement.ClientUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"error404"` [404] no body Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ClientManagement.ClientUpdateApiRequest` (110): + +| Field | Channel | Wire | Type | Req | +| --- | --- | --- | --- | --- | +| `serviceId` | `path` | β€” | `string` | yes | +| `clientIdTemplate` | `path` | `clientId` | `string` | yes | +| `number` | `form` | β€” | `number` | no | +| `serviceNumber` | `form` | β€” | `number` | no | +| `clientName` | `form` | β€” | `string` | no | +| `clientNames` | `form` | β€” | `TaggedValue[]` | no | +| `description` | `form` | β€” | `string` | no | +| `descriptions` | `form` | β€” | `TaggedValue[]` | no | +| `clientId` | `form` | β€” | `number` | no | +| `clientSecret` | `form` | β€” | `string` | no | +| `clientIdAlias` | `form` | β€” | `string` | no | +| `clientIdAliasEnabled` | `form` | β€” | `boolean` | no | +| `clientType` | `form` | β€” | `ClientType` | no | +| `applicationType` | `form` | β€” | `ApplicationType` | no | +| `logoUri` | `form` | β€” | `string` | no | +| `logoUris` | `form` | β€” | `TaggedValue[]` | no | +| `contacts` | `form` | β€” | `string[]` | no | +| `tlsClientCertificateBoundAccessTokens` | `form` | β€” | `boolean` | no | +| `dynamicallyRegistered` | `form` | β€” | `boolean` | no | +| `softwareId` | `form` | β€” | `string` | no | +| `developer` | `form` | β€” | `string` | no | +| `softwareVersion` | `form` | β€” | `string` | no | +| `registrationAccessTokenHash` | `form` | β€” | `string` | no | +| `createdAt` | `form` | β€” | `number` | no | +| `modifiedAt` | `form` | β€” | `number` | no | +| `grantTypes` | `form` | β€” | `GrantType[]` | no | +| `responseTypes` | `form` | β€” | `ResponseType[]` | no | +| `redirectUris` | `form` | β€” | `string[]` | no | +| `authorizationSignAlg` | `form` | β€” | `JwsAlg` | no | +| `authorizationEncryptionAlg` | `form` | β€” | `JweAlg` | no | +| `authorizationEncryptionEnc` | `form` | β€” | `JweEnc` | no | +| `tokenAuthMethod` | `form` | β€” | `ClientAuthMethod` | no | +| `tokenAuthSignAlg` | `form` | β€” | `JwsAlg` | no | +| `selfSignedCertificateKeyId` | `form` | β€” | `string` | no | +| `tlsClientAuthSubjectDn` | `form` | β€” | `string` | no | +| `tlsClientAuthSanDns` | `form` | β€” | `string` | no | +| `tlsClientAuthSanUri` | `form` | β€” | `string` | no | +| `tlsClientAuthSanIp` | `form` | β€” | `string` | no | +| `tlsClientAuthSanEmail` | `form` | β€” | `string` | no | +| `parRequired` | `form` | β€” | `boolean` | no | +| `requestObjectRequired` | `form` | β€” | `boolean` | no | +| `requestSignAlg` | `form` | β€” | `JwsAlg` | no | +| `requestEncryptionAlg` | `form` | β€” | `JweAlg` | no | +| `requestEncryptionEnc` | `form` | β€” | `JweEnc` | no | +| `requestUris` | `form` | β€” | `string[]` | no | +| `defaultMaxAge` | `form` | β€” | `number` | no | +| `defaultAcrs` | `form` | β€” | `string[]` | no | +| `idTokenSignAlg` | `form` | β€” | `JwsAlg` | no | +| `idTokenEncryptionAlg` | `form` | β€” | `JweAlg` | no | +| `idTokenEncryptionEnc` | `form` | β€” | `JweEnc` | no | +| `authTimeRequired` | `form` | β€” | `boolean` | no | +| `subjectType` | `form` | β€” | `SubjectType` | no | +| `sectorIdentifierUri` | `form` | β€” | `string` | no | +| `derivedSectorIdentifier` | `form` | β€” | `string` | no | +| `jwksUri` | `form` | β€” | `string` | no | +| `jwks` | `form` | β€” | `string` | no | +| `userInfoSignAlg` | `form` | β€” | `JwsAlg` | no | +| `userInfoEncryptionAlg` | `form` | β€” | `JweAlg` | no | +| `userInfoEncryptionEnc` | `form` | β€” | `JweEnc` | no | +| `loginUri` | `form` | β€” | `string` | no | +| `tosUri` | `form` | β€” | `string` | no | +| `tosUris` | `form` | β€” | `TaggedValue[]` | no | +| `policyUri` | `form` | β€” | `string` | no | +| `policyUris` | `form` | β€” | `TaggedValue[]` | no | +| `clientUri` | `form` | β€” | `string` | no | +| `clientUris` | `form` | β€” | `TaggedValue[]` | no | +| `bcDeliveryMode` | `form` | β€” | `string` | no | +| `bcNotificationEndpoint` | `form` | β€” | `string` | no | +| `bcRequestSignAlg` | `form` | β€” | `JwsAlg` | no | +| `bcUserCodeRequired` | `form` | β€” | `boolean` | no | +| `backchannelLogoutUri` | `form` | β€” | `string` | no | +| `backchannelLogoutSessionRequired` | `form` | β€” | `boolean` | no | +| `attributes` | `form` | β€” | `Pair[]` | no | +| `extension` | `form` | β€” | `ClientExtension` | no | +| `authorizationDetailsTypes` | `form` | β€” | `string[]` | no | +| `customMetadata` | `form` | β€” | `string` | no | +| `frontChannelRequestObjectEncryptionRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionAlgMatchRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionEncMatchRequired` | `form` | β€” | `boolean` | no | +| `digestAlgorithm` | `form` | β€” | `string` | no | +| `singleAccessTokenPerSubject` | `form` | β€” | `boolean` | no | +| `pkceRequired` | `form` | β€” | `boolean` | no | +| `pkceS256Required` | `form` | β€” | `boolean` | no | +| `dpopRequired` | `form` | β€” | `boolean` | no | +| `automaticallyRegistered` | `form` | β€” | `boolean` | no | +| `explicitlyRegistered` | `form` | β€” | `boolean` | no | +| `rsRequestSigned` | `form` | β€” | `boolean` | no | +| `rsSignedRequestKeyId` | `form` | β€” | `string` | no | +| `clientRegistrationTypes` | `form` | β€” | `ClientRegistrationType[]` | no | +| `organizationName` | `form` | β€” | `string` | no | +| `signedJwksUri` | `form` | β€” | `string` | no | +| `entityId` | `form` | β€” | `string` | no | +| `trustAnchorId` | `form` | β€” | `string` | no | +| `trustChain` | `form` | β€” | `string[]` | no | +| `trustChainExpiresAt` | `form` | β€” | `number` | no | +| `trustChainUpdatedAt` | `form` | β€” | `number` | no | +| `locked` | `form` | β€” | `boolean` | no | +| `credentialOfferEndpoint` | `form` | β€” | `string` | no | +| `fapiModes` | `form` | β€” | `FapiMode[]` | no | +| `responseModes` | `form` | β€” | `ResponseMode[]` | no | +| `credentialResponseEncryptionRequired` | `form` | β€” | `boolean` | no | +| `mtlsEndpointAliasesUsed` | `form` | β€” | `boolean` | no | +| `inScopeForTokenMigration` | `form` | β€” | `boolean` | no | +| `metadataDocumentLocation` | `form` | β€” | `string` | no | +| `metadataDocumentExpiresAt` | `form` | β€” | `number` | no | +| `metadataDocumentUpdatedAt` | `form` | β€” | `number` | no | +| `discoveredByMetadataDocument` | `form` | β€” | `boolean` | no | +| `clientSource` | `form` | β€” | `ClientSource` | no | +| `spiffeId` | `form` | β€” | `string` | no | +| `spiffeBundleEndpoint` | `form` | β€” | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `TaggedValue` | `taggedValueSchema` | `src/models/tagged-value.ts` | +| `ClientType` | `clientTypeSchema` | `src/models/client-type.ts` | +| `ApplicationType` | `applicationTypeSchema` | `src/models/application-type.ts` | +| `GrantType` | `grantTypeSchema` | `src/models/grant-type.ts` | +| `ResponseType` | `responseTypeSchema` | `src/models/response-type.ts` | +| `JwsAlg` | `jwsAlgSchema` | `src/models/jws-alg.ts` | +| `JweAlg` | `jweAlgSchema` | `src/models/jwe-alg.ts` | +| `JweEnc` | `jweEncSchema` | `src/models/jwe-enc.ts` | +| `ClientAuthMethod` | `clientAuthMethodSchema` | `src/models/client-auth-method.ts` | +| `SubjectType` | `subjectTypeSchema` | `src/models/subject-type.ts` | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `ClientExtension` | `clientExtensionSchema` | `src/models/client-extension.ts` | +| `ClientRegistrationType` | `clientRegistrationTypeSchema` | `src/models/client-registration-type.ts` | +| `FapiMode` | `fapiModeSchema` | `src/models/fapi-mode.ts` | +| `ResponseMode` | `responseModeSchema` | `src/models/response-mode.ts` | +| `ClientSource` | `clientSourceSchema` | `src/models/client-source.ts` | +| `Client` | `clientSchema` | `src/models/client.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/device-flow.md b/plugins/authlete/sdk/typescript/map/operations/device-flow.md new file mode 100644 index 00000000..e5723518 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/device-flow.md @@ -0,0 +1,103 @@ + + +# DeviceFlow β€” operations + +Accessor: `client.deviceFlow` Β· Source: `src/resources/device-flow.ts` Β· 3 operations Β· Request and error types: namespace `DeviceFlow` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### deviceAuthorizationApi + +- **Signature**: `deviceAuthorizationApi(request: DeviceFlow.DeviceAuthorizationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/device/authorization` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `DeviceAuthorizationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DeviceFlow.DeviceAuthorizationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DeviceFlow.DeviceAuthorizationApiRequest` (9): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `clientId` | `form` | `string` | no | +| `clientSecret` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `clientCertificatePath` | `form` | `string[]` | no | +| `oauthClientAttestation` | `form` | `string` | no | +| `oauthClientAttestationPop` | `form` | `string` | no | +| `cimdOptions` | `form` | `CimdOptions` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `CimdOptions` | `cimdOptionsSchema` | `src/models/cimd-options.ts` | +| `DeviceAuthorizationResponse` | `deviceAuthorizationResponseSchema` | `src/models/device-authorization-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### deviceCompleteApi + +- **Signature**: `deviceCompleteApi(request: DeviceFlow.DeviceCompleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/device/complete` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `DeviceCompleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DeviceFlow.DeviceCompleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DeviceFlow.DeviceCompleteApiRequest` (18): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `userCode` | `form` | `string` | yes | +| `result` | `form` | `Result1` | yes | +| `subject` | `form` | `string` | yes | +| `sub` | `form` | `string` | no | +| `authTime` | `form` | `number` | no | +| `acr` | `form` | `string` | no | +| `claims` | `form` | `string` | no | +| `properties` | `form` | `Property[]` | no | +| `scopes` | `form` | `string[]` | no | +| `errorDescription` | `form` | `string` | no | +| `errorUri` | `form` | `string` | no | +| `idtHeaderParams` | `form` | `string` | no | +| `consentedClaims` | `form` | `string[]` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `refreshTokenDuration` | `form` | `number` | no | +| `idTokenAudType` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result1` | `result1Schema` | `src/models/result1.ts` | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `DeviceCompleteResponse` | `deviceCompleteResponseSchema` | `src/models/device-complete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### deviceVerificationApi + +- **Signature**: `deviceVerificationApi(request: DeviceFlow.DeviceVerificationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/device/verification` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `DeviceVerificationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DeviceFlow.DeviceVerificationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DeviceFlow.DeviceVerificationApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `userCode` | `form` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `DeviceVerificationResponse` | `deviceVerificationResponseSchema` | `src/models/device-verification-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/dynamic-client-registration.md b/plugins/authlete/sdk/typescript/map/operations/dynamic-client-registration.md new file mode 100644 index 00000000..75daf0a5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/dynamic-client-registration.md @@ -0,0 +1,104 @@ + + +# DynamicClientRegistration β€” operations + +Accessor: `client.dynamicClientRegistration` Β· Source: `src/resources/dynamic-client-registration.ts` Β· 4 operations Β· Request and error types: namespace `DynamicClientRegistration` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### clientRegistrationApi + +- **Signature**: `clientRegistrationApi(request: DynamicClientRegistration.ClientRegistrationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/registration` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientRegistrationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DynamicClientRegistration.ClientRegistrationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DynamicClientRegistration.ClientRegistrationApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `ApiClientRegistrationRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ApiClientRegistrationRequest` | `apiClientRegistrationRequestSchema` | `src/models/api-client-registration-request.ts` | +| `ClientRegistrationResponse` | `clientRegistrationResponseSchema` | `src/models/client-registration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientRegistrationDeleteApi + +- **Signature**: `clientRegistrationDeleteApi(request: DynamicClientRegistration.ClientRegistrationDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/registration/delete` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientRegistrationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DynamicClientRegistration.ClientRegistrationDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DynamicClientRegistration.ClientRegistrationDeleteApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `ApiClientRegistrationDeleteRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ApiClientRegistrationDeleteRequest` | `apiClientRegistrationDeleteRequestSchema` | `src/models/api-client-registration-delete-request.ts` | +| `ClientRegistrationResponse` | `clientRegistrationResponseSchema` | `src/models/client-registration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientRegistrationGetApi + +- **Signature**: `clientRegistrationGetApi(request: DynamicClientRegistration.ClientRegistrationGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/registration/get` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientRegistrationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DynamicClientRegistration.ClientRegistrationGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DynamicClientRegistration.ClientRegistrationGetApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `ApiClientRegistrationGetRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ApiClientRegistrationGetRequest` | `apiClientRegistrationGetRequestSchema` | `src/models/api-client-registration-get-request.ts` | +| `ClientRegistrationResponse` | `clientRegistrationResponseSchema` | `src/models/client-registration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### clientRegistrationUpdateApi + +- **Signature**: `clientRegistrationUpdateApi(request: DynamicClientRegistration.ClientRegistrationUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/client/registration/update` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `ClientRegistrationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `DynamicClientRegistration.ClientRegistrationUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `DynamicClientRegistration.ClientRegistrationUpdateApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `ApiClientRegistrationUpdateRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ApiClientRegistrationUpdateRequest` | `apiClientRegistrationUpdateRequestSchema` | `src/models/api-client-registration-update-request.ts` | +| `ClientRegistrationResponse` | `clientRegistrationResponseSchema` | `src/models/client-registration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/federation-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/federation-endpoint.md new file mode 100644 index 00000000..55badfd8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/federation-endpoint.md @@ -0,0 +1,56 @@ + + +# FederationEndpoint β€” operations + +Accessor: `client.federationEndpoint` Β· Source: `src/resources/federation-endpoint.ts` Β· 2 operations Β· Request and error types: namespace `FederationEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### federationConfigurationApi + +- **Signature**: `federationConfigurationApi(request: FederationEndpoint.FederationConfigurationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/federation/configuration` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `FederationConfigurationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `FederationEndpoint.FederationConfigurationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `FederationEndpoint.FederationConfigurationApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `ApiFederationConfigurationRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ApiFederationConfigurationRequest` | `apiFederationConfigurationRequestSchema` | `src/models/api-federation-configuration-request.ts` | +| `FederationConfigurationResponse` | `federationConfigurationResponseSchema` | `src/models/federation-configuration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### federationRegistrationApi + +- **Signature**: `federationRegistrationApi(request: FederationEndpoint.FederationRegistrationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/federation/registration` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `FederationRegistrationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `FederationEndpoint.FederationRegistrationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `FederationEndpoint.FederationRegistrationApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `entityConfiguration` | `form` | `string` | no | +| `trustChain` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `FederationRegistrationResponse` | `federationRegistrationResponseSchema` | `src/models/federation-registration-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/grant-management-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/grant-management-endpoint.md new file mode 100644 index 00000000..30195b8c --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/grant-management-endpoint.md @@ -0,0 +1,32 @@ + + +# GrantManagementEndpoint β€” operations + +Accessor: `client.grantManagementEndpoint` Β· Source: `src/resources/grant-management-endpoint.ts` Β· 1 operation Β· Request and error types: namespace `GrantManagementEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### grantMApi + +- **Signature**: `grantMApi(request: GrantManagementEndpoint.GrantMApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/gm` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `GmResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `GrantManagementEndpoint.GrantMApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `GrantManagementEndpoint.GrantMApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `GmRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `GmRequest` | `gmRequestSchema` | `src/models/gm-request.ts` | +| `GmResponse` | `gmResponseSchema` | `src/models/gm-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/hardware-security-key.md b/plugins/authlete/sdk/typescript/map/operations/hardware-security-key.md new file mode 100644 index 00000000..9211b92c --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/hardware-security-key.md @@ -0,0 +1,101 @@ + + +# HardwareSecurityKey β€” operations + +Accessor: `client.hardwareSecurityKey` Β· Source: `src/resources/hardware-security-key.ts` Β· 4 operations Β· Request and error types: namespace `HardwareSecurityKey` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### hskCreateApi + +- **Signature**: `hskCreateApi(request: HardwareSecurityKey.HskCreateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/hsk/create` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `HskCreateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `HardwareSecurityKey.HskCreateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `HardwareSecurityKey.HskCreateApiRequest` (6): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `kty` | `form` | `string` | no | +| `use` | `form` | `string` | no | +| `kid` | `form` | `string` | no | +| `hsmName` | `form` | `string` | no | +| `alg` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `HskCreateResponse` | `hskCreateResponseSchema` | `src/models/hsk-create-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### hskDeleteApi + +- **Signature**: `hskDeleteApi(request: HardwareSecurityKey.HskDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/hsk/delete/{handle}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `HskDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `HardwareSecurityKey.HskDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `HardwareSecurityKey.HskDeleteApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `handle` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `HskDeleteResponse` | `hskDeleteResponseSchema` | `src/models/hsk-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### hskGetApi + +- **Signature**: `hskGetApi(request: HardwareSecurityKey.HskGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/hsk/get/{handle}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `HskDeleteResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `HardwareSecurityKey.HskGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `HardwareSecurityKey.HskGetApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `handle` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `HskDeleteResponse` | `hskDeleteResponseSchema` | `src/models/hsk-delete-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### hskGetListApi + +- **Signature**: `hskGetListApi(request: HardwareSecurityKey.HskGetListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/hsk/get/list` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `HskGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `HardwareSecurityKey.HskGetListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `HardwareSecurityKey.HskGetListApiRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `HskGetListResponse` | `hskGetListResponseSchema` | `src/models/hsk-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/introspection-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/introspection-endpoint.md new file mode 100644 index 00000000..f0642a68 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/introspection-endpoint.md @@ -0,0 +1,80 @@ + + +# IntrospectionEndpoint β€” operations + +Accessor: `client.introspectionEndpoint` Β· Source: `src/resources/introspection-endpoint.ts` Β· 2 operations Β· Request and error types: namespace `IntrospectionEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authIntrospectionApi + +- **Signature**: `authIntrospectionApi(request: IntrospectionEndpoint.AuthIntrospectionApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/introspection` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `IntrospectionResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `IntrospectionEndpoint.AuthIntrospectionApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `IntrospectionEndpoint.AuthIntrospectionApiRequest` (18): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `token` | `form` | `string` | yes | +| `scopes` | `form` | `string[]` | no | +| `subject` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `dpop` | `form` | `string` | no | +| `htm` | `form` | `string` | no | +| `htu` | `form` | `string` | no | +| `resources` | `form` | `string[]` | no | +| `acrValues` | `form` | `string[]` | no | +| `maxAge` | `form` | `number` | no | +| `requiredComponents` | `form` | `string[]` | no | +| `uri` | `form` | `string` | no | +| `message` | `form` | `string` | no | +| `headers` | `form` | `Pair[]` | no | +| `targetUri` | `form` | `string` | no | +| `dpopNonceRequired` | `form` | `boolean` | no | +| `requestBodyContained` | `form` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `IntrospectionResponse` | `introspectionResponseSchema` | `src/models/introspection-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authIntrospectionStandardApi + +- **Signature**: `authIntrospectionStandardApi(request: IntrospectionEndpoint.AuthIntrospectionStandardApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/introspection/standard` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `StandardIntrospectionResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `IntrospectionEndpoint.AuthIntrospectionStandardApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `IntrospectionEndpoint.AuthIntrospectionStandardApiRequest` (11): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `withHiddenProperties` | `form` | `boolean` | no | +| `rsUri` | `form` | `string` | no | +| `httpAcceptHeader` | `form` | `string` | no | +| `introspectionSignAlg` | `form` | `string` | no | +| `introspectionEncryptionAlg` | `form` | `string` | no | +| `introspectionEncryptionEnc` | `form` | `string` | no | +| `sharedKeyForSign` | `form` | `string` | no | +| `sharedKeyForEncryption` | `form` | `string` | no | +| `publicKeyForEncryption` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `StandardIntrospectionResponse` | `standardIntrospectionResponseSchema` | `src/models/standard-introspection-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/jose-object.md b/plugins/authlete/sdk/typescript/map/operations/jose-object.md new file mode 100644 index 00000000..20e2f606 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/jose-object.md @@ -0,0 +1,35 @@ + + +# JoseObject β€” operations + +Accessor: `client.joseObject` Β· Source: `src/resources/jose-object.ts` Β· 1 operation Β· Request and error types: namespace `JoseObject` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### joseVerifyApi + +- **Signature**: `joseVerifyApi(request: JoseObject.JoseVerifyApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/jose/verify` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `JoseVerifyResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `JoseObject.JoseVerifyApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `JoseObject.JoseVerifyApiRequest` (6): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `jose` | `form` | `string` | yes | +| `mandatoryClaims` | `form` | `string[]` | no | +| `clockSkew` | `form` | `number` | no | +| `clientIdentifier` | `form` | `string` | no | +| `signedByClient` | `form` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `JoseVerifyResponse` | `joseVerifyResponseSchema` | `src/models/jose-verify-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/jwk-set-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/jwk-set-endpoint.md new file mode 100644 index 00000000..bf0209e0 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/jwk-set-endpoint.md @@ -0,0 +1,31 @@ + + +# JwkSetEndpoint β€” operations + +Accessor: `client.jwkSetEndpoint` Β· Source: `src/resources/jwk-set-endpoint.ts` Β· 1 operation Β· Request and error types: namespace `JwkSetEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### serviceJwksGetApi + +- **Signature**: `serviceJwksGetApi(request: JwkSetEndpoint.ServiceJwksGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/service/jwks/get` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ServiceJwksGetResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `JwkSetEndpoint.ServiceJwksGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `JwkSetEndpoint.ServiceJwksGetApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `includePrivateKeys` | `query` | `boolean` | no | +| `pretty` | `query` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ServiceJwksGetResponse` | `serviceJwksGetResponseSchema` | `src/models/service-jwks-get-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/lifecycle.md b/plugins/authlete/sdk/typescript/map/operations/lifecycle.md new file mode 100644 index 00000000..1b14959e --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/lifecycle.md @@ -0,0 +1,24 @@ + + +# Lifecycle β€” operations + +Accessor: `client.lifecycle` Β· Source: `src/resources/lifecycle.ts` Β· 1 operation Β· Request and error types: namespace `Lifecycle` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### healthCheck + +- **Signature**: `healthCheck(request: Lifecycle.HealthCheckRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/lifecycle/healthcheck` +- **Auth**: none β€” public; no credential is sent +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `string` β€” `text/plain;charset=utf-8`, decoded by the plain-text scalar decoder, not by a model +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `Lifecycle.HealthCheckError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"error500"` [500] `string` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Lifecycle.HealthCheckRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `extended` | `query` | `boolean` | no | + diff --git a/plugins/authlete/sdk/typescript/map/operations/native-sso.md b/plugins/authlete/sdk/typescript/map/operations/native-sso.md new file mode 100644 index 00000000..7691792a --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/native-sso.md @@ -0,0 +1,56 @@ + + +# NativeSso β€” operations + +Accessor: `client.nativeSso` Β· Source: `src/resources/native-sso.ts` Β· 2 operations Β· Request and error types: namespace `NativeSso` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### nativeSsoApi + +- **Signature**: `nativeSsoApi(request: NativeSso.NativeSsoApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/nativesso` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `NativeSsoResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `NativeSso.NativeSsoApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `NativeSso.NativeSsoApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `NativeSsoRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `NativeSsoRequest` | `nativeSsoRequestSchema` | `src/models/native-sso-request.ts` | +| `NativeSsoResponse` | `nativeSsoResponseSchema` | `src/models/native-sso-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### nativeSsoLogoutApi + +- **Signature**: `nativeSsoLogoutApi(request: NativeSso.NativeSsoLogoutApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/nativesso/logout` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `NativeSsoLogoutResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `NativeSso.NativeSsoLogoutApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `NativeSso.NativeSsoLogoutApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `NativeSsoLogoutRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `NativeSsoLogoutRequest` | `nativeSsoLogoutRequestSchema` | `src/models/native-sso-logout-request.ts` | +| `NativeSsoLogoutResponse` | `nativeSsoLogoutResponseSchema` | `src/models/native-sso-logout-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/pushed-authorization-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/pushed-authorization-endpoint.md new file mode 100644 index 00000000..b1fdd88e --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/pushed-authorization-endpoint.md @@ -0,0 +1,41 @@ + + +# PushedAuthorizationEndpoint β€” operations + +Accessor: `client.pushedAuthorizationEndpoint` Β· Source: `src/resources/pushed-authorization-endpoint.ts` Β· 1 operation Β· Request and error types: namespace `PushedAuthorizationEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### pushedAuthReqApi + +- **Signature**: `pushedAuthReqApi(request: PushedAuthorizationEndpoint.PushedAuthReqApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/pushed_auth_req` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `PushedAuthorizationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `PushedAuthorizationEndpoint.PushedAuthReqApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `PushedAuthorizationEndpoint.PushedAuthReqApiRequest` (12): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `clientId` | `form` | `string` | no | +| `clientSecret` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `clientCertificatePath` | `form` | `string[]` | no | +| `dpop` | `form` | `string` | no | +| `htm` | `form` | `string` | no | +| `htu` | `form` | `string` | no | +| `oauthClientAttestation` | `form` | `string` | no | +| `oauthClientAttestationPop` | `form` | `string` | no | +| `dpopNonceRequired` | `form` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `PushedAuthorizationResponse` | `pushedAuthorizationResponseSchema` | `src/models/pushed-authorization-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/revocation-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/revocation-endpoint.md new file mode 100644 index 00000000..b5ad088a --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/revocation-endpoint.md @@ -0,0 +1,37 @@ + + +# RevocationEndpoint β€” operations + +Accessor: `client.revocationEndpoint` Β· Source: `src/resources/revocation-endpoint.ts` Β· 1 operation Β· Request and error types: namespace `RevocationEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authRevocationApi + +- **Signature**: `authRevocationApi(request: RevocationEndpoint.AuthRevocationApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/revocation` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `RevocationResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `RevocationEndpoint.AuthRevocationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `RevocationEndpoint.AuthRevocationApiRequest` (8): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `clientId` | `form` | `string` | no | +| `clientSecret` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `clientCertificatePath` | `form` | `string[]` | no | +| `oauthClientAttestation` | `form` | `string` | no | +| `oauthClientAttestationPop` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `RevocationResponse` | `revocationResponseSchema` | `src/models/revocation-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/service-management.md b/plugins/authlete/sdk/typescript/map/operations/service-management.md new file mode 100644 index 00000000..e93ab058 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/service-management.md @@ -0,0 +1,622 @@ + + +# ServiceManagement β€” operations + +Accessor: `client.serviceManagement` Β· Source: `src/resources/service-management.ts` Β· 8 operations Β· Request and error types: namespace `ServiceManagement` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### serviceConfigurationApi + +- **Signature**: `serviceConfigurationApi(request: ServiceManagement.ServiceConfigurationApiRequest, options?: RequestOptions): ApiPromise, ServiceManagement.ServiceConfigurationApiError>` +- **Wire**: `GET /api/{serviceId}/service/configuration` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `Record` β€” a bare `application/json` map; the success type *is* the map +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceConfigurationApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceConfigurationApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `pretty` | `query` | `boolean` | no | +| `patch` | `query` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### serviceCreateApi + +- **Signature**: `serviceCreateApi(request: ServiceManagement.ServiceCreateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/service/create` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Service` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceCreateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceCreateApiRequest` (198): + +| Field | Channel | Wire | Type | Req | +| --- | --- | --- | --- | --- | +| `number` | `form` | β€” | `number` | no | +| `serviceName` | `form` | β€” | `string` | no | +| `issuer` | `form` | β€” | `string` | no | +| `description` | `form` | β€” | `string` | no | +| `apiKey` | `form` | β€” | `number` | no | +| `apiSecret` | `form` | β€” | `string` | no | +| `tokenBatchNotificationEndpoint` | `form` | β€” | `string` | no | +| `clientAssertionAudRestrictedToIssuer` | `form` | β€” | `boolean` | no | +| `serviceOwnerNumber` | `form` | β€” | `number` | no | +| `clientsPerDeveloper` | `form` | β€” | `number` | no | +| `developerAuthenticationCallbackEndpoint` | `form` | β€” | `string` | no | +| `developerAuthenticationCallbackApiKey` | `form` | β€” | `string` | no | +| `developerAuthenticationCallbackApiSecret` | `form` | β€” | `string` | no | +| `supportedSnses` | `form` | β€” | `Sns[]` | no | +| `snsCredentials` | `form` | β€” | `SnsCredentials[]` | no | +| `supportedDeveloperSnses` | `form` | β€” | `Sns[]` | no | +| `developerSnsCredentials` | `form` | β€” | `SnsCredentials[]` | no | +| `clientIdAliasEnabled` | `form` | β€” | `boolean` | no | +| `metadata` | `form` | β€” | `Pair[]` | no | +| `createdAt` | `form` | β€” | `number` | no | +| `modifiedAt` | `form` | β€” | `number` | no | +| `authenticationCallbackEndpoint` | `form` | β€” | `string` | no | +| `authenticationCallbackApiKey` | `form` | β€” | `string` | no | +| `authenticationCallbackApiSecret` | `form` | β€” | `string` | no | +| `supportedAcrs` | `form` | β€” | `string[]` | no | +| `supportedGrantTypes` | `form` | β€” | `GrantType[]` | no | +| `supportedResponseTypes` | `form` | β€” | `ResponseType[]` | no | +| `supportedAuthorizationDetailsTypes` | `form` | β€” | `string[]` | no | +| `supportedServiceProfiles` | `form` | β€” | `ServiceProfile[]` | no | +| `errorDescriptionOmitted` | `form` | β€” | `boolean` | no | +| `errorUriOmitted` | `form` | β€” | `boolean` | no | +| `authorizationEndpoint` | `form` | β€” | `string` | no | +| `directAuthorizationEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedUiLocales` | `form` | β€” | `string[]` | no | +| `supportedDisplays` | `form` | β€” | `Display[]` | no | +| `pkceRequired` | `form` | β€” | `boolean` | no | +| `pkceS256Required` | `form` | β€” | `boolean` | no | +| `authorizationResponseDuration` | `form` | β€” | `number` | no | +| `authorizationCodeDuration` | `form` | β€” | `number` | no | +| `tokenEndpoint` | `form` | β€” | `string` | no | +| `directTokenEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedTokenAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `missingClientIdAllowed` | `form` | β€” | `boolean` | no | +| `revocationEndpoint` | `form` | β€” | `string` | no | +| `directRevocationEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedRevocationAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `introspectionEndpoint` | `form` | β€” | `string` | no | +| `directIntrospectionEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedIntrospectionAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `pushedAuthReqEndpoint` | `form` | β€” | `string` | no | +| `pushedAuthReqDuration` | `form` | β€” | `number` | no | +| `parRequired` | `form` | β€” | `boolean` | no | +| `requestObjectRequired` | `form` | β€” | `boolean` | no | +| `traditionalRequestObjectProcessingApplied` | `form` | β€” | `boolean` | no | +| `mutualTlsValidatePkiCertChain` | `form` | β€” | `boolean` | no | +| `trustedRootCertificates` | `form` | β€” | `string[]` | no | +| `mtlsEndpointAliases` | `form` | β€” | `NamedUri[]` | no | +| `accessTokenType` | `form` | β€” | `string` | no | +| `tlsClientCertificateBoundAccessTokens` | `form` | β€” | `boolean` | no | +| `accessTokenDuration` | `form` | β€” | `number` | no | +| `singleAccessTokenPerSubject` | `form` | β€” | `boolean` | no | +| `accessTokenSignAlg` | `form` | β€” | `JwsAlg` | no | +| `accessTokenSignatureKeyId` | `form` | β€” | `string` | no | +| `refreshTokenDuration` | `form` | β€” | `number` | no | +| `refreshTokenDurationKept` | `form` | β€” | `boolean` | no | +| `refreshTokenDurationReset` | `form` | β€” | `boolean` | no | +| `refreshTokenKept` | `form` | β€” | `boolean` | no | +| `supportedScopes` | `form` | β€” | `Scope[]` | no | +| `scopeRequired` | `form` | β€” | `boolean` | no | +| `idTokenDuration` | `form` | β€” | `number` | no | +| `allowableClockSkew` | `form` | β€” | `number` | no | +| `supportedClaimTypes` | `form` | β€” | `ClaimType[]` | no | +| `supportedClaimLocales` | `form` | β€” | `string[]` | no | +| `supportedClaims` | `form` | β€” | `string[]` | no | +| `claimShortcutRestrictive` | `form` | β€” | `boolean` | no | +| `jwksUri` | `form` | β€” | `string` | no | +| `directJwksEndpointEnabled` | `form` | β€” | `boolean` | no | +| `jwks` | `form` | β€” | `string` | no | +| `idTokenSignatureKeyId` | `form` | β€” | `string` | no | +| `userInfoSignatureKeyId` | `form` | β€” | `string` | no | +| `authorizationSignatureKeyId` | `form` | β€” | `string` | no | +| `userInfoEndpoint` | `form` | β€” | `string` | no | +| `directUserInfoEndpointEnabled` | `form` | β€” | `boolean` | no | +| `dynamicRegistrationSupported` | `form` | β€” | `boolean` | no | +| `registrationEndpoint` | `form` | β€” | `string` | no | +| `registrationManagementEndpoint` | `form` | β€” | `string` | no | +| `policyUri` | `form` | β€” | `string` | no | +| `tosUri` | `form` | β€” | `string` | no | +| `serviceDocumentation` | `form` | β€” | `string` | no | +| `backchannelAuthenticationEndpoint` | `form` | β€” | `string` | no | +| `supportedBackchannelTokenDeliveryModes` | `form` | β€” | `DeliveryMode[]` | no | +| `backchannelAuthReqIdDuration` | `form` | β€” | `number` | no | +| `backchannelPollingInterval` | `form` | β€” | `number` | no | +| `backchannelUserCodeParameterSupported` | `form` | β€” | `boolean` | no | +| `backchannelBindingMessageRequiredInFapi` | `form` | β€” | `boolean` | no | +| `deviceAuthorizationEndpoint` | `form` | β€” | `string` | no | +| `deviceVerificationUri` | `form` | β€” | `string` | no | +| `deviceVerificationUriComplete` | `form` | β€” | `string` | no | +| `deviceFlowCodeDuration` | `form` | β€” | `number` | no | +| `deviceFlowPollingInterval` | `form` | β€” | `number` | no | +| `userCodeCharset` | `form` | β€” | `UserCodeCharset` | no | +| `userCodeLength` | `form` | β€” | `number` | no | +| `supportedTrustFrameworks` | `form` | β€” | `string[]` | no | +| `supportedEvidence` | `form` | β€” | `string[]` | no | +| `supportedIdentityDocuments` | `form` | β€” | `string[]` | no | +| `supportedVerificationMethods` | `form` | β€” | `string[]` | no | +| `supportedVerifiedClaims` | `form` | β€” | `string[]` | no | +| `verifiedClaimsValidationSchemaSet` | `form` | β€” | `VerifiedClaimsValidationSchema` | no | +| `attributes` | `form` | β€” | `Pair[]` | no | +| `nbfOptional` | `form` | β€” | `boolean` | no | +| `issSuppressed` | `form` | β€” | `boolean` | no | +| `supportedCustomClientMetadata` | `form` | β€” | `string[]` | no | +| `tokenExpirationLinked` | `form` | β€” | `boolean` | no | +| `frontChannelRequestObjectEncryptionRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionAlgMatchRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionEncMatchRequired` | `form` | β€” | `boolean` | no | +| `hsmEnabled` | `form` | β€” | `boolean` | no | +| `hsks` | `form` | β€” | `Hsk[]` | no | +| `grantManagementEndpoint` | `form` | β€” | `string` | no | +| `grantManagementActionRequired` | `form` | β€” | `boolean` | no | +| `unauthorizedOnClientConfigSupported` | `form` | β€” | `boolean` | no | +| `dcrScopeUsedAsRequestable` | `form` | β€” | `boolean` | no | +| `endSessionEndpoint` | `form` | β€” | `string` | no | +| `challengeEndpoint` | `form` | β€” | `string` | no | +| `loopbackRedirectionUriVariable` | `form` | β€” | `boolean` | no | +| `requestObjectAudienceChecked` | `form` | β€” | `boolean` | no | +| `accessTokenForExternalAttachmentEmbedded` | `form` | β€” | `boolean` | no | +| `authorityHints` | `form` | β€” | `string[]` | no | +| `federationEnabled` | `form` | β€” | `boolean` | no | +| `federationJwks` | `form` | β€” | `string` | no | +| `federationSignatureKeyId` | `form` | β€” | `string` | no | +| `federationConfigurationDuration` | `form` | β€” | `number` | no | +| `federationRegistrationEndpoint` | `form` | β€” | `string` | no | +| `organizationName` | `form` | β€” | `string` | no | +| `predefinedTransformedClaims` | `form` | β€” | `string` | no | +| `refreshTokenIdempotent` | `form` | β€” | `boolean` | no | +| `signedJwksUri` | `form` | β€” | `string` | no | +| `supportedAttachments` | `form` | β€” | `AttachmentType[]` | no | +| `supportedDigestAlgorithms` | `form` | β€” | `string[]` | no | +| `supportedDocuments` | `form` | β€” | `string[]` | no | +| `supportedDocumentsMethods` | `form` | β€” | `string[]` | no | +| `supportedDocumentsValidationMethods` | `form` | β€” | `string[]` | no | +| `supportedDocumentsVerificationMethods` | `form` | β€” | `string[]` | no | +| `supportedElectronicRecords` | `form` | β€” | `string[]` | no | +| `supportedClientRegistrationTypes` | `form` | β€” | `ClientRegistrationType[]` | no | +| `tokenExchangeByIdentifiableClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeByConfidentialClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeByPermittedClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeEncryptedJwtRejected` | `form` | β€” | `boolean` | no | +| `tokenExchangeUnsignedJwtRejected` | `form` | β€” | `boolean` | no | +| `jwtGrantByIdentifiableClientsOnly` | `form` | β€” | `boolean` | no | +| `jwtGrantEncryptedJwtRejected` | `form` | β€” | `boolean` | no | +| `jwtGrantUnsignedJwtRejected` | `form` | β€” | `boolean` | no | +| `dcrDuplicateSoftwareIdBlocked` | `form` | β€” | `boolean` | no | +| `trustAnchors` | `form` | β€” | `TrustAnchor[]` | no | +| `openidDroppedOnRefreshWithoutOfflineAccess` | `form` | β€” | `boolean` | no | +| `idTokenDroppedOnIssueWithoutOpenid` | `form` | β€” | `boolean` | no | +| `supportedDocumentsCheckMethods` | `form` | β€” | `string[]` | no | +| `rsResponseSigned` | `form` | β€” | `boolean` | no | +| `cnonceDuration` | `form` | β€” | `number` | no | +| `dpopNonceRequired` | `form` | β€” | `boolean` | no | +| `verifiableCredentialsEnabled` | `form` | β€” | `boolean` | no | +| `credentialJwksUri` | `form` | β€” | `string` | no | +| `credentialOfferDuration` | `form` | β€” | `number` | no | +| `dpopNonceDuration` | `form` | β€” | `number` | no | +| `preAuthorizedGrantAnonymousAccessSupported` | `form` | β€” | `boolean` | no | +| `credentialTransactionDuration` | `form` | β€” | `number` | no | +| `introspectionSignatureKeyId` | `form` | β€” | `string` | no | +| `resourceSignatureKeyId` | `form` | β€” | `string` | no | +| `userPinLength` | `form` | β€” | `number` | no | +| `supportedPromptValues` | `form` | β€” | `Prompt[]` | no | +| `idTokenReissuable` | `form` | β€” | `boolean` | no | +| `credentialJwks` | `form` | β€” | `string` | no | +| `fapiModes` | `form` | β€” | `FapiMode[]` | no | +| `credentialDuration` | `form` | β€” | `number` | no | +| `credentialIssuerMetadata` | `form` | β€” | `CredentialIssuerMetadata` | no | +| `idTokenAudType` | `form` | β€” | `string` | no | +| `nativeSsoSupported` | `form` | β€” | `boolean` | no | +| `oid4VciVersion` | `form` | `oid4vciVersion` | `string` | no | +| `cimdMetadataPolicyEnabled` | `form` | β€” | `boolean` | no | +| `clientIdMetadataDocumentSupported` | `form` | β€” | `boolean` | no | +| `cimdAllowlistEnabled` | `form` | β€” | `boolean` | no | +| `cimdAllowlist` | `form` | β€” | `string[]` | no | +| `cimdAlwaysRetrieved` | `form` | β€” | `boolean` | no | +| `cimdHttpPermitted` | `form` | β€” | `boolean` | no | +| `cimdQueryPermitted` | `form` | β€” | `boolean` | no | +| `cimdMetadataPolicy` | `form` | β€” | `string` | no | +| `httpAliasProhibited` | `form` | β€” | `boolean` | no | +| `attestationChallengeTimeWindow` | `form` | β€” | `number` | no | +| `clientAttesterRootsEnabled` | `form` | β€” | `boolean` | no | +| `clientAttesterRootsOnly` | `form` | β€” | `boolean` | no | +| `keyAttesterRootsEnabled` | `form` | β€” | `boolean` | no | +| `keyAttesterRootsOnly` | `form` | β€” | `boolean` | no | +| `clientAttesterRoots` | `form` | β€” | `string[]` | no | +| `keyAttesterRoots` | `form` | β€” | `string[]` | no | +| `backchannelLogoutSupported` | `form` | β€” | `boolean` | no | +| `backchannelLogoutSessionSupported` | `form` | β€” | `boolean` | no | +| `haipVersion` | `form` | β€” | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Sns` | `snsSchema` | `src/models/sns.ts` | +| `SnsCredentials` | `snsCredentialsSchema` | `src/models/sns-credentials.ts` | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `GrantType` | `grantTypeSchema` | `src/models/grant-type.ts` | +| `ResponseType` | `responseTypeSchema` | `src/models/response-type.ts` | +| `ServiceProfile` | `serviceProfileSchema` | `src/models/service-profile.ts` | +| `Display` | `displaySchema` | `src/models/display.ts` | +| `ClientAuthMethod` | `clientAuthMethodSchema` | `src/models/client-auth-method.ts` | +| `NamedUri` | `namedUriSchema` | `src/models/named-uri.ts` | +| `JwsAlg` | `jwsAlgSchema` | `src/models/jws-alg.ts` | +| `Scope` | `scopeSchema` | `src/models/scope.ts` | +| `ClaimType` | `claimTypeSchema` | `src/models/claim-type.ts` | +| `DeliveryMode` | `deliveryModeSchema` | `src/models/delivery-mode.ts` | +| `UserCodeCharset` | `userCodeCharsetSchema` | `src/models/user-code-charset.ts` | +| `VerifiedClaimsValidationSchema` | `verifiedClaimsValidationSchemaSchema` | `src/models/verified-claims-validation-schema.ts` | +| `Hsk` | `hskSchema` | `src/models/hsk.ts` | +| `AttachmentType` | `attachmentTypeSchema` | `src/models/attachment-type.ts` | +| `ClientRegistrationType` | `clientRegistrationTypeSchema` | `src/models/client-registration-type.ts` | +| `TrustAnchor` | `trustAnchorSchema` | `src/models/trust-anchor.ts` | +| `Prompt` | `promptSchema` | `src/models/prompt.ts` | +| `FapiMode` | `fapiModeSchema` | `src/models/fapi-mode.ts` | +| `CredentialIssuerMetadata` | `credentialIssuerMetadataSchema` | `src/models/credential-issuer-metadata.ts` | +| `Service` | `serviceSchema` | `src/models/service.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### serviceCreateIdpApi + +- **Signature**: `serviceCreateIdpApi(request: ServiceManagement.ServiceCreateIdpApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/service` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Service` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceCreateIdpApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"idpError"` [400] `IdpError` Β· `"idpError2"` [401] `IdpError` Β· `"idpError3"` [403] `IdpError` Β· `"idpError4"` [500] `IdpError` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceCreateIdpApiRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `ServiceCreateIdpRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ServiceCreateIdpRequest` | `serviceCreateIdpRequestSchema` | `src/models/service-create-idp-request.ts` | +| `Service` | `serviceSchema` | `src/models/service.ts` | +| `IdpError` | `idpErrorSchema` | `src/models/idp-error.ts` | + +### serviceDeleteApi + +- **Signature**: `serviceDeleteApi(request: ServiceManagement.ServiceDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/service/delete` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceDeleteApiRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### serviceGetApi + +- **Signature**: `serviceGetApi(request: ServiceManagement.ServiceGetApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/service/get` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `Service` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceGetApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceGetApiRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Service` | `serviceSchema` | `src/models/service.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### serviceGetListApi + +- **Signature**: `serviceGetListApi(request: ServiceManagement.ServiceGetListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/service/get/list` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `ServiceGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceGetListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceGetListApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `start` | `query` | `number` | no | +| `end` | `query` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ServiceGetListResponse` | `serviceGetListResponseSchema` | `src/models/service-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### serviceRemoveIdpApi + +- **Signature**: `serviceRemoveIdpApi(request: ServiceManagement.ServiceRemoveIdpApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/service/remove` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceRemoveIdpApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"idpError"` [400] `IdpError` Β· `"idpError2"` [401] `IdpError` Β· `"idpError3"` [403] `IdpError` Β· `"idpError4"` [500] `IdpError` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceRemoveIdpApiRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `ServiceRemoveIdpRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `ServiceRemoveIdpRequest` | `serviceRemoveIdpRequestSchema` | `src/models/service-remove-idp-request.ts` | +| `IdpError` | `idpErrorSchema` | `src/models/idp-error.ts` | + +### serviceUpdateApi + +- **Signature**: `serviceUpdateApi(request: ServiceManagement.ServiceUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/service/update` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `Service` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `ServiceManagement.ServiceUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `ServiceManagement.ServiceUpdateApiRequest` (199): + +| Field | Channel | Wire | Type | Req | +| --- | --- | --- | --- | --- | +| `serviceId` | `path` | β€” | `string` | yes | +| `number` | `form` | β€” | `number` | no | +| `serviceName` | `form` | β€” | `string` | no | +| `issuer` | `form` | β€” | `string` | no | +| `description` | `form` | β€” | `string` | no | +| `apiKey` | `form` | β€” | `number` | no | +| `apiSecret` | `form` | β€” | `string` | no | +| `tokenBatchNotificationEndpoint` | `form` | β€” | `string` | no | +| `clientAssertionAudRestrictedToIssuer` | `form` | β€” | `boolean` | no | +| `serviceOwnerNumber` | `form` | β€” | `number` | no | +| `clientsPerDeveloper` | `form` | β€” | `number` | no | +| `developerAuthenticationCallbackEndpoint` | `form` | β€” | `string` | no | +| `developerAuthenticationCallbackApiKey` | `form` | β€” | `string` | no | +| `developerAuthenticationCallbackApiSecret` | `form` | β€” | `string` | no | +| `supportedSnses` | `form` | β€” | `Sns[]` | no | +| `snsCredentials` | `form` | β€” | `SnsCredentials[]` | no | +| `supportedDeveloperSnses` | `form` | β€” | `Sns[]` | no | +| `developerSnsCredentials` | `form` | β€” | `SnsCredentials[]` | no | +| `clientIdAliasEnabled` | `form` | β€” | `boolean` | no | +| `metadata` | `form` | β€” | `Pair[]` | no | +| `createdAt` | `form` | β€” | `number` | no | +| `modifiedAt` | `form` | β€” | `number` | no | +| `authenticationCallbackEndpoint` | `form` | β€” | `string` | no | +| `authenticationCallbackApiKey` | `form` | β€” | `string` | no | +| `authenticationCallbackApiSecret` | `form` | β€” | `string` | no | +| `supportedAcrs` | `form` | β€” | `string[]` | no | +| `supportedGrantTypes` | `form` | β€” | `GrantType[]` | no | +| `supportedResponseTypes` | `form` | β€” | `ResponseType[]` | no | +| `supportedAuthorizationDetailsTypes` | `form` | β€” | `string[]` | no | +| `supportedServiceProfiles` | `form` | β€” | `ServiceProfile[]` | no | +| `errorDescriptionOmitted` | `form` | β€” | `boolean` | no | +| `errorUriOmitted` | `form` | β€” | `boolean` | no | +| `authorizationEndpoint` | `form` | β€” | `string` | no | +| `directAuthorizationEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedUiLocales` | `form` | β€” | `string[]` | no | +| `supportedDisplays` | `form` | β€” | `Display[]` | no | +| `pkceRequired` | `form` | β€” | `boolean` | no | +| `pkceS256Required` | `form` | β€” | `boolean` | no | +| `authorizationResponseDuration` | `form` | β€” | `number` | no | +| `authorizationCodeDuration` | `form` | β€” | `number` | no | +| `tokenEndpoint` | `form` | β€” | `string` | no | +| `directTokenEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedTokenAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `missingClientIdAllowed` | `form` | β€” | `boolean` | no | +| `revocationEndpoint` | `form` | β€” | `string` | no | +| `directRevocationEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedRevocationAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `introspectionEndpoint` | `form` | β€” | `string` | no | +| `directIntrospectionEndpointEnabled` | `form` | β€” | `boolean` | no | +| `supportedIntrospectionAuthMethods` | `form` | β€” | `ClientAuthMethod[]` | no | +| `pushedAuthReqEndpoint` | `form` | β€” | `string` | no | +| `pushedAuthReqDuration` | `form` | β€” | `number` | no | +| `parRequired` | `form` | β€” | `boolean` | no | +| `requestObjectRequired` | `form` | β€” | `boolean` | no | +| `traditionalRequestObjectProcessingApplied` | `form` | β€” | `boolean` | no | +| `mutualTlsValidatePkiCertChain` | `form` | β€” | `boolean` | no | +| `trustedRootCertificates` | `form` | β€” | `string[]` | no | +| `mtlsEndpointAliases` | `form` | β€” | `NamedUri[]` | no | +| `accessTokenType` | `form` | β€” | `string` | no | +| `tlsClientCertificateBoundAccessTokens` | `form` | β€” | `boolean` | no | +| `accessTokenDuration` | `form` | β€” | `number` | no | +| `singleAccessTokenPerSubject` | `form` | β€” | `boolean` | no | +| `accessTokenSignAlg` | `form` | β€” | `JwsAlg` | no | +| `accessTokenSignatureKeyId` | `form` | β€” | `string` | no | +| `refreshTokenDuration` | `form` | β€” | `number` | no | +| `refreshTokenDurationKept` | `form` | β€” | `boolean` | no | +| `refreshTokenDurationReset` | `form` | β€” | `boolean` | no | +| `refreshTokenKept` | `form` | β€” | `boolean` | no | +| `supportedScopes` | `form` | β€” | `Scope[]` | no | +| `scopeRequired` | `form` | β€” | `boolean` | no | +| `idTokenDuration` | `form` | β€” | `number` | no | +| `allowableClockSkew` | `form` | β€” | `number` | no | +| `supportedClaimTypes` | `form` | β€” | `ClaimType[]` | no | +| `supportedClaimLocales` | `form` | β€” | `string[]` | no | +| `supportedClaims` | `form` | β€” | `string[]` | no | +| `claimShortcutRestrictive` | `form` | β€” | `boolean` | no | +| `jwksUri` | `form` | β€” | `string` | no | +| `directJwksEndpointEnabled` | `form` | β€” | `boolean` | no | +| `jwks` | `form` | β€” | `string` | no | +| `idTokenSignatureKeyId` | `form` | β€” | `string` | no | +| `userInfoSignatureKeyId` | `form` | β€” | `string` | no | +| `authorizationSignatureKeyId` | `form` | β€” | `string` | no | +| `userInfoEndpoint` | `form` | β€” | `string` | no | +| `directUserInfoEndpointEnabled` | `form` | β€” | `boolean` | no | +| `dynamicRegistrationSupported` | `form` | β€” | `boolean` | no | +| `registrationEndpoint` | `form` | β€” | `string` | no | +| `registrationManagementEndpoint` | `form` | β€” | `string` | no | +| `policyUri` | `form` | β€” | `string` | no | +| `tosUri` | `form` | β€” | `string` | no | +| `serviceDocumentation` | `form` | β€” | `string` | no | +| `backchannelAuthenticationEndpoint` | `form` | β€” | `string` | no | +| `supportedBackchannelTokenDeliveryModes` | `form` | β€” | `DeliveryMode[]` | no | +| `backchannelAuthReqIdDuration` | `form` | β€” | `number` | no | +| `backchannelPollingInterval` | `form` | β€” | `number` | no | +| `backchannelUserCodeParameterSupported` | `form` | β€” | `boolean` | no | +| `backchannelBindingMessageRequiredInFapi` | `form` | β€” | `boolean` | no | +| `deviceAuthorizationEndpoint` | `form` | β€” | `string` | no | +| `deviceVerificationUri` | `form` | β€” | `string` | no | +| `deviceVerificationUriComplete` | `form` | β€” | `string` | no | +| `deviceFlowCodeDuration` | `form` | β€” | `number` | no | +| `deviceFlowPollingInterval` | `form` | β€” | `number` | no | +| `userCodeCharset` | `form` | β€” | `UserCodeCharset` | no | +| `userCodeLength` | `form` | β€” | `number` | no | +| `supportedTrustFrameworks` | `form` | β€” | `string[]` | no | +| `supportedEvidence` | `form` | β€” | `string[]` | no | +| `supportedIdentityDocuments` | `form` | β€” | `string[]` | no | +| `supportedVerificationMethods` | `form` | β€” | `string[]` | no | +| `supportedVerifiedClaims` | `form` | β€” | `string[]` | no | +| `verifiedClaimsValidationSchemaSet` | `form` | β€” | `VerifiedClaimsValidationSchema` | no | +| `attributes` | `form` | β€” | `Pair[]` | no | +| `nbfOptional` | `form` | β€” | `boolean` | no | +| `issSuppressed` | `form` | β€” | `boolean` | no | +| `supportedCustomClientMetadata` | `form` | β€” | `string[]` | no | +| `tokenExpirationLinked` | `form` | β€” | `boolean` | no | +| `frontChannelRequestObjectEncryptionRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionAlgMatchRequired` | `form` | β€” | `boolean` | no | +| `requestObjectEncryptionEncMatchRequired` | `form` | β€” | `boolean` | no | +| `hsmEnabled` | `form` | β€” | `boolean` | no | +| `hsks` | `form` | β€” | `Hsk[]` | no | +| `grantManagementEndpoint` | `form` | β€” | `string` | no | +| `grantManagementActionRequired` | `form` | β€” | `boolean` | no | +| `unauthorizedOnClientConfigSupported` | `form` | β€” | `boolean` | no | +| `dcrScopeUsedAsRequestable` | `form` | β€” | `boolean` | no | +| `endSessionEndpoint` | `form` | β€” | `string` | no | +| `challengeEndpoint` | `form` | β€” | `string` | no | +| `loopbackRedirectionUriVariable` | `form` | β€” | `boolean` | no | +| `requestObjectAudienceChecked` | `form` | β€” | `boolean` | no | +| `accessTokenForExternalAttachmentEmbedded` | `form` | β€” | `boolean` | no | +| `authorityHints` | `form` | β€” | `string[]` | no | +| `federationEnabled` | `form` | β€” | `boolean` | no | +| `federationJwks` | `form` | β€” | `string` | no | +| `federationSignatureKeyId` | `form` | β€” | `string` | no | +| `federationConfigurationDuration` | `form` | β€” | `number` | no | +| `federationRegistrationEndpoint` | `form` | β€” | `string` | no | +| `organizationName` | `form` | β€” | `string` | no | +| `predefinedTransformedClaims` | `form` | β€” | `string` | no | +| `refreshTokenIdempotent` | `form` | β€” | `boolean` | no | +| `signedJwksUri` | `form` | β€” | `string` | no | +| `supportedAttachments` | `form` | β€” | `AttachmentType[]` | no | +| `supportedDigestAlgorithms` | `form` | β€” | `string[]` | no | +| `supportedDocuments` | `form` | β€” | `string[]` | no | +| `supportedDocumentsMethods` | `form` | β€” | `string[]` | no | +| `supportedDocumentsValidationMethods` | `form` | β€” | `string[]` | no | +| `supportedDocumentsVerificationMethods` | `form` | β€” | `string[]` | no | +| `supportedElectronicRecords` | `form` | β€” | `string[]` | no | +| `supportedClientRegistrationTypes` | `form` | β€” | `ClientRegistrationType[]` | no | +| `tokenExchangeByIdentifiableClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeByConfidentialClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeByPermittedClientsOnly` | `form` | β€” | `boolean` | no | +| `tokenExchangeEncryptedJwtRejected` | `form` | β€” | `boolean` | no | +| `tokenExchangeUnsignedJwtRejected` | `form` | β€” | `boolean` | no | +| `jwtGrantByIdentifiableClientsOnly` | `form` | β€” | `boolean` | no | +| `jwtGrantEncryptedJwtRejected` | `form` | β€” | `boolean` | no | +| `jwtGrantUnsignedJwtRejected` | `form` | β€” | `boolean` | no | +| `dcrDuplicateSoftwareIdBlocked` | `form` | β€” | `boolean` | no | +| `trustAnchors` | `form` | β€” | `TrustAnchor[]` | no | +| `openidDroppedOnRefreshWithoutOfflineAccess` | `form` | β€” | `boolean` | no | +| `idTokenDroppedOnIssueWithoutOpenid` | `form` | β€” | `boolean` | no | +| `supportedDocumentsCheckMethods` | `form` | β€” | `string[]` | no | +| `rsResponseSigned` | `form` | β€” | `boolean` | no | +| `cnonceDuration` | `form` | β€” | `number` | no | +| `dpopNonceRequired` | `form` | β€” | `boolean` | no | +| `verifiableCredentialsEnabled` | `form` | β€” | `boolean` | no | +| `credentialJwksUri` | `form` | β€” | `string` | no | +| `credentialOfferDuration` | `form` | β€” | `number` | no | +| `dpopNonceDuration` | `form` | β€” | `number` | no | +| `preAuthorizedGrantAnonymousAccessSupported` | `form` | β€” | `boolean` | no | +| `credentialTransactionDuration` | `form` | β€” | `number` | no | +| `introspectionSignatureKeyId` | `form` | β€” | `string` | no | +| `resourceSignatureKeyId` | `form` | β€” | `string` | no | +| `userPinLength` | `form` | β€” | `number` | no | +| `supportedPromptValues` | `form` | β€” | `Prompt[]` | no | +| `idTokenReissuable` | `form` | β€” | `boolean` | no | +| `credentialJwks` | `form` | β€” | `string` | no | +| `fapiModes` | `form` | β€” | `FapiMode[]` | no | +| `credentialDuration` | `form` | β€” | `number` | no | +| `credentialIssuerMetadata` | `form` | β€” | `CredentialIssuerMetadata` | no | +| `idTokenAudType` | `form` | β€” | `string` | no | +| `nativeSsoSupported` | `form` | β€” | `boolean` | no | +| `oid4VciVersion` | `form` | `oid4vciVersion` | `string` | no | +| `cimdMetadataPolicyEnabled` | `form` | β€” | `boolean` | no | +| `clientIdMetadataDocumentSupported` | `form` | β€” | `boolean` | no | +| `cimdAllowlistEnabled` | `form` | β€” | `boolean` | no | +| `cimdAllowlist` | `form` | β€” | `string[]` | no | +| `cimdAlwaysRetrieved` | `form` | β€” | `boolean` | no | +| `cimdHttpPermitted` | `form` | β€” | `boolean` | no | +| `cimdQueryPermitted` | `form` | β€” | `boolean` | no | +| `cimdMetadataPolicy` | `form` | β€” | `string` | no | +| `httpAliasProhibited` | `form` | β€” | `boolean` | no | +| `attestationChallengeTimeWindow` | `form` | β€” | `number` | no | +| `clientAttesterRootsEnabled` | `form` | β€” | `boolean` | no | +| `clientAttesterRootsOnly` | `form` | β€” | `boolean` | no | +| `keyAttesterRootsEnabled` | `form` | β€” | `boolean` | no | +| `keyAttesterRootsOnly` | `form` | β€” | `boolean` | no | +| `clientAttesterRoots` | `form` | β€” | `string[]` | no | +| `keyAttesterRoots` | `form` | β€” | `string[]` | no | +| `backchannelLogoutSupported` | `form` | β€” | `boolean` | no | +| `backchannelLogoutSessionSupported` | `form` | β€” | `boolean` | no | +| `haipVersion` | `form` | β€” | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Sns` | `snsSchema` | `src/models/sns.ts` | +| `SnsCredentials` | `snsCredentialsSchema` | `src/models/sns-credentials.ts` | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `GrantType` | `grantTypeSchema` | `src/models/grant-type.ts` | +| `ResponseType` | `responseTypeSchema` | `src/models/response-type.ts` | +| `ServiceProfile` | `serviceProfileSchema` | `src/models/service-profile.ts` | +| `Display` | `displaySchema` | `src/models/display.ts` | +| `ClientAuthMethod` | `clientAuthMethodSchema` | `src/models/client-auth-method.ts` | +| `NamedUri` | `namedUriSchema` | `src/models/named-uri.ts` | +| `JwsAlg` | `jwsAlgSchema` | `src/models/jws-alg.ts` | +| `Scope` | `scopeSchema` | `src/models/scope.ts` | +| `ClaimType` | `claimTypeSchema` | `src/models/claim-type.ts` | +| `DeliveryMode` | `deliveryModeSchema` | `src/models/delivery-mode.ts` | +| `UserCodeCharset` | `userCodeCharsetSchema` | `src/models/user-code-charset.ts` | +| `VerifiedClaimsValidationSchema` | `verifiedClaimsValidationSchemaSchema` | `src/models/verified-claims-validation-schema.ts` | +| `Hsk` | `hskSchema` | `src/models/hsk.ts` | +| `AttachmentType` | `attachmentTypeSchema` | `src/models/attachment-type.ts` | +| `ClientRegistrationType` | `clientRegistrationTypeSchema` | `src/models/client-registration-type.ts` | +| `TrustAnchor` | `trustAnchorSchema` | `src/models/trust-anchor.ts` | +| `Prompt` | `promptSchema` | `src/models/prompt.ts` | +| `FapiMode` | `fapiModeSchema` | `src/models/fapi-mode.ts` | +| `CredentialIssuerMetadata` | `credentialIssuerMetadataSchema` | `src/models/credential-issuer-metadata.ts` | +| `Service` | `serviceSchema` | `src/models/service.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/token-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/token-endpoint.md new file mode 100644 index 00000000..8d709c2e --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/token-endpoint.md @@ -0,0 +1,128 @@ + + +# TokenEndpoint β€” operations + +Accessor: `client.tokenEndpoint` Β· Source: `src/resources/token-endpoint.ts` Β· 4 operations Β· Request and error types: namespace `TokenEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authTokenApi + +- **Signature**: `authTokenApi(request: TokenEndpoint.AuthTokenApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenEndpoint.AuthTokenApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenEndpoint.AuthTokenApiRequest` (18): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `parameters` | `form` | `string` | yes | +| `clientId` | `form` | `string` | no | +| `clientSecret` | `form` | `string` | no | +| `clientCertificate` | `form` | `string` | no | +| `clientCertificatePath` | `form` | `string[]` | no | +| `properties` | `form` | `Property[]` | no | +| `dpop` | `form` | `string` | no | +| `htm` | `form` | `string` | no | +| `htu` | `form` | `string` | no | +| `accessToken` | `form` | `string` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `oauthClientAttestation` | `form` | `string` | no | +| `oauthClientAttestationPop` | `form` | `string` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `refreshTokenDuration` | `form` | `number` | no | +| `dpopNonceRequired` | `form` | `boolean` | no | +| `cimdOptions` | `form` | `CimdOptions` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `CimdOptions` | `cimdOptionsSchema` | `src/models/cimd-options.ts` | +| `TokenResponse` | `tokenResponseSchema` | `src/models/token-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenFailApi + +- **Signature**: `authTokenFailApi(request: TokenEndpoint.AuthTokenFailApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token/fail` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenFailResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenEndpoint.AuthTokenFailApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenEndpoint.AuthTokenFailApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `reason` | `form` | `Reason1` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Reason1` | `reason1Schema` | `src/models/reason1.ts` | +| `TokenFailResponse` | `tokenFailResponseSchema` | `src/models/token-fail-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenIssueApi + +- **Signature**: `authTokenIssueApi(request: TokenEndpoint.AuthTokenIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token/issue` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenEndpoint.AuthTokenIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenEndpoint.AuthTokenIssueApiRequest` (8): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `ticket` | `form` | `string` | yes | +| `subject` | `form` | `string` | yes | +| `properties` | `form` | `Property[]` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `accessToken` | `form` | `string` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `refreshTokenDuration` | `form` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `TokenIssueResponse` | `tokenIssueResponseSchema` | `src/models/token-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### idtokenReissueApi + +- **Signature**: `idtokenReissueApi(request: TokenEndpoint.IdtokenReissueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/idtoken/reissue` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field. **Optional**: omit it and the request carries no body and no `Content-Type` header at all +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `IdtokenReissueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenEndpoint.IdtokenReissueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenEndpoint.IdtokenReissueApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `IdtokenReissueRequest` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `IdtokenReissueRequest` | `idtokenReissueRequestSchema` | `src/models/idtoken-reissue-request.ts` | +| `IdtokenReissueResponse` | `idtokenReissueResponseSchema` | `src/models/idtoken-reissue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/token-operations.md b/plugins/authlete/sdk/typescript/map/operations/token-operations.md new file mode 100644 index 00000000..71222f61 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/token-operations.md @@ -0,0 +1,168 @@ + + +# TokenOperations β€” operations + +Accessor: `client.tokenOperations` Β· Source: `src/resources/token-operations.ts` Β· 5 operations Β· Request and error types: namespace `TokenOperations` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authTokenCreateApi + +- **Signature**: `authTokenCreateApi(request: TokenOperations.AuthTokenCreateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token/create` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenCreateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenOperations.AuthTokenCreateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenOperations.AuthTokenCreateApiRequest` (24): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `grantType` | `form` | `GrantType` | yes | +| `clientId` | `form` | `number` | no | +| `subject` | `form` | `string` | no | +| `scopes` | `form` | `string[]` | no | +| `accessTokenDuration` | `form` | `number` | no | +| `refreshTokenDuration` | `form` | `number` | no | +| `properties` | `form` | `Property[]` | no | +| `clientIdAliasUsed` | `form` | `boolean` | no | +| `accessToken` | `form` | `string` | no | +| `refreshToken` | `form` | `string` | no | +| `accessTokenPersistent` | `form` | `boolean` | no | +| `certificateThumbprint` | `form` | `string` | no | +| `dpopKeyThumbprint` | `form` | `string` | no | +| `authorizationDetails` | `form` | `AuthzDetails` | no | +| `resources` | `form` | `string[]` | no | +| `forExternalAttachment` | `form` | `boolean` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `acr` | `form` | `string` | no | +| `authTime` | `form` | `number` | no | +| `clientEntityIdUsed` | `form` | `boolean` | no | +| `clientIdentifier` | `form` | `string` | no | +| `sessionId` | `form` | `string` | no | +| `metadataDocumentUsed` | `form` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `GrantType` | `grantTypeSchema` | `src/models/grant-type.ts` | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `AuthzDetails` | `authzDetailsSchema` | `src/models/authz-details.ts` | +| `TokenCreateResponse` | `tokenCreateResponseSchema` | `src/models/token-create-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenDeleteApi + +- **Signature**: `authTokenDeleteApi(request: TokenOperations.AuthTokenDeleteApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `DELETE /api/{serviceId}/auth/token/delete/{accessTokenIdentifier}` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenOperations.AuthTokenDeleteApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenOperations.AuthTokenDeleteApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessTokenIdentifier` | `path` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenGetListApi + +- **Signature**: `authTokenGetListApi(request: TokenOperations.AuthTokenGetListApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/{serviceId}/auth/token/get/list` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `TokenGetListResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenOperations.AuthTokenGetListApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenOperations.AuthTokenGetListApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `clientIdentifier` | `query` | `string` | no | +| `subject` | `query` | `string` | no | +| `start` | `query` | `number` | no | +| `end` | `query` | `number` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `TokenGetListResponse` | `tokenGetListResponseSchema` | `src/models/token-get-list-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenRevokeApi + +- **Signature**: `authTokenRevokeApi(request: TokenOperations.AuthTokenRevokeApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token/revoke` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenRevokeResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenOperations.AuthTokenRevokeApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [404] `Result` Β· `"result5"` [429] `Result` Β· `"result6"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenOperations.AuthTokenRevokeApiRequest` (5): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessTokenIdentifier` | `form` | `string` | no | +| `refreshTokenIdentifier` | `form` | `string` | no | +| `clientIdentifier` | `form` | `string` | no | +| `subject` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `TokenRevokeResponse` | `tokenRevokeResponseSchema` | `src/models/token-revoke-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authTokenUpdateApi + +- **Signature**: `authTokenUpdateApi(request: TokenOperations.AuthTokenUpdateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/token/update` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `TokenUpdateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `TokenOperations.AuthTokenUpdateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `TokenOperations.AuthTokenUpdateApiRequest` (16): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessToken` | `form` | `string` | no | +| `accessTokenExpiresAt` | `form` | `number` | no | +| `scopes` | `form` | `string[]` | no | +| `properties` | `form` | `Property[]` | no | +| `accessTokenExpiresAtUpdatedOnScopeUpdate` | `form` | `boolean` | no | +| `accessTokenHash` | `form` | `string` | no | +| `accessTokenValueUpdated` | `form` | `boolean` | no | +| `accessTokenPersistent` | `form` | `boolean` | no | +| `certificateThumbprint` | `form` | `string` | no | +| `dpopKeyThumbprint` | `form` | `string` | no | +| `authorizationDetails` | `form` | `AuthzDetails` | no | +| `forExternalAttachment` | `form` | `boolean` | no | +| `refreshTokenExpiresAt` | `form` | `number` | no | +| `refreshTokenExpiresAtUpdatedOnScopeUpdate` | `form` | `boolean` | no | +| `tokenId` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `AuthzDetails` | `authzDetailsSchema` | `src/models/authz-details.ts` | +| `TokenUpdateResponse` | `tokenUpdateResponseSchema` | `src/models/token-update-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/user-info-endpoint.md b/plugins/authlete/sdk/typescript/map/operations/user-info-endpoint.md new file mode 100644 index 00000000..3a413657 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/user-info-endpoint.md @@ -0,0 +1,72 @@ + + +# UserInfoEndpoint β€” operations + +Accessor: `client.userInfoEndpoint` Β· Source: `src/resources/user-info-endpoint.ts` Β· 2 operations Β· Request and error types: namespace `UserInfoEndpoint` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### authUserinfoApi + +- **Signature**: `authUserinfoApi(request: UserInfoEndpoint.AuthUserinfoApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/userinfo` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `UserinfoResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `UserInfoEndpoint.AuthUserinfoApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `UserInfoEndpoint.AuthUserinfoApiRequest` (12): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `token` | `form` | `string` | yes | +| `clientCertificate` | `form` | `string` | no | +| `dpop` | `form` | `string` | no | +| `htm` | `form` | `string` | no | +| `htu` | `form` | `string` | no | +| `uri` | `form` | `string` | no | +| `message` | `form` | `string` | no | +| `headers` | `form` | `Pair[]` | no | +| `targetUri` | `form` | `string` | no | +| `dpopNonceRequired` | `form` | `boolean` | no | +| `requestBodyContained` | `form` | `boolean` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `UserinfoResponse` | `userinfoResponseSchema` | `src/models/userinfo-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### authUserinfoIssueApi + +- **Signature**: `authUserinfoIssueApi(request: UserInfoEndpoint.AuthUserinfoIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/auth/userinfo/issue` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `UserinfoIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `UserInfoEndpoint.AuthUserinfoIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `UserInfoEndpoint.AuthUserinfoIssueApiRequest` (8): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `token` | `form` | `string` | yes | +| `claims` | `form` | `string` | no | +| `sub` | `form` | `string` | no | +| `claimsForTx` | `form` | `string` | no | +| `requestSignature` | `form` | `string` | no | +| `headers` | `form` | `Pair[]` | no | +| `verifiedClaimsForTx` | `form` | `string[]` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Pair` | `pairSchema` | `src/models/pair.ts` | +| `UserinfoIssueResponse` | `userinfoIssueResponseSchema` | `src/models/userinfo-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/map/operations/utility-endpoints.md b/plugins/authlete/sdk/typescript/map/operations/utility-endpoints.md new file mode 100644 index 00000000..a17fc6e5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/utility-endpoints.md @@ -0,0 +1,32 @@ + + +# UtilityEndpoints β€” operations + +Accessor: `client.utilityEndpoints` Β· Source: `src/resources/utility-endpoints.ts` Β· 2 operations Β· Request and error types: namespace `UtilityEndpoints` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### infoApi + +- **Signature**: `infoApi(options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/info` +- **Auth**: none β€” public; no credential is sent +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `InfoResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `UtilityEndpoints.InfoApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +| Type | Schema value | Source | +| --- | --- | --- | +| `InfoResponse` | `infoResponseSchema` | `src/models/info-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### miscEchoApi + +- **Signature**: `miscEchoApi(options?: RequestOptions): ApiPromise` +- **Wire**: `GET /api/misc/echo` +- **Auth**: `bearer` +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `undefined` β€” the operation resolves to nothing +- **Error**: `AuthleteApiError` with `kind: "api"` β€” untyped, `payload.kind` always `"undeclared"` + diff --git a/plugins/authlete/sdk/typescript/map/operations/verifiable-credential-issuer.md b/plugins/authlete/sdk/typescript/map/operations/verifiable-credential-issuer.md new file mode 100644 index 00000000..978483d5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/map/operations/verifiable-credential-issuer.md @@ -0,0 +1,281 @@ + + +# VerifiableCredentialIssuer β€” operations + +Accessor: `client.verifiableCredentialIssuer` Β· Source: `src/resources/verifiable-credential-issuer.ts` Β· 11 operations Β· Request and error types: namespace `VerifiableCredentialIssuer` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `authlete-api`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### vciBatchIssueApi + +- **Signature**: `vciBatchIssueApi(request: VerifiableCredentialIssuer.VciBatchIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/batch/issue` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciBatchIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciBatchIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciBatchIssueApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `VciBatchIssueRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciBatchIssueRequest` | `vciBatchIssueRequestSchema` | `src/models/vci-batch-issue-request.ts` | +| `VciBatchIssueResponse` | `vciBatchIssueResponseSchema` | `src/models/vci-batch-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciBatchParseApi + +- **Signature**: `vciBatchParseApi(request: VerifiableCredentialIssuer.VciBatchParseApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/batch/parse` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciBatchParseResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciBatchParseApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciBatchParseApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessToken` | `form` | `string` | no | +| `requestContent` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciBatchParseResponse` | `vciBatchParseResponseSchema` | `src/models/vci-batch-parse-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciDeferredIssueApi + +- **Signature**: `vciDeferredIssueApi(request: VerifiableCredentialIssuer.VciDeferredIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/deferred/issue` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciDeferredIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciDeferredIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciDeferredIssueApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `VciDeferredIssueRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciDeferredIssueRequest` | `vciDeferredIssueRequestSchema` | `src/models/vci-deferred-issue-request.ts` | +| `VciDeferredIssueResponse` | `vciDeferredIssueResponseSchema` | `src/models/vci-deferred-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciDeferredParseApi + +- **Signature**: `vciDeferredParseApi(request: VerifiableCredentialIssuer.VciDeferredParseApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/deferred/parse` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciDeferredParseResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciDeferredParseApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciDeferredParseApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessToken` | `form` | `string` | no | +| `requestContent` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciDeferredParseResponse` | `vciDeferredParseResponseSchema` | `src/models/vci-deferred-parse-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciJwksApi + +- **Signature**: `vciJwksApi(request: VerifiableCredentialIssuer.VciJwksApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/jwks` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciJwksResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciJwksApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciJwksApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `pretty` | `form` | `boolean` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciJwksResponse` | `vciJwksResponseSchema` | `src/models/vci-jwks-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciJwtissuerApi + +- **Signature**: `vciJwtissuerApi(request: VerifiableCredentialIssuer.VciJwtissuerApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/jwtissuer` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciJwtissuerResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciJwtissuerApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciJwtissuerApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `pretty` | `form` | `boolean` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciJwtissuerResponse` | `vciJwtissuerResponseSchema` | `src/models/vci-jwtissuer-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciMetadataApi + +- **Signature**: `vciMetadataApi(request: VerifiableCredentialIssuer.VciMetadataApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/metadata` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciMetadataResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciMetadataApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciMetadataApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `pretty` | `form` | `boolean` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciMetadataResponse` | `vciMetadataResponseSchema` | `src/models/vci-metadata-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciOfferCreateApi + +- **Signature**: `vciOfferCreateApi(request: VerifiableCredentialIssuer.VciOfferCreateApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/offer/create` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciOfferCreateResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciOfferCreateApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciOfferCreateApiRequest` (15): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `authorizationCodeGrantIncluded` | `form` | `boolean` | no | +| `issuerStateIncluded` | `form` | `boolean` | no | +| `preAuthorizedCodeGrantIncluded` | `form` | `boolean` | no | +| `subject` | `form` | `string` | no | +| `duration` | `form` | `number` | no | +| `context` | `form` | `string` | no | +| `properties` | `form` | `Property[]` | no | +| `jwtAtClaims` | `form` | `string` | no | +| `authTime` | `form` | `number` | no | +| `acr` | `form` | `string` | no | +| `credentialConfigurationIds` | `form` | `string[]` | no | +| `txCode` | `form` | `string` | no | +| `txCodeInputMode` | `form` | `string` | no | +| `txCodeDescription` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `Property` | `propertySchema` | `src/models/property.ts` | +| `VciOfferCreateResponse` | `vciOfferCreateResponseSchema` | `src/models/vci-offer-create-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciOfferInfoApi + +- **Signature**: `vciOfferInfoApi(request: VerifiableCredentialIssuer.VciOfferInfoApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/offer/info` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciOfferInfoResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciOfferInfoApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciOfferInfoApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `identifier` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciOfferInfoResponse` | `vciOfferInfoResponseSchema` | `src/models/vci-offer-info-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciSingleIssueApi + +- **Signature**: `vciSingleIssueApi(request: VerifiableCredentialIssuer.VciSingleIssueApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/single/issue` +- **Auth**: `bearer` +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciSingleIssueResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciSingleIssueApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciSingleIssueApiRequest` (2): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `body` | `body` | `VciSingleIssueRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciSingleIssueRequest` | `vciSingleIssueRequestSchema` | `src/models/vci-single-issue-request.ts` | +| `VciSingleIssueResponse` | `vciSingleIssueResponseSchema` | `src/models/vci-single-issue-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + +### vciSingleParseApi + +- **Signature**: `vciSingleParseApi(request: VerifiableCredentialIssuer.VciSingleParseApiRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /api/{serviceId}/vci/single/parse` +- **Auth**: `bearer` +- **Request body**: `application/x-www-form-urlencoded;charset=UTF-8` β€” every field marked `form` +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `VciSingleParseResponse` +- **Error**: `AuthleteApiError` with `kind: "api"`, an instance of `VerifiableCredentialIssuer.VciSingleParseApiError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"result"` [400] `Result` Β· `"result2"` [401] `Result` Β· `"result3"` [403] `Result` Β· `"result4"` [429] `Result` Β· `"result5"` [500] `Result` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `VerifiableCredentialIssuer.VciSingleParseApiRequest` (3): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `serviceId` | `path` | `string` | yes | +| `accessToken` | `form` | `string` | no | +| `requestContent` | `form` | `string` | no | + +| Type | Schema value | Source | +| --- | --- | --- | +| `VciSingleParseResponse` | `vciSingleParseResponseSchema` | `src/models/vci-single-parse-response.ts` | +| `Result` | `resultSchema` | `src/models/result.ts` | + diff --git a/plugins/authlete/sdk/typescript/package.json b/plugins/authlete/sdk/typescript/package.json new file mode 100644 index 00000000..f7188da5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/package.json @@ -0,0 +1,70 @@ +{ + "name": "authlete-api", + "version": "3.0.16", + "description": "Welcome to the **Authlete API documentation**. Authlete is an **API-first service** where every aspect of the platform is configurable via API. This documentation will help you authenticate and integrate with Authlete to build powerful OAuth 2.0 and OpenID Connect servers.\n\nAt a high level, the Authlete API is grouped into two categories:\n\n- **Management APIs**: Enable you to manage services and clients.\n- **Runtime APIs**: Allow you to build your own Authorization Servers or Verifiable Credential (VC) issuers.\n\n## \uD83C\uDF10 API Servers\n\nAuthlete is a global service with clusters available in multiple regions across the world:\n\n- \uD83C\uDDFA\uD83C\uDDF8 **US**: `https://us.authlete.com`\n- \uD83C\uDDEF\uD83C\uDDF5 **Japan**: `https://jp.authlete.com`\n- \uD83C\uDDEA\uD83C\uDDFA **Europe**: `https://eu.authlete.com`\n- \uD83C\uDDE7\uD83C\uDDF7 **Brazil**: `https://br.authlete.com`\n\nOur customers can host their data in the region that best meets their requirements.\n\n## \uD83D\uDD11 Authentication\n\nAll API endpoints are secured using **Bearer token authentication**. You must include an access token in every request:\n\n```\nAuthorization: Bearer YOUR_ACCESS_TOKEN\n```\n\n### Getting Your Access Token\n\nAuthlete supports two types of access tokens:\n\n**Service Access Token** - Scoped to a single service (authorization server instance)\n\n1. Log in to [Authlete Console](https://console.authlete.com)\n2. Navigate to your service β†’ **Settings** β†’ **Access Tokens**\n3. Click **Create Token** and select permissions (e.g., `service.read`, `client.write`)\n4. Copy the generated token\n\n**Organization Token** - Scoped to your entire organization\n\n1. Log in to [Authlete Console](https://console.authlete.com)\n2. Navigate to **Organization Settings** β†’ **Access Tokens**\n3. Click **Create Token** and select org-level permissions\n4. Copy the generated token\n\n> ⚠️ **Important Note**: Tokens inherit the permissions of the account that creates them. Service tokens can only access their specific service, while organization tokens can access all services within your org.\n\n### Token Security Best Practices\n\n- **Never commit tokens to version control** - Store in environment variables or secure secret managers\n- **Rotate regularly** - Generate new tokens periodically and revoke old ones\n- **Scope appropriately** - Request only the permissions your application needs\n- **Revoke unused tokens** - Delete tokens you're no longer using from the console\n\n### Quick Test\n\nVerify your token works with a simple API call:\n\n```bash\ncurl -X GET https://us.authlete.com/api/service/get/list \\\n -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\"\n```\n\n## \uD83C\uDF93 Tutorials\n\nIf you're new to Authlete or want to see sample implementations, these resources will help you get started:\n\n- [Getting Started with Authlete](https://www.authlete.com/developers/getting_started/)\n- [From Sign-Up to the First API Request](https://www.authlete.com/developers/tutorial/signup/)\n\n## \uD83D\uDEE0 Contact Us\n\nIf you have any questions or need assistance, our team is here to help:\n\n- [Contact Page](https://www.authlete.com/contact/)\n", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "dialects": [ + "esm", + "commonjs" + ], + "project": "./tsconfig.tshy.json", + "selfLink": false + }, + "main": "./dist/commonjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/commonjs/index.d.ts", + "files": [ + "dist", + "src", + "tsconfig.json", + "tsconfig.tshy.json", + "README.md", + "api-reference.md", + "sdk-map.md", + "map" + ], + "sideEffects": false, + "engines": { + "node": ">=20.3" + }, + "scripts": { + "prepare": "tshy", + "build": "tshy", + "smoke": "node -e \"require('authlete-api')\" && node --input-type=module -e \"import('authlete-api')\"", + "typecheck": "tsc --noEmit", + "typecheck:dom": "tsc --noEmit -p tsconfig.dom.json", + "lint": "oxlint --type-aware --max-warnings=0 src", + "format": "oxfmt src", + "format:check": "oxfmt --check src" + }, + "license": "MIT", + "devDependencies": { + "@types/node": "^20.14.0", + "oxfmt": "^0.61.0", + "oxlint": "^1.76.0", + "oxlint-tsgolint": "^7.0.2001", + "tshy": "^4.1.3", + "typescript": "^7.0.2" + }, + "dependencies": { + "zod": "^4.1.0" + } +} diff --git a/plugins/authlete/sdk/typescript/sdk-map.md b/plugins/authlete/sdk/typescript/sdk-map.md new file mode 100644 index 00000000..17ad6f8c --- /dev/null +++ b/plugins/authlete/sdk/typescript/sdk-map.md @@ -0,0 +1,376 @@ + + +# SDK map β€” Authlete API (TypeScript) + +> A generated table of contents for this SDK. Consult this map and its sub-pages to learn signatures, request-field placement, error types and server wiring **by lookup**. Model shapes and enum values are *not* duplicated here β€” the map names the file declaring each type and the schema value exported beside it; read the shape there. The compiler is the backstop: a wrong name fails to build. + +| | | +| --- | --- | +| SDK display name | Authlete API | +| Package | `authlete-api` | +| Package version | `3.0.16` | +| API spec version | `3.0.16` | +| Import specifier | `authlete-api` β€” the package root is the **only** entry. Deep imports (`authlete-api/models/...`) do not resolve; the `exports` map exposes `.` and `./package.json` and nothing else | +| Module format | dual ESM + CommonJS, as folder dialects (`dist/esm`, `dist/commonjs`), each with its own `package.json` marker. No `.mjs`, `.cjs`, `.d.mts` or `.d.cts` files exist | +| Node floor | `>=20.3` (`engines.node`) | +| TypeScript floor | a resolver that reads `exports` (4.7+), plus whatever the pinned `zod` requires β€” `zod@4` needs 5.5 or later. The public `.d.ts` chain reaches `zod/v4-mini`, so this is a real constraint rather than a build-tool version | +| Runtime dependency | `zod` (`^4.1.0`), imported as `zod/v4-mini`. The only runtime dependency | +| Generator | APIMatic | + +Staleness check: the API spec version above changes when the SDK is regenerated from a new spec. If a lookup here fails to compile, trust the compiler and re-read the source file named in the row. + +All `Source` paths on this map and its sub-pages are relative to the **SDK root** β€” the directory holding this file and `package.json` β€” never to the page that carries them: a page two directories deep writes exactly what a page at the root would. The package ships its `src/` tree, so the same paths resolve inside `node_modules/authlete-api/` too. An import specifier ending `.js` inside that source is the NodeNext spelling of the sibling `.ts` file. + +--- + +## Getting a client + +```ts +import { AuthleteApiClient, ServerEnvironment } from "authlete-api"; + +const client = new AuthleteApiClient({ + serverEnvironment: ServerEnvironment.Production, + bearer: "YOUR_BEARER_TOKEN", +}); +``` + +The only constructor is `new AuthleteApiClient(options: ClientOptions = {})`, so `new AuthleteApiClient()` is the minimum. Resources are memoized lazy getters on the client β€” `client.serviceManagement`, `client.clientManagement`, `client.authorizationEndpoint`, `client.pushedAuthorizationEndpoint`, `client.tokenEndpoint`, `client.tokenOperations`, `client.introspectionEndpoint`, `client.revocationEndpoint`, `client.userInfoEndpoint`, `client.jwkSetEndpoint`, `client.dynamicClientRegistration`, `client.ciba`, `client.grantManagementEndpoint`, `client.joseObject`, `client.deviceFlow`, `client.federationEndpoint`, `client.verifiableCredentialIssuer`, `client.hardwareSecurityKey`, `client.utilityEndpoints`, `client.nativeSso`, `client.auditLogs`, `client.backChannelLogout`, `client.lifecycle` β€” and their classes are exported only for their merged namespaces and for `instanceof`; their constructors take engine internals that are not exported, so reach a resource only through its getter. + +All `ClientOptions` fields (source: `src/client-options.ts`; every field is `readonly`): + +| Field | Type | Default | +| --- | --- | --- | +| `serverEnvironment` | `typeof ServerEnvironment.`, one per union arm | `ServerEnvironment.Production` | +| `serverOptions` | the selected environment's server overrides | `{}` β€” each resolver merges its own per-environment defaults in | +| `retry` | `RetryOptions` | the `RetryOptions` defaults below | +| `logging` | `LoggingOptions` | off β€” the `LoggingOptions` defaults below | +| `fetch` | `FetchLike \| undefined` | the global `fetch`, resolved by the transport | +| `bearer` | `TokenProvider \| undefined` | unset | + +The 1 auth field is optional, and an unset one is not an error β€” the operation that wanted it simply sends no credential. What each one puts on the wire, and which operations require it, are under Servers & auth. + +When no `fetch` is reachable the **constructor** throws `ConfigurationError`, not the first call. + +`RetryOptions` fields (source: `src/core/retry.ts`; exported from `authlete-api` as a type). Every field is optional, so pass only the fields you change β€” each one left out takes its default: + +| Field | Type | Default | +| --- | --- | --- | +| `timeout` | `number` (ms) | `60_000` | +| `statusCodesToRetry` | `readonly number[]` | `[408, 429, 500, 502, 503, 504]` | +| `httpMethodsToRetry` | `readonly HttpMethod[]` | `["GET", "HEAD", "PUT", "OPTIONS"]` | +| `maxRetries` | `number` | `3` | +| `delay` | `number` (ms) | `1000` | +| `backoffFactor` | `number` | `2` | +| `useExponentialBackoff` | `boolean` | `true` | +| `maxJitter` | `number` (a fraction, `0` to `1`) | `0.25` | +| `onRetry` | `((attempt: RetryAttempt) => void) \| undefined` | unset | + +`retry: { maxRetries: 0 }` turns retries off. + +A call may override three of these through `RequestOptions.retry`, typed `RequestRetryOptions`: `maxRetries`, `timeout` and `statusCodesToRetry`. + +Retry types named by the fields above β€” public members with their **declared types**, verbatim from source; every member is `readonly`, and both are exported as types: + +| Type | Public members | Source | +| --- | --- | --- | +| `RetryAttempt` β€” the `onRetry` callback argument | `attemptNumber: number` Β· `delay: number` Β· `reason: RetryReason` | `src/core/retry.ts` | +| `RetryReason` β€” narrow on `kind` | `{ kind: "status"; status: number; headers: Headers }` or `{ kind: "fault"; error: ConnectionError \| TimeoutError }`, the two retryable leaves of `AuthleteApiError` | `src/core/retry.ts` | + +`LoggingOptions` fields (source: `src/core/logging.ts`; exported from `authlete-api` as a type). Every field is optional, so pass only the fields you change β€” each one left out takes its default: + +| Field | Type | Default | +| --- | --- | --- | +| `logger` | `Logger` | `console` | +| `level` | `LogLevel` β€” `"off" \| "error" \| "warn" \| "info" \| "debug"` | `"off"`; `"debug"` when `logger` is set | +| `logRequestHeaders` | `boolean` | `false` | +| `logResponseHeaders` | `boolean` | `false` | +| `logRequestBody` | `boolean` | `false` | +| `bodySizeLimit` | `number` (bytes of UTF-8) | `32_768` | +| `loggableContentTypes` | `readonly string[]` | `["application/json", "application/x-www-form-urlencoded"]` | +| `redactedHeaders` | `readonly string[]` | `[]` | +| `unmaskHeaders` | `readonly string[]` | `[]` | +| `redactedKeys` | `readonly string[]` | `["sig", "signature", "access_token", "apikey", "api_key", "client_secret", "password", "refresh_token", "code", "assertion", "client_assertion"]` | +| `redactionPlaceholder` | `string` | `"***"` | + +A call may override one of these through `RequestOptions.logging`, typed `RequestLoggingOptions`: `level`. + +Logging types named by the fields above β€” their **declared types**, verbatim from source; both are exported as types: + +| Type | Public members | Source | +| --- | --- | --- | +| `Logger` β€” the `logger` field | `error(message: string, details: LogDetails): void` Β· `warn(message: string, details: LogDetails): void` Β· `info(message: string, details: LogDetails): void` Β· `debug(message: string, details: LogDetails): void` | `src/core/logging.ts` | +| `LogDetails` β€” narrow on `event` | `HttpRequestDetails \| RequestHeadersDetails \| RequestBodyDetails \| HttpResponseDetails \| ResponseHeadersDetails \| HttpRetryingDetails \| HttpFailedDetails`, each exported as a type | `src/core/logging.ts` | + +**`ClientOptions.fetch` is the one transport extension point** β€” there are no hooks, no middleware and no interceptors, so a proxy, a custom agent and extra headers all go here. The SDK calls a replacement with one argument, the `Request` of the attempt, and no `init`. A replacement **must forward that `Request`, or its `signal`,** to whatever actually performs the request. Drop the signal and both the per-call signal and `retry.timeout` go inert β€” the call neither aborts nor times out. To add a header, copy the `Request` with `new Request(request)` and set it on the copy's `headers`: passing `{ headers }` to the copy replaces every header the SDK set rather than adding to them. A replacement may reject with an error of its own when that signal aborts: a timeout is still a `TimeoutError` that is retried, and an abort of `RequestOptions.signal` still rejects with the caller's own `reason`. + +**Cancellation.** The `signal` on `RequestOptions` is the per-request cancellation surface. Aborting rejects with the signal's **own `reason`** β€” whatever you passed to `abort()`, or the platform `DOMException` a bare `abort()` supplies β€” unwrapped, so it is **not** an `AuthleteApiError` and a `catch` that tests the family must rethrow it. An already-aborted signal rejects immediately. The `retry.timeout` that bounded the attempt is the SDK's own and does stay in the family, as `err.kind === "timeout"`. It starts once the credential is in hand and covers the request up to its response headers β€” not obtaining the credential and not reading the body, which only the signal bounds, so a body that stalls after its headers holds a call with no signal until the transport gives up. + +The entire per-request surface is the optional second argument of every operation: + +| Type | Members | Source | +| --- | --- | --- | +| `RequestOptions` | `signal?: AbortSignal \| undefined` Β· `retry?: RequestRetryOptions` Β· `logging?: RequestLoggingOptions` | `src/core/api-request.ts` | +| `RequestRetryOptions` β€” `Pick` | `maxRetries?: number` Β· `timeout?: number` Β· `statusCodesToRetry?: readonly number[]` | `src/core/retry.ts` | +| `RequestLoggingOptions` β€” `Pick` | `level?: LogLevel` | `src/core/logging.ts` | + +**A per-call `retry` is merged field by field over the client's resolved policy**, so `{ retry: { maxRetries: 0 } }` changes that one field for that one call and leaves every other call alone. + +**Not on this SDK.** These are absent by design, not undocumented. This table ships with `src/core/` and is versioned with it. + +| You might reach for | Reality | +| --- | --- | +| hooks, middleware, interceptors, `onRequest`/`onResponse` | none. `fetch` is the one transport extension point | +| pagination, `for await`, auto-paging helpers | no operation is paginated and nothing is async-iterable | +| SSE, `text/event-stream` | no event streams. Every decoder reads the body to completion, bar a binary success, which hands its stream over unread | +| multipart responses, XML bodies | none. A multipart reply is not decoded and an XML body is not sent β€” an operation declaring either is still emitted, with no body to supply or read | +| per-request `headers`, `baseUrl`, idempotency key | none. `RequestOptions` is `{ signal, retry, logging }`; a header, a base URL and a caller-supplied idempotency key are not on it | +| the raw `fetch` `Response` | deliberately unreachable. `status` and `headers` are on `asApiResult()` and on a thrown `ResponseError` | + +--- + +## Error-handling model (read once β€” applies to every operation) + +Operations are **throw-based**, and every **operational** failure belongs to **one family**: `AuthleteApiError`, a union over six leaves, so one `instanceof AuthleteApiError` sees all of them. It is not the whole escape set β€” four throwables sit outside it, enumerated below. Every leaf names the call it raised β€” `err.method` and `err.uri` β€” and `message` opens with that name. `instanceof` is reliable **within one dialect**: a process that loads both β€” `import` in one file, `require` in another β€” gets two independent copies of every error class, and `instanceof` across that boundary is `false`. Narrow on `err.kind` there, or on `err.name`, which is stable across copies. + +Core types (public members with their declared types; all are `readonly`): + +| Type | Public members | Source | +| --- | --- | --- | +| `AuthleteApiError` (declared as `CoreError`) | `kind: ErrorKind` Β· `method: HttpMethod` Β· `uri: string` Β· `message` Β· `cause` β€” the union every failure below belongs to | `src/core/errors.ts` | +| `ResponseError` | the rung the server answered on, `ApiError \| DecodeError`; adds `status: number` Β· `headers: Headers` | `src/core/errors.ts` | +| `ApiError` | `kind: "api"` Β· `payload` β€” the open arm, whose `kind` is `string`. **Not generic**: a typed operation's subclass redeclares `payload` with its own literal arms | `src/core/api-error.ts` | +| `TimeoutError` | `kind: "timeout"` Β· `timeout: number` | `src/core/errors.ts` | +| `DecodeError`, `EncodeError`, `ConnectionError`, `AuthError` | their `kind`, and nothing beyond the two rows above | `src/core/errors.ts` | +| `Declared` | `kind: K` Β· `body: B` | `src/core/api-error.ts` | +| `Undeclared` | `kind: "undeclared"` Β· `rawBody: ArrayBuffer` β€” the last arm of every error class, carrying the untouched bytes of a status no arm covers, a middleware's included | `src/core/api-error.ts` | +| `ApiResult` | on success `{ ok: true; status; headers; value: T }`, on failure `{ ok: false; status; headers; message: string; method: HttpMethod; uri: string; payload: P }`, `P` being the error class's payload union β€” the failure branch carries the error's own members, never the error object | `src/core/api-promise.ts` | + +`AuthleteApiError` and `ResponseError` are each a **type and a value**: the type is the union, the value is the abstract class every leaf extends, so `instanceof` and `err.kind` select the same set. Neither can be constructed or extended. `uri` is the absolute URL the call dialled, with the server variables expanded and the path parameters filled. It carries no query, fragment or userinfo, so no query parameter reaches it. One failure names an unresolved URI: a path parameter rejected by its schema arrives as an `EncodeError` whose `uri` still shows the unfilled `{braces}` β€” an `undefined` one included, since a path parameter is always required, so its schema rejects it first. + +`ErrorKind` is closed, so a `switch` over `err.kind` is exhaustive: + +| `err.kind` | What happened | Adds | +| --- | --- | --- | +| `"api"` | the API answered with an error status | `status` Β· `headers` Β· `payload` | +| `"decode"` | the answer could not be turned into the declared value β€” the body was not JSON, failed its schema, arrived where none is declared, or died mid-read after the response line; `cause` carries the underlying failure | `status` Β· `headers` | +| `"encode"` | a request value did not match its declared type, so **nothing was sent**. `cause` is the `SchemaError` that rejected it | β€” | +| `"connection"` | `fetch` rejected before a response line arrived | β€” | +| `"timeout"` | `ClientOptions.retry.timeout` elapsed. `timeout` is the budget that ran out | `timeout` | +| `"auth"` | a credential could not be **obtained**, per the paragraph below | β€” | + +**Four throwables sit outside the family**, so `instanceof AuthleteApiError` is `false` on each and a `catch` that tests it has to rethrow what is left. `ConfigurationError` comes out of the **`AuthleteApiClient` constructor**, synchronously and before any `ApiPromise` exists: no reachable `fetch`, an unknown `ClientOptions.serverEnvironment`, or a `ClientOptions.retry` or `ClientOptions.logging` whose reads throw. One call can reject with it too: a `RequestOptions.retry` or `RequestOptions.logging` whose reads throw. Either way the failed read is on `cause`. `SchemaError` is what a codec throws when called directly β€” `actionSchema.decode(json)` β€” so it names no call; through an operation the same failure arrives one level down, on `DecodeError.cause` or `EncodeError.cause`, and a `serverOptions` override whose value is not a string raises it from the constructor too. Bugs stay outside the family and reach you raw β€” an unparseable `baseUrl` is a `TypeError`. And a caller abort arrives as the signal's own `reason`, unwrapped. The first two are exported from the package root; the other two are not ours to export. + +**`AuthError` is about obtaining a credential, never about being refused one.** A 401 *from the API* is an `ApiError` like any other status. A 401 does have one auth consequence: it invalidates whatever that operation's scheme had cached, so the **next** call re-acquires. + +```ts +try { + const response = await client.serviceManagement.serviceConfigurationApi({ + serviceId: "some example string", + }); +} catch (err) { + if (err instanceof AuthleteApiError) { + switch (err.kind) { + case "api": + // TODO: the API answered with an error status β€” read err.status and err.payload + break; + case "decode": + // TODO: the answer did not fit the spec β€” read err.status and err.cause + break; + case "encode": + // TODO: nothing was sent β€” err.cause is the SchemaError that rejected the value + break; + case "connection": + case "timeout": + case "auth": + // TODO: no response was produced β€” err.kind says which + break; + } + } else { + throw err; + } +} +``` + +**Narrowing the payload.** A typed subclass declares its arms as literals, so `switch (err.payload.kind)` narrows `payload.body` to exactly one model. The `kind` is named after the arm's **body**, *not* its status code: a body that references a model takes that model's name in lower camel, any other body `error{Status}`, and a second arm that would land on the same name takes a numeric suffix. On the base `ApiError` β€” what an operation with no declared error bodies rejects with β€” `payload.kind` is `string`, so comparing it to `"undeclared"` narrows **nothing**: use `"rawBody" in err.payload`. Which arms an operation declares, with the status each covers, is the **Error arms** bullet on its page below. + +**Matcher precedence** for a subclass with several arms, in two passes: an exact numeric status is looked up across the whole table **first**, then the first covering `[lo, hi]` range. A body that does not fit the arm it matched is a `DecodeError`. A status no row covers is the `"undeclared"` arm with its bytes untouched β€” a spec's `default` response is not a row, so a gateway's or a WAF's own status lands there rather than failing the `default` schema. + +**The non-throwing form exists on every operation.** `.asApiResult()` returns `ApiResult` and does **not** reject for an HTTP error status β€” every other failure still rejects, `DecodeError` included, so the `catch` stays. It must be called on the value the operation returned: `ApiPromise` overrides `Symbol.species`, so `.then()`, `.catch()` and `.finally()` hand back a plain `Promise` and the method is gone. + +```ts +try { + const result = await client.serviceManagement.serviceConfigurationApi({ + serviceId: "some example string", + }).asApiResult(); + // TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers + if (result.ok) { + // TODO: Use 'result.value' β€” what this operation resolves to + } else { + // TODO: Use 'result.message', 'result.method' and 'result.uri', and narrow 'result.payload' + } +} catch (err) { + if (err instanceof AuthleteApiError) { + // TODO: no error status was produced β€” err.kind says which failure this is + } else { + throw err; + } +} +``` + +`result.payload` is the same payload union a thrown `ApiError` carries on `err.payload`, and `result.message`, `result.method` and `result.uri` are that error's own members β€” so the **Error arms** bullet on an operation's page enumerates the payload either way, and the `catch` above it is for the rest of the family. `result.method` and `result.uri` are named on this map alone. + +Of **93 operations**, **92** declare typed error bodies and **1** rejects with the base `ApiError`, whose payload is always the `"undeclared"` arm. + +--- + +## Operations β€” by resource (23 groups, 93 operations) + +Each page below carries one block per operation, with bullets in the fixed order **Server**, **Signature**, **Wire**, **Auth**, **Request body**, **SDK-sent**, **Returns**, **Error**, **Error arms**, then a **Fields** table mapping every request field to the channel it travels on, and a **Type sources** table naming the declaring file and schema value of every type the operation mentions. With `api-reference.md` documenting operations only, that table is the route from an operation to the file declaring what it takes. + +**Each block states what is specific to its operation. Everything in the table below holds for EVERY operation unless that operation says otherwise, so a block silent on one of these points is telling you the default here applies β€” take it and move on rather than opening the source to confirm it.** + +| Applies to every operation | Stated where | A block departs from it only by | +| --- | --- | --- | +| **Call shape `op(request, options?)`** β€” one flat request object first, the per-call options second. There is no positional overload. What the second argument carries is a `signal`, a `retry` override and a log level; a per-call base URL, header or auth override does not exist | here, Getting a client | never β€” it always holds | +| **The request object is flat and channel-blind.** A field named `body` *is* the whole request body; every other field is fanned out to path, query, header or form by the SDK. Nothing in the object is nested by channel | here | never β€” the **Fields** table `Channel` column always resolves it | +| **Throw-based, returning `ApiPromise`.** `await` it for `T`; call `.asApiResult()` on the returned value for the non-throwing `ApiResult`. No operation is result-only | here, Error-handling model | never | +| **`E` is the base `ApiError`** and the payload is always the `"undeclared"` arm | Error-handling model | the spec declared error bodies β€” the **Error** bullet names a subclass and an **Error arms** bullet gives each arm's tag, status and body | +| **The request body and its media type are stated on every block**, by a **Request body** bullet that is never omitted. `none` means no body **and no `Content-Type` header**, and a named media type means the body is **required** β€” the request type's field is not optional | here | the spec declared the body optional β€” the bullet adds **Optional**, the field is `field?:`, and omitting it sends no body and no `Content-Type` header at all | +| **Resolves once, to one whole value** β€” except a binary body, which resolves to a stream the caller reads. No pagination, no SSE, no async iterables and no partial results | here, Not on this SDK | never at this SDK version | +| **Six identity headers ride every request** β€” `User-Agent`, `X-APIMatic-Lang`, `X-APIMatic-Package-Version`, `X-APIMatic-Gen-Version`, `X-APIMatic-OS` and `X-APIMatic-Runtime`. They identify the generated SDK, so **no option configures them** | here | the operation declared a header of the same name β€” the operation's layer is folded after the client's, so its value wins | +| **A fresh `Idempotency-Key` rides every non-GET call that does not declare that header itself**, minted per call in the operation's own header layer. It makes a *replayed* request safe, not a repeated one β€” a value that changes per call deduplicates nothing, so it is no substitute for a key the API documents. **No option sets it**, and once minted it is always sent β€” a runtime with no `crypto` global mints it from `Math.random` mixed with the clock and a per-process counter | here | the operation is a GET, or declared that header itself β€” then its own value stands and nothing is minted | +| **Server group `default`** | here, Servers & auth | the operation is on another group β€” its block carries a **Server** bullet | +| **Every operation states its auth requirement**, by an **Auth** bullet that is never omitted β€” one scheme, a composition over schemes, or `none` for a public operation | here, Servers & auth | never β€” the bullet is always present | +| **Every value is schema-encoded before the request is built** β€” a wrong type or format rejects and nothing is sent. **An omitted field that has a default is still sent, with that default**, filled by the SDK rather than by the server | here | the field has a default β€” it appears in the **Fields** table `Default` column | +| **Field names are TypeScript camelCase and the wire name is the same** | here | some field differs β€” the **Fields** table gains a `Wire` column, where an em dash means "same as the field name" | +| **Arrays repeat their key and objects bracket-expand** | the serialization block below | never β€” this SDK declares no per-field serialization style, so every array takes this one | + +**Wire serialization, once, for every channel** (source: `src/core/param-value.ts`, `src/core/url.ts`, `src/core/headers.ts`, `src/core/params.ts`). This block ships with `src/core/` and is versioned with it: + +- **`path`** takes no style. An array is comma-joined with each element percent-encoded **separately**; an object becomes one percent-encoded JSON document inside the segment. A field whose encoded value is `undefined` throws `TypeError` naming the unfilled placeholder β€” a guard no operation reaches, since a path parameter is always required and its schema rejects `undefined` first, as an `EncodeError`; `null` collapses the segment. +- **`header`** takes no style. An array is comma-joined un-encoded (OpenAPI `simple`). `undefined` says nothing, while `null` and an empty array are tombstones that remove the header. Later layers win by **lowercased** name, in the order body content type, then client defaults, then operation. +- **`query`** and **`form`** repeat an array's key and bracket-expand an object at any depth (`filter[status]=open`, `ranges[amount][min]=10`). An array of *objects* bracket-expands per element with **no index**, so element boundaries collapse. +- Nullish **fields** are dropped from every channel except `path`, where `null` collapses the segment. A nullish array **element** is dropped, so an all-nullish array emits no key at all. +- `form` bodies use RFC 1866 encoding (space becomes `+`); `query` uses `%20`. On the wire both key and value go through `encodeURIComponent`, plus a further escape of `!`, `'`, `(`, `)` and `*`. + +**The verb and route are on the pages below**, where a map for a language whose method names are derived from the route can leave them to the source. A TypeScript method name carries none of it, and a `path` field row is unreadable without the route template it fills. + +**Endpoint prose is not on this map.** Where the *semantics* of an operation decide what you must pass β€” a field whose value changes server-side behaviour, an ordering or exclusivity rule between fields β€” read `api-reference.md`, whose entries are keyed by the same signature these pages print. Blocks here give you the contract: names, channels, types, defaults, errors. + +| Resource (`client.X`) | Ops | Operations | Page | +| --- | --- | --- | --- | +| `serviceManagement` | 8 | `serviceConfigurationApi` Β· `serviceCreateApi` Β· `serviceCreateIdpApi` Β· `serviceDeleteApi` Β· `serviceGetApi` Β· `serviceGetListApi` Β· `serviceRemoveIdpApi` Β· `serviceUpdateApi` | [map/operations/service-management.md](map/operations/service-management.md) | +| `clientManagement` | 26 | `clientAuthorizationDeleteApi` Β· `clientAuthorizationDeleteApiPost` Β· `clientAuthorizationDeleteBySubjectApi` Β· `clientAuthorizationGetListApi` Β· `clientAuthorizationGetListApiPost` Β· `clientAuthorizationGetListBySubjectApi` Β· `clientAuthorizationUpdateApi` Β· `clientCreateApi` Β· `clientCursorListApi` Β· `clientDeleteApi` Β· `clientExtensionRequestablesScopesDeleteApi` Β· `clientExtensionRequestablesScopesGetApi` Β· `clientExtensionRequestablesScopesUpdateApi` Β· `clientExtensionRequestablesScopesUpdateApiPost` Β· `clientFlagUpdateApi` Β· `clientGetApi` Β· `clientGetListApi` Β· `clientGrantedScopesDeleteApi` Β· `clientGrantedScopesDeleteBySubjectApi` Β· `clientGrantedScopesGetApi` Β· `clientGrantedScopesGetApiPost` Β· `clientGrantedScopesGetBySubjectApi` Β· `clientSearchApi` Β· `clientSecretRefreshApi` Β· `clientSecretUpdateApi` Β· `clientUpdateApi` | [map/operations/client-management.md](map/operations/client-management.md) | +| `authorizationEndpoint` | 5 | `authAuthorizationApi` Β· `authAuthorizationFailApi` Β· `authAuthorizationIssueApi` Β· `getAuthorizationTicketInfo` Β· `updateAuthorizationTicket` | [map/operations/authorization-endpoint.md](map/operations/authorization-endpoint.md) | +| `pushedAuthorizationEndpoint` | 1 | `pushedAuthReqApi` | [map/operations/pushed-authorization-endpoint.md](map/operations/pushed-authorization-endpoint.md) | +| `tokenEndpoint` | 4 | `authTokenApi` Β· `authTokenFailApi` Β· `authTokenIssueApi` Β· `idtokenReissueApi` | [map/operations/token-endpoint.md](map/operations/token-endpoint.md) | +| `tokenOperations` | 5 | `authTokenCreateApi` Β· `authTokenDeleteApi` Β· `authTokenGetListApi` Β· `authTokenRevokeApi` Β· `authTokenUpdateApi` | [map/operations/token-operations.md](map/operations/token-operations.md) | +| `introspectionEndpoint` | 2 | `authIntrospectionApi` Β· `authIntrospectionStandardApi` | [map/operations/introspection-endpoint.md](map/operations/introspection-endpoint.md) | +| `revocationEndpoint` | 1 | `authRevocationApi` | [map/operations/revocation-endpoint.md](map/operations/revocation-endpoint.md) | +| `userInfoEndpoint` | 2 | `authUserinfoApi` Β· `authUserinfoIssueApi` | [map/operations/user-info-endpoint.md](map/operations/user-info-endpoint.md) | +| `jwkSetEndpoint` | 1 | `serviceJwksGetApi` | [map/operations/jwk-set-endpoint.md](map/operations/jwk-set-endpoint.md) | +| `dynamicClientRegistration` | 4 | `clientRegistrationApi` Β· `clientRegistrationDeleteApi` Β· `clientRegistrationGetApi` Β· `clientRegistrationUpdateApi` | [map/operations/dynamic-client-registration.md](map/operations/dynamic-client-registration.md) | +| `ciba` | 4 | `backchannelAuthenticationApi` Β· `backchannelAuthenticationCompleteApi` Β· `backchannelAuthenticationFailApi` Β· `backchannelAuthenticationIssueApi` | [map/operations/ciba.md](map/operations/ciba.md) | +| `grantManagementEndpoint` | 1 | `grantMApi` | [map/operations/grant-management-endpoint.md](map/operations/grant-management-endpoint.md) | +| `joseObject` | 1 | `joseVerifyApi` | [map/operations/jose-object.md](map/operations/jose-object.md) | +| `deviceFlow` | 3 | `deviceAuthorizationApi` Β· `deviceCompleteApi` Β· `deviceVerificationApi` | [map/operations/device-flow.md](map/operations/device-flow.md) | +| `federationEndpoint` | 2 | `federationConfigurationApi` Β· `federationRegistrationApi` | [map/operations/federation-endpoint.md](map/operations/federation-endpoint.md) | +| `verifiableCredentialIssuer` | 11 | `vciBatchIssueApi` Β· `vciBatchParseApi` Β· `vciDeferredIssueApi` Β· `vciDeferredParseApi` Β· `vciJwksApi` Β· `vciJwtissuerApi` Β· `vciMetadataApi` Β· `vciOfferCreateApi` Β· `vciOfferInfoApi` Β· `vciSingleIssueApi` Β· `vciSingleParseApi` | [map/operations/verifiable-credential-issuer.md](map/operations/verifiable-credential-issuer.md) | +| `hardwareSecurityKey` | 4 | `hskCreateApi` Β· `hskDeleteApi` Β· `hskGetApi` Β· `hskGetListApi` | [map/operations/hardware-security-key.md](map/operations/hardware-security-key.md) | +| `utilityEndpoints` | 2 | `infoApi` Β· `miscEchoApi` | [map/operations/utility-endpoints.md](map/operations/utility-endpoints.md) | +| `nativeSso` | 2 | `nativeSsoApi` Β· `nativeSsoLogoutApi` | [map/operations/native-sso.md](map/operations/native-sso.md) | +| `auditLogs` | 2 | `auditEntriesGetIdpApi` Β· `auditTypesGetIdpApi` | [map/operations/audit-logs.md](map/operations/audit-logs.md) | +| `backChannelLogout` | 1 | `backchannelLogoutTokenApi` | [map/operations/back-channel-logout.md](map/operations/back-channel-logout.md) | +| `lifecycle` | 1 | `healthCheck` | [map/operations/lifecycle.md](map/operations/lifecycle.md) | + +--- + +## Models β€” where they live, how to build them + +**Shapes live only in the source.** Every module under `src/models/` declares exactly one model type and the schema value beside it, and both are re-exported from the package root. Take the pair from an operation's **Type sources** table, or build the directory from the kind below. **Do not derive the path from the type name** β€” the transform is not reversible in general, and the table is the authority. Never grep for a type. + +| Group | Count | Directory | +| --- | --- | --- | +| Objects (plain `type`, no class) | 147 | `src/models/` | +| Enums (open; const companion plus schema) | 71 | `src/models/` | +| Typed error classes (`ApiError` subclass, one per typed operation) | 92 | `src/resources/`, in the declaring module's namespace | + +Conventions: every model is a plain `type`, not a class β€” build one with an object literal; there is no constructor and no builder. `f: T` is required, `f?: T` is optional (omit the key), and `f: T | null` is a **required, nullable** field where `null` is a value distinct from an omitted key. Optional properties are declared `f?: T`, not `f?: T | undefined`, so under `exactOptionalPropertyTypes` you must **omit or spread** an absent field rather than assign `undefined` to it. A schema value is directly usable both ways: `Schema>` is `{ decode(v: unknown): T; encode(v: unknown): W }`, and `Encoded` is the wire projection β€” a `Date` becomes `string | number`, a `Uint8Array` becomes a base64 `string`, recursing through arrays and objects. `EnumSchema` adds `readonly values: readonly T[]`, so an enum's known set is testable at run time. Enums are **not** TypeScript `enum`s and are open: a `const` companion plus a union that includes `(string & {})` or `(number & {})`, so **any** value of the base type is assignable and the schema validates the base type only, never membership β€” read the member names and the values they send off the companion, and use `.values` to test membership yourself. A discriminated union is narrowed with an exhaustive `switch` on its tag, with no fallback arm and no type guard to import; one without a discriminant is narrowed on the shape of its arms, which its declaration spells out. A property default is filled by the SDK on **encode as well as decode**, so omitting one still sends it β€” read it off the `defaulted(…)` entry in the schema, or off the property's `@default`. A numeric property is a `number` whatever its format, and its schema follows the type. `type: integer` with no format, `int32` or `int64` rejects a fraction and any value outside the safe-integer range; `type: number` with no format, `float`, `double` or `bigdecimal` rejects a non-finite value. A property's wire name is its `_keysMap` entry in the schema and may differ from the TypeScript name β€” read it there rather than deriving it. A named spec schema whose resolved form is a bare container, or which is used only as a form-encoded body, gets no model file and no exported name: the first is written inline at each use site, the second is flattened onto the operation's request type, one field per property, so read that field list from the request type. + +Every name comes from the package root β€” there is no default export, and no deep imports: + +```ts +import { type Action, actionSchema } from "authlete-api"; +``` + +--- + +## Servers & auth + +**Authentication is per operation.** Every operation declares the requirement it enforces and the SDK sends exactly that: **91 of the 93 operations** require a credential and **2** are public. Each block on a page above carries an **Auth** bullet naming its requirement, `none` included. There is no client-global switch and no per-call override. + +| Scheme (as an **Auth** bullet names it) | Configured with | What the SDK sends | +| --- | --- | --- | +| `bearer` | `bearer` | `Authorization: Bearer ` | + +A scheme **contributes** headers, query parameters and cookies rather than mutating the request, so a credential is encoded by exactly the code that encodes an operation's own parameters. The auth layer goes on **last**, which means a scheme's `Authorization` wins over one the operation declared. + +**Composition is emitted, not configured.** Where the spec puts two schemes in one requirement the SDK sends **both**; where it lists alternatives the SDK sends the **first configured** one, in the order the **Auth** bullet prints them. The combinators that express this (`allAuth`, `anyAuth`, `noneAuth`) live in the generated resource modules and are **not exported**. + +**A credential may be a function.** Every field typed `TokenProvider` is re-read on **every** request with no caching, so a key can rotate without rebuilding the client. An empty string counts as absent, and a function is treated as present without being invoked. The function is handed the call's `signal`, or one that never aborts when the call was given none, and the SDK waits for it to settle β€” so a function that fetches its credential should pass that signal on, or a cancelled call waits for the fetch to finish. + +**An unconfigured scheme does not throw.** The request goes out without that credential and the server decides. So a 401 on a call you believed was authenticated is usually an unset credential field rather than an SDK failure β€” check the operation's **Auth** bullet against what the client was given. + +**A 401 invalidates the cached credential.** On a **401** β€” 401 only, not 403 β€” the SDK clears whatever that operation's scheme had cached, so the *next* call re-acquires. The current request still rejects with the operation's `ApiError`. The credential fields are on `ClientOptions`. + +**Environments.** `ClientOptions.serverEnvironment` selects one for the whole client (source: `src/servers.ts`). `ServerEnvironment` is a `const` object with a derived union type, not a TypeScript `enum` β€” and unlike the model enums it is **closed**, so only the values below are assignable. + +| `ServerEnvironment` member | Value | +| --- | --- | +| `ServerEnvironment.Production` *(default)* | `production` | +| `ServerEnvironment.Environment2` | `environment2` | +| `ServerEnvironment.Environment3` | `environment3` | +| `ServerEnvironment.Environment4` | `environment4` | +| `ServerEnvironment.Environment5` | `environment5` | + +**serverOptions.** 1 logical server; each operation is bound to one at generation time, and a block carries a **Server** bullet only when its group is not `default`. Override `serverOptions`. + +**Base URLs and overrides.** One row per group-and-environment pair, and every cell is overridden at `serverOptions.`, where `` is `baseUrl` for the whole template or the variable name for one substitution. Which environment a cell belongs to is selected by `serverEnvironment`, not written into the path β€” the options type only admits the keys legal under the environment named there. An override merges with the built-in defaults **per pair, key by key**. + +| Group | Environment | Base URL template | Template variables (default) | +| --- | --- | --- | --- | +| `default` | `production` | `https://us.authlete.com` | β€” | +| `default` | `environment2` | `https://jp.authlete.com` | β€” | +| `default` | `environment3` | `https://eu.authlete.com` | β€” | +| `default` | `environment4` | `https://br.authlete.com` | β€” | +| `default` | `environment5` | `https://login.authlete.com` | β€” | + +A `baseUrl` override replaces the template verbatim; variable values are percent-encoded into it. Server variables are filled in once, as the client is built; only the path parameters are expanded per request. An environment value the SDK does not know throws `ConfigurationError`, and it is the **constructor** that throws it: every server group is resolved once, by `buildServers`, as the client is built, and an accessor afterwards only attaches the operation's sub-path. No operation method throws synchronously. + +Retries are configurable via `ClientOptions.retry` (`RetryOptions`, source `src/core/retry.ts`) β€” the field table is under Getting a client. + +--- + +## Runtime & packaging + +The facts that change what you type, and the floors that decide whether the package loads at all. This section is the home for all of them. + +| | | +| --- | --- | +| One entry, two dialects | `import` resolves `dist/esm`, `require` resolves `dist/commonjs`, both through the single `.` export. In a TypeScript CommonJS file the typed spelling is `import sdk = require("authlete-api")`; a plain `require` destructure works at run time but yields no types. `instanceof` is reliable **within** one dialect β€” if your app loads both, the two copies declare separate error classes | +| Consumer compiler settings | Under `exactOptionalPropertyTypes`, **omit or spread** an absent optional rather than assigning `undefined` to it. Under `verbatimModuleSyntax`, names that carry no runtime value (the options types, every model type) must be imported with `import type` | +| Required globals, and only these | Always: `fetch` (or a replacement passed as the `fetch` option), `AbortController`, `AbortSignal.any`, `Headers`, `Request`, `URL`, `setTimeout` and `clearTimeout`, `JSON`, `BigInt`, and `console`, which is read as the package loads. `crypto.randomUUID` or `crypto.getRandomValues` mints the `Idempotency-Key` a non-GET call carries β€” **read and never required**, since a runtime offering neither fills the bytes from `Math.random` mixed with the clock and a per-process counter, so the header is always sent. Three more are **read and never required** β€” `process`, `navigator` and `EdgeRuntime`, which name the host in `X-APIMatic-OS` and `X-APIMatic-Runtime`. A runtime offering none of them sends neither header and works unchanged. `process.env.AUTHLETE_API_CLIENT_LOG` is read at most once, when the client is built and code sets neither a `logger` nor a valid `level`. Logging writes to `console` only as the default logger, and for the warning on a `AUTHLETE_API_CLIENT_LOG` value that is not a level. | +| Values that cross the boundary | `Date` for `date-time`, `string` for `date`, `ArrayBuffer` for an undeclared error body, `Headers` on a result and on a thrown `ResponseError`. The engine also carries a `bigint` int64 path and a base64 `bytes()` codec, reached only where a model uses them | +| Browser distribution | The package ships `dist/esm` and `dist/commonjs` and nothing else β€” **no bundle, no UMD file, no CDN artifact**. Use it through a bundler, which resolves `zod/v4-mini`, deduplicates it against your own copy and tree-shakes the rest | +| Other runtimes | Deno, Bun, Cloudflare Workers and Vercel Edge are all likely to work β€” the SDK needs only the globals above and imports no Node built-in β€” but **none of them is tested for this package**, so nothing here claims support for them | + +The browser floor is set by `AbortSignal.any`, which every call uses to combine `RequestOptions.signal`, or a signal that never aborts when there is none, with the attempt's timer. The emitted output needs less: `tshy` builds at `target: ES2022`, so native `#private` fields and methods survive into `dist/`, and those load from Chrome 85, Firefox 90 and Safari 15. + +| Browser | Minimum | +| --- | --- | +| Chrome / Edge | **116** | +| Firefox | **124** | +| Safari / iOS Safari | **17.4** | + +Below that table the module still loads, down to the emitted-output floor, but every call rejects with a `TypeError`. + diff --git a/plugins/authlete/sdk/typescript/src/auth-schemes.ts b/plugins/authlete/sdk/typescript/src/auth-schemes.ts new file mode 100644 index 00000000..de8c4b33 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/auth-schemes.ts @@ -0,0 +1,13 @@ +import type { ClientOptions } from "./client-options.js"; +import type { AuthScheme } from "./core/api-request.js"; +import { bearerAuth } from "./core/auth/schemes.js"; + +export type AuthSchemes = { + readonly bearer: AuthScheme; +}; + +export function buildAuthSchemes(options: ClientOptions): AuthSchemes { + return { + bearer: bearerAuth(options.bearer), + }; +} diff --git a/plugins/authlete/sdk/typescript/src/client-options.ts b/plugins/authlete/sdk/typescript/src/client-options.ts new file mode 100644 index 00000000..527a24aa --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/client-options.ts @@ -0,0 +1,58 @@ +import type { TokenProvider } from "./core/auth/credentials.js"; +import type { CoreClientOptions } from "./core/client-options.js"; +import { ServerEnvironment } from "./servers.js"; + +export type ClientOptions = SdkClientOptions & CoreClientOptions; + +type SdkClientOptions = ServerOptions & { + /** + * Authenticate every request with a **Service Access Token** or **Organization Token**. Set the + * token value in the `Authorization: Bearer ` header. + * + * **Service Access Token**: Scoped to a single service. Use when automating service-level + * configuration or runtime flows. + * + * **Organization Token**: Scoped to the organization; inherits permissions across services. Use + * for org-wide automation or when managing multiple services programmatically. + * + * Both token types are issued by the Authlete console or provisioning APIs. + */ + readonly bearer?: TokenProvider | undefined; +}; + +type ServerOptions = + | { + readonly serverEnvironment?: typeof ServerEnvironment.Production; + /** πŸ‡ΊπŸ‡Έ US Cluster */ + readonly serverOptions?: { + baseUrl?: string; + }; + } + | { + readonly serverEnvironment: typeof ServerEnvironment.Environment2; + /** πŸ‡ΊπŸ‡Έ US Cluster */ + readonly serverOptions?: { + baseUrl?: string; + }; + } + | { + readonly serverEnvironment: typeof ServerEnvironment.Environment3; + /** πŸ‡ΊπŸ‡Έ US Cluster */ + readonly serverOptions?: { + baseUrl?: string; + }; + } + | { + readonly serverEnvironment: typeof ServerEnvironment.Environment4; + /** πŸ‡ΊπŸ‡Έ US Cluster */ + readonly serverOptions?: { + baseUrl?: string; + }; + } + | { + readonly serverEnvironment: typeof ServerEnvironment.Environment5; + /** πŸ‡ΊπŸ‡Έ US Cluster */ + readonly serverOptions?: { + baseUrl?: string; + }; + }; diff --git a/plugins/authlete/sdk/typescript/src/client.ts b/plugins/authlete/sdk/typescript/src/client.ts new file mode 100644 index 00000000..933f94c1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/client.ts @@ -0,0 +1,348 @@ +import { buildAuthSchemes, type AuthSchemes } from "./auth-schemes.js"; +import type { ClientOptions } from "./client-options.js"; +import { buildCoreClientOptions } from "./core/client-options.js"; +import { RawClient } from "./core/raw-client.js"; +import * as host from "./core/runtime-environment.js"; +import * as s from "./core/validation/index.js"; +import { AuditLogs } from "./resources/audit-logs.js"; +import { AuthorizationEndpoint } from "./resources/authorization-endpoint.js"; +import { BackChannelLogout } from "./resources/back-channel-logout.js"; +import { Ciba } from "./resources/ciba.js"; +import { ClientManagement } from "./resources/client-management.js"; +import { DeviceFlow } from "./resources/device-flow.js"; +import { DynamicClientRegistration } from "./resources/dynamic-client-registration.js"; +import { FederationEndpoint } from "./resources/federation-endpoint.js"; +import { GrantManagementEndpoint } from "./resources/grant-management-endpoint.js"; +import { HardwareSecurityKey } from "./resources/hardware-security-key.js"; +import { IntrospectionEndpoint } from "./resources/introspection-endpoint.js"; +import { JoseObject } from "./resources/jose-object.js"; +import { JwkSetEndpoint } from "./resources/jwk-set-endpoint.js"; +import { Lifecycle } from "./resources/lifecycle.js"; +import { NativeSso } from "./resources/native-sso.js"; +import { PushedAuthorizationEndpoint } from "./resources/pushed-authorization-endpoint.js"; +import { RevocationEndpoint } from "./resources/revocation-endpoint.js"; +import { ServiceManagement } from "./resources/service-management.js"; +import { TokenEndpoint } from "./resources/token-endpoint.js"; +import { TokenOperations } from "./resources/token-operations.js"; +import { UserInfoEndpoint } from "./resources/user-info-endpoint.js"; +import { UtilityEndpoints } from "./resources/utility-endpoints.js"; +import { VerifiableCredentialIssuer } from "./resources/verifiable-credential-issuer.js"; +import { buildServers, type Servers } from "./servers.js"; + +/** + * Welcome to the **Authlete API documentation**. Authlete is an **API-first service** where every + * aspect of the platform is configurable via API. This documentation will help you authenticate and + * integrate with Authlete to build powerful OAuth 2.0 and OpenID Connect servers. + * + * At a high level, the Authlete API is grouped into two categories: + * + * - **Management APIs**: Enable you to manage services and clients. + * - **Runtime APIs**: Allow you to build your own Authorization Servers or Verifiable Credential + * (VC) issuers. + * + * ## 🌐 API Servers + * + * Authlete is a global service with clusters available in multiple regions across the world: + * + * - πŸ‡ΊπŸ‡Έ **US**: `https://us.authlete.com` + * - πŸ‡―πŸ‡΅ **Japan**: `https://jp.authlete.com` + * - πŸ‡ͺπŸ‡Ί **Europe**: `https://eu.authlete.com` + * - πŸ‡§πŸ‡· **Brazil**: `https://br.authlete.com` + * + * Our customers can host their data in the region that best meets their requirements. + * + * ## πŸ”‘ Authentication + * + * All API endpoints are secured using **Bearer token authentication**. You must include an access + * token in every request: + * + * ``` + * Authorization: Bearer YOUR_ACCESS_TOKEN + * ``` + * + * ### Getting Your Access Token + * + * Authlete supports two types of access tokens: + * + * **Service Access Token** - Scoped to a single service (authorization server instance) + * + * 1. Log in to [Authlete Console](https://console.authlete.com) + * 2. Navigate to your service β†’ **Settings** β†’ **Access Tokens** + * 3. Click **Create Token** and select permissions (e.g., `service.read`, `client.write`) + * 4. Copy the generated token + * + * **Organization Token** - Scoped to your entire organization + * + * 1. Log in to [Authlete Console](https://console.authlete.com) + * 2. Navigate to **Organization Settings** β†’ **Access Tokens** + * 3. Click **Create Token** and select org-level permissions + * 4. Copy the generated token + * + * > ⚠️ **Important Note**: Tokens inherit the permissions of the account that creates them. Service + * tokens can only access their specific service, while organization tokens can access all services + * within your org. + * + * ### Token Security Best Practices + * + * - **Never commit tokens to version control** - Store in environment variables or secure secret + * managers + * - **Rotate regularly** - Generate new tokens periodically and revoke old ones + * - **Scope appropriately** - Request only the permissions your application needs + * - **Revoke unused tokens** - Delete tokens you're no longer using from the console + * + * ### Quick Test + * + * Verify your token works with a simple API call: + * + * ```bash + * curl -X GET https://us.authlete.com/api/service/get/list \ + * -H "Authorization: Bearer YOUR_ACCESS_TOKEN" + * ``` + * + * ## πŸŽ“ Tutorials + * + * If you're new to Authlete or want to see sample implementations, these resources will help you + * get started: + * + * - [Getting Started with Authlete](https://www.authlete.com/developers/getting_started/) + * - [From Sign-Up to the First API Request](https://www.authlete.com/developers/tutorial/signup/) + * + * ## πŸ›  Contact Us + * + * If you have any questions or need assistance, our team is here to help: + * + * - [Contact Page](https://www.authlete.com/contact/) + */ +export class AuthleteApiClient { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + #serviceManagement?: ServiceManagement; + #clientManagement?: ClientManagement; + #authorizationEndpoint?: AuthorizationEndpoint; + #pushedAuthorizationEndpoint?: PushedAuthorizationEndpoint; + #tokenEndpoint?: TokenEndpoint; + #tokenOperations?: TokenOperations; + #introspectionEndpoint?: IntrospectionEndpoint; + #revocationEndpoint?: RevocationEndpoint; + #userInfoEndpoint?: UserInfoEndpoint; + #jwkSetEndpoint?: JwkSetEndpoint; + #dynamicClientRegistration?: DynamicClientRegistration; + #ciba?: Ciba; + #grantManagementEndpoint?: GrantManagementEndpoint; + #joseObject?: JoseObject; + #deviceFlow?: DeviceFlow; + #federationEndpoint?: FederationEndpoint; + #verifiableCredentialIssuer?: VerifiableCredentialIssuer; + #hardwareSecurityKey?: HardwareSecurityKey; + #utilityEndpoints?: UtilityEndpoints; + #nativeSso?: NativeSso; + #auditLogs?: AuditLogs; + #backChannelLogout?: BackChannelLogout; + #lifecycle?: Lifecycle; + + constructor(options: ClientOptions = {}) { + this.#rawClient = new RawClient({ + ...buildCoreClientOptions(options, "AUTHLETE_API_CLIENT_LOG"), + defaultHeaders: [ + { name: "User-Agent", value: "AuthleteApiClient/3.0.16 TypeScript", schema: s.string() }, + { name: "X-APIMatic-Lang", value: "TypeScript", schema: s.string() }, + { name: "X-APIMatic-Package-Version", value: "3.0.16", schema: s.string() }, + { name: "X-APIMatic-Gen-Version", value: "4.0.0", schema: s.string() }, + { name: "X-APIMatic-OS", value: host.operatingSystem(), schema: s.optional(s.string()) }, + { name: "X-APIMatic-Runtime", value: host.runtimeDescription(), schema: s.optional(s.string()) }, + ], + defaultQuery: [], + defaultPathParams: [], + }); + + this.#servers = buildServers(options); + + this.#auth = buildAuthSchemes(options); + } + + /** + * API endpoints for managing services, including creation, update, and deletion of services. + */ + get serviceManagement(): ServiceManagement { + return (this.#serviceManagement ??= new ServiceManagement(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for managing OAuth clients, including creation, update, and deletion of clients. + */ + get clientManagement(): ClientManagement { + return (this.#clientManagement ??= new ClientManagement(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OAuth 2.0 Authorization Endpoint. + */ + get authorizationEndpoint(): AuthorizationEndpoint { + return (this.#authorizationEndpoint ??= new AuthorizationEndpoint( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for implementing OAuth 2.0 Pushed Authorization Requests (PAR). + */ + get pushedAuthorizationEndpoint(): PushedAuthorizationEndpoint { + return (this.#pushedAuthorizationEndpoint ??= new PushedAuthorizationEndpoint( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for implementing OAuth 2.0 Token Endpoint. + */ + get tokenEndpoint(): TokenEndpoint { + return (this.#tokenEndpoint ??= new TokenEndpoint(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for various token related operations, including creating, revoking and deleting + * access_tokens with specified scopes. + */ + get tokenOperations(): TokenOperations { + return (this.#tokenOperations ??= new TokenOperations(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OAuth 2.0 Introspection Endpoint. + */ + get introspectionEndpoint(): IntrospectionEndpoint { + return (this.#introspectionEndpoint ??= new IntrospectionEndpoint( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoint for implementing OAuth 2.0 Revocation Endpoint. + */ + get revocationEndpoint(): RevocationEndpoint { + return (this.#revocationEndpoint ??= new RevocationEndpoint(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OpenID Connect UserInfo Endpoint. + */ + get userInfoEndpoint(): UserInfoEndpoint { + return (this.#userInfoEndpoint ??= new UserInfoEndpoint(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for to generate JSON Web Key Set (JWKS) for a service. + */ + get jwkSetEndpoint(): JwkSetEndpoint { + return (this.#jwkSetEndpoint ??= new JwkSetEndpoint(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OAuth 2.0 Dynamic Client Registration. + */ + get dynamicClientRegistration(): DynamicClientRegistration { + return (this.#dynamicClientRegistration ??= new DynamicClientRegistration( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for implementing Client-Initiated Backchannel Authentication (CIBA). + */ + get ciba(): Ciba { + return (this.#ciba ??= new Ciba(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoint for implementing OAuth 2.0 grants, including grant management actions like + * updating and revoking grants. + */ + get grantManagementEndpoint(): GrantManagementEndpoint { + return (this.#grantManagementEndpoint ??= new GrantManagementEndpoint( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for JOSE objects. + */ + get joseObject(): JoseObject { + return (this.#joseObject ??= new JoseObject(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OAuth 2.0 Device Flow + */ + get deviceFlow(): DeviceFlow { + return (this.#deviceFlow ??= new DeviceFlow(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing OpenID Federation using Authlete. + */ + get federationEndpoint(): FederationEndpoint { + return (this.#federationEndpoint ??= new FederationEndpoint(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for implementing and running a Verifiable Credential Issuer (VCI). + */ + get verifiableCredentialIssuer(): VerifiableCredentialIssuer { + return (this.#verifiableCredentialIssuer ??= new VerifiableCredentialIssuer( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for managing hardware security keys (HSK). + */ + get hardwareSecurityKey(): HardwareSecurityKey { + return (this.#hardwareSecurityKey ??= new HardwareSecurityKey( + this.#rawClient, + this.#servers, + this.#auth, + )); + } + + /** + * API endpoints for various utility operations. + */ + get utilityEndpoints(): UtilityEndpoints { + return (this.#utilityEndpoints ??= new UtilityEndpoints(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for Native SSO + */ + get nativeSso(): NativeSso { + return (this.#nativeSso ??= new NativeSso(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * API endpoints for retrieving audit logs, hosted on the Authlete IdP server. + */ + get auditLogs(): AuditLogs { + return (this.#auditLogs ??= new AuditLogs(this.#rawClient, this.#servers, this.#auth)); + } + + get backChannelLogout(): BackChannelLogout { + return (this.#backChannelLogout ??= new BackChannelLogout(this.#rawClient, this.#servers, this.#auth)); + } + + get lifecycle(): Lifecycle { + return (this.#lifecycle ??= new Lifecycle(this.#rawClient, this.#servers)); + } +} diff --git a/plugins/authlete/sdk/typescript/src/core/api-error.ts b/plugins/authlete/sdk/typescript/src/core/api-error.ts new file mode 100644 index 00000000..145fed81 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/api-error.ts @@ -0,0 +1,58 @@ +import { ResponseErrorBase, type ResponseErrorInit } from "./errors.js"; + +/** + * An error body the spec describes, tagged with the arm name to narrow on. + * + * @remarks + * `kind` is the arm name the operation's error class declares. Narrow `err.payload.kind` and + * `body` is typed for that arm, with no cast. + */ +export type Declared = { + readonly kind: K; + readonly body: B; +}; + +/** + * An error body the spec does not describe: the server answered with a status no matcher covers, + * and the operation declares no `default` response. + * + * @remarks + * The untouched bytes come back as `rawBody` so the failure stays diagnosable. A status the spec + * *does* describe whose body fails to decode is **not** this and throws `DecodeError`, because + * shape drift on a declared status is a contract violation rather than an open error. A `default` + * response describes every status the spec does not list, so an operation that declares one never + * produces this arm: its body either decodes or is that same contract violation. + */ +export type Undeclared = { + readonly kind: "undeclared"; + readonly rawBody: ArrayBuffer; +}; + +/** What an {@link ApiError} is built with: the call it names, and the answer the API gave. */ +export type ApiErrorInit = ResponseErrorInit & { + readonly payload: Payload; +}; + +/** + * An error answer from the API. + * + * @remarks + * Thrown when the server responded with an error status. It is a `ResponseError`, so a `catch` for + * everything the server had a say in sees it beside `DecodeError`, and it is a `CoreError`, so one + * `instanceof AcmeError` catches it too. An operation that declares error bodies throws a subclass + * redeclaring `payload` as a typed union; the rest throw this class directly. + * + * On this class `payload.kind` is `string`, so comparing it to `"undeclared"` does not narrow and + * `"rawBody" in payload` does. A subclass declares a union of literals, which narrows on its own. + */ +export class ApiError extends ResponseErrorBase { + readonly kind = "api" as const; + + /** The error body, as the arm the spec declared for this status, or `"undeclared"`. */ + readonly payload: Declared | Undeclared; + + constructor(init: ApiErrorInit | Undeclared>) { + super(`${init.method} ${init.uri} failed with ${init.status}`, init); + this.payload = init.payload; + } +} diff --git a/plugins/authlete/sdk/typescript/src/core/api-promise.ts b/plugins/authlete/sdk/typescript/src/core/api-promise.ts new file mode 100644 index 00000000..f3ba789b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/api-promise.ts @@ -0,0 +1,92 @@ +import type { ApiError } from "./api-error.js"; +import type { HttpMethod } from "./api-request.js"; +import type { PayloadOf } from "./response-decoder.js"; + +export type RequestOutcome = + | { ok: true; status: number; headers: Headers; data: T } + | { ok: false; status: number; headers: Headers; error: E }; + +/** + * The non-throwing view of a call, returned by {@link ApiPromise.asApiResult}. + * + * @remarks + * Narrow on `ok`. The success branch carries `value`. The failure branch is a plain object rather + * than the error instance, carrying its `status`, `headers`, `message` and `payload` plus the + * `method` and `uri` of the call, and `result.payload` narrows exactly as `err.payload` would. + */ +export type ApiResult = + | { ok: true; status: number; headers: Headers; value: T } + | { + ok: false; + status: number; + headers: Headers; + message: string; + method: HttpMethod; + uri: string; + payload: PayloadOf; + }; + +/** + * The promise every operation returns: resolves with `T`, rejects with `E` or a `CoreError`. + * + * @remarks + * It is a real `Promise`, so `await`, `.then` and `Promise.all` all work. Awaiting it throws `E` on + * an error status and a `CoreError` when no usable response was produced, so a complete `catch` + * needs both. A bug rejects as the raw error it threw, outside the family. Call + * {@link ApiPromise.asApiResult} for a non-throwing {@link ApiResult} instead. + * + * `.then`, `.catch` and `.finally` hand back a plain `Promise`, which has no `asApiResult` on it. + * Call that method on the value the operation returned. + * + * Nothing here suppresses an unhandled rejection. An abandoned call that fails is reported by the + * runtime: `unhandledRejection` on Node, which by default ends the process, and + * `unhandledrejection` in a browser. + */ +export class ApiPromise extends Promise { + readonly #outcome: Promise>; + + constructor(outcome: Promise>) { + super((resolve, reject) => { + outcome.then((o) => (o.ok ? resolve(o.data) : reject(o.error)), reject); + }); + this.#outcome = outcome; + } + + static override get [Symbol.species](): PromiseConstructor { + return Promise; + } + + /** + * Resolves to an {@link ApiResult} instead of throwing: an error status arrives as a value, not + * as a rejection. + * + * @remarks + * It is also the only way to read the HTTP status and response headers of a successful call. An + * error status is the only failure it turns into a value. A request value that would not encode, + * auth, a dropped connection, a timeout and a body that would not decode all still reject, and a + * caller abort rejects with the signal's own `reason`. + * + * Call it on the value the operation returned, in the same turn: marking this promise handled is + * its first act, so a call deferred past a turn leaves the rejection unhandled until it runs. + */ + async asApiResult(): Promise> { + this.catch(() => {}); + const o = await this.#outcome; + return o.ok + ? { + ok: true, + value: o.data, + status: o.status, + headers: o.headers, + } + : { + ok: false, + status: o.error.status, + headers: o.error.headers, + message: o.error.message, + method: o.error.method, + uri: o.error.uri, + payload: o.error.payload, + }; + } +} diff --git a/plugins/authlete/sdk/typescript/src/core/api-request.ts b/plugins/authlete/sdk/typescript/src/core/api-request.ts new file mode 100644 index 00000000..561c19f1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/api-request.ts @@ -0,0 +1,89 @@ +import type { RequestBody } from "./request-body.js"; +import type { RequestRetryOptions } from "./retry.js"; +import type { RequestLoggingOptions } from "./logging.js"; +import type { ResolvedCoreClientOptions } from "./client-options.js"; +import type { StyledParam, Param, ParamPair } from "./param-value.js"; + +/** HTTP method of a call. Closed, and the type of `method` on every failure the SDK raises. */ +export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS"; + +/** + * Per-call options, the optional second argument of every operation. + * + * @remarks + * Credentials and the server are configured once on the client rather than per call. Three fields + * of the retry policy and the log level are the two things a single call may shape for itself. + */ +export type RequestOptions = { + /** Cancels the call. Aborting rejects the returned promise with the signal's own `reason`. */ + signal?: AbortSignal | undefined; + + /** + * Shapes the retry policy for this call alone, leaving every other call on the client's. + * + * @remarks + * Three fields of `RetryOptions`, the ones {@link RequestRetryOptions} names, merged field by + * field over what the client resolved: a field left out β€” or passed as `undefined` β€” keeps the + * client's value, and `0` and an empty list are values rather than misses. `timeout` bounds one + * attempt here exactly as it does there, so `{ retry: { timeout: 5000 } }` is a per-attempt + * budget and not a budget for the call. + * + * A value the engine cannot honour is dropped rather than raising: that field keeps the client's + * value, and the fields beside it in the same literal still apply. A record whose reads throw + * rejects the call with a `ConfigurationError` before anything is sent. + */ + retry?: RequestRetryOptions; + + /** + * Sets the log level for this call alone, leaving every other call on the client's. + * + * @remarks + * One field, {@link RequestLoggingOptions}. A level named here decides every line of this call + * by itself, the client's `level` and its three switches set aside, so `{ logging: { level: + * "debug" } }` logs this one call in full and `{ logging: { level: "off" } }` silences it. A + * value that is not a level is dropped and the call logs under the client's settings. + */ + logging?: RequestLoggingOptions; +}; + +export type RawClientOptions = ResolvedCoreClientOptions & { + readonly defaultHeaders: readonly Param[]; + readonly defaultQuery: readonly StyledParam[]; + readonly defaultPathParams: readonly Param[]; +}; + +export type UrlTemplate = { + readonly baseUrl: string; + readonly subPath: string; +}; + +/** + * A named server with its options already decoded: the base URL template and the variables that + * fill it, still unexpanded. + */ +export type ServerBase = { + baseUrl: string; + variables?: Record | undefined; +}; + +export type AuthParams = { + readonly headers: readonly ParamPair[]; + readonly query: readonly ParamPair[]; + readonly cookies: readonly ParamPair[]; +}; + +export type AuthScheme = { + resolve(signal: AbortSignal): Promise | AuthParams; + hasCredentials(): boolean; + invalidate?(): void; +}; + +export type ApiRequest = { + readonly method: HttpMethod; + readonly urlTemplate: UrlTemplate; + readonly auth: AuthScheme; + readonly pathParams: readonly Param[]; + readonly query: readonly StyledParam[]; + readonly headers: readonly Param[]; + readonly body: RequestBody; +}; diff --git a/plugins/authlete/sdk/typescript/src/core/auth/credentials.ts b/plugins/authlete/sdk/typescript/src/core/auth/credentials.ts new file mode 100644 index 00000000..26f0bc59 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/auth/credentials.ts @@ -0,0 +1,80 @@ +/** + * A credential, or a function that produces one. + * + * @remarks + * A function is called on **every** request and its result is never cached, so a key or token can + * be rotated without rebuilding the client. An empty string counts as absent. + * + * The function is handed the call's `signal`, or one that never aborts when the call was given + * none. The SDK waits for the function to settle, so a function that does I/O should honour that + * signal for a cancelled call to end promptly. + */ +export type TokenProvider = string | ((signal: AbortSignal) => string | Promise); + +/** Username and password for HTTP Basic, sent base64 in the Authorization header. */ +export type BasicAuthCredentials = { + readonly username: string; + readonly password: string; +}; + +/** Credentials for the OAuth 2.0 client-credentials grant (RFC 6749 section 4.4). */ +export type OAuth2ClientCredentials = { + readonly clientId: string; + readonly clientSecret: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; +}; + +/** + * Credentials for the OAuth 2.0 resource-owner password grant (RFC 6749 section 4.3). + * + * @remarks + * Legacy: the grant hands the user password to the client. Prefer the authorization-code grant. + */ +export type OAuth2PasswordCredentials = { + readonly clientId: string; + readonly clientSecret?: string | undefined; + readonly username: string; + readonly password: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; +}; + +/** PKCE challenge method (RFC 7636). {@link PkceMethod.S256} unless the server cannot do it. */ +export const PkceMethod = { + S256: "S256", + Plain: "plain", +} as const; +export type PkceMethod = (typeof PkceMethod)[keyof typeof PkceMethod]; + +/** + * Sends the user to the authorization URL and returns the code they come back with. + * + * @remarks + * The SDK builds the URL and hands it over; it never opens it itself. Typically this opens a + * browser, waits for the redirect, and returns its `code` query parameter. + * + * `signal` is the one the call was given, or one that never aborts when it was given none. No + * timeout covers the prompt, so it may wait on the user for as long as that signal allows. + */ +export type AuthorizationCodePrompt = (authorizationUrl: string, signal: AbortSignal) => Promise; + +/** + * Credentials for the OAuth 2.0 authorization-code grant (RFC 6749 section 4.1) with PKCE. + * + * @remarks + * The only grant that needs a human β€” supply {@link AuthorizationCodePrompt} as + * `promptForAuthorizationCode` β€” and the only one whose token response may carry a refresh token. + * PKCE is on by default (`pkce` defaults to `S256`); setting it to `null` disables it and then a + * `clientSecret` is required. + */ +export type OAuth2AuthorizationCodeCredentials = { + readonly clientId: string; + readonly clientSecret?: string | undefined; + readonly redirectUri: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; + readonly state?: string | undefined; + readonly pkce?: PkceMethod | null | undefined; + readonly promptForAuthorizationCode: AuthorizationCodePrompt; +}; diff --git a/plugins/authlete/sdk/typescript/src/core/auth/oauth2-schemes.ts b/plugins/authlete/sdk/typescript/src/core/auth/oauth2-schemes.ts new file mode 100644 index 00000000..01735f49 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/auth/oauth2-schemes.ts @@ -0,0 +1,81 @@ +import type { AuthParams, AuthScheme } from "../api-request.js"; +import type { + OAuth2RefreshableTokenStrategy, + OAuth2TokenStrategy, + OAuthToken, + OAuthTokenRefreshable, +} from "./oauth2-strategies.js"; +import { header, noneAuth } from "./schemes.js"; + +const EXPIRY_BUFFER_MS = 30_000; + +export function oauth2Scheme( + credentials: TCredentials | undefined, + strategy: OAuth2TokenStrategy, +): AuthScheme { + if (credentials === undefined) return noneAuth; + return cachedTokenScheme((signal) => strategy.getToken(credentials, signal)); +} + +export function oauth2RefreshableScheme( + credentials: TCredentials | undefined, + strategy: OAuth2RefreshableTokenStrategy, +): AuthScheme { + if (credentials === undefined) return noneAuth; + return cachedTokenScheme(async (signal, previous) => { + const refreshToken = previous?.refreshToken; + if (refreshToken === undefined) return strategy.getToken(credentials, signal); + + const refreshed = await strategy.tryRefreshToken(credentials, refreshToken, signal); + if (refreshed === null) return strategy.getToken(credentials, signal); + return refreshed.refreshToken === undefined ? { ...refreshed, refreshToken } : refreshed; + }); +} + +function cachedTokenScheme( + acquire: (signal: AbortSignal, previous?: TToken) => Promise, +): AuthScheme { + let cached: { token: TToken; expiresAt: number } | undefined; + let inflight: Promise | undefined; + + async function obtainAndCacheToken(signal: AbortSignal): Promise { + try { + const token = await acquire(signal, cached?.token); + cached = { token, expiresAt: expiryOf(token) }; + return token; + } finally { + inflight = undefined; + } + } + + return { + async resolve(signal) { + if (cached !== undefined && Date.now() < cached.expiresAt) return bearer(cached.token); + inflight ??= obtainAndCacheToken(signal); + return bearer(await untilAborted(inflight, signal)); + }, + hasCredentials: () => true, + invalidate: () => { + cached = undefined; + }, + }; +} + +function untilAborted(promise: Promise, signal: AbortSignal): Promise { + return new Promise((resolve, reject) => { + const onAbort = (): void => reject(signal.reason); + signal.addEventListener("abort", onAbort, { once: true }); + if (signal.aborted) onAbort(); + promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort)); + }); +} + +function bearer(token: OAuthToken): AuthParams { + return header("Authorization", `Bearer ${token.accessToken}`); +} + +function expiryOf(token: OAuthToken): number { + if (token.expiresIn === undefined || token.expiresIn <= 0) return Number.POSITIVE_INFINITY; + const lifetimeMs = token.expiresIn * 1000; + return Date.now() + Math.max(lifetimeMs - EXPIRY_BUFFER_MS, lifetimeMs / 2); +} diff --git a/plugins/authlete/sdk/typescript/src/core/auth/oauth2-strategies.ts b/plugins/authlete/sdk/typescript/src/core/auth/oauth2-strategies.ts new file mode 100644 index 00000000..e721e919 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/auth/oauth2-strategies.ts @@ -0,0 +1,365 @@ +import type { UrlTemplate } from "../api-request.js"; +import type { Param, StyledParam } from "../param-value.js"; +import type { RawClient } from "../raw-client.js"; +import type { Entry, Schema } from "../validation/schema.js"; +import type { + OAuth2AuthorizationCodeCredentials, + OAuth2ClientCredentials, + OAuth2PasswordCredentials, +} from "./credentials.js"; +import { PkceMethod } from "./credentials.js"; +import { base64, basicCredential, noneAuth } from "./schemes.js"; +import { CoreError } from "../errors.js"; +import { ApiError } from "../api-error.js"; +import { buildQueryUrl, templateUri } from "../url.js"; +import * as s from "../validation/index.js"; + +/** + * An OAuth 2.0 token response (RFC 6749 section 5.1). + * + * @remarks + * Wire names are snake_case; these are the decoded SDK names. Returned by an + * {@link OAuth2TokenStrategy}, and cached by the scheme that owns it until it expires. + */ +export type OAuthToken = { + /** The token to send in `Authorization`. */ + accessToken: string; + + /** How the token is presented, normally `Bearer`. */ + tokenType: string; + + /** + * Lifetime in seconds. RECOMMENDED, not required (RFC 6749 section 5.1) β€” a token with no + * `expiresIn` is treated as never expiring. + */ + expiresIn?: number; + + /** Scopes the server actually granted, which may be narrower than those requested. */ + scope?: string; +}; + +export const oauthTokenSchema: Schema = s.object({ + accessToken: s.string(), + tokenType: s.string(), + expiresIn: s.optional(s.int()), + scope: s.optional(s.string()), + _keysMap: { + accessToken: "access_token", + tokenType: "token_type", + expiresIn: "expires_in", + }, +}); + +/** + * An OAuth 2.0 token response that may carry a refresh token. + * + * @remarks + * Only the authorization-code grant issues one β€” RFC 6749 forbids it for the implicit grant and + * says client-credentials SHOULD NOT issue one. + */ +export type OAuthTokenRefreshable = OAuthToken & { + /** + * Issued at the server's discretion (RFC 6749 sections 4.1.4 and 4.3.3). When a refresh response + * omits it, the previously issued one is carried forward. + */ + refreshToken?: string; +}; + +export const oauthTokenRefreshableSchema: Schema = s.object({ + accessToken: s.string(), + tokenType: s.string(), + expiresIn: s.optional(s.int()), + scope: s.optional(s.string()), + refreshToken: s.optional(s.string()), + _keysMap: { + accessToken: "access_token", + tokenType: "token_type", + expiresIn: "expires_in", + refreshToken: "refresh_token", + }, +}); + +/** + * How a token is obtained for a grant. The replaceable half of the OAuth2 design. + * + * @remarks + * The scheme owns caching, single-flight and expiry; a strategy owns only the exchange. Supply one + * on {@link ClientOptions} to talk to a server whose token endpoint does not follow the built-in + * shape; the SDK then never builds the request itself. + * + * `signal` is the one the call was given, or one that never aborts when it was given none. + * Nothing times a strategy you supply; the built-in ones time their own token request. + */ +export type OAuth2TokenStrategy = { + getToken(credentials: TCredentials, signal: AbortSignal): Promise; +}; + +/** + * A token strategy that can also refresh, used by the authorization-code grant. + * + * @remarks + * `tryRefreshToken` returns `null` rather than throwing when the refresh is rejected, which is what + * makes "refresh failed, re-authorize" a normal path instead of an error path. Both methods get + * `signal` as {@link OAuth2TokenStrategy} does. + */ +export type OAuth2RefreshableTokenStrategy = { + getToken(credentials: TCredentials, signal: AbortSignal): Promise; + tryRefreshToken( + credentials: TCredentials, + refreshToken: string, + signal: AbortSignal, + ): Promise; +}; + +/** + * Where client credentials travel on a token request. + * + * @remarks + * `"header"` sends them as `Authorization: Basic`; `"body"` sends them as form fields, the + * `client_secret_post` style. A client with no secret is a public client: under either + * placement it sends only `client_id`, as a form field, and no `Authorization` header. + */ +export type OAuth2CredentialPlacement = "header" | "body"; + +export function oauth2ClientCredentialsStrategy(config: { + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2TokenStrategy { + return { + getToken(credentials, signal: AbortSignal) { + const placed = placeCredentials( + config.placement ?? "header", + credentials.clientId, + credentials.clientSecret, + ); + return requestToken( + config, + oauthTokenSchema, + placed.headers, + [ + { name: "grant_type", value: "client_credentials", schema: s.string() }, + ...scopeField(credentials.scope), + ...placed.fields, + ], + signal, + ); + }, + }; +} + +export function oauth2PasswordStrategy(config: { + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2TokenStrategy { + return { + getToken(credentials, signal: AbortSignal) { + const placed = placeCredentials( + config.placement ?? "header", + credentials.clientId, + credentials.clientSecret, + ); + return requestToken( + config, + oauthTokenSchema, + placed.headers, + [ + { name: "grant_type", value: "password", schema: s.string() }, + { name: "username", value: credentials.username, schema: s.string() }, + { name: "password", value: credentials.password, schema: s.string() }, + ...scopeField(credentials.scope), + ...placed.fields, + ], + signal, + ); + }, + }; +} + +export function oauth2AuthorizationCodeStrategy(config: { + authorizationUrl: UrlTemplate; + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2RefreshableTokenStrategy { + const placementOf = ( + credentials: OAuth2AuthorizationCodeCredentials, + ): { headers: Param[]; fields: StyledParam[] } => + placeCredentials(config.placement ?? "header", credentials.clientId, credentials.clientSecret); + + return { + async getToken(credentials, signal: AbortSignal) { + const method = credentials.pkce === undefined ? PkceMethod.S256 : credentials.pkce; + if (method === null && (credentials.clientSecret === undefined || credentials.clientSecret === "")) { + throw new Error( + "A client secret is required when PKCE is disabled. Set pkce to PkceMethod.S256 for a public client.", + ); + } + + const pkce = method === null ? undefined : await generatePkce(method); + const authorizationUri = buildQueryUrl(new URL(templateUri(config.authorizationUrl)), [ + authorizationFields(credentials, pkce), + ]); + const code = await promptForCode(credentials, authorizationUri.href, signal); + const placed = placementOf(credentials); + + return requestToken( + config, + oauthTokenRefreshableSchema, + placed.headers, + [ + { name: "grant_type", value: "authorization_code", schema: s.string() }, + { name: "code", value: code, schema: s.string() }, + { name: "redirect_uri", value: credentials.redirectUri, schema: s.string() }, + ...(pkce === undefined + ? [] + : [{ name: "code_verifier", value: pkce.verifier, schema: s.string() }]), + ...placed.fields, + ], + signal, + ); + }, + + async tryRefreshToken(credentials, refreshToken, signal: AbortSignal) { + const placed = placementOf(credentials); + const outcome = await config.rawClient + .execute( + { + method: "POST", + urlTemplate: config.tokenUrl, + auth: noneAuth, + pathParams: [], + query: [], + headers: placed.headers, + body: { + kind: "formUrlEncoded", + value: [ + { name: "grant_type", value: "refresh_token", schema: s.string() }, + { name: "refresh_token", value: refreshToken, schema: s.string() }, + ...placed.fields, + ], + }, + }, + { + success: { kind: "json", schema: oauthTokenRefreshableSchema }, + errorFactory: ApiError, + }, + { signal }, + ) + .asApiResult(); + + return outcome.ok ? outcome.value : null; + }, + }; +} + +function scopeField(scope: string | undefined): StyledParam[] { + if (scope === undefined || scope === "") return []; + return [{ name: "scope", value: scope, schema: s.string() }]; +} + +function placeCredentials( + placement: OAuth2CredentialPlacement, + clientId: string, + clientSecret: string | undefined, +): { headers: Param[]; fields: StyledParam[] } { + const clientIdField: StyledParam = { name: "client_id", value: clientId, schema: s.string() }; + if (clientSecret === undefined || clientSecret === "") { + return { headers: [], fields: [clientIdField] }; + } + + if (placement === "header") { + return { + headers: [ + { + name: "Authorization", + value: basicCredential(clientId, clientSecret), + schema: s.string(), + }, + ], + fields: [], + }; + } + + return { + headers: [], + fields: [clientIdField, { name: "client_secret", value: clientSecret, schema: s.string() }], + }; +} + +async function requestToken( + config: { tokenUrl: UrlTemplate; rawClient: RawClient }, + schema: Entry, + headers: readonly Param[], + fields: readonly StyledParam[], + signal: AbortSignal, +): Promise { + return config.rawClient.execute( + { + method: "POST", + urlTemplate: config.tokenUrl, + auth: noneAuth, + pathParams: [], + query: [], + headers, + body: { kind: "formUrlEncoded", value: fields }, + }, + { + success: { kind: "json", schema }, + errorFactory: ApiError, + }, + { signal }, + ); +} + +async function promptForCode( + credentials: OAuth2AuthorizationCodeCredentials, + authorizationUrl: string, + signal: AbortSignal, +): Promise { + try { + return await credentials.promptForAuthorizationCode(authorizationUrl, signal); + } catch (err) { + if (!(err instanceof CoreError) && signal.aborted) throw signal.reason; + throw err; + } +} + +function authorizationFields( + credentials: OAuth2AuthorizationCodeCredentials, + pkce: PkceValues | undefined, +): StyledParam[] { + const fields: StyledParam[] = [ + { name: "response_type", value: "code", schema: s.string() }, + { name: "client_id", value: credentials.clientId, schema: s.string() }, + { name: "redirect_uri", value: credentials.redirectUri, schema: s.string() }, + ...scopeField(credentials.scope), + ]; + if (credentials.state !== undefined && credentials.state !== "") { + fields.push({ name: "state", value: credentials.state, schema: s.string() }); + } + if (pkce !== undefined) { + fields.push( + { name: "code_challenge", value: pkce.challenge, schema: s.string() }, + { name: "code_challenge_method", value: pkce.method, schema: s.string() }, + ); + } + return fields; +} + +type PkceValues = { verifier: string; challenge: string; method: PkceMethod }; + +async function generatePkce(method: PkceMethod): Promise { + const entropy = new Uint8Array(32); + crypto.getRandomValues(entropy); + const verifier = base64Url(entropy); + if (method === PkceMethod.Plain) return { verifier, challenge: verifier, method }; + + const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)); + return { verifier, challenge: base64Url(new Uint8Array(digest)), method }; +} + +function base64Url(bytes: Uint8Array): string { + return base64(bytes).replace(/=+$/, "").replaceAll("+", "-").replaceAll("/", "_"); +} diff --git a/plugins/authlete/sdk/typescript/src/core/auth/schemes.ts b/plugins/authlete/sdk/typescript/src/core/auth/schemes.ts new file mode 100644 index 00000000..a01c35db --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/auth/schemes.ts @@ -0,0 +1,183 @@ +import type { AuthParams, AuthScheme, HttpMethod } from "../api-request.js"; +import type { BasicAuthCredentials, TokenProvider } from "./credentials.js"; +import type { RequestHeaders } from "../headers.js"; +import type { RequestUrl } from "../url.js"; +import type { HttpLogger } from "../http-logger.js"; +import { AuthError, ConfigurationError, ConnectionError, TimeoutError } from "../errors.js"; + +const NO_PARAMS: AuthParams = { headers: [], query: [], cookies: [] }; + +export const noneAuth: AuthScheme = { + resolve: () => NO_PARAMS, + hasCredentials: () => false, +}; + +export function bearerAuth(token: TokenProvider | undefined): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(token, signal); + return value === undefined ? NO_PARAMS : header("Authorization", `Bearer ${value}`); + }, + hasCredentials: () => hasToken(token), + }; +} + +export function basicAuth(credentials: BasicAuthCredentials | undefined): AuthScheme { + if (credentials !== undefined && credentials.username.includes(":")) { + throw new ConfigurationError("A basic-auth username cannot contain a colon (RFC 7617 section 2)."); + } + return { + resolve() { + if (credentials === undefined) return NO_PARAMS; + return header("Authorization", basicCredential(credentials.username, credentials.password)); + }, + hasCredentials: () => credentials !== undefined, + }; +} + +export function apiKeyHeaderAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + return value === undefined ? NO_PARAMS : header(config.name, value); + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function apiKeyQueryAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + if (value === undefined) return NO_PARAMS; + return { headers: [], query: [[config.name, value]], cookies: [] }; + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function apiKeyCookieAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + if (value === undefined) return NO_PARAMS; + return { headers: [], query: [], cookies: [[config.name, value]] }; + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function allAuth(...schemes: readonly AuthScheme[]): AuthScheme { + return { + async resolve(signal) { + const parts: AuthParams[] = []; + for (const scheme of schemes) parts.push(await scheme.resolve(signal)); + return { + headers: parts.flatMap((part) => part.headers), + query: parts.flatMap((part) => part.query), + cookies: parts.flatMap((part) => part.cookies), + }; + }, + hasCredentials: () => schemes.length > 0 && schemes.every((scheme) => scheme.hasCredentials()), + invalidate() { + for (const scheme of schemes) scheme.invalidate?.(); + }, + }; +} + +export function anyAuth(...schemes: readonly AuthScheme[]): AuthScheme { + return { + async resolve(signal) { + const failures: unknown[] = []; + for (const scheme of schemes) { + if (!scheme.hasCredentials()) continue; + try { + return await scheme.resolve(signal); + } catch (err) { + if (signal.aborted) throw signal.reason; + if (err instanceof TimeoutError) throw err; + failures.push(err); + } + } + if (failures.length === 0) return NO_PARAMS; + if (failures.length === 1) throw failures[0]; + throw new AggregateError(failures, "No authentication scheme succeeded."); + }, + hasCredentials: () => schemes.some((scheme) => scheme.hasCredentials()), + invalidate() { + for (const scheme of schemes) scheme.invalidate?.(); + }, + }; +} + +export type AuthenticatedRequest = { + readonly url: URL; + readonly headers: Headers; +}; + +export async function authenticateRequest( + method: HttpMethod, + auth: AuthScheme, + url: RequestUrl, + headers: RequestHeaders, + signal: AbortSignal, + startedAt: number, + logger: HttpLogger, +): Promise { + try { + const credential = await auth.resolve(signal); + return { + url: url.withQuery(credential.query), + headers: headers.withCredential(credential.headers, credential.cookies), + }; + } catch (err) { + if (signal.aborted) throw signal.reason; + if (err instanceof ConnectionError || err instanceof TimeoutError) throw err; + if (err instanceof AuthError) { + logger.logFailure(err, startedAt); + throw err; + } + const { uri } = url; + const error = new AuthError( + `${method} ${uri} failed: ${ + err instanceof Error && err.message !== "" ? err.message : "A credential could not be obtained." + }`, + { cause: err, method, uri }, + ); + logger.logFailure(error, startedAt); + throw error; + } +} + +export function header(name: string, value: string): AuthParams { + return { headers: [[name, value]], query: [], cookies: [] }; +} + +export function basicCredential(username: string, password: string): string { + return `Basic ${base64(new TextEncoder().encode(`${username}:${password}`))}`; +} + +export function base64(bytes: Uint8Array): string { + let binary = ""; + for (const byte of bytes) binary += String.fromCharCode(byte); + return btoa(binary); +} + +async function resolveToken( + token: TokenProvider | undefined, + signal: AbortSignal, +): Promise { + const value: unknown = typeof token === "function" ? await token(signal) : token; + if (value !== undefined && typeof value !== "string") { + throw new TypeError(`A credential must be a string, received ${typeof value}.`); + } + return present(value); +} + +function hasToken(token: TokenProvider | undefined): boolean { + return typeof token === "function" || present(token) !== undefined; +} + +function present(value: string | undefined): string | undefined { + return value !== undefined && value !== "" ? value : undefined; +} diff --git a/plugins/authlete/sdk/typescript/src/core/binary.ts b/plugins/authlete/sdk/typescript/src/core/binary.ts new file mode 100644 index 00000000..18e9a4d9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/binary.ts @@ -0,0 +1,428 @@ +import { CoreError, DecodeError } from "./errors.js"; +import type { HttpMethod } from "./api-request.js"; +import type { BinaryResponseDecoder } from "./response-decoder.js"; + +/** + * Bytes, in any form the platform can send. + * + * @remarks + * Bytes, not a file: nothing here carries a name or a media type. Wrap them in {@link FileData} + * to attach either. + * + * A `ReadableStream` streams the request rather than buffering it: the fetch is sent with + * `duplex: "half"`, and the body cannot be replayed, so a retry would have nothing left to send. + * That holds for a multipart file part too: one streaming part makes the SDK frame the whole form + * itself as a single chunked stream, taking the `Content-Length` and the replayability with it. + * + * If the request is aborted or times out mid-upload the SDK stops reading within one chunk and + * hands the lock back, so the source is yours again to cancel or dispose from the `catch`. It is + * never cancelled on your behalf β€” the SDK closes only what it opened. A stream that was fully + * sent stays locked, which is the platform's own guard against sending a spent body twice. + * + * The async-iterable arm is what a Node stream satisfies, so `createReadStream(path)` can be + * passed straight in and is forwarded chunk by chunk rather than buffered, under that same + * one-shot rule. It is spelled structurally on purpose: naming Node's `Readable` would put + * `@types/node` in the published declarations, which the DOM typecheck lane exists to prevent. + */ +export type BinaryData = + | Blob + | Uint8Array + | ArrayBuffer + | ReadableStream + | AsyncIterable>; + +/** + * Bytes plus the name and media type to declare for them β€” what turns {@link BinaryData} into a + * file, as the platform's own `File` does to a `Blob`. + * + * @remarks + * Wrap a {@link BinaryData} in this to send a `Content-Disposition` filename, or to override the + * media type the operation declares. Both are optional, and a `Blob` that already carries a `type` + * or a `name` supplies them on its own. + */ +export type FileData = { + readonly data: BinaryData; + readonly fileName?: string | undefined; + readonly contentType?: string | undefined; +}; + +/** A file to send: bare {@link BinaryData} bytes, or a {@link FileData} that names them. */ +export type FileInput = BinaryData | FileData; + +export type BufferedResolvedFile = { + readonly body: Blob | Uint8Array; + readonly contentType: string | undefined; + readonly fileName: string | undefined; + readonly streaming: false; +}; + +export type StreamingResolvedFile = { + readonly body: ReadableStream; + readonly contentType: string | undefined; + readonly fileName: string | undefined; + readonly streaming: true; +}; + +export type ResolvedFile = BufferedResolvedFile | StreamingResolvedFile; + +export type ByteSource = Blob | Uint8Array | ReadableStream; + +/** + * An error response whose body is bytes rather than JSON, buffered in full. + * + * @remarks + * Unlike a binary *success* body this is a value, not a resource: a thrown error's payload has no + * lifetime, so the bytes are read before the rejection surfaces and there is nothing to release. + */ +export type BinaryErrorContent = { + readonly bytes: Uint8Array; + readonly contentType: string | undefined; + readonly fileName: string | undefined; +}; + +/** + * A binary response body you own β€” a handle over the still-open connection, not bytes. + * + * @remarks + * The body arrives as an unread `ReadableStream` and is handled the way any web stream is: buffer + * it with `new Response(file.stream)` and one of `bytes()`, `text()` or `blob()`, read it chunk by + * chunk with a reader or `for await`, or release it unread with `file.stream.cancel()`. Once a + * reader holds the lock, cancel through the reader instead. The stream is the response body + * itself, so an abort or a dropped connection surfaces on it as the platform reports it: an abort + * as the signal's `reason` when `fetch` ties the body to the signal, a dropped connection as the + * platform's own error. + */ +export type BinaryContent = { + /** + * The body, unread β€” a web `ReadableStream` whose lifetime is yours. + */ + readonly stream: ReadableStream>; + + /** The media type the server declared, or the operation's declared type when it declared none. */ + readonly contentType: string; + + /** + * The name from `Content-Disposition`, when the response carried one. + * + * @remarks + * Server-chosen, and therefore **untrusted input**. Never use it as a path: sanitize it, or name + * the file yourself. + */ + readonly fileName: string | undefined; +}; + +export function readBinary(decoder: BinaryResponseDecoder, response: Response): BinaryContent { + const contentType = response.headers.get("content-type") ?? decoder.contentType; + const fileName = parseFileName(response.headers.get("content-disposition")); + return { stream: response.body ?? emptyStream(), contentType, fileName }; +} + +export async function readBinaryError( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let buffer: ArrayBuffer; + try { + buffer = await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return { + bytes: new Uint8Array(buffer), + contentType: response.headers.get("content-type") ?? undefined, + fileName: parseFileName(response.headers.get("content-disposition")), + }; +} + +export function resolveFile( + input: FileInput, + declaredContentType: string | undefined, + signal?: AbortSignal, +): ResolvedFile { + const file: FileData = isFileData(input) ? input : { data: input }; + if (!isBinaryData(file.data)) throw unusableBinaryData(file.data); + const declared = file.contentType ?? blobContentType(file.data); + const contentType = declared === undefined || declared === "" ? declaredContentType : declared; + const fileName = file.fileName ?? blobFileName(file.data); + const body = toBody(file.data, signal); + if (body instanceof ReadableStream) return { body, contentType, fileName, streaming: true }; + return { body, contentType, fileName, streaming: false }; +} + +export function concatStream( + sources: readonly ByteSource[], + signal: AbortSignal | undefined, +): ReadableStream { + return streamFrom(concatenate(sources), signal); +} + +export function attachmentDisposition(fileName: string | undefined): string | undefined { + if (fileName === undefined || fileName === "") return undefined; + return `attachment; filename="${asciiFileName(fileName)}"; filename*=UTF-8''${encodeRfc5987(fileName)}`; +} + +const EXTENDED_FILENAME = /;[ \t]*filename\*[ \t]*=[ \t]*([^;]*)/i; +const QUOTED_FILENAME = /;[ \t]*filename[ \t]*=[ \t]*"((?:[^"\\]|\\[\s\S])*)"/i; +const TOKEN_FILENAME = /;[ \t]*filename[ \t]*=[ \t]*([^;",]+)/i; +const ESCAPED_PAIR = /\\([\s\S])/g; + +export function parseFileName(disposition: string | null): string | undefined { + if (disposition === null) return undefined; + + const extended = EXTENDED_FILENAME.exec(disposition); + if (extended !== null) { + const decoded = decodeRfc5987((extended[1] ?? "").trim()); + if (decoded !== undefined && decoded !== "") return decoded; + } + + const quoted = QUOTED_FILENAME.exec(disposition); + if (quoted !== null) { + const unescaped = (quoted[1] ?? "").replace(ESCAPED_PAIR, "$1"); + if (unescaped !== "") return unescaped; + } + + const token = TOKEN_FILENAME.exec(disposition); + const value = (token?.[1] ?? "").trim(); + return value === "" ? undefined : value; +} + +function emptyStream(): ReadableStream> { + return new ReadableStream>({ start: (controller) => controller.close() }); +} + +function toBody(data: BinaryData, signal: AbortSignal | undefined): ResolvedFile["body"] { + if (data instanceof ArrayBuffer) return new Uint8Array(data); + if (data instanceof ReadableStream) return signal === undefined ? data : borrowStream(data, signal); + if (isAsyncIterable(data)) return streamFrom(data, signal); + return data; +} + +function streamFrom( + iterable: AsyncIterable, + signal: AbortSignal | undefined, +): ReadableStream { + const iterator = iterable[Symbol.asyncIterator](); + let sink: ReadableStreamDefaultController | undefined; + let settled = false; + + const settle = (): boolean => { + if (settled) return false; + settled = true; + return true; + }; + + const onAbort = (): void => { + if (!settle()) return; + sink?.error(signal?.reason); + void iterator.return?.(signal?.reason).catch(() => {}); + }; + + signal?.addEventListener("abort", onAbort, { once: true }); + + return new ReadableStream({ + start: (controller) => { + sink = controller; + if (signal?.aborted) onAbort(); + }, + pull: async (controller) => { + const { done, value } = await iterator.next(); + if (settled) return; + if (done) { + settle(); + controller.close(); + return; + } + controller.enqueue(value); + }, + cancel: async (reason) => { + settle(); + await iterator.return?.(reason); + }, + }); +} + +function borrowStream(source: ReadableStream, signal: AbortSignal): ReadableStream { + let reader: ReadableStreamDefaultReader | undefined; + let sink: ReadableStreamDefaultController | undefined; + let settled = false; + + const settle = (): boolean => { + if (settled) return false; + settled = true; + return true; + }; + + const onAbort = (): void => { + if (!settle()) return; + sink?.error(signal.reason); + reader?.releaseLock(); + }; + + signal.addEventListener("abort", onAbort, { once: true }); + + return new ReadableStream( + { + start: (controller) => { + sink = controller; + if (signal.aborted) onAbort(); + }, + pull: async (controller) => { + try { + reader ??= source.getReader(); + const { done, value } = await reader.read(); + if (settled) return; + if (done) { + settle(); + controller.close(); + return; + } + controller.enqueue(value); + } catch (err) { + if (settle()) controller.error(err); + } + }, + cancel: () => { + if (settle()) reader?.releaseLock(); + }, + }, + { highWaterMark: 0 }, + ); +} + +async function* concatenate(sources: readonly ByteSource[]): AsyncGenerator { + for (const source of sources) { + if (source instanceof Uint8Array) { + yield source; + } else if (source instanceof Blob) { + const reader = source.stream().getReader(); + try { + yield* readChunks(reader); + } finally { + await reader.cancel().catch(() => {}); + } + } else { + const reader = source.getReader(); + try { + yield* readChunks(reader); + } finally { + reader.releaseLock(); + } + } + } +} + +async function* readChunks(reader: ReadableStreamDefaultReader): AsyncGenerator { + for (;;) { + const { done, value } = await reader.read(); + if (done) return; + yield value; + } +} + +function isFileData(input: FileInput): input is FileData { + return typeof input === "object" && input !== null && "data" in input; +} + +function isBinaryData(data: unknown): data is BinaryData { + return ( + data instanceof Blob || + data instanceof Uint8Array || + data instanceof ArrayBuffer || + data instanceof ReadableStream || + isAsyncIterable(data) + ); +} + +function isAsyncIterable(data: unknown): data is AsyncIterable> { + return typeof data === "object" && data !== null && Symbol.asyncIterator in data; +} + +function unusableBinaryData(data: unknown): TypeError { + return new TypeError( + `A file body must be a Blob, Uint8Array, ArrayBuffer, ReadableStream or an async iterable of ` + + `bytes, or a { data } wrapper around one β€” received ${receivedType(data)}. A filesystem path is ` + + `not a file body: open it with createReadStream(path), or read it with await openAsBlob(path).`, + ); +} + +function receivedType(data: unknown): string { + if (data === null) return "null"; + if (typeof data !== "object") return typeof data; + return data.constructor?.name ?? "object"; +} + +function blobContentType(data: BinaryData): string | undefined { + return data instanceof Blob ? data.type : undefined; +} + +function blobFileName(data: BinaryData): string | undefined { + if (!(data instanceof Blob) || !("name" in data)) return undefined; + const name = data.name; + return typeof name === "string" && name !== "" ? name : undefined; +} + +const DISPOSITION_UNSAFE = /[^ -~]|["\\;]/g; + +function asciiFileName(fileName: string): string { + return fileName.replace(DISPOSITION_UNSAFE, "_"); +} + +const RFC5987_ATTR_CHAR = /^[A-Za-z0-9!#$&+\-.^_`|~]$/; + +function encodeRfc5987(value: string): string { + let encoded = ""; + for (const byte of new TextEncoder().encode(value)) { + const char = String.fromCharCode(byte); + encoded += RFC5987_ATTR_CHAR.test(char) ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`; + } + return encoded; +} + +const EXT_VALUE = /^([^']*)'[^']*'([\s\S]*)$/; + +function decodeRfc5987(value: string): string | undefined { + const parts = EXT_VALUE.exec(value); + if (parts === null) return undefined; + + const charset = (parts[1] ?? "").toLowerCase(); + const label = charset === "" ? "utf-8" : charset; + if (label !== "utf-8" && label !== "iso-8859-1") return undefined; + + const bytes = percentDecode(parts[2] ?? ""); + if (bytes === undefined) return undefined; + + try { + return new TextDecoder(label, { fatal: true }).decode(bytes); + } catch { + return undefined; + } +} + +const HEX_PAIR = /^[0-9A-Fa-f]{2}$/; + +function percentDecode(value: string): Uint8Array | undefined { + const bytes: number[] = []; + for (let index = 0; index < value.length; index += 1) { + const char = value[index] ?? ""; + if (char !== "%") { + const code = char.charCodeAt(0); + if (code > 0xff) return undefined; + bytes.push(code); + continue; + } + const hex = value.slice(index + 1, index + 3); + if (!HEX_PAIR.test(hex)) return undefined; + bytes.push(Number.parseInt(hex, 16)); + index += 2; + } + return new Uint8Array(bytes); +} diff --git a/plugins/authlete/sdk/typescript/src/core/client-options.ts b/plugins/authlete/sdk/typescript/src/core/client-options.ts new file mode 100644 index 00000000..23c803b6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/client-options.ts @@ -0,0 +1,90 @@ +import { ConfigurationError } from "./errors.js"; +import { buildLoggingOptions, type LoggingOptions, type ResolvedLoggingOptions } from "./logging.js"; +import { buildRetryOptions, type RetryOptions, type ResolvedRetryOptions } from "./retry.js"; +import { + buildEventStreamOptions, + type EventStreamOptions, + type ResolvedEventStreamOptions, +} from "./event-stream.js"; + +/** The signature a replacement `fetch` must have, which is the global `fetch`'s own. */ +export type FetchLike = typeof fetch; + +/** + * What the caller configures the engine with: every field optional, and one left out resolved to + * the default its own block names. + * + * @remarks + * The shell's `ClientOptions` intersects this directly, so a new engine option costs no emitted + * line. {@link buildCoreClientOptions} fills every field in, and a value the engine cannot honour + * falls back to its own default rather than being rejected. + */ +export type CoreClientOptions = { + /** + * How a request is timed and how a failed one is retried. + * + * @remarks + * Any subset of {@link RetryOptions}. Retries are **on** by default β€” that type says what is + * retried, what is not, and which value each field falls back to. Its `timeout` is the budget + * for one attempt, so it applies whether or not a request is ever re-sent. + */ + readonly retry?: RetryOptions | undefined; + + /** + * How an event stream is watched for a silent server. + * + * @remarks + * Any subset of {@link EventStreamOptions}, resolved once when the client is built and read only + * by an operation that resolves to an `EventStream`. A field left out takes its default. + */ + readonly eventStream?: EventStreamOptions | undefined; + + /** Logging of requests, responses and retries. Off by default, see {@link LoggingOptions}. */ + readonly logging?: LoggingOptions | undefined; + + /** + * Replaces the global `fetch`. The one extension point for proxying or mocking. + * + * @remarks + * It is called with one argument, the attempt's `Request`, and no `init`. A replacement must + * forward that `Request`, or at least its `signal`, to whatever performs the request: one that + * reads `init` forwards nothing, and drops the call's abort and its timeout. To add a header, + * copy the `Request` with `new Request(request)` and set it on the copy's `headers`: passing + * `{ headers }` to the copy replaces every header the SDK set. + */ + readonly fetch?: FetchLike | undefined; +}; + +export type ResolvedCoreClientOptions = { + readonly retry: ResolvedRetryOptions; + readonly eventStream: ResolvedEventStreamOptions; + readonly logging: ResolvedLoggingOptions; + readonly fetch: FetchLike; +}; + +/** + * Resolves the engine-wide options once, when the client is built, from what the caller passed. + * + * @remarks + * `loggingEnvironmentVariable` is the name of the environment variable that can turn logging on. + * It is read here, once, and only when `logging` sets neither `logger` nor `level`. + * + * @throws {@link ConfigurationError} when no `fetch` was supplied and the global one is missing, + * or when reading `retry`, `eventStream` or `logging` throws. No value throws: every bounded field + * resolves, its own default standing in for a value the engine cannot honour. + */ +export function buildCoreClientOptions( + options: CoreClientOptions, + loggingEnvironmentVariable: string, +): ResolvedCoreClientOptions { + const fetchImpl = options.fetch ?? globalThis.fetch; + if (typeof fetchImpl !== "function") { + throw new ConfigurationError("No fetch implementation available; pass ClientOptions.fetch."); + } + return { + retry: buildRetryOptions(options.retry), + eventStream: buildEventStreamOptions(options.eventStream), + logging: buildLoggingOptions(options.logging, loggingEnvironmentVariable), + fetch: fetchImpl, + }; +} diff --git a/plugins/authlete/sdk/typescript/src/core/errors.ts b/plugins/authlete/sdk/typescript/src/core/errors.ts new file mode 100644 index 00000000..d600fb4e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/errors.ts @@ -0,0 +1,253 @@ +import type { HttpMethod } from "./api-request.js"; +import type { ApiError } from "./api-error.js"; + +/** Discriminant of the {@link CoreError} family. Closed, so a `switch` over it is exhaustive. */ +export type ErrorKind = "api" | "decode" | "encode" | "connection" | "timeout" | "auth"; + +/** + * What every {@link CoreError} is built with: the call it names, and an optional `cause`. + * + * @remarks + * It widens `ErrorOptions`, so `cause` is the platform's own and reaches the instance through + * `Error` rather than being copied. The message is not here: each throw site writes its own. + */ +export type CoreErrorInit = ErrorOptions & { + readonly method: HttpMethod; + readonly uri: string; +}; + +/** + * The runtime root every failure in the family extends. Its public face is {@link CoreError}. + * + * @remarks + * Not exported from the package and never named in a signature: a caught failure is one of the + * leaves, and {@link CoreError} is how they are spelled together. + */ +export abstract class CoreErrorBase extends Error { + abstract readonly kind: ErrorKind; + + /** HTTP method of the call that failed. */ + readonly method: HttpMethod; + + /** + * Absolute URI of the call that failed, as `https://us.api.acme.com/v1/accounts/acc_1/payments`. + * + * @remarks + * Path parameters are filled in and server variables expanded, so it names the resource actually + * dialled. It carries no query, fragment or userinfo, so no query parameter reaches it, not even + * an `apiKeyQuery` credential. With `method` it opens `message`. + * + * One failure names an unresolved URI: a path parameter rejected by its schema arrives as an + * `EncodeError` whose URI still shows the unfilled `{braces}` β€” an `undefined` one included, + * since a path parameter is always required, so its schema rejects it first. A `baseUrl` that + * is not a URL is a bug, and leaves as a raw `TypeError` naming no call. + */ + readonly uri: string; + + protected constructor(message: string, init: CoreErrorInit) { + super(message, init); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + this.method = init.method; + this.uri = init.uri; + } +} + +/** What a {@link ResponseError} adds to {@link CoreErrorInit}: the answer the API gave. */ +export type ResponseErrorInit = CoreErrorInit & { + readonly status: number; + readonly headers: Headers; +}; + +/** + * The runtime rung under {@link ResponseError}, carrying the two facts that exist exactly when + * the server answered. + * + * @remarks + * Not exported from the package. Its two arms are `ApiError` and {@link DecodeError}. + */ +export abstract class ResponseErrorBase extends CoreErrorBase { + abstract override readonly kind: "api" | "decode"; + + /** Status the API answered with. */ + readonly status: number; + + /** Headers the API answered with. The correlation id is read off these. */ + readonly headers: Headers; + + protected constructor(message: string, init: ResponseErrorInit) { + super(message, init); + this.status = init.status; + this.headers = init.headers; + } +} + +/** + * The server answered, and the answer could not be turned into the value the spec declares. + * + * @remarks + * `status` and `headers` are the ones it answered with, so the failure still says what came back + * and still carries the correlation id. `cause` says how it failed: a `SchemaError` when the body + * did not fit its contract, a `SyntaxError` when it was not JSON, or the platform's read fault + * when it died mid-stream. A body arriving where the operation declares none has no `cause`. + * + * Shape drift on a **declared** status is this, because the spec was wrong about the API. A status + * the spec does not describe arrives instead as an `ApiError` whose payload is the `"undeclared"` + * arm, which is the open world working as intended. + */ +export class DecodeError extends ResponseErrorBase { + readonly kind = "decode" as const; + + constructor(message: string, init: ResponseErrorInit) { + super(message, init); + } +} + +/** + * A request value did not match its declared type, so nothing was sent. + * + * @remarks + * The mirror of {@link DecodeError}, and it carries no `status` or `headers` because there is no + * response: the call never left. `cause` is the `SchemaError` that rejected the value, whose own + * `cause` is the issue list naming the field. + */ +export class EncodeError extends CoreErrorBase { + readonly kind = "encode" as const; + + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** + * `fetch` rejected before a response line arrived β€” the request never reached the server, or the + * server never answered β€” or an event stream's connection failed while you read it. + * + * @remarks + * Once a response line has arrived this is no longer the kind: a body that then dies mid-read is a + * {@link DecodeError}, carrying the status and headers the server did send. A binary success body + * is the exception, since it is yours from the moment it resolves, so a socket dying under it + * surfaces on that stream as the platform's own error, not as this. An event stream is the other: + * its loop raises this when a read fails, with the fault on `cause`. + */ +export class ConnectionError extends CoreErrorBase { + readonly kind = "connection" as const; + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** What a {@link TimeoutError} adds to {@link CoreErrorInit}: the budget that elapsed. */ +export type TimeoutErrorInit = CoreErrorInit & { + readonly timeout: number; +}; + +/** + * The request exceeded the client timeout, or an event stream stayed silent past its idle timeout. + * + * @remarks + * The client timeout starts once the credential is in hand and covers the request up to its + * response headers. Obtaining the credential and reading any response body are not covered. An + * OAuth2 token request is timed on its own, so a slow token endpoint raises this with the token + * endpoint's `uri`, not the operation's. An event stream's loop raises this when the server sends + * nothing for `eventStream.idleTimeout` while the loop waits. + */ +export class TimeoutError extends CoreErrorBase { + readonly kind = "timeout" as const; + + /** + * The budget that elapsed, in milliseconds: `ClientOptions.retry.timeout` as it resolved for this + * client, or the stream's `idleTimeout` when an event stream raised it, so a handler reads the + * limit off the failure rather than the configuration. + */ + readonly timeout: number; + + constructor(message: string, init: TimeoutErrorInit) { + super(message, init); + this.timeout = init.timeout; + } +} + +/** + * A credential could not be obtained, so the call was never sent. + * + * @remarks + * Raised when the token exchange answered with an error status, returned a body that would not + * decode, or could not be encoded, and when every branch of an `anyAuth` failed. `cause` carries + * that failure, and the operation's retry loop never re-sends it: a refusal is not transient. A + * dropped connection or a timeout during the exchange keeps its own kind instead, names the token + * endpoint, and ends the call without the operation being sent. + * + * `method` and `uri` name the operation you called; the token endpoint appears in the message + * after them. A 401 answered by the operation itself is an `ApiError`, not this. + */ +export class AuthError extends CoreErrorBase { + readonly kind = "auth" as const; + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** + * The API answered, and the answer is the failure. + * + * @remarks + * The one branch of the family that reaches a response: an error status arrives as `ApiError` and + * a body that would not decode as {@link DecodeError}. Catch it to handle everything the server + * had a say in, and read `headers` for the correlation id either way. `err.kind` tells the two + * arms apart and narrows to the one you have. + */ +export type ResponseError = ApiError | DecodeError; + +/** + * The value behind the {@link ResponseError} type: `err instanceof ResponseError` narrows to that + * union. It cannot be constructed and cannot be extended. + */ +export const ResponseError = ResponseErrorBase as unknown as abstract new (...args: never) => ResponseError; + +/** + * Root of every failure the SDK raises. Exported as `AcmeError`. + * + * @remarks + * One `catch (err) { if (err instanceof AcmeError) }` sees everything: the answers the API gave and + * the bodies it could not decode, both {@link ResponseError}, plus a request value that would not + * encode, a dropped connection, a timeout and auth that could not be obtained. Every one names the + * call that raised it, and `message` opens with that name. + * + * Narrow with `instanceof` on a leaf, or on `err.kind`, which is exhaustive and selects the leaf + * with its own members: `status` and `headers` on `"api"` and `"decode"`, `payload` on `"api"`, + * `timeout` on `"timeout"`. + * + * Bugs stay outside the family and reach you raw. An unparseable base URL and a non-file value + * for a file are both `TypeError`. A caller abort is outside it too, arriving as the `reason` of + * the signal you aborted. + */ +export type CoreError = ApiError | DecodeError | EncodeError | ConnectionError | TimeoutError | AuthError; + +/** + * The value behind the {@link CoreError} type, exported as `AcmeError`: `err instanceof AcmeError` + * narrows to that union. It cannot be constructed and cannot be extended. + */ +export const CoreError = CoreErrorBase as unknown as abstract new (...args: never) => CoreError; + +/** + * The client, or one call's options, could not be resolved. + * + * @remarks + * Not a member of the {@link CoreError} family: it carries no `method` or `uri`. The constructor + * throws it synchronously for three defects: no `fetch`, a basic-auth username containing a + * colon, and an unknown `serverEnvironment`. A `serverOptions` override of the wrong type is a + * `SchemaError` instead, raised by the codec that rejected it. + * + * A `retry` field the engine cannot honour is **not** a fourth: it falls back to its default. The + * `retry` and `logging` records reach this error only when reading one throws, as a getter that + * throws or a revoked proxy does, and that failure is on `cause`. Passed to the constructor, such a + * record is thrown from it. Passed to one call, it rejects that call before anything is sent. + */ +export class ConfigurationError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/plugins/authlete/sdk/typescript/src/core/event-stream-reader.ts b/plugins/authlete/sdk/typescript/src/core/event-stream-reader.ts new file mode 100644 index 00000000..500cfd7a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/event-stream-reader.ts @@ -0,0 +1,248 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConnectionError, TimeoutError } from "./errors.js"; +import type { ResolvedEventStreamOptions } from "./event-stream.js"; + +const LINE_FEED = "\n"; +const CARRIAGE_RETURN = "\r"; +const LINE_FEED_CODE = 0x0a; +const SPACE_CODE = 0x20; +const BYTE_ORDER_MARK_CODE = 0xfeff; +const DATA_FIELD = "data"; + +type Failure = { readonly error: unknown }; + +type ReadResult = Awaited["read"]>>; + +export class EventStreamReader { + readonly #response: Response; + readonly #method: HttpMethod; + readonly #uri: string; + readonly #signal: AbortSignal; + readonly #abort: () => void; + readonly #sentinel: string | undefined; + readonly #clock: IdleClock; + readonly #parser = new EventStreamParser(); + #reader: ReadableStreamDefaultReader | undefined; + #released: Promise | undefined; + #failure: Failure | undefined; + #onAbort: (() => void) | undefined; + + constructor( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, + sentinel: string | undefined, + ) { + this.#response = response; + this.#method = method; + this.#uri = uri; + this.#signal = signal; + this.#abort = abort; + this.#sentinel = sentinel; + this.#clock = new IdleClock(options.idleTimeout, (bound) => this.#lapse(bound)); + } + + readonly close = (): Promise => this.#release(undefined); + + async *frames(): AsyncGenerator { + try { + this.#watchSignal(); + for (;;) { + if (this.#halted()) return undefined; + const bytes = await this.#readChunk(); + if (bytes === undefined) return undefined; + for (const data of this.#parser.parse(bytes)) { + if (this.#halted()) return undefined; + if (data === this.#sentinel) return undefined; + yield data; + } + } + } finally { + void this.#release(undefined); + } + } + + #halted(): boolean { + if (this.#failure !== undefined) throw this.#failure.error; + return this.#released !== undefined; + } + + async #readChunk(): Promise { + const body = this.#response.body; + if (body === null) return undefined; + let result: ReadResult | undefined; + let fault: unknown; + this.#clock.start(); + try { + result = await (this.#reader ??= body.getReader()).read(); + } catch (error) { + fault = error; + } finally { + this.#clock.pause(); + } + if (this.#halted()) return undefined; + const lapsed = this.#clock.take(); + if (lapsed !== undefined) throw lapsed; + if (result === undefined) { + const signal = this.#signal; + throw signal.aborted && fault === signal.reason ? fault : this.#readError(fault); + } + return result.done ? undefined : result.value; + } + + #watchSignal(): void { + const signal = this.#signal; + if (this.#released !== undefined) return; + if (signal.aborted) { + void this.#release({ error: signal.reason }); + return; + } + const onAbort = (): void => { + void this.#release({ error: signal.reason }); + }; + this.#onAbort = onAbort; + signal.addEventListener("abort", onAbort, { once: true }); + } + + #lapse(bound: number): TimeoutError { + const error = new TimeoutError( + `${this.#method} ${this.#uri} failed: Event stream was idle for ${bound}ms.`, + { + method: this.#method, + uri: this.#uri, + timeout: bound, + }, + ); + void this.#drop(error); + return error; + } + + #release(failure: Failure | undefined): Promise { + if (this.#released !== undefined) return this.#released; + this.#failure = failure; + this.#clock.stop(); + if (this.#onAbort !== undefined) this.#signal.removeEventListener("abort", this.#onAbort); + this.#onAbort = undefined; + void this.#drop(failure?.error); + this.#released = Promise.resolve(); + return this.#released; + } + + #drop(reason: unknown): Promise { + const reader = this.#reader; + const cancelled = Promise.resolve( + reader === undefined ? this.#response.body?.cancel(reason) : reader.cancel(reason), + ).catch(() => {}); + this.#abort(); + return cancelled; + } + + #readError(error: unknown): ConnectionError { + return new ConnectionError(`${this.#method} ${this.#uri} failed: Event stream could not be read.`, { + method: this.#method, + uri: this.#uri, + cause: error, + }); + } +} + +class IdleClock { + readonly #bound: number | null; + readonly #lapse: (bound: number) => TimeoutError; + #timer: ReturnType | undefined; + #waitingSince: number | undefined; + #lapsed: TimeoutError | undefined; + + constructor(bound: number | null, lapse: (bound: number) => TimeoutError) { + this.#bound = bound; + this.#lapse = lapse; + } + + start(): void { + const bound = this.#bound; + if (bound === null) return; + this.#waitingSince = performance.now(); + this.#timer ??= setTimeout(() => this.#tick(bound), bound); + } + + pause(): void { + this.#waitingSince = undefined; + } + + stop(): void { + clearTimeout(this.#timer); + this.#timer = undefined; + } + + take(): TimeoutError | undefined { + const lapsed = this.#lapsed; + this.#lapsed = undefined; + return lapsed; + } + + #tick(bound: number): void { + this.#timer = undefined; + const since = this.#waitingSince; + if (since === undefined) return; + const remaining = bound - (performance.now() - since); + if (remaining > 0) { + this.#timer = setTimeout(() => this.#tick(bound), Math.ceil(remaining)); + return; + } + this.#lapsed = this.#lapse(bound); + } +} + +class EventStreamParser { + readonly #decoder = new TextDecoder("utf-8", { ignoreBOM: true }); + #started = false; + #rest = ""; + + parse(bytes: Uint8Array): readonly string[] { + const chunk = this.#decode(bytes); + const text = this.#rest === "" ? chunk : this.#rest + chunk; + const frames: string[] = []; + let blockStart = 0; + let data = ""; + let position = 0; + let lineFeed = text.indexOf(LINE_FEED); + let carriageReturn = text.indexOf(CARRIAGE_RETURN); + while (lineFeed !== -1 || carriageReturn !== -1) { + const end = + lineFeed === -1 || (carriageReturn !== -1 && carriageReturn < lineFeed) ? carriageReturn : lineFeed; + const line = text.slice(position, end); + position = end + 1; + if (end === carriageReturn && text.charCodeAt(position) === LINE_FEED_CODE) position += 1; + if (line === "") { + if (data !== "") frames.push(data.slice(0, -1)); + data = ""; + blockStart = position; + } else { + const value = this.#dataOf(line); + if (value !== undefined) data += value + LINE_FEED; + } + if (lineFeed !== -1 && lineFeed < position) lineFeed = text.indexOf(LINE_FEED, position); + if (carriageReturn !== -1 && carriageReturn < position) + carriageReturn = text.indexOf(CARRIAGE_RETURN, position); + } + this.#rest = text.slice(blockStart); + return frames; + } + + #dataOf(line: string): string | undefined { + const colon = line.indexOf(":"); + if (colon === -1) return line === DATA_FIELD ? "" : undefined; + if (colon !== DATA_FIELD.length || !line.startsWith(DATA_FIELD)) return undefined; + return line.slice(line.charCodeAt(colon + 1) === SPACE_CODE ? colon + 2 : colon + 1); + } + + #decode(bytes: Uint8Array): string { + const text = this.#decoder.decode(bytes, { stream: true }); + if (this.#started || text === "") return text; + this.#started = true; + return text.charCodeAt(0) === BYTE_ORDER_MARK_CODE ? text.slice(1) : text; + } +} diff --git a/plugins/authlete/sdk/typescript/src/core/event-stream.ts b/plugins/authlete/sdk/typescript/src/core/event-stream.ts new file mode 100644 index 00000000..365de8dc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/event-stream.ts @@ -0,0 +1,143 @@ +import * as s from "./validation/index.js"; +import type { Schema } from "./validation/schema.js"; +import { ConfigurationError } from "./errors.js"; + +const MAX_TIMEOUT_MS = 2_147_483_647; +const DEFAULT_EVENT_STREAM_OPTIONS: ResolvedEventStreamOptions = { + idleTimeout: 60_000, +}; + +type AsyncDisposeKey = SymbolConstructor extends { readonly asyncDispose: infer K extends symbol } + ? K + : never; + +type AsyncDisposeMember = { readonly [K in AsyncDisposeKey]: () => Promise }; + +const AsyncDisposable = class {} as new () => AsyncDisposeMember; + +/** + * A server-sent event stream you own: the still-open response, read one event at a time. + * + * @remarks + * The operation resolves as soon as the response headers arrive. Iterate the stream once with + * `for await`, or release it unread with `close()`. Each step is one event's `data`, decoded + * through the schema the operation declares, and nothing is read ahead of your loop. Leaving the + * loop early releases the connection. Aborting the call's `signal` ends the loop with the signal's + * own `reason`, and every other failure is a `CoreError`. Where the platform supports + * `await using`, disposing the stream releases the connection too. + */ +export class EventStream extends AsyncDisposable implements AsyncIterable { + readonly #close: () => Promise; + readonly #events: AsyncGenerator; + #taken = false; + + /** @internal Engine-only: the stream decoders in `response-decoder.ts` build it. */ + constructor(close: () => Promise, events: AsyncGenerator) { + super(); + this.#close = close; + this.#events = events; + } + + /** + * The events' data, in order. Callable once: a second call throws a `TypeError`, as a second read + * of a web stream does. + */ + [Symbol.asyncIterator](): AsyncIterator { + return this.#take(); + } + + /** Releases the connection, iterated or not. Safe to call more than once. */ + close(): Promise { + return this.#close(); + } + + /** + * The same events as a web `ReadableStream`, pulled one at a time; cancelling it releases the + * connection. It takes the stream's one iteration. + */ + toReadableStream(): ReadableStream { + return pullStream(this.#take()); + } + + #take(): EventIterator { + if (this.#taken) { + throw new TypeError("An event stream can be read once, and this one is already being read."); + } + this.#taken = true; + const events = this.#events; + return { + next: () => events.next(), + return: () => { + void this.#close(); + return events.return(undefined); + }, + }; + } +} + +const asyncDispose: unknown = Reflect.get(Symbol, "asyncDispose"); +if (typeof asyncDispose === "symbol") { + Object.defineProperty(EventStream.prototype, asyncDispose, { + value(this: EventStream): Promise { + return this.close(); + }, + }); +} + +/** + * How an event stream is watched for a silent server. + * + * @remarks + * Resolved once, when the client is built. A field left out takes its default. + */ +export type EventStreamOptions = { + /** + * Milliseconds the server may stay silent while your loop waits for the next event. When it runs + * out the loop ends with a `TimeoutError`. `null` disables it. + * + * @default 60000 + */ + readonly idleTimeout?: number | null; +}; + +export type ResolvedEventStreamOptions = Required; + +export function buildEventStreamOptions(options: EventStreamOptions | undefined): ResolvedEventStreamOptions { + const schema: Schema = s.of( + s.fallback( + s.object({ + idleTimeout: s.fallback( + s.nullable(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS))), + DEFAULT_EVENT_STREAM_OPTIONS.idleTimeout, + ), + }), + DEFAULT_EVENT_STREAM_OPTIONS, + ), + ); + try { + return schema.decode(options); + } catch (err) { + throw new ConfigurationError("The event stream options could not be resolved.", { cause: err }); + } +} + +type EventIterator = { + next(): Promise>; + return(): Promise>; +}; + +function pullStream(iterator: EventIterator): ReadableStream { + return new ReadableStream( + { + pull: async (controller) => { + const next = await iterator.next(); + if (next.done === true) controller.close(); + else controller.enqueue(next.value); + }, + cancel: async () => { + await iterator.return(); + }, + }, + { highWaterMark: 0 }, + ); +} diff --git a/plugins/authlete/sdk/typescript/src/core/headers.ts b/plugins/authlete/sdk/typescript/src/core/headers.ts new file mode 100644 index 00000000..9309491b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/headers.ts @@ -0,0 +1,91 @@ +import type { ApiRequest, RawClientOptions } from "./api-request.js"; +import { EncodeError } from "./errors.js"; +import type { Param, ParamPair } from "./param-value.js"; +import { encodedParam, flattenValue } from "./param-value.js"; +import { percentEncode } from "./params.js"; +import type { BodyContent } from "./request-body.js"; +import { SchemaError } from "./validation/schema-error.js"; +import * as s from "./validation/index.js"; + +type HeaderDefaults = Pick; + +type HeadersRequest = Pick; + +type ContentHeaders = Pick; + +export class HeadersBuilder { + readonly #defaultHeaders: readonly Param[]; + + constructor(defaults: HeaderDefaults) { + this.#defaultHeaders = defaults.defaultHeaders; + } + + build({ method, headers }: HeadersRequest, uri: string, body: ContentHeaders): RequestHeaders { + try { + return new RequestHeaders(foldHeaders([contentHeaders(body), this.#defaultHeaders, headers])); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new EncodeError(`${method} ${uri} failed: Request header could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } +} + +export class RequestHeaders { + readonly #headers: Headers; + + constructor(headers: Headers) { + this.#headers = headers; + } + + withCredential(headers: readonly ParamPair[], cookies: readonly ParamPair[]): Headers { + const result = new Headers(this.#headers); + for (const [name, value] of headers) result.set(name, value); + + const cookie = mergeCookies(result.get("cookie"), cookies); + if (cookie !== undefined) result.set("cookie", cookie); + return result; + } +} + +const CONTENT_HEADER = s.optional(s.string()); + +function contentHeaders({ contentType, contentDisposition }: ContentHeaders): Param[] { + return [ + { name: "content-type", value: contentType, schema: CONTENT_HEADER }, + { name: "content-disposition", value: contentDisposition, schema: CONTENT_HEADER }, + ]; +} + +function foldHeaders(layers: ReadonlyArray): Headers { + const headers = new Headers(); + for (const layer of layers) { + for (const header of layer) { + const value = encodedParam(header); + if (value === undefined) continue; + const parts = flattenValue(value); + if (parts.length === 0) headers.delete(header.name); + else headers.set(header.name, parts.join(",")); + } + } + return headers; +} + +function mergeCookies(existing: string | null, cookies: readonly ParamPair[]): string | undefined { + if (cookies.length === 0) return existing ?? undefined; + + const pairs = new Map(); + for (const part of (existing ?? "").split(";")) { + const trimmed = part.trim(); + const separator = trimmed.indexOf("="); + if (separator <= 0) continue; + pairs.set(trimmed.slice(0, separator), trimmed.slice(separator + 1)); + } + for (const [name, value] of cookies) pairs.set(name, percentEncode(value)); + + const merged = [...pairs].map(([name, value]) => `${name}=${value}`).join("; "); + return merged === "" ? undefined : merged; +} diff --git a/plugins/authlete/sdk/typescript/src/core/http-logger.ts b/plugins/authlete/sdk/typescript/src/core/http-logger.ts new file mode 100644 index 00000000..b7fc6e18 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/http-logger.ts @@ -0,0 +1,338 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConfigurationError } from "./errors.js"; +import { + logLevels, + type HttpResponseDetails, + type Logger, + type LogLevel, + type RequestLoggingOptions, + type ResolvedLoggingOptions, +} from "./logging.js"; +import type { RetryAttempt } from "./retry.js"; + +const SAFE_HEADERS: ReadonlySet = new Set([ + "accept", + "accept-charset", + "accept-encoding", + "accept-language", + "accept-ranges", + "age", + "allow", + "cache-control", + "connection", + "content-disposition", + "content-encoding", + "content-language", + "content-length", + "content-md5", + "content-range", + "content-type", + "date", + "etag", + "expect", + "expires", + "host", + "idempotency-key", + "if-match", + "if-modified-since", + "if-none-match", + "if-range", + "if-unmodified-since", + "keep-alive", + "last-modified", + "max-forwards", + "origin", + "pragma", + "range", + "ratelimit-limit", + "ratelimit-policy", + "ratelimit-remaining", + "ratelimit-reset", + "request-id", + "retry-after", + "server", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "user-agent", + "vary", + "via", + "warning", + "www-authenticate", + "traceparent", + "tracestate", + "x-correlation-id", + "x-ratelimit-limit", + "x-ratelimit-remaining", + "x-ratelimit-reset", + "x-request-id", + "x-apimatic-lang", + "x-apimatic-package-version", + "x-apimatic-gen-version", + "x-apimatic-os", + "x-apimatic-runtime", +]); + +const SAFE_QUERY_KEYS: ReadonlySet = new Set([ + "cursor", + "dates", + "datetime", + "limit", + "offset", + "page", + "since", + "size", + "strings", +]); + +export class HttpLogger { + readonly #logger: Logger; + readonly #logsInfo: boolean; + readonly #logsWarn: boolean; + readonly #logsError: boolean; + readonly #logsRequestHeaders: boolean; + readonly #logsRequestBody: boolean; + readonly #logsResponseHeaders: boolean; + readonly #loggableContentTypes: readonly string[]; + readonly #bodySizeLimit: number; + readonly #redaction: Redaction; + readonly #method: HttpMethod; + readonly #url: URL; + #sentAt = 0; + + constructor( + requestLoggingOptions: RequestLoggingOptions | undefined, + loggingOptions: ResolvedLoggingOptions, + method: HttpMethod, + url: URL, + ) { + this.#method = method; + this.#url = url; + const { level, logRequestHeaders, logResponseHeaders, logRequestBody } = applyRequestLoggingOptions( + requestLoggingOptions, + loggingOptions, + ); + this.#logger = loggingOptions.logger; + this.#logsInfo = enables(level, "info"); + this.#logsWarn = enables(level, "warn"); + this.#logsError = enables(level, "error"); + this.#logsRequestHeaders = enables(level, "debug") && logRequestHeaders; + this.#logsRequestBody = enables(level, "debug") && logRequestBody; + this.#logsResponseHeaders = enables(level, "debug") && logResponseHeaders; + this.#loggableContentTypes = loggingOptions.loggableContentTypes.map((type) => type.toLowerCase()); + this.#bodySizeLimit = loggingOptions.bodySizeLimit; + this.#redaction = new Redaction(loggingOptions); + } + + async logRequest(request: Request): Promise { + if (this.#logsInfo) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + this.#logger.info(`sending ${method} ${href}`, { event: "HttpRequest", method, url: href }); + } + + if (this.#logsRequestHeaders) { + this.#logger.debug("request headers", { + ...this.#redaction.redactHeaders(request.headers), + event: "RequestHeaders", + }); + } + + if (this.#logsRequestBody && request.body !== null) { + const mediaType = mediaTypeOf(request.headers.get("content-type")); + if (mediaType !== "" && this.#loggableContentTypes.some((type) => mediaType.startsWith(type))) { + this.#logger.debug("request body", { + event: "RequestBody", + mediaType, + body: await this.#renderBody(request, mediaType), + }); + } + } + this.#sentAt = Date.now(); + } + + logResponse(response: Response): void { + if (response.ok ? this.#logsInfo : this.#logsWarn) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const { status } = response; + const durationMs = Date.now() - this.#sentAt; + const details: HttpResponseDetails = { event: "HttpResponse", method, url: href, status, durationMs }; + if (response.ok) { + this.#logger.info(`${method} ${href} succeeded with status ${status} in ${durationMs}ms`, details); + } else { + this.#logger.warn(`${method} ${href} failed with status ${status} in ${durationMs}ms`, details); + } + } + + if (this.#logsResponseHeaders) { + this.#logger.debug("response headers", { + ...this.#redaction.redactHeaders(response.headers), + event: "ResponseHeaders", + }); + } + } + + logRetry(retry: RetryAttempt, maxRetries: number): void { + if (this.#logsWarn) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const { reason } = retry; + this.#logger.warn( + `${method} ${href} retrying in ${retry.delay}ms (retry ${retry.attemptNumber} of ${maxRetries}) after ${ + reason.kind === "status" ? `status ${reason.status}` : reason.error.name + }`, + { + event: "HttpRetrying", + method, + url: href, + delayMs: retry.delay, + retry: retry.attemptNumber, + maxRetries, + ...(reason.kind === "status" + ? { reason: "status", status: reason.status } + : { reason: "fault", errorName: reason.error.name }), + }, + ); + } + } + + logFailure(error: Error, startedAt: number): void { + if (this.#logsError) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const durationMs = Date.now() - startedAt; + this.#logger.error(`${method} ${href} failed after ${durationMs}ms: ${error.name}`, { + event: "HttpFailed", + method, + url: href, + durationMs, + errorName: error.name, + }); + } + } + + async #renderBody(request: Request, mediaType: string): Promise { + const body = await request.clone().text(); + const redacted = + mediaType === "application/x-www-form-urlencoded" ? this.#redaction.redactFormBody(body) : body; + return truncateToBytes(redacted, this.#bodySizeLimit); + } +} + +class Redaction { + readonly placeholder: string; + readonly #redactedHeaders: ReadonlySet; + readonly #unmaskedHeaders: ReadonlySet; + readonly #redactedKeys: ReadonlySet; + + constructor(options: ResolvedLoggingOptions) { + this.placeholder = options.redactionPlaceholder; + this.#redactedHeaders = new Set(options.redactedHeaders.map((name) => name.toLowerCase())); + this.#unmaskedHeaders = new Set(options.unmaskHeaders.map((name) => name.toLowerCase())); + this.#redactedKeys = new Set(options.redactedKeys.map((key) => key.toLowerCase())); + } + + redactHeaders(headers: Headers): Record { + const redacted: Record = {}; + for (const [name, value] of headers) { + if (this.#isRedactedHeader(name)) { + redacted[name] = this.placeholder; + continue; + } + const previous = redacted[name]; + redacted[name] = previous === undefined ? value : `${previous}, ${value}`; + } + return redacted; + } + + redactUrl(url: URL): string { + const query = maskPairs(url.search.slice(1), this.placeholder, (key) => this.#isRedactedQueryKey(key)); + return `${url.origin}${url.pathname}${query === "" ? "" : `?${query}`}${url.hash}`; + } + + redactFormBody(body: string): string { + return maskPairs(body, this.placeholder, (key) => this.isRedactedKey(key)); + } + + isRedactedKey(key: string): boolean { + return this.#redactedKeys.has(key.toLowerCase()); + } + + #isRedactedHeader(name: string): boolean { + if (this.#unmaskedHeaders.has(name)) return false; + return this.#redactedHeaders.has(name) || !SAFE_HEADERS.has(name); + } + + #isRedactedQueryKey(key: string): boolean { + return this.isRedactedKey(key) || !SAFE_QUERY_KEYS.has(key.toLowerCase()); + } +} + +type AppliedLoggingOptions = Pick< + ResolvedLoggingOptions, + "level" | "logRequestHeaders" | "logResponseHeaders" | "logRequestBody" +>; + +function applyRequestLoggingOptions( + requestLoggingOptions: RequestLoggingOptions | undefined, + loggingOptions: ResolvedLoggingOptions, +): AppliedLoggingOptions { + let override: LogLevel | undefined; + try { + override = logLevels.find((candidate) => candidate === requestLoggingOptions?.level); + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } + if (override === undefined) return loggingOptions; + return { level: override, logRequestHeaders: true, logResponseHeaders: true, logRequestBody: true }; +} + +function enables(level: LogLevel, threshold: LogLevel): boolean { + return logLevels.indexOf(level) >= logLevels.indexOf(threshold); +} + +function mediaTypeOf(contentType: string | null): string { + if (contentType === null) return ""; + return (contentType.split(";")[0] ?? "").trim().toLowerCase(); +} + +function truncateToBytes(body: string, limit: number): string { + const totalBytes = new TextEncoder().encode(body).length; + if (totalBytes <= limit) return body; + let bytesUsed = 0; + let charsKept = 0; + for (const char of body) { + const code = char.codePointAt(0) ?? 0; + const charBytes = code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4; + if (bytesUsed + charBytes > limit) break; + bytesUsed += charBytes; + charsKept += char.length; + } + return `${body.slice(0, charsKept)} …(${totalBytes} bytes total)`; +} + +function maskPairs(pairs: string, placeholder: string, isRedactedKey: (key: string) => boolean): string { + if (pairs === "") return pairs; + return pairs + .split("&") + .map((pair) => { + const equals = pair.indexOf("="); + if (equals === -1) return pair; + const rawKey = pair.slice(0, equals); + return isRedactedKey(paramName(rawKey)) ? `${rawKey}=${placeholder}` : pair; + }) + .join("&"); +} + +function paramName(rawKey: string): string { + let key: string; + try { + key = decodeURIComponent(rawKey.replaceAll("+", " ")); + } catch { + key = rawKey; + } + const bracket = key.indexOf("["); + return bracket === -1 ? key : key.slice(0, bracket); +} diff --git a/plugins/authlete/sdk/typescript/src/core/logging.ts b/plugins/authlete/sdk/typescript/src/core/logging.ts new file mode 100644 index 00000000..cace467d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/logging.ts @@ -0,0 +1,206 @@ +import { ConfigurationError } from "./errors.js"; +import * as host from "./runtime-environment.js"; +import * as s from "./validation/index.js"; + +export const logLevels = ["off", "error", "warn", "info", "debug"] as const; + +/** The built-in defaults behind {@link LoggingOptions}; spread a list to extend it. */ +export const defaultLoggingOptions: ResolvedLoggingOptions = Object.freeze({ + logger: console, + level: "off", + logRequestHeaders: false, + logResponseHeaders: false, + logRequestBody: false, + bodySizeLimit: 32_768, + loggableContentTypes: Object.freeze(["application/json", "application/x-www-form-urlencoded"]), + redactedHeaders: Object.freeze([]), + unmaskHeaders: Object.freeze([]), + redactedKeys: Object.freeze([ + "sig", + "signature", + "access_token", + "apikey", + "api_key", + "client_secret", + "password", + "refresh_token", + "code", + "assertion", + "client_assertion", + ]), + redactionPlaceholder: "***", +}); + +/** How the client logs its HTTP traffic, set through `ClientOptions.logging`. */ +export type LoggingOptions = { + readonly logger?: Logger; + readonly level?: LogLevel; + readonly logRequestHeaders?: boolean; + readonly logResponseHeaders?: boolean; + readonly logRequestBody?: boolean; + readonly bodySizeLimit?: number; + readonly loggableContentTypes?: readonly string[]; + readonly redactedHeaders?: readonly string[]; + readonly unmaskHeaders?: readonly string[]; + readonly redactedKeys?: readonly string[]; + readonly redactionPlaceholder?: string; +}; + +/** The part of {@link LoggingOptions} a call may override through `RequestOptions.logging`. */ +export type RequestLoggingOptions = Pick; + +/** Where the client writes its log lines; `console` already has this shape. */ +export type Logger = { + error(message: string, details: LogDetails): void; + warn(message: string, details: LogDetails): void; + info(message: string, details: LogDetails): void; + debug(message: string, details: LogDetails): void; +}; + +/** The structured fields passed with every log line; narrow on `event` to read the rest. */ +export type LogDetails = + | HttpRequestDetails + | RequestHeadersDetails + | RequestBodyDetails + | HttpResponseDetails + | ResponseHeadersDetails + | HttpRetryingDetails + | HttpFailedDetails; + +/** Logged at `info` as a request is sent. */ +export type HttpRequestDetails = { + readonly event: "HttpRequest"; + readonly method: string; + readonly url: string; +}; + +/** Logged at `debug` with a request's headers, one field per header. */ +export type RequestHeadersDetails = { + readonly event: "RequestHeaders"; + readonly [header: string]: string; +}; + +/** Logged at `debug` with a request's body. */ +export type RequestBodyDetails = { + readonly event: "RequestBody"; + readonly mediaType: string; + readonly body: string; +}; + +/** Logged at `info` when a response arrives, or `warn` for an error status. */ +export type HttpResponseDetails = { + readonly event: "HttpResponse"; + readonly method: string; + readonly url: string; + readonly status: number; + readonly durationMs: number; +}; + +/** Logged at `debug` with a response's headers, one field per header. */ +export type ResponseHeadersDetails = { + readonly event: "ResponseHeaders"; + readonly [header: string]: string; +}; + +/** Logged at `warn` before a failed attempt is retried. */ +export type HttpRetryingDetails = { + readonly event: "HttpRetrying"; + readonly method: string; + readonly url: string; + readonly delayMs: number; + readonly retry: number; + readonly maxRetries: number; +} & ( + | { readonly reason: "status"; readonly status: number } + | { readonly reason: "fault"; readonly errorName: string } +); + +/** Logged at `error` when a call ends without a response. */ +export type HttpFailedDetails = { + readonly event: "HttpFailed"; + readonly method: string; + readonly url: string; + readonly durationMs: number; + readonly errorName: string; +}; + +export type LogSeverity = "error" | "warn" | "info" | "debug"; + +/** The most detailed level the client logs, or `"off"` to log nothing. */ +export type LogLevel = LogSeverity | "off"; + +export type ResolvedLoggingOptions = Required; + +export function buildLoggingOptions( + options: LoggingOptions | undefined, + environmentVariable: string, +): ResolvedLoggingOptions { + let logger: Logger | undefined; + let levelInCode: boolean; + try { + logger = options?.logger; + levelInCode = logLevels.some((level) => level === options?.level); + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } + if (logger !== undefined) { + return validateLoggingOptions(options, { ...defaultLoggingOptions, level: "debug" }); + } + if (levelInCode) { + return validateLoggingOptions(options, defaultLoggingOptions); + } + const level = environmentLogLevel(environmentVariable); + if (level === undefined) return validateLoggingOptions(options, defaultLoggingOptions); + const headers = level === "debug"; + return validateLoggingOptions(options, { + ...defaultLoggingOptions, + level, + logRequestHeaders: headers, + logResponseHeaders: headers, + }); +} + +function validateLoggingOptions( + options: LoggingOptions | undefined, + fallbacks: Omit, +): ResolvedLoggingOptions { + const schema = s.of( + s.fallback( + s.object>({ + level: s.fallback(s.literal(logLevels), fallbacks.level), + logRequestHeaders: s.fallback(s.boolean(), fallbacks.logRequestHeaders), + logResponseHeaders: s.fallback(s.boolean(), fallbacks.logResponseHeaders), + logRequestBody: s.fallback(s.boolean(), fallbacks.logRequestBody), + bodySizeLimit: s.fallback(s.int().check(s.gte(0)), fallbacks.bodySizeLimit), + loggableContentTypes: s.fallback(s.array(s.string()), fallbacks.loggableContentTypes), + redactedHeaders: s.fallback(s.array(s.string()), fallbacks.redactedHeaders), + unmaskHeaders: s.fallback(s.array(s.string()), fallbacks.unmaskHeaders), + redactedKeys: s.fallback(s.array(s.string()), fallbacks.redactedKeys), + redactionPlaceholder: s.fallback(s.string(), fallbacks.redactionPlaceholder), + }), + fallbacks, + ), + ); + try { + const logger = options?.logger; + return { + ...schema.decode(options), + logger: logger === undefined ? defaultLoggingOptions.logger : logger, + }; + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } +} + +function environmentLogLevel(name: string): LogLevel | undefined { + const value = host.environmentVariable(name); + if (value === undefined) return undefined; + const level = logLevels.find((candidate) => candidate === value.toLowerCase()); + if (level === undefined) { + console.warn( + `${name} is set to ${JSON.stringify(value)}, which is not a log level. ` + + `Expected one of: ${logLevels.join(", ")}. Logging stays off.`, + ); + } + return level; +} diff --git a/plugins/authlete/sdk/typescript/src/core/param-value.ts b/plugins/authlete/sdk/typescript/src/core/param-value.ts new file mode 100644 index 00000000..456fa994 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/param-value.ts @@ -0,0 +1,104 @@ +import type { Entry } from "./validation/schema.js"; +import { encodeEntry } from "./validation/schema-error.js"; + +export type ParamStyle = "plain" | "indexed" | "unindexed" | "csv" | "tsv" | "psv"; + +export type ParamValue = + | string + | number + | boolean + | bigint + | null + | undefined + | readonly ParamValue[] + | { readonly [key: string]: ParamValue }; + +export type Param = { + readonly name: Name; + readonly value: unknown; + readonly schema: Entry; +}; + +export type StyledParam = Param & { readonly style?: ParamStyle | undefined }; + +export type ParamPair = readonly [name: string, value: string]; + +const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER); + +export function flattenValue(value: ParamValue): string[] { + if (value === undefined || value === null) return []; + if (isScalar(value)) return [scalar(value)]; + if (!Array.isArray(value)) return [jsonValue(value)]; + + const folded: string[] = []; + for (const element of value) { + if (element === undefined || element === null) folded.push(""); + else if (isScalar(element)) folded.push(scalar(element)); + else return [jsonValue(value)]; + } + return folded; +} + +export function encodedParam(source: Param): ParamValue { + return encodeEntry(source.schema, source.value) as ParamValue; +} + +export function flattenParams(sources?: ReadonlyArray): ParamPair[] { + return (sources ?? []).flatMap((source) => + flattenParam(source.name, encodedParam(source), source.style ?? "plain"), + ); +} + +function flattenParam(key: string, value: ParamValue, style: ParamStyle): ParamPair[] { + if (value === undefined || value === null) return []; + if (isScalar(value)) return [[key, scalar(value)]]; + if (Array.isArray(value)) return flattenArray(key, value, style); + return Object.entries(value).flatMap(([sub, subValue]) => flattenParam(`${key}[${sub}]`, subValue, style)); +} + +function flattenArray(key: string, values: readonly ParamValue[], style: ParamStyle): ParamPair[] { + const present = values.filter((value) => value !== undefined && value !== null); + switch (style) { + case "plain": + return present.flatMap((value) => flattenParam(key, value, style)); + case "indexed": + return values.flatMap((value, index) => flattenParam(`${key}[${index}]`, value, style)); + case "unindexed": + return present.flatMap((value) => flattenParam(`${key}[]`, value, style)); + case "csv": + return [joined(key, values, ",")]; + case "tsv": + return [joined(key, values, "\t")]; + case "psv": + return [joined(key, values, "|")]; + default: + return unknownStyle(style); + } +} + +function joined(key: string, values: readonly ParamValue[], separator: string): ParamPair { + return [key, flattenValue(values).join(separator)]; +} + +function unknownStyle(style: never): never { + throw new TypeError(`Unknown parameter style: ${String(style)}.`); +} + +export function isScalar(value: ParamValue): value is string | number | boolean | bigint { + if (value === undefined || value === null) return false; + return typeof value !== "object"; +} + +export function scalar(value: string | number | boolean | bigint): string { + return String(value); +} + +export function jsonValue(value: ParamValue): string { + return JSON.stringify(value, bigintReplacer); +} + +function bigintReplacer(_key: string, value: unknown): unknown { + if (typeof value !== "bigint") return value; + if (value <= MAX_SAFE_BIGINT && value >= -MAX_SAFE_BIGINT) return Number(value); + return String(value); +} diff --git a/plugins/authlete/sdk/typescript/src/core/params.ts b/plugins/authlete/sdk/typescript/src/core/params.ts new file mode 100644 index 00000000..ab2904b7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/params.ts @@ -0,0 +1,25 @@ +import type { ParamPair, StyledParam } from "./param-value.js"; +import { flattenParams } from "./param-value.js"; + +export function percentEncode(value: string): string { + return encodeURIComponent(value).replace( + /[!'()*]/g, + (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`, + ); +} + +export function formEncode(value: string): string { + return percentEncode(value).replace(/%20/g, "+"); +} + +export function queryString(layers: ReadonlyArray): string { + return serializePairs(layers.flatMap(flattenParams), percentEncode); +} + +export function formString(fields?: ReadonlyArray): string { + return serializePairs(flattenParams(fields), formEncode); +} + +export function serializePairs(pairs: ReadonlyArray, encode: (value: string) => string): string { + return pairs.map(([key, value]) => `${encode(key)}=${encode(value)}`).join("&"); +} diff --git a/plugins/authlete/sdk/typescript/src/core/raw-client.ts b/plugins/authlete/sdk/typescript/src/core/raw-client.ts new file mode 100644 index 00000000..15587535 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/raw-client.ts @@ -0,0 +1,191 @@ +import type { ApiRequest, HttpMethod, RequestOptions, RawClientOptions } from "./api-request.js"; +import { CoreError, ConnectionError, TimeoutError } from "./errors.js"; +import type { ApiError } from "./api-error.js"; +import { ResponseReader, type ResponseHandler } from "./response-decoder.js"; +import { ApiPromise, type RequestOutcome } from "./api-promise.js"; +import { buildBody } from "./request-body.js"; +import { RetryPolicy } from "./retry.js"; +import { UrlBuilder } from "./url.js"; +import { HeadersBuilder } from "./headers.js"; +import { HttpLogger } from "./http-logger.js"; +import { authenticateRequest } from "./auth/schemes.js"; + +export class RawClient { + readonly #config: RawClientOptions; + readonly #urlBuilder: UrlBuilder; + readonly #headersBuilder: HeadersBuilder; + readonly #responseReader: ResponseReader; + + constructor(config: RawClientOptions) { + this.#config = config; + this.#urlBuilder = new UrlBuilder(config); + this.#headersBuilder = new HeadersBuilder(config); + this.#responseReader = new ResponseReader(config); + } + + execute( + apiRequest: ApiRequest, + responseHandler: ResponseHandler, + options?: RequestOptions, + ): ApiPromise { + return new ApiPromise(this.#dispatch(apiRequest, responseHandler, options)); + } + + async #dispatch( + apiRequest: ApiRequest, + responseHandler: ResponseHandler, + options: RequestOptions | undefined, + ): Promise> { + const startedAt = Date.now(); + const callerSignal = options?.signal ?? neverAbortedSignal(); + const uploadController = new AbortController(); + const streamRequestController = new AbortController(); + const abortStreamRequest = (): void => streamRequestController.abort(); + + const url = this.#urlBuilder.build(apiRequest); + const { uri } = url; + + const body = buildBody( + apiRequest.method, + uri, + apiRequest.body, + AbortSignal.any([callerSignal, uploadController.signal]), + ); + + const logger = new HttpLogger( + options?.logging, + this.#config.logging, + apiRequest.method, + url.withQuery([]), + ); + const headers = this.#headersBuilder.build(apiRequest, uri, body); + const policy = new RetryPolicy(options?.retry, this.#config.retry); + const response = await policy.execute( + apiRequest.method, + callerSignal, + startedAt, + logger, + async (timeout) => { + const authenticated = await authenticateRequest( + apiRequest.method, + apiRequest.auth, + url, + headers, + callerSignal, + startedAt, + logger, + ); + + const isStreaming = body.streaming === true; + const timeoutController = new AbortController(); + const signal = AbortSignal.any([ + callerSignal, + timeoutController.signal, + streamRequestController.signal, + ]); + if (isStreaming) { + timeoutController.signal.addEventListener( + "abort", + () => uploadController.abort(timeoutController.signal.reason), + { once: true }, + ); + } + + const request = new Request(authenticated.url, { + method: apiRequest.method, + headers: authenticated.headers, + body: body.body, + signal, + ...(isStreaming ? { duplex: "half", window: null, redirect: "error" } : {}), + }); + await logger.logRequest(request); + + const timer = startTimeout(timeoutController, timeout, apiRequest.method, uri); + let response: Response; + try { + response = await this.#config.fetch.call(undefined, request); + } catch (err) { + if (err instanceof ConnectionError || err instanceof TimeoutError) + return { kind: "fault", error: err, isStreaming }; + if (err instanceof CoreError) throw err; + if (signal.reason instanceof TimeoutError) + return { kind: "fault", error: signal.reason, isStreaming }; + if (signal.aborted) throw signal.reason; + return { + kind: "fault", + error: new ConnectionError( + `${apiRequest.method} ${uri} failed: ${ + err instanceof Error && err.message !== "" ? err.message : "Connection error." + }`, + { cause: err, method: apiRequest.method, uri }, + ), + isStreaming, + }; + } finally { + clearTimeout(timer); + } + + const discard = (): void => { + void Promise.resolve(response.body?.cancel()).catch(() => {}); + }; + try { + logger.logResponse(response); + } catch (err) { + discard(); + throw err; + } + if (response.status === 401) apiRequest.auth.invalidate?.(); + return { kind: "response", response, isStreaming, discard }; + }, + ); + + if (isSuccess(response.status)) { + const data = await this.#responseReader.read( + responseHandler.success, + response, + apiRequest.method, + uri, + callerSignal, + abortStreamRequest, + ); + return { ok: true, status: response.status, headers: response.headers, data }; + } + + const error = await this.#responseReader.readError( + responseHandler.errorFactory, + response, + apiRequest.method, + uri, + callerSignal, + abortStreamRequest, + ); + return { ok: false, status: response.status, headers: response.headers, error }; + } +} + +function isSuccess(status: number): boolean { + return status >= 200 && status <= 299; +} + +function neverAbortedSignal(): AbortSignal { + return new AbortController().signal; +} + +function startTimeout( + controller: AbortController, + timeoutMs: number, + method: HttpMethod, + uri: string, +): ReturnType { + return setTimeout( + () => + controller.abort( + new TimeoutError(`${method} ${uri} failed: Request timed out after ${timeoutMs}ms.`, { + method, + uri, + timeout: timeoutMs, + }), + ), + timeoutMs, + ); +} diff --git a/plugins/authlete/sdk/typescript/src/core/request-body.ts b/plugins/authlete/sdk/typescript/src/core/request-body.ts new file mode 100644 index 00000000..9b871b7e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/request-body.ts @@ -0,0 +1,300 @@ +import type { HttpMethod } from "./api-request.js"; +import { + attachmentDisposition, + concatStream, + resolveFile, + type BufferedResolvedFile, + type ByteSource, + type FileInput, + type ResolvedFile, +} from "./binary.js"; +import { EncodeError } from "./errors.js"; +import type { Param, ParamValue, StyledParam } from "./param-value.js"; +import { flattenParams, isScalar, jsonValue, scalar } from "./param-value.js"; +import { formString } from "./params.js"; +import type { Entry } from "./validation/schema.js"; +import { SchemaError, encodeEntry } from "./validation/schema-error.js"; + +export type Json = string | number | boolean | null | readonly Json[] | JsonObject; + +export type JsonObject = { readonly [key: string]: Json | undefined }; + +export type ContentType = + | "application/x-www-form-urlencoded;charset=UTF-8" + | "application/json" + | "text/plain;charset=utf-8" + | "application/octet-stream" + | `multipart/form-data; boundary=${string}` + | (string & {}); + +export type BodyContent = { + body: string | FormData | Blob | Uint8Array | ReadableStream | null; + contentType?: ContentType; + contentDisposition?: string; + streaming?: true; +}; + +export type EmptyBody = { + readonly kind: "empty"; +}; + +export type JsonBody = { + readonly kind: "json"; + readonly value: unknown; + readonly schema: Entry; +}; + +export type FormUrlEncodedBody = { + readonly kind: "formUrlEncoded"; + readonly value: readonly StyledParam[]; +}; + +export type TextBody = { + readonly kind: "text"; + readonly value: unknown; + readonly schema: Entry; +}; + +export type MultipartTextPart = StyledParam & { + readonly kind: "text"; +}; + +export type MultipartJsonPart = Param & { + readonly kind: "json"; + readonly contentType: "application/json" | `application/${string}+json`; +}; + +export type MultipartFilePart = { + readonly kind: "file"; + readonly name: string; + readonly value: FileInput | readonly (FileInput | null | undefined)[] | null | undefined; + readonly contentType?: string | undefined; +}; + +export type MultipartPart = MultipartTextPart | MultipartJsonPart | MultipartFilePart; + +export type MultipartBody = { + readonly kind: "multipart"; + readonly value: readonly MultipartPart[]; +}; + +export type BinaryBody = { + readonly kind: "binary"; + readonly value: FileInput | null | undefined; + readonly contentType: string; +}; + +export type RequestBody = EmptyBody | JsonBody | FormUrlEncodedBody | TextBody | MultipartBody | BinaryBody; + +export function buildBody( + method: HttpMethod, + uri: string, + body: RequestBody, + signal?: AbortSignal, +): BodyContent { + switch (body.kind) { + case "empty": + return { body: null }; + case "json": + return buildJson(body, method, uri); + case "formUrlEncoded": + return buildFormUrlEncoded(body, method, uri); + case "text": + return buildText(body, method, uri); + case "multipart": + return buildMultipart(body, signal, method, uri); + case "binary": + return buildBinary(body, signal); + default: + return unknownBodyKind(body); + } +} + +function unknownBodyKind(body: never): never { + const kind = (body as { kind?: unknown }).kind; + throw new TypeError(`Unsupported request body kind: ${String(kind)}`); +} + +function encodeFailure(err: unknown, method: HttpMethod, uri: string): EncodeError { + if (!(err instanceof SchemaError)) throw err; + return new EncodeError(`${method} ${uri} failed: Request value could not be encoded.`, { + cause: err, + method, + uri, + }); +} + +function buildFormUrlEncoded(body: FormUrlEncodedBody, method: HttpMethod, uri: string): BodyContent { + let text: string; + try { + text = formString(body.value); + } catch (err) { + throw encodeFailure(err, method, uri); + } + return { body: text, contentType: "application/x-www-form-urlencoded;charset=UTF-8" }; +} + +function buildJson(body: JsonBody, method: HttpMethod, uri: string): BodyContent { + let encoded: unknown; + try { + encoded = encodeEntry(body.schema, body.value); + } catch (err) { + throw encodeFailure(err, method, uri); + } + const text = JSON.stringify(encoded); + if (text === undefined) return { body: null }; + return { body: text, contentType: "application/json" }; +} + +function buildBinary(body: BinaryBody, signal: AbortSignal | undefined): BodyContent { + if (body.value === undefined || body.value === null) return { body: null }; + + const file = resolveFile(body.value, body.contentType, signal); + const content: BodyContent = { body: file.body }; + if (file.contentType !== undefined) content.contentType = file.contentType; + const disposition = attachmentDisposition(file.fileName); + if (disposition !== undefined) content.contentDisposition = disposition; + if (file.streaming) content.streaming = true; + return content; +} + +function buildText(body: TextBody, method: HttpMethod, uri: string): BodyContent { + let encoded: ParamValue; + try { + encoded = encodeEntry(body.schema, body.value) as ParamValue; + } catch (err) { + throw encodeFailure(err, method, uri); + } + if (encoded === undefined) return { body: null }; + return { + body: isScalar(encoded) ? scalar(encoded) : jsonValue(encoded), + contentType: "text/plain;charset=utf-8", + }; +} + +type MultipartEntry = + | { readonly kind: "text"; readonly name: string; readonly value: string } + | { readonly kind: "json"; readonly name: string; readonly value: string; readonly contentType: string } + | { readonly kind: "file"; readonly name: string; readonly file: File }; + +function buildMultipart( + body: MultipartBody, + signal: AbortSignal | undefined, + method: HttpMethod, + uri: string, +): BodyContent { + let entries: readonly MultipartEntry[]; + try { + entries = body.value.flatMap(partEntries); + } catch (err) { + throw encodeFailure(err, method, uri); + } + if (allBuffered(entries)) return { body: toFormData(entries) }; + + const boundary = `----${crypto.randomUUID()}`; + return { + body: frameMultipart(entries, boundary, signal), + contentType: `multipart/form-data; boundary=${boundary}`, + streaming: true, + }; +} + +function partEntries(part: MultipartPart): readonly MultipartEntry[] { + switch (part.kind) { + case "text": + return flattenParams([part]).map(([name, value]) => ({ kind: "text", name, value })); + case "json": { + const encoded = encodeEntry(part.schema, part.value); + const entries: MultipartEntry[] = []; + for (const document of Array.isArray(encoded) ? encoded : [encoded]) { + const value = JSON.stringify(document); + if (value === undefined) continue; + entries.push({ kind: "json", name: part.name, value, contentType: part.contentType }); + } + return entries; + } + case "file": { + const entries: MultipartEntry[] = []; + for (const input of Array.isArray(part.value) ? part.value : [part.value]) { + if (input === undefined || input === null) continue; + entries.push({ kind: "file", name: part.name, file: resolveFile(input, part.contentType) }); + } + return entries; + } + default: + return unknownPartKind(part); + } +} + +function allBuffered( + entries: readonly MultipartEntry[], +): entries is readonly MultipartEntry[] { + return entries.every((entry) => entry.kind !== "file" || !entry.file.streaming); +} + +function toFormData(entries: readonly MultipartEntry[]): FormData { + const form = new FormData(); + for (const entry of entries) { + if (entry.kind === "text") { + form.append(entry.name, entry.value); + } else if (entry.kind === "json") { + form.append(entry.name, new Blob([entry.value], { type: entry.contentType }), ""); + } else { + const { body, contentType, fileName } = entry.file; + form.append(entry.name, new Blob([body], { type: contentType ?? "" }), fileName ?? ""); + } + } + return form; +} + +function frameMultipart( + entries: readonly MultipartEntry[], + boundary: string, + signal: AbortSignal | undefined, +): ReadableStream { + const encoder = new TextEncoder(); + const segments: ByteSource[] = []; + for (const entry of entries) { + const name = escapeParameter(normalizeNewlines(entry.name)); + const disposition = `--${boundary}\r\nContent-Disposition: form-data; name="${name}"`; + if (entry.kind === "text") { + segments.push(encoder.encode(`${disposition}\r\n\r\n${normalizeNewlines(entry.value)}\r\n`)); + } else if (entry.kind === "json") { + const contentType = partContentType(entry.contentType); + segments.push( + encoder.encode(`${disposition}\r\nContent-Type: ${contentType}\r\n\r\n${entry.value}\r\n`), + ); + } else { + const { body, contentType, fileName } = entry.file; + const filename = + fileName === undefined || fileName === "" ? "" : `; filename="${escapeParameter(fileName)}"`; + segments.push( + encoder.encode(`${disposition}${filename}\r\nContent-Type: ${partContentType(contentType)}\r\n\r\n`), + body, + encoder.encode("\r\n"), + ); + } + } + segments.push(encoder.encode(`--${boundary}--\r\n`)); + return concatStream(segments, signal); +} + +function normalizeNewlines(value: string): string { + return value.replace(/\r\n|\r|\n/g, "\r\n"); +} + +function escapeParameter(value: string): string { + return value.replaceAll('"', "%22").replaceAll("\r", "%0D").replaceAll("\n", "%0A"); +} + +const PRINTABLE_ASCII = /^[ -~]*$/; + +function partContentType(type: string | undefined): string { + if (type === undefined || type === "" || !PRINTABLE_ASCII.test(type)) return "application/octet-stream"; + return type.toLowerCase(); +} + +function unknownPartKind(part: never): never { + const kind = (part as { kind?: unknown }).kind; + throw new TypeError(`Unsupported multipart part kind: ${String(kind)}`); +} diff --git a/plugins/authlete/sdk/typescript/src/core/response-decoder.ts b/plugins/authlete/sdk/typescript/src/core/response-decoder.ts new file mode 100644 index 00000000..727116d2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/response-decoder.ts @@ -0,0 +1,402 @@ +import type { Entry } from "./validation/schema.js"; +import type { HttpMethod, RawClientOptions } from "./api-request.js"; +import type { BinaryContent, BinaryErrorContent } from "./binary.js"; +import { readBinary, readBinaryError } from "./binary.js"; +import type { ResolvedEventStreamOptions } from "./event-stream.js"; +import { EventStream } from "./event-stream.js"; +import { EventStreamReader } from "./event-stream-reader.js"; +import { CoreError, DecodeError } from "./errors.js"; +import { SchemaError, decodeEntry } from "./validation/schema-error.js"; +import type { ApiError, ApiErrorInit, Declared, Undeclared } from "./api-error.js"; + +export type JsonResponseDecoder = { + readonly kind: "json"; + readonly schema: Entry; +}; + +export type TextResponseDecoder = { + readonly kind: "text"; + readonly schema: Entry; +}; + +export type EmptyResponseDecoder = { + readonly kind: "empty"; +}; + +export type BinaryResponseDecoder = { + readonly kind: "binary"; + readonly contentType: string; +}; + +export type BinaryErrorResponseDecoder = { + readonly kind: "binaryError"; +}; + +export type EventStreamTextResponseDecoder = { + readonly kind: "eventStreamText"; + readonly dataSchema: Entry; + readonly sentinel?: string | undefined; +}; + +export type EventStreamJsonResponseDecoder = { + readonly kind: "eventStreamJson"; + readonly dataSchema: Entry; + readonly sentinel?: string | undefined; +}; + +export type EventStreamResponseDecoder = + | EventStreamTextResponseDecoder + | EventStreamJsonResponseDecoder; + +export type ResponseDecoder = + | JsonResponseDecoder + | TextResponseDecoder + | ([T] extends [undefined] ? EmptyResponseDecoder : never) + | ([T] extends [BinaryContent] ? BinaryResponseDecoder : never) + | ([T] extends [EventStream] ? EventStreamResponseDecoder : never); + +export type ErrorResponseDecoder = + | JsonResponseDecoder + | TextResponseDecoder + | ([T] extends [undefined] ? EmptyResponseDecoder : never) + | ([T] extends [BinaryErrorContent] ? BinaryErrorResponseDecoder : never); + +export type StatusPattern = + | number + | "1XX" + | "2XX" + | "3XX" + | "4XX" + | "5XX" + | "default" + | readonly [number, number]; + +/** + * The payload union an operation's error class declares. + * + * @remarks + * It is the type of `err.payload` on the thrown error and of `result.payload` on the + * `ok: false` arm of `ApiResult`, so a narrowing written for one works unchanged for the other. + */ +export type PayloadOf = E["payload"]; + +type BodyByKind = { + [Arm in Extract> as Arm["kind"]]: Arm["body"]; +}; + +type KindOf = keyof BodyByKind & string; + +export type ErrorMatcher = KindOf> = { + readonly on: StatusPattern; + readonly kind: K; + readonly decode: ErrorResponseDecoder[K]>; +}; + +type ErrorMatcherOf = { [K in KindOf]: ErrorMatcher }[KindOf]; + +type DeclaredArmOf = { + [K in KindOf]: Declared[K]>; +}[KindOf]; + +export type ErrorDecoders = readonly ErrorMatcherOf>[]; + +export type ErrorFactory = (new (init: ApiErrorInit>) => E) & { + readonly errors?: ErrorDecoders | undefined; +}; + +export type ResponseHandler = { + success: ResponseDecoder; + errorFactory: ErrorFactory; +}; + +type ReaderOptions = Pick; + +export class ResponseReader { + readonly #eventStreamOptions: ResolvedEventStreamOptions; + + constructor(options: ReaderOptions) { + this.#eventStreamOptions = options.eventStream; + } + + async read( + decoder: ResponseDecoder | ErrorResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise { + switch (decoder.kind) { + case "json": + return decodeJson(decoder, response, method, uri, signal); + case "text": + return decodeText(decoder, response, method, uri, signal); + case "empty": + return (await decodeEmpty(response, method, uri, signal)) as T; + case "binary": + return readBinary(decoder, response) as T; + case "binaryError": + return (await readBinaryError(response, method, uri, signal)) as T; + case "eventStreamText": + return readEventStreamText( + decoder, + response, + method, + uri, + signal, + this.#eventStreamOptions, + abort, + ) as T; + case "eventStreamJson": + return readEventStreamJson( + decoder, + response, + method, + uri, + signal, + this.#eventStreamOptions, + abort, + ) as T; + default: { + await response.body?.cancel().catch(() => {}); + return unknownDecoderKind(decoder); + } + } + } + + async readError( + factory: ErrorFactory, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise { + const matchers = factory.errors; + const status = response.status; + const matcher = + matchers?.find((m) => m.on === status) ?? + matchers?.find((m) => typeof m.on !== "number" && m.on !== "default" && matchesStatus(m.on, status)) ?? + matchers?.find((m) => m.on === "default"); + const payload: DeclaredArmOf> | Undeclared = matcher + ? await this.#declared(matcher, response, method, uri, signal, abort) + : { kind: "undeclared", rawBody: await readRawBody(response, method, uri, signal) }; + return new factory({ status, headers: response.headers, method, uri, payload }); + } + + async #declared>( + matcher: ErrorMatcher, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise[K]>> { + return { + kind: matcher.kind, + body: await this.read(matcher.decode, response, method, uri, signal, abort), + }; + } +} + +async function decodeJson( + decoder: JsonResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let text: string; + try { + text = await response.text(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return decodeJsonText(decoder.schema, text, "Response body", response, method, uri); +} + +async function decodeText( + decoder: TextResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let text: string; + try { + text = await response.text(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return decodeTextValue(decoder.schema, text, "Response body", response, method, uri); +} + +function decodeJsonText( + schema: Entry, + text: string, + subject: string, + response: Response, + method: HttpMethod, + uri: string, +): T { + let parsed: unknown; + try { + parsed = JSON.parse(text); + } catch (err) { + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be parsed as JSON.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + try { + return decodeEntry(schema, parsed); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be decoded.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +function decodeTextValue( + schema: Entry, + text: string, + subject: string, + response: Response, + method: HttpMethod, + uri: string, +): T { + try { + return decodeEntry(schema, text); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be decoded.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +async function decodeEmpty( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let bytes: ArrayBuffer; + try { + bytes = await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + if (bytes.byteLength > 0) + throw new DecodeError(`${method} ${uri} failed: Expected an empty response body.`, { + method, + uri, + status: response.status, + headers: response.headers, + }); + return undefined; +} + +async function readRawBody( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + try { + return await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +export function readEventStreamJson( + decoder: EventStreamJsonResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, +): EventStream { + const reader = new EventStreamReader(response, method, uri, signal, options, abort, decoder.sentinel); + const events = (async function* (): AsyncGenerator { + for await (const data of reader.frames()) { + yield decodeJsonText(decoder.dataSchema, data, "Event data", response, method, uri); + } + return undefined; + })(); + + return new EventStream(reader.close, events); +} + +export function readEventStreamText( + decoder: EventStreamTextResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, +): EventStream { + const reader = new EventStreamReader(response, method, uri, signal, options, abort, decoder.sentinel); + const events = (async function* (): AsyncGenerator { + for await (const data of reader.frames()) { + yield decodeTextValue(decoder.dataSchema, data, "Event data", response, method, uri); + } + return undefined; + })(); + + return new EventStream(reader.close, events); +} + +function matchesStatus(pattern: StatusPattern, status: number): boolean { + if (typeof pattern === "number") return pattern === status; + if (typeof pattern === "string") return `${Math.floor(status / 100)}XX` === pattern; + return status >= pattern[0] && status <= pattern[1]; +} + +function unknownDecoderKind(decoder: never): never { + const kind = (decoder as { kind?: unknown }).kind; + throw new TypeError(`Unsupported response decoder kind: ${String(kind)}`); +} diff --git a/plugins/authlete/sdk/typescript/src/core/retry.ts b/plugins/authlete/sdk/typescript/src/core/retry.ts new file mode 100644 index 00000000..70b59af5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/retry.ts @@ -0,0 +1,417 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConfigurationError, type ConnectionError, type TimeoutError } from "./errors.js"; +import type { HttpLogger } from "./http-logger.js"; +import * as s from "./validation/index.js"; +import type { Schema } from "./validation/schema.js"; + +const MAX_DELAY_MS = 60_000; +const MAX_TIMEOUT_MS = 2_147_483_647; +const MAX_BACKOFF_FACTOR = 100; +const MIN_STATUS = 100; +const MAX_STATUS = 599; +const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const; +const DEFAULT_RETRY_OPTIONS: ResolvedRetryOptions = { + timeout: 60_000, + statusCodesToRetry: [408, 429, 500, 502, 503, 504], + httpMethodsToRetry: ["GET", "HEAD", "PUT", "OPTIONS"], + maxRetries: 3, + delay: 1000, + backoffFactor: 2, + useExponentialBackoff: true, + maxJitter: 0.25, +}; + +/** + * How a failed request is retried. + * + * @remarks + * Every field is optional, so any subset is accepted and "the defaults with two of them changed" + * is a two-field literal. What the engine reads is this type **resolved**, with every field filled + * in, so the policy can be read off the options rather than inferred from engine behaviour. It is + * resolved at two layers: once when the client is built, over the engine's defaults, and again on + * every call, over the client's resolved options, when a call passes its own `retry`, which is + * {@link RequestRetryOptions}, three of these fields. + * + * A field a call leaves out keeps the client's value. A field given a value the engine cannot + * honour falls back to what stands behind it β€” the default at the client, the client's value on a + * call β€” and takes none of the fields beside it with it. The per-field blocks say which values + * those are. `0`, `false` and an empty list are values, never misses. + * + * `timeout` is the one field that is not about retrying: it bounds every attempt, the only one + * under `maxRetries: 0` included, so this type is what a single request may do as well as what a + * retried one may. + * + * Two things are retried: a response **status**, and a **transport fault** β€” a connection failure + * or a per-attempt timeout. An abort is never retried, nor is a fault the SDK itself caused, nor a + * request whose body is a stream, because a stream cannot be replayed. + * + * Obtaining a credential comes before the attempt's timer starts, and a failure there is never + * retried by the operation. The OAuth2 token request is a request of its own, timed and retried + * under the client's policy, so a token endpoint that drops the connection or runs out its own + * `timeout` ends the call with that request's `ConnectionError` or `TimeoutError`. A token + * endpoint that answers with a refusal is an `AuthError` and ends the call too. + */ +export type RetryOptions = { + /** + * Milliseconds a single attempt may take, from sending the request to its response headers. + * + * @remarks + * A value `setTimeout` cannot honour β€” negative, fractional, `NaN`, `Infinity`, or above + * `2_147_483_647` β€” silently falls back to the default, or on a call to the client's value. + * `0` is passed through literally, a zero-length deadline rather than "no timeout". + * + * Every attempt gets its own deadline, so this is the budget for one attempt and not for the + * call. `maxRetries: 0` does not lift it β€” the single attempt is timed like any other. + * + * The timer starts once the credential is in hand and stops when the response headers arrive, so + * it covers neither obtaining the credential nor reading any response body. The call's `signal` + * bounds both, and an OAuth2 token request is also timed on its own. + * + * @default 60000 + */ + readonly timeout?: number; + + /** + * Response statuses that are retried. Exactly these and no others. + * + * @remarks + * There is no family rule: a status is retried if and only if it appears here. So `[429, 503]` + * stops retrying 500 and 502. An empty list retries no status at all, while a list carrying + * anything that is not a status code between 100 and 599 β€” and any value that is not a list of + * them at all β€” falls back to the default. + * + * The default is the C# SDK's own set, each with a transient story β€” a timeout, a rate limit, an + * overloaded or misbehaving origin. The rest of the 5xx family is deliberately not in it: 501 + * and 505 will not succeed on a second attempt, and 510 and 511 both mean the caller must change + * something first. 409 is absent for its own reason β€” two of the three `ConflictReason` values + * this API declares are permanent, and the engine cancels the body before it could tell which + * one arrived. + * + * @default [408, 429, 500, 502, 503, 504] + */ + readonly statusCodesToRetry?: readonly number[]; + + /** + * Request methods that are retried. Exactly these and no others. + * + * @remarks + * The default is the conservative policy the C# SDK defaults to, which never sends a POST, a + * PATCH or a DELETE twice. Every non-GET operation mints its own `Idempotency-Key`, which is + * what makes widening it safe: name all seven methods and a write is retried like anything + * else. An empty list disables retries entirely, while a list carrying an entry that is not one + * of the seven methods β€” a lower-cased `"get"` among them β€” is rejected whole and falls back to + * the default, exactly as a bad status list does. + * + * @default ["GET", "HEAD", "PUT", "OPTIONS"] + */ + readonly httpMethodsToRetry?: readonly HttpMethod[]; + + /** + * How many further attempts a failed request may get, on top of the first one. + * + * @remarks + * A value that is not a safe, non-negative integer falls back to the default. `0` sends every + * request exactly once. + * + * @default 3 + */ + readonly maxRetries?: number; + + /** + * Milliseconds to wait before the first retry, and the base the backoff curve grows from. + * + * @remarks + * A value `setTimeout` cannot honour β€” negative, fractional, `NaN`, `Infinity`, or above + * `2_147_483_647` β€” falls back to the default. `0` retries immediately. + * + * No computed wait exceeds **60 seconds**, whatever this and `backoffFactor` compute between + * them. That ceiling is the engine's own rather than an option, and it bounds a `Retry-After` + * by the same number, so a value above it is accepted here and then never reached. Jitter sits + * on top of that ceiling rather than inside it, so the longest single wait is + * `60_000 Γ— (1 + maxJitter)` β€” 75 seconds at the default. + * + * @default 1000 + */ + readonly delay?: number; + + /** + * What each successive delay is multiplied by, when `useExponentialBackoff` is set. + * + * @remarks + * A value below 1 or above 100, or one that is not finite, falls back to the default. Growth + * stops at the ceiling {@link RetryOptions.delay} describes. + * + * @default 2 + */ + readonly backoffFactor?: number; + + /** + * Grow the delay by `backoffFactor` per attempt, rather than repeating `delay` unchanged. + * + * @default true + */ + readonly useExponentialBackoff?: boolean; + + /** + * The largest **fraction** of a delay that jitter may move it by at random. + * + * @remarks + * A fraction between 0 and 1, and it is **additive**: a wait is never shorter than the delay it + * spreads. `0.25` spreads a 1000ms delay off the curve over 1000-1250ms, and a 30s `Retry-After` + * over 30-37.5s β€” a client that woke before the time the server named would be answering a + * request to back off by backing off less. + * + * It is not a number of milliseconds: a value outside 0 to 1 falls back to the default, which + * is the one mistake a reader of the C# `RetryOptions` is most likely to make, its `MaxJitter` + * being a `TimeSpan`. `0` makes both exact, which is what a test wanting a predictable delay + * should pass. + * + * @default 0.25 + */ + readonly maxJitter?: number; + + /** + * Called before each retry sleep, with the attempt about to be made. + * + * @remarks + * Runs after the failed attempt's release has begun β€” its response body's cancel started, its + * timer already cleared β€” so a callback that throws cannot leak a connection or orphan a timer, + * but it does end the call, surfacing as the error it threw. Keep it to logging and metrics. + * + * Called for a retried transport fault as well as a retried status; narrow on + * {@link RetryAttempt.reason} before reading either. + * + * Anything other than a function falls back to no callback at all, which is what leaving it + * out means too β€” the absence of a callback is this field's value rather than a missing one. + * + * @default undefined + */ + readonly onRetry?: (attempt: RetryAttempt) => void; +}; + +/** + * The part of {@link RetryOptions} a single call may override, through `RequestOptions.retry`. + * + * @remarks + * Three fields, the ones whose right value differs from one operation to the next: how many + * attempts this call gets, how long each may take, and which statuses are worth a second try + * for this resource. The backoff curve, the method gate and `onRetry` are how the client paces + * itself against one server, so they are set on the client alone. + * + * A field named here replaces the client's for this call, and one left out keeps the client's + * value. The restriction is the type's: a value carrying another `RetryOptions` field, from + * JavaScript or through a constant typed as the wider record, still has it honoured. + */ +export type RequestRetryOptions = Pick; + +/** + * {@link RetryOptions} with every field filled in: what the engine reads, never what a caller + * writes. + * + * @remarks + * `RetryOptions` is itself the partial β€” every field optional, so any subset is accepted β€” and this + * is its resolved twin, built once when the client is built and again, over that one, on every + * call. The engine therefore reads a policy rather than re-deciding what an absent field meant, and + * no read site needs a `!`. + * + * `onRetry` is the one field that stays optional: the absence of a callback is its value, so a + * resolved record may leave the key out, and a missing key means the same as `undefined`. + */ +export type ResolvedRetryOptions = Required> & { + readonly onRetry?: RetryOptions["onRetry"]; +}; + +export function buildRetryOptions( + retry: RetryOptions | undefined, + fallbacks: ResolvedRetryOptions = DEFAULT_RETRY_OPTIONS, +): ResolvedRetryOptions { + const schema: Schema = s.of( + s.fallback( + s.object({ + timeout: s.fallback(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS)), fallbacks.timeout), + statusCodesToRetry: s.fallback( + s.array(s.int().check(s.gte(MIN_STATUS), s.lte(MAX_STATUS))), + fallbacks.statusCodesToRetry, + ), + httpMethodsToRetry: s.fallback(s.array(s.literal(HTTP_METHODS)), fallbacks.httpMethodsToRetry), + maxRetries: s.fallback(s.int().check(s.gte(0)), fallbacks.maxRetries), + delay: s.fallback(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS)), fallbacks.delay), + backoffFactor: s.fallback( + s.float64().check(s.gte(1), s.lte(MAX_BACKOFF_FACTOR)), + fallbacks.backoffFactor, + ), + useExponentialBackoff: s.fallback(s.boolean(), fallbacks.useExponentialBackoff), + maxJitter: s.fallback(s.float64().check(s.gte(0), s.lte(1)), fallbacks.maxJitter), + onRetry: s.fallback(s.callback<(attempt: RetryAttempt) => void>(), fallbacks.onRetry), + }), + fallbacks, + ), + ); + try { + return schema.decode(retry); + } catch (err) { + throw new ConfigurationError("The retry options could not be resolved.", { cause: err }); + } +} + +/** + * One retry, as {@link RetryOptions.onRetry} sees it. + */ +export type RetryAttempt = { + /** Which retry this is: `1` for the first one, after the original attempt failed. */ + readonly attemptNumber: number; + + /** Milliseconds the engine is about to wait before re-sending. */ + readonly delay: number; + + /** What went wrong on the attempt being abandoned. */ + readonly reason: RetryReason; +}; + +/** + * Why an attempt is being retried β€” a response the engine rejected, or no response at all. + * + * @remarks + * Narrow on `kind` before reading the rest: a fault carries no status and no headers, because + * nothing came back. + */ +export type RetryReason = + | { + /** A response arrived and its status is retryable. */ + readonly kind: "status"; + + /** The status that triggered the retry. */ + readonly status: number; + + /** + * Headers of the response being discarded. + * + * @remarks + * The one place a retried-away response reaches the caller. Its body is already being + * cancelled by the time this runs, so only the headers survive. + */ + readonly headers: Headers; + } + | { + /** No response arrived: the request failed at the transport layer. */ + readonly kind: "fault"; + + /** + * The classified failure, always a `connection` or a `timeout`. + * + * @remarks + * An abort is never retried, and neither is a fault the SDK itself caused, so neither ever + * reaches this callback. + * + * The OAuth2 token request's own retries arrive here too, since it runs under the client's + * policy. A fault names the call that dialled, so one from the token request carries the + * token endpoint's `uri` rather than the operation's. + */ + readonly error: ConnectionError | TimeoutError; + }; + +export type RespondedAttempt = { + readonly kind: "response"; + readonly response: Response; + readonly isStreaming: boolean; + discard(): void; +}; + +export type FaultedAttempt = { + readonly kind: "fault"; + readonly error: ConnectionError | TimeoutError; + readonly isStreaming: boolean; +}; + +export class RetryPolicy { + readonly #options: ResolvedRetryOptions; + + constructor(retry: RetryOptions | undefined, fallbacks: ResolvedRetryOptions) { + this.#options = buildRetryOptions(retry, fallbacks); + } + + async execute( + method: HttpMethod, + callerSignal: AbortSignal, + startedAt: number, + logger: HttpLogger, + send: (timeout: number) => Promise, + ): Promise { + for (let retriesMade = 0; ; retriesMade += 1) { + const attempt = await send(this.#options.timeout); + if (!shouldRetry(this.#options, retriesMade, method, attempt)) { + if (attempt.kind === "fault") { + logger.logFailure(attempt.error, startedAt); + throw attempt.error; + } + return attempt.response; + } + let reason: RetryReason; + switch (attempt.kind) { + case "response": + attempt.discard(); + reason = { kind: "status", status: attempt.response.status, headers: attempt.response.headers }; + break; + case "fault": + reason = { kind: "fault", error: attempt.error }; + break; + } + if (callerSignal.aborted) throw callerSignal.reason; + const retry: RetryAttempt = { + attemptNumber: retriesMade + 1, + delay: retryDelayMs(this.#options, retriesMade, reason), + reason, + }; + logger.logRetry(retry, this.#options.maxRetries); + this.#options.onRetry?.(retry); + await sleep(retry.delay, callerSignal); + if (callerSignal.aborted) throw callerSignal.reason; + } + } +} + +function retryDelayMs(options: ResolvedRetryOptions, retriesMade: number, reason: RetryReason): number { + const hint = reason.kind === "status" ? delayFromRetryAfterHeader(reason.headers, Date.now()) : undefined; + const growth = options.useExponentialBackoff ? options.backoffFactor ** retriesMade : 1; + const delay = Math.min(Math.max(hint ?? options.delay * growth, 0), MAX_DELAY_MS); + return Math.ceil(delay * (1 + Math.random() * options.maxJitter)); +} + +function shouldRetry( + options: ResolvedRetryOptions, + attempt: number, + method: HttpMethod, + outcome: RespondedAttempt | FaultedAttempt, +): boolean { + if (attempt >= options.maxRetries || outcome.isStreaming) return false; + if (!options.httpMethodsToRetry.includes(method)) return false; + return outcome.kind === "fault" || options.statusCodesToRetry.includes(outcome.response.status); +} + +function delayFromRetryAfterHeader(headers: Headers, now: number): number | undefined { + const value = headers.get("retry-after"); + if (value === null || value === "") return undefined; + + if (value[0]! >= "0" && value[0]! <= "9") { + for (const c of value) if (c < "0" || c > "9") return undefined; + return Number(value) * 1000; + } + + const first = value[0]!.toLowerCase(); + if (first < "a" || first > "z") return undefined; + const date = Date.parse(value); + return Number.isNaN(date) ? undefined : date - now; +} + +function sleep(ms: number, signal: AbortSignal): Promise { + if (signal.aborted) return Promise.resolve(); + return new Promise((resolve) => { + const timer = setTimeout(done, ms); + signal.addEventListener("abort", done); + function done(): void { + clearTimeout(timer); + signal.removeEventListener("abort", done); + resolve(); + } + }); +} diff --git a/plugins/authlete/sdk/typescript/src/core/runtime-environment.ts b/plugins/authlete/sdk/typescript/src/core/runtime-environment.ts new file mode 100644 index 00000000..fe6fd8c8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/runtime-environment.ts @@ -0,0 +1,74 @@ +export function operatingSystem(): string | undefined { + const nodeProcess = runtimeGlobals.process; + const versions = nodeProcess?.versions; + if ( + present(versions?.node) !== undefined || + present(versions?.bun) !== undefined || + present(versions?.deno) !== undefined + ) { + return processPlatform(nodeProcess); + } + return present(runtimeGlobals.navigator?.userAgentData?.platform); +} + +export function runtimeDescription(): string | undefined { + const versions = runtimeGlobals.process?.versions; + const bun = present(versions?.bun); + if (bun !== undefined) return `Bun/${bun}`; + const deno = present(versions?.deno); + if (deno !== undefined) return `Deno/${deno}`; + const edge = present(runtimeGlobals.EdgeRuntime); + if (edge !== undefined) return edge; + + const userAgent = present(runtimeGlobals.navigator?.userAgent); + if (userAgent === CLOUDFLARE_WORKERS_USER_AGENT) return userAgent; + + const node = present(versions?.node); + return node === undefined ? userAgent : `Node.js/${node}`; +} + +export function environmentVariable(name: string): string | undefined { + try { + return present(runtimeGlobals.process?.env?.[name]?.trim()); + } catch { + return undefined; + } +} + +type RuntimeVersions = { + readonly node?: string; + readonly bun?: string; + readonly deno?: string; +}; + +type NodeProcess = { + readonly versions?: RuntimeVersions; + readonly platform?: string; + readonly arch?: string; + readonly env?: Readonly>; +}; + +type NavigatorLike = { + readonly userAgent?: string; + readonly userAgentData?: { readonly platform?: string }; +}; + +type RuntimeGlobals = { + readonly process?: NodeProcess; + readonly navigator?: NavigatorLike; + readonly EdgeRuntime?: string; +}; + +const runtimeGlobals: RuntimeGlobals = globalThis; + +const CLOUDFLARE_WORKERS_USER_AGENT = "Cloudflare-Workers"; + +const present = (value: string | undefined): string | undefined => + value === undefined || value === "" ? undefined : value; + +function processPlatform(nodeProcess: NodeProcess | undefined): string | undefined { + const platform = present(nodeProcess?.platform); + if (platform === undefined) return undefined; + const arch = present(nodeProcess?.arch); + return arch === undefined ? platform : `${platform} ${arch}`; +} diff --git a/plugins/authlete/sdk/typescript/src/core/url.ts b/plugins/authlete/sdk/typescript/src/core/url.ts new file mode 100644 index 00000000..ffa56466 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/url.ts @@ -0,0 +1,122 @@ +import type { ApiRequest, RawClientOptions, ServerBase, UrlTemplate } from "./api-request.js"; +import type { StyledParam, Param, ParamPair } from "./param-value.js"; +import { encodedParam, flattenValue } from "./param-value.js"; +import { percentEncode, queryString, serializePairs } from "./params.js"; +import { EncodeError } from "./errors.js"; +import { SchemaError } from "./validation/schema-error.js"; + +type UrlDefaults = Pick; + +type UrlRequest = Pick; + +export class UrlBuilder { + readonly #defaultPathParams: readonly Param[]; + readonly #defaultQuery: readonly StyledParam[]; + + constructor(defaults: UrlDefaults) { + this.#defaultPathParams = defaults.defaultPathParams; + this.#defaultQuery = defaults.defaultQuery; + } + + build(request: UrlRequest): RequestUrl { + return new RequestUrl(this.#applyQuery(this.#resolvePath(request), request)); + } + + #resolvePath({ method, urlTemplate, pathParams }: UrlRequest): URL { + try { + return new URL( + joinUrl(urlTemplate.baseUrl, expandPath(urlTemplate.subPath, pathParams, this.#defaultPathParams)), + ); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + const uri = templateUri(urlTemplate); + throw new EncodeError(`${method} ${uri} failed: Request path parameter could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } + + #applyQuery(path: URL, { method, query }: UrlRequest): URL { + try { + return buildQueryUrl(path, [this.#defaultQuery, query]); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + const uri = uriOf(path); + throw new EncodeError(`${method} ${uri} failed: Request query parameter could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } +} + +export class RequestUrl { + readonly uri: string; + readonly #url: URL; + + constructor(url: URL) { + this.#url = url; + this.uri = uriOf(url); + } + + withQuery(pairs: readonly ParamPair[]): URL { + return copyWithQuery(this.#url, serializePairs(pairs, percentEncode)); + } +} + +export function buildQueryUrl(url: URL, queryInWireOrder: ReadonlyArray): URL { + return copyWithQuery(url, queryString(queryInWireOrder)); +} + +export function resolveBaseUrl(server: ServerBase): string { + let out = server.baseUrl; + for (const [key, value] of Object.entries(server.variables ?? {})) { + if (value === undefined) continue; + out = out.replaceAll(`{${key}}`, percentEncode(value)); + } + return out; +} + +export function templateUri(template: UrlTemplate): string { + return joinUrl(template.baseUrl, template.subPath); +} + +function uriOf(url: URL): string { + return `${url.origin}${url.pathname}`; +} + +function copyWithQuery(url: URL, added: string): URL { + const result = new URL(url); + if (added !== "") result.search = url.search === "" ? added : `${url.search.slice(1)}&${added}`; + return result; +} + +function expandPath(template: string, ...layers: ReadonlyArray): string { + let out = template; + const skipped: string[] = []; + for (const layer of layers) { + for (const source of layer) { + const value = encodedParam(source); + if (value === undefined) { + skipped.push(source.name); + continue; + } + const replacement = value === null ? "" : flattenValue(value).map(percentEncode).join(","); + out = out.replaceAll(`{${source.name}}`, replacement); + } + } + for (const name of skipped) { + if (!out.includes(`{${name}}`)) continue; + throw new TypeError(`Path parameter "${name}" resolved to undefined and left {${name}} unfilled.`); + } + return out; +} + +function joinUrl(base: string, path: string): string { + const left = base.replace(/\/+$/, ""); + const right = path.replace(/^\/+/, ""); + return `${left}/${right}`; +} diff --git a/plugins/authlete/sdk/typescript/src/core/uuid.ts b/plugins/authlete/sdk/typescript/src/core/uuid.ts new file mode 100644 index 00000000..de949470 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/uuid.ts @@ -0,0 +1,43 @@ +export function uuid(): string { + const webCrypto = cryptoGlobals.crypto; + if (webCrypto?.randomUUID !== undefined) return webCrypto.randomUUID(); + + const bytes = new Uint8Array(16); + if (webCrypto?.getRandomValues !== undefined) webCrypto.getRandomValues(bytes); + else fillPseudoRandomBytes(bytes); + bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40; + bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; + + const text = hex(bytes); + return `${text.slice(0, 8)}-${text.slice(8, 12)}-${text.slice(12, 16)}-${text.slice(16, 20)}-${text.slice(20)}`; +} + +type CryptoLike = { + readonly randomUUID?: () => string; + readonly getRandomValues?: (array: Uint8Array) => unknown; +}; + +type CryptoGlobals = { + readonly crypto?: CryptoLike; +}; + +const cryptoGlobals: CryptoGlobals = globalThis; + +let pseudoRandomCalls = 0; + +function fillPseudoRandomBytes(bytes: Uint8Array): void { + for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256); + + let time = Date.now(); + for (let i = 5; i >= 0; i--) { + bytes[i] = (bytes[i] ?? 0) ^ (time % 256); + time = Math.floor(time / 256); + } + + pseudoRandomCalls = (pseudoRandomCalls + 1) % 65536; + bytes[10] = (bytes[10] ?? 0) ^ Math.floor(pseudoRandomCalls / 256); + bytes[11] = (bytes[11] ?? 0) ^ (pseudoRandomCalls % 256); +} + +const hex = (bytes: Uint8Array): string => + Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(""); diff --git a/plugins/authlete/sdk/typescript/src/core/validation/entries.ts b/plugins/authlete/sdk/typescript/src/core/validation/entries.ts new file mode 100644 index 00000000..818442f1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/validation/entries.ts @@ -0,0 +1,162 @@ +import * as zod from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import { resolve, type Encoded, type Entry, type Schema } from "./schema.js"; + +export function lazy(target: () => Schema>): ZodMiniType> { + return zod.lazy(() => resolve(target())); +} + +export function defaulted(entry: Entry>, value: T): ZodMiniType> { + return zod.codec(zod.optional(resolve(entry)), zod.custom(), { + decode: (given: T | undefined) => (given === undefined ? value : given), + encode: (given: T | undefined) => (given === undefined ? value : given), + }) as unknown as ZodMiniType>; +} + +export function fallback(entry: Entry>, value: T): ZodMiniType> { + return zod.catch(resolve(entry), () => value); +} + +export function callback unknown>(): ZodMiniType { + return zod.custom((value) => typeof value === "function"); +} + +export function optionalNullable( + entry: Entry>, +): ZodMiniType | null | undefined> { + return zod.optional(zod.nullable(resolve(entry))); +} + +export const dateTime = (): ZodMiniType => + zod.codec(zod.iso.datetime({ offset: true }), zod.date(), { + decode: (value) => new Date(value), + encode: (date) => date.toISOString(), + }); + +const IMF_FIXDATE = + /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/; + +export const rfc1123DateTime = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(IMF_FIXDATE)), zod.date(), { + decode: (value) => new Date(value), + encode: (date) => date.toUTCString(), + }); + +const EPOCH_SECONDS = /^-?\d+(?:\.\d+)?$/; + +export const unixSecondsDateTime = (): ZodMiniType => + zod.codec(zod.union([zod.number(), zod.string().check(zod.regex(EPOCH_SECONDS))]), zod.date(), { + decode: (value) => new Date(Number(value) * 1000), + encode: (date) => Math.floor(date.getTime() / 1000), + }) as unknown as ZodMiniType; + +export const dateOnly = (): ZodMiniType => zod.iso.date(); + +const BYTES = zod.custom((value) => value instanceof Uint8Array); + +const BASE64URL = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2}(?:==)?|[A-Za-z0-9_-]{3}=?)?$/; +const BASE32 = + /^(?:[A-Za-z2-7]{8})*(?:[A-Za-z2-7]{2}={6}|[A-Za-z2-7]{4}={4}|[A-Za-z2-7]{5}={3}|[A-Za-z2-7]{7}=)?$/; +const BASE32HEX = + /^(?:[0-9A-Va-v]{8})*(?:[0-9A-Va-v]{2}={6}|[0-9A-Va-v]{4}={4}|[0-9A-Va-v]{5}={3}|[0-9A-Va-v]{7}=)?$/; +const BASE16 = /^(?:[0-9A-Fa-f]{2})*$/; + +const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +const BASE32HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + +export const bytes = (): ZodMiniType => + zod.codec(zod.base64(), BYTES, { + decode: (value) => decodeBase64(value), + encode: (value) => encodeBase64(value), + }); + +export const base64UrlBytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE64URL)), BYTES, { + decode: (value) => decodeBase64(padBase64(value.replace(/-/g, "+").replace(/_/g, "/"))), + encode: (value) => encodeBase64(value).replace(/\+/g, "-").replace(/\//g, "_"), + }); + +export const base32Bytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE32)), BYTES, { + decode: (value) => decodeBase32(value, BASE32_ALPHABET), + encode: (value) => encodeBase32(value, BASE32_ALPHABET), + }); + +export const base32HexBytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE32HEX)), BYTES, { + decode: (value) => decodeBase32(value, BASE32HEX_ALPHABET), + encode: (value) => encodeBase32(value, BASE32HEX_ALPHABET), + }); + +export const base16Bytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE16)), BYTES, { + decode: (value) => decodeBase16(value), + encode: (value) => encodeBase16(value), + }); + +function decodeBase64(value: string): Uint8Array { + const latin1 = atob(value); + const bytes = new Uint8Array(latin1.length); + for (let index = 0; index < latin1.length; index++) bytes[index] = latin1.charCodeAt(index); + return bytes; +} + +function encodeBase64(value: Uint8Array): string { + let latin1 = ""; + for (const byte of value) latin1 += String.fromCharCode(byte); + return btoa(latin1); +} + +function padBase64(value: string): string { + const remainder = value.length % 4; + return remainder === 0 ? value : `${value}${"=".repeat(4 - remainder)}`; +} + +function decodeBase32(value: string, alphabet: string): Uint8Array { + const text = value.replace(/=+$/, "").toUpperCase(); + const bytes = new Uint8Array((text.length * 5) >> 3); + let accumulator = 0; + let bits = 0; + let offset = 0; + + for (const char of text) { + accumulator = (accumulator << 5) | alphabet.indexOf(char); + bits += 5; + if (bits < 8) continue; + bits -= 8; + bytes[offset] = (accumulator >> bits) & 0xff; + offset += 1; + } + return bytes; +} + +function encodeBase32(value: Uint8Array, alphabet: string): string { + let text = ""; + let accumulator = 0; + let bits = 0; + + for (const byte of value) { + accumulator = (accumulator << 8) | byte; + bits += 8; + while (bits >= 5) { + bits -= 5; + text += alphabet[(accumulator >> bits) & 0x1f]; + } + } + if (bits > 0) text += alphabet[(accumulator << (5 - bits)) & 0x1f]; + return text.padEnd(Math.ceil(text.length / 8) * 8, "="); +} + +function decodeBase16(value: string): Uint8Array { + const bytes = new Uint8Array(value.length / 2); + for (let index = 0; index < bytes.length; index++) { + bytes[index] = Number.parseInt(value.slice(index * 2, index * 2 + 2), 16); + } + return bytes; +} + +function encodeBase16(value: Uint8Array): string { + let text = ""; + for (const byte of value) text += byte.toString(16).toUpperCase().padStart(2, "0"); + return text; +} diff --git a/plugins/authlete/sdk/typescript/src/core/validation/index.ts b/plugins/authlete/sdk/typescript/src/core/validation/index.ts new file mode 100644 index 00000000..ada685e2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/validation/index.ts @@ -0,0 +1,32 @@ +export { + array, + boolean, + literal, + nullable, + optional, + record, + string, + union, + unknown, + float64, + gte, + int, + lte, +} from "zod/v4-mini"; +export { + lazy, + fallback, + callback, + optionalNullable, + defaulted, + dateTime, + rfc1123DateTime, + unixSecondsDateTime, + dateOnly, + bytes, + base64UrlBytes, + base32Bytes, + base32HexBytes, + base16Bytes, +} from "./entries.js"; +export { object, discriminatedUnion, enumOf, of } from "./schema.js"; diff --git a/plugins/authlete/sdk/typescript/src/core/validation/schema-error.ts b/plugins/authlete/sdk/typescript/src/core/validation/schema-error.ts new file mode 100644 index 00000000..12a09ab9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/validation/schema-error.ts @@ -0,0 +1,99 @@ +import * as schema from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import type { Entry, Schema } from "./schema.js"; + +/** + * A value did not match the schema that describes it. + * + * @remarks + * Thrown on both directions by a codec used directly, as in `paymentInputSchema.decode(json)`, so + * it names no call. Through an operation the same failure reaches you one level down, on the + * `cause` of the `DecodeError` or `EncodeError` that does name the call. + * + * `message` names the field and the type expected, never the value, and `cause` carries the issue + * list with its original paths. The value that failed is not kept. + */ +export class SchemaError extends Error { + constructor(message: string, cause?: unknown) { + super(message, cause === undefined ? undefined : { cause }); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export function decodeWith(zodType: ZodMiniType, value: unknown): T { + const result = zodType.safeParse(value); + if (result.success) return result.data; + throw new SchemaError(issuesMessage(result.error, value), result.error); +} + +export function encodeWith(zodType: ZodMiniType, value: unknown): W { + const result = schema.safeEncode(zodType, value as T); + if (result.success) return result.data; + throw new SchemaError( + issuesMessage(result.error, value, "Type could not be encoded for the wire."), + result.error, + ); +} + +export function decodeEntry(schema: Entry, value: unknown): V { + return isSchemaEntry(schema) ? schema.decode(value) : decodeWith(schema, value); +} + +export function encodeEntry(schema: Entry, value: unknown): W { + return isSchemaEntry(schema) ? schema.encode(value) : encodeWith(schema, value); +} + +function isSchemaEntry(entry: Entry): entry is Schema { + return typeof (entry as { decode?: unknown }).decode === "function"; +} + +type SchemaIssue = { + readonly path: readonly PropertyKey[]; + readonly message: string; + readonly expected?: string | undefined; + readonly format?: string | undefined; + readonly options?: readonly unknown[] | undefined; +}; + +type SchemaIssues = { readonly issues: readonly SchemaIssue[] }; + +function issuesMessage( + error: SchemaIssues, + value: unknown, + prefix = "Wire value could not be decoded.", +): string { + const issues = error.issues.map((issue) => { + const path = issue.path.map(String).join(".") || ""; + return `${path}: ${issueReason(issue, valueAt(value, issue.path))}`; + }); + return `${prefix} ${issues.join("; ")}`; +} + +function issueReason(issue: SchemaIssue, actual: unknown): string { + if (issue.expected !== undefined) return `expected ${issue.expected}, received ${typeName(actual)}`; + if (issue.format !== undefined) return `expected ${issue.format} format, received ${typeName(actual)}`; + if (issue.options !== undefined) { + return `expected one of ${issue.options.map(literal).join(" | ")}, received ${typeName(actual)}`; + } + return issue.message; +} + +function valueAt(value: unknown, path: readonly PropertyKey[]): unknown { + let current = value; + for (const key of path) { + if (current === null || typeof current !== "object") return undefined; + current = (current as Record)[key]; + } + return current; +} + +function typeName(value: unknown): string { + if (value === null) return "null"; + if (Array.isArray(value)) return "array"; + return typeof value; +} + +function literal(value: unknown): string { + return JSON.stringify(value) ?? "undefined"; +} diff --git a/plugins/authlete/sdk/typescript/src/core/validation/schema.ts b/plugins/authlete/sdk/typescript/src/core/validation/schema.ts new file mode 100644 index 00000000..cb2c33a7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/core/validation/schema.ts @@ -0,0 +1,168 @@ +import * as zod from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import { decodeWith, encodeWith } from "./schema-error.js"; + +/** + * What a value looks like on the wire. + * + * @remarks + * Only `Date` and `Uint8Array` change shape β€” a `Date` becomes its serialized form and a + * `Uint8Array` a string in the RFC 4648 alphabet its field declares β€” recursing through arrays and + * objects. Everything else is unchanged, because the schema layer stops at *values*: how a number + * is written into a query string versus a JSON body is the engine's decision, not the schema's. + */ +export type Encoded = T extends Date + ? string | number + : T extends Uint8Array + ? string + : T extends string | number | boolean | null | undefined + ? T + : T extends readonly (infer E)[] + ? Encoded[] + : T extends object + ? { [K in keyof T]: Encoded } + : T; + +export type KeysMap = { readonly [K in keyof T]?: string }; + +export type Entry = ZodMiniType | Schema; + +export type Shape = { [K in keyof T]-?: Entry } & { readonly _keysMap?: KeysMap }; + +/** + * The codec exported beside every model: `decode` from the wire, `encode` back to it. + * + * @remarks + * Both directions are complete on their own β€” one call each, never a pair. `decode` accepts the + * wire form only and `encode` the SDK form only, so passing one direction's value to the other is + * rejected rather than waved through. A failure throws `SchemaError`; through an operation the + * same failure reaches you one level down, on the `cause` of a `DecodeError` or an + * `EncodeError`, which name the call. + * + * Validation is **structural**: types, shape, and required keys. Value constraints such as + * `minLength`, `pattern` or enum membership are the service's to enforce, and the SDK does not + * duplicate them. + */ +export type Schema> = { + readonly decode: (value: unknown) => T; + readonly encode: (value: unknown) => W; +}; + +/** + * The codec of an open enum, which also exposes the known members at run time. + * + * @remarks + * `values` is the set the spec declared. It is **not** enforced β€” an open enum validates the base + * type only, so an unrecognized server value round-trips instead of throwing. Test membership + * yourself against `values` when you need to. + */ +export type EnumSchema = Schema & { + readonly values: readonly T[]; +}; + +type Built = Schema & { + readonly zod: () => ZodMiniType; + readonly tagged: (discriminator: string, value: string) => ZodMiniType; +}; + +export function object(shape: Shape): Schema> { + let memo: ZodMiniType> | undefined; + const zodType = (): ZodMiniType> => (memo ??= buildObject(shape)); + return { + zod: zodType, + tagged: (discriminator, value) => buildObject(shape, [discriminator, value]), + decode: (value) => decodeWith(zodType(), value), + encode: (value) => encodeWith(zodType(), value), + } as Built>; +} + +export function discriminatedUnion( + discriminator: string, + variants: Readonly>>, +): Schema> { + let memo: ZodMiniType> | undefined; + const build = (): ZodMiniType> => { + const members = Object.entries(variants).map(([value, variant]) => + built(variant).tagged(discriminator, value), + ); + return zod.discriminatedUnion(discriminator, members as never); + }; + const zodType = (): ZodMiniType> => (memo ??= build()); + return { + zod: zodType, + tagged: () => zodType(), + decode: (value) => decodeWith(zodType(), value), + encode: (value) => encodeWith(zodType(), value), + } as Built>; +} + +export function enumOf(members: Readonly>): EnumSchema { + const values = Object.values(members); + const base: ZodMiniType = values.every((value) => typeof value === "number") + ? zod.number() + : zod.string(); + return { + ...of(base as ZodMiniType), + values, + }; +} + +export function of>(zodType: ZodMiniType): Schema { + return { + zod: () => zodType, + tagged: () => zodType, + decode: (value) => decodeWith(zodType, value), + encode: (value) => encodeWith(zodType, value), + } as Built; +} + +function buildObject(shape: Shape, tag?: readonly [string, string]): ZodMiniType> { + const keysMap = (shape as { _keysMap?: Record })._keysMap ?? {}; + const wire: Record> = {}; + const renames: [string, string][] = []; + + for (const [key, entry] of Object.entries(shape)) { + if (key === "_keysMap") continue; + const wireKey = keysMap[key] ?? key; + wire[wireKey] = resolve(entry as Entry); + if (wireKey !== key) renames.push([wireKey, key]); + } + if (tag) wire[tag[0]] = zod.literal(tag[1]); + + const object: ZodMiniType, Record> = zod.looseObject(wire); + const renaming: ZodMiniType = + renames.length === 0 + ? object + : zod.codec(object, zod.custom>(), { + decode: (value) => renameKeys(value, renames, "decode"), + encode: (value) => renameKeys(value, renames, "encode"), + }); + return renaming as ZodMiniType>; +} + +function renameKeys( + value: Record, + renames: readonly [string, string][], + direction: "decode" | "encode", +): Record { + const out = { ...value }; + for (const [wireKey, key] of renames) { + const [from, to] = direction === "decode" ? [wireKey, key] : [key, wireKey]; + if (!(from in out)) continue; + out[to] = out[from]; + delete out[from]; + } + return out; +} + +export function resolve(entry: Entry>): ZodMiniType> { + return isSchema(entry) ? built(entry).zod() : entry; +} + +function built(schema: Schema>): Built> { + return schema as Built>; +} + +function isSchema(entry: Entry): entry is Schema { + return typeof (entry as { decode?: unknown }).decode === "function"; +} diff --git a/plugins/authlete/sdk/typescript/src/index.ts b/plugins/authlete/sdk/typescript/src/index.ts new file mode 100644 index 00000000..4b3f42c4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/index.ts @@ -0,0 +1,512 @@ +export { AuthleteApiClient } from "./client.js"; +export type { ClientOptions } from "./client-options.js"; + +export type { TokenProvider } from "./core/auth/credentials.js"; + +export { ServerEnvironment } from "./servers.js"; + +export { ServiceManagement } from "./resources/service-management.js"; +export { ClientManagement } from "./resources/client-management.js"; +export { AuthorizationEndpoint } from "./resources/authorization-endpoint.js"; +export { PushedAuthorizationEndpoint } from "./resources/pushed-authorization-endpoint.js"; +export { TokenEndpoint } from "./resources/token-endpoint.js"; +export { TokenOperations } from "./resources/token-operations.js"; +export { IntrospectionEndpoint } from "./resources/introspection-endpoint.js"; +export { RevocationEndpoint } from "./resources/revocation-endpoint.js"; +export { UserInfoEndpoint } from "./resources/user-info-endpoint.js"; +export { JwkSetEndpoint } from "./resources/jwk-set-endpoint.js"; +export { DynamicClientRegistration } from "./resources/dynamic-client-registration.js"; +export { Ciba } from "./resources/ciba.js"; +export { GrantManagementEndpoint } from "./resources/grant-management-endpoint.js"; +export { JoseObject } from "./resources/jose-object.js"; +export { DeviceFlow } from "./resources/device-flow.js"; +export { FederationEndpoint } from "./resources/federation-endpoint.js"; +export { VerifiableCredentialIssuer } from "./resources/verifiable-credential-issuer.js"; +export { HardwareSecurityKey } from "./resources/hardware-security-key.js"; +export { UtilityEndpoints } from "./resources/utility-endpoints.js"; +export { NativeSso } from "./resources/native-sso.js"; +export { AuditLogs } from "./resources/audit-logs.js"; +export { BackChannelLogout } from "./resources/back-channel-logout.js"; +export { Lifecycle } from "./resources/lifecycle.js"; + +export { Action, actionSchema } from "./models/action.js"; +export { Action1, action1Schema } from "./models/action1.js"; +export { Action11, action11Schema } from "./models/action11.js"; +export { Action12, action12Schema } from "./models/action12.js"; +export { Action13, action13Schema } from "./models/action13.js"; +export { Action14, action14Schema } from "./models/action14.js"; +export { Action15, action15Schema } from "./models/action15.js"; +export { Action16, action16Schema } from "./models/action16.js"; +export { Action17, action17Schema } from "./models/action17.js"; +export { Action18, action18Schema } from "./models/action18.js"; +export { Action19, action19Schema } from "./models/action19.js"; +export { Action20, action20Schema } from "./models/action20.js"; +export { Action21, action21Schema } from "./models/action21.js"; +export { Action22, action22Schema } from "./models/action22.js"; +export { Action23, action23Schema } from "./models/action23.js"; +export { Action24, action24Schema } from "./models/action24.js"; +export { Action25, action25Schema } from "./models/action25.js"; +export { Action26, action26Schema } from "./models/action26.js"; +export { Action27, action27Schema } from "./models/action27.js"; +export { Action28, action28Schema } from "./models/action28.js"; +export { Action3, action3Schema } from "./models/action3.js"; +export { Action30, action30Schema } from "./models/action30.js"; +export { Action31, action31Schema } from "./models/action31.js"; +export { Action32, action32Schema } from "./models/action32.js"; +export { Action33, action33Schema } from "./models/action33.js"; +export { Action34, action34Schema } from "./models/action34.js"; +export { Action35, action35Schema } from "./models/action35.js"; +export { Action36, action36Schema } from "./models/action36.js"; +export { Action37, action37Schema } from "./models/action37.js"; +export { Action38, action38Schema } from "./models/action38.js"; +export { Action39, action39Schema } from "./models/action39.js"; +export { Action4, action4Schema } from "./models/action4.js"; +export { Action40, action40Schema } from "./models/action40.js"; +export { Action41, action41Schema } from "./models/action41.js"; +export { Action42, action42Schema } from "./models/action42.js"; +export { Action43, action43Schema } from "./models/action43.js"; +export { Action5, action5Schema } from "./models/action5.js"; +export { Action6, action6Schema } from "./models/action6.js"; +export { Action7, action7Schema } from "./models/action7.js"; +export { Action8, action8Schema } from "./models/action8.js"; +export { Action9, action9Schema } from "./models/action9.js"; +export { + apiClientRegistrationDeleteRequestSchema, + type ApiClientRegistrationDeleteRequest, +} from "./models/api-client-registration-delete-request.js"; +export { + apiClientRegistrationGetRequestSchema, + type ApiClientRegistrationGetRequest, +} from "./models/api-client-registration-get-request.js"; +export { + apiClientRegistrationRequestSchema, + type ApiClientRegistrationRequest, +} from "./models/api-client-registration-request.js"; +export { + apiClientRegistrationUpdateRequestSchema, + type ApiClientRegistrationUpdateRequest, +} from "./models/api-client-registration-update-request.js"; +export { + apiFederationConfigurationRequestSchema, + type ApiFederationConfigurationRequest, +} from "./models/api-federation-configuration-request.js"; +export { ClientSource, clientSourceSchema } from "./models/client-source.js"; +export { Reason, reasonSchema } from "./models/reason.js"; +export { Reason1, reason1Schema } from "./models/reason1.js"; +export { Reason2, reason2Schema } from "./models/reason2.js"; +export { ResponseMode, responseModeSchema } from "./models/response-mode.js"; +export { Result1, result1Schema } from "./models/result1.js"; +export { serviceTokenCacheSchema, type ServiceTokenCache } from "./models/service-token-cache.js"; +export { Status, statusSchema } from "./models/status.js"; +export { accessTokenSchema, type AccessToken } from "./models/access-token.js"; +export { ApplicationType, applicationTypeSchema } from "./models/application-type.js"; +export { AttachmentType, attachmentTypeSchema } from "./models/attachment-type.js"; +export { + auditEntriesGetResponseSchema, + type AuditEntriesGetResponse, +} from "./models/audit-entries-get-response.js"; +export { auditLogEntrySchema, type AuditLogEntry } from "./models/audit-log-entry.js"; +export { + authorizationDetailsElementSchema, + type AuthorizationDetailsElement, +} from "./models/authorization-details-element.js"; +export { + authorizationFailRequestSchema, + type AuthorizationFailRequest, +} from "./models/authorization-fail-request.js"; +export { + authorizationFailResponseSchema, + type AuthorizationFailResponse, +} from "./models/authorization-fail-response.js"; +export { + authorizationIssueRequestSchema, + type AuthorizationIssueRequest, +} from "./models/authorization-issue-request.js"; +export { + authorizationIssueResponseSchema, + type AuthorizationIssueResponse, +} from "./models/authorization-issue-response.js"; +export { authorizationRequestSchema, type AuthorizationRequest } from "./models/authorization-request.js"; +export { authorizationResponseSchema, type AuthorizationResponse } from "./models/authorization-response.js"; +export { + authorizationTicketInfoSchema, + type AuthorizationTicketInfo, +} from "./models/authorization-ticket-info.js"; +export { + authorizationTicketInfoRequestSchema, + type AuthorizationTicketInfoRequest, +} from "./models/authorization-ticket-info-request.js"; +export { + authorizationTicketInfoResponseSchema, + type AuthorizationTicketInfoResponse, +} from "./models/authorization-ticket-info-response.js"; +export { + authorizationTicketUpdateRequestSchema, + type AuthorizationTicketUpdateRequest, +} from "./models/authorization-ticket-update-request.js"; +export { + authorizationTicketUpdateResponseSchema, + type AuthorizationTicketUpdateResponse, +} from "./models/authorization-ticket-update-response.js"; +export { authzDetailsSchema, type AuthzDetails } from "./models/authz-details.js"; +export { + backchannelAuthenticationCompleteRequestSchema, + type BackchannelAuthenticationCompleteRequest, +} from "./models/backchannel-authentication-complete-request.js"; +export { + backchannelAuthenticationCompleteResponseSchema, + type BackchannelAuthenticationCompleteResponse, +} from "./models/backchannel-authentication-complete-response.js"; +export { + backchannelAuthenticationFailRequestSchema, + type BackchannelAuthenticationFailRequest, +} from "./models/backchannel-authentication-fail-request.js"; +export { + backchannelAuthenticationFailResponseSchema, + type BackchannelAuthenticationFailResponse, +} from "./models/backchannel-authentication-fail-response.js"; +export { + backchannelAuthenticationIssueRequestSchema, + type BackchannelAuthenticationIssueRequest, +} from "./models/backchannel-authentication-issue-request.js"; +export { + backchannelAuthenticationIssueResponseSchema, + type BackchannelAuthenticationIssueResponse, +} from "./models/backchannel-authentication-issue-response.js"; +export { + backchannelAuthenticationRequestSchema, + type BackchannelAuthenticationRequest, +} from "./models/backchannel-authentication-request.js"; +export { + backchannelAuthenticationResponseSchema, + type BackchannelAuthenticationResponse, +} from "./models/backchannel-authentication-response.js"; +export { + backchannelLogoutTokenRequestSchema, + type BackchannelLogoutTokenRequest, +} from "./models/backchannel-logout-token-request.js"; +export { + backchannelLogoutTokenResponseSchema, + type BackchannelLogoutTokenResponse, +} from "./models/backchannel-logout-token-response.js"; +export { cimdOptionsSchema, type CimdOptions } from "./models/cimd-options.js"; +export { ClaimType, claimTypeSchema } from "./models/claim-type.js"; +export { clientSchema, type Client } from "./models/client.js"; +export { ClientAuthMethod, clientAuthMethodSchema } from "./models/client-auth-method.js"; +export { + clientAuthorizationDeleteResponseSchema, + type ClientAuthorizationDeleteResponse, +} from "./models/client-authorization-delete-response.js"; +export { + clientAuthorizationGetListRequestSchema, + type ClientAuthorizationGetListRequest, +} from "./models/client-authorization-get-list-request.js"; +export { + clientAuthorizationGetListResponseSchema, + type ClientAuthorizationGetListResponse, +} from "./models/client-authorization-get-list-response.js"; +export { + clientAuthorizationUpdateRequestSchema, + type ClientAuthorizationUpdateRequest, +} from "./models/client-authorization-update-request.js"; +export { + clientCursorListResponseSchema, + type ClientCursorListResponse, +} from "./models/client-cursor-list-response.js"; +export { clientExtensionSchema, type ClientExtension } from "./models/client-extension.js"; +export { + clientExtensionRequestableScopesGetResponseSchema, + type ClientExtensionRequestableScopesGetResponse, +} from "./models/client-extension-requestable-scopes-get-response.js"; +export { + clientExtensionRequestableScopesUpdateRequestSchema, + type ClientExtensionRequestableScopesUpdateRequest, +} from "./models/client-extension-requestable-scopes-update-request.js"; +export { + clientExtensionRequestableScopesUpdateResponseSchema, + type ClientExtensionRequestableScopesUpdateResponse, +} from "./models/client-extension-requestable-scopes-update-response.js"; +export { + clientFlagUpdateRequestSchema, + type ClientFlagUpdateRequest, +} from "./models/client-flag-update-request.js"; +export { + clientFlagUpdateResponseSchema, + type ClientFlagUpdateResponse, +} from "./models/client-flag-update-response.js"; +export { + clientGetListResponseSchema, + type ClientGetListResponse, +} from "./models/client-get-list-response.js"; +export { clientLimitedSchema, type ClientLimited } from "./models/client-limited.js"; +export { + clientLimitedAuthorizationSchema, + type ClientLimitedAuthorization, +} from "./models/client-limited-authorization.js"; +export { + clientRegistrationRequestSchema, + type ClientRegistrationRequest, +} from "./models/client-registration-request.js"; +export { + clientRegistrationResponseSchema, + type ClientRegistrationResponse, +} from "./models/client-registration-response.js"; +export { ClientRegistrationType, clientRegistrationTypeSchema } from "./models/client-registration-type.js"; +export { clientSearchResponseSchema, type ClientSearchResponse } from "./models/client-search-response.js"; +export { + clientSecretRefreshResponseSchema, + type ClientSecretRefreshResponse, +} from "./models/client-secret-refresh-response.js"; +export { + clientSecretUpdateRequestSchema, + type ClientSecretUpdateRequest, +} from "./models/client-secret-update-request.js"; +export { ClientType, clientTypeSchema } from "./models/client-type.js"; +export { + credentialIssuanceOrderSchema, + type CredentialIssuanceOrder, +} from "./models/credential-issuance-order.js"; +export { + credentialIssuerMetadataSchema, + type CredentialIssuerMetadata, +} from "./models/credential-issuer-metadata.js"; +export { credentialOfferInfoSchema, type CredentialOfferInfo } from "./models/credential-offer-info.js"; +export { credentialRequestInfoSchema, type CredentialRequestInfo } from "./models/credential-request-info.js"; +export { DeliveryMode, deliveryModeSchema } from "./models/delivery-mode.js"; +export { + deviceAuthorizationRequestSchema, + type DeviceAuthorizationRequest, +} from "./models/device-authorization-request.js"; +export { + deviceAuthorizationResponseSchema, + type DeviceAuthorizationResponse, +} from "./models/device-authorization-response.js"; +export { deviceCompleteRequestSchema, type DeviceCompleteRequest } from "./models/device-complete-request.js"; +export { + deviceCompleteResponseSchema, + type DeviceCompleteResponse, +} from "./models/device-complete-response.js"; +export { + deviceVerificationRequestSchema, + type DeviceVerificationRequest, +} from "./models/device-verification-request.js"; +export { + deviceVerificationResponseSchema, + type DeviceVerificationResponse, +} from "./models/device-verification-response.js"; +export { Display, displaySchema } from "./models/display.js"; +export { dynamicScopeSchema, type DynamicScope } from "./models/dynamic-scope.js"; +export { EntityType, entityTypeSchema } from "./models/entity-type.js"; +export { FapiMode, fapiModeSchema } from "./models/fapi-mode.js"; +export { + federationConfigurationResponseSchema, + type FederationConfigurationResponse, +} from "./models/federation-configuration-response.js"; +export { + federationRegistrationRequestSchema, + type FederationRegistrationRequest, +} from "./models/federation-registration-request.js"; +export { + federationRegistrationResponseSchema, + type FederationRegistrationResponse, +} from "./models/federation-registration-response.js"; +export { gmRequestSchema, type GmRequest } from "./models/gm-request.js"; +export { gmResponseSchema, type GmResponse } from "./models/gm-response.js"; +export { grantSchema, type Grant } from "./models/grant.js"; +export { GrantManagementAction, grantManagementActionSchema } from "./models/grant-management-action.js"; +export { grantScopeSchema, type GrantScope } from "./models/grant-scope.js"; +export { GrantType, grantTypeSchema } from "./models/grant-type.js"; +export { hskSchema, type Hsk } from "./models/hsk.js"; +export { hskCreateRequestSchema, type HskCreateRequest } from "./models/hsk-create-request.js"; +export { hskCreateResponseSchema, type HskCreateResponse } from "./models/hsk-create-response.js"; +export { hskDeleteResponseSchema, type HskDeleteResponse } from "./models/hsk-delete-response.js"; +export { hskGetListResponseSchema, type HskGetListResponse } from "./models/hsk-get-list-response.js"; +export { idpErrorSchema, type IdpError } from "./models/idp-error.js"; +export { idpErrorErrorSchema, type IdpErrorError } from "./models/idp-error-error.js"; +export { idtokenReissueRequestSchema, type IdtokenReissueRequest } from "./models/idtoken-reissue-request.js"; +export { + idtokenReissueResponseSchema, + type IdtokenReissueResponse, +} from "./models/idtoken-reissue-response.js"; +export { infoResponseSchema, type InfoResponse } from "./models/info-response.js"; +export { introspectionRequestSchema, type IntrospectionRequest } from "./models/introspection-request.js"; +export { introspectionResponseSchema, type IntrospectionResponse } from "./models/introspection-response.js"; +export { joseVerifyRequestSchema, type JoseVerifyRequest } from "./models/jose-verify-request.js"; +export { joseVerifyResponseSchema, type JoseVerifyResponse } from "./models/jose-verify-response.js"; +export { JweAlg, jweAlgSchema } from "./models/jwe-alg.js"; +export { JweEnc, jweEncSchema } from "./models/jwe-enc.js"; +export { JwsAlg, jwsAlgSchema } from "./models/jws-alg.js"; +export { namedUriSchema, type NamedUri } from "./models/named-uri.js"; +export { + nativeSsoLogoutRequestSchema, + type NativeSsoLogoutRequest, +} from "./models/native-sso-logout-request.js"; +export { + nativeSsoLogoutResponseSchema, + type NativeSsoLogoutResponse, +} from "./models/native-sso-logout-response.js"; +export { nativeSsoRequestSchema, type NativeSsoRequest } from "./models/native-sso-request.js"; +export { nativeSsoResponseSchema, type NativeSsoResponse } from "./models/native-sso-response.js"; +export { pairSchema, type Pair } from "./models/pair.js"; +export { Prompt, promptSchema } from "./models/prompt.js"; +export { propertySchema, type Property } from "./models/property.js"; +export { + pushedAuthorizationRequestSchema, + type PushedAuthorizationRequest, +} from "./models/pushed-authorization-request.js"; +export { + pushedAuthorizationResponseSchema, + type PushedAuthorizationResponse, +} from "./models/pushed-authorization-response.js"; +export { ResponseType, responseTypeSchema } from "./models/response-type.js"; +export { resultSchema, type Result } from "./models/result.js"; +export { resultErrorSchema, type ResultError } from "./models/result-error.js"; +export { revocationRequestSchema, type RevocationRequest } from "./models/revocation-request.js"; +export { revocationResponseSchema, type RevocationResponse } from "./models/revocation-response.js"; +export { scopeSchema, type Scope } from "./models/scope.js"; +export { serviceSchema, type Service } from "./models/service.js"; +export { + serviceCreateIdpRequestSchema, + type ServiceCreateIdpRequest, +} from "./models/service-create-idp-request.js"; +export { + serviceGetListResponseSchema, + type ServiceGetListResponse, +} from "./models/service-get-list-response.js"; +export { + serviceJwksGetResponseSchema, + type ServiceJwksGetResponse, +} from "./models/service-jwks-get-response.js"; +export { ServiceProfile, serviceProfileSchema } from "./models/service-profile.js"; +export { + serviceRemoveIdpRequestSchema, + type ServiceRemoveIdpRequest, +} from "./models/service-remove-idp-request.js"; +export { Sns, snsSchema } from "./models/sns.js"; +export { snsCredentialsSchema, type SnsCredentials } from "./models/sns-credentials.js"; +export { + standardIntrospectionRequestSchema, + type StandardIntrospectionRequest, +} from "./models/standard-introspection-request.js"; +export { + standardIntrospectionResponseSchema, + type StandardIntrospectionResponse, +} from "./models/standard-introspection-response.js"; +export { SubjectType, subjectTypeSchema } from "./models/subject-type.js"; +export { taggedValueSchema, type TaggedValue } from "./models/tagged-value.js"; +export { tokenCreateRequestSchema, type TokenCreateRequest } from "./models/token-create-request.js"; +export { tokenCreateResponseSchema, type TokenCreateResponse } from "./models/token-create-response.js"; +export { tokenFailRequestSchema, type TokenFailRequest } from "./models/token-fail-request.js"; +export { tokenFailResponseSchema, type TokenFailResponse } from "./models/token-fail-response.js"; +export { tokenGetListResponseSchema, type TokenGetListResponse } from "./models/token-get-list-response.js"; +export { tokenInfoSchema, type TokenInfo } from "./models/token-info.js"; +export { tokenIssueRequestSchema, type TokenIssueRequest } from "./models/token-issue-request.js"; +export { tokenIssueResponseSchema, type TokenIssueResponse } from "./models/token-issue-response.js"; +export { tokenRequestSchema, type TokenRequest } from "./models/token-request.js"; +export { tokenResponseSchema, type TokenResponse } from "./models/token-response.js"; +export { tokenRevokeRequestSchema, type TokenRevokeRequest } from "./models/token-revoke-request.js"; +export { tokenRevokeResponseSchema, type TokenRevokeResponse } from "./models/token-revoke-response.js"; +export { TokenType, tokenTypeSchema } from "./models/token-type.js"; +export { tokenUpdateRequestSchema, type TokenUpdateRequest } from "./models/token-update-request.js"; +export { tokenUpdateResponseSchema, type TokenUpdateResponse } from "./models/token-update-response.js"; +export { trustAnchorSchema, type TrustAnchor } from "./models/trust-anchor.js"; +export { UserCodeCharset, userCodeCharsetSchema } from "./models/user-code-charset.js"; +export { userinfoIssueRequestSchema, type UserinfoIssueRequest } from "./models/userinfo-issue-request.js"; +export { userinfoIssueResponseSchema, type UserinfoIssueResponse } from "./models/userinfo-issue-response.js"; +export { userinfoRequestSchema, type UserinfoRequest } from "./models/userinfo-request.js"; +export { userinfoResponseSchema, type UserinfoResponse } from "./models/userinfo-response.js"; +export { vciBatchIssueRequestSchema, type VciBatchIssueRequest } from "./models/vci-batch-issue-request.js"; +export { + vciBatchIssueResponseSchema, + type VciBatchIssueResponse, +} from "./models/vci-batch-issue-response.js"; +export { vciBatchParseRequestSchema, type VciBatchParseRequest } from "./models/vci-batch-parse-request.js"; +export { + vciBatchParseResponseSchema, + type VciBatchParseResponse, +} from "./models/vci-batch-parse-response.js"; +export { + vciDeferredIssueRequestSchema, + type VciDeferredIssueRequest, +} from "./models/vci-deferred-issue-request.js"; +export { + vciDeferredIssueResponseSchema, + type VciDeferredIssueResponse, +} from "./models/vci-deferred-issue-response.js"; +export { + vciDeferredParseRequestSchema, + type VciDeferredParseRequest, +} from "./models/vci-deferred-parse-request.js"; +export { + vciDeferredParseResponseSchema, + type VciDeferredParseResponse, +} from "./models/vci-deferred-parse-response.js"; +export { vciJwksResponseSchema, type VciJwksResponse } from "./models/vci-jwks-response.js"; +export { vciJwtissuerResponseSchema, type VciJwtissuerResponse } from "./models/vci-jwtissuer-response.js"; +export { vciMetadataRequestSchema, type VciMetadataRequest } from "./models/vci-metadata-request.js"; +export { vciMetadataResponseSchema, type VciMetadataResponse } from "./models/vci-metadata-response.js"; +export { + vciOfferCreateRequestSchema, + type VciOfferCreateRequest, +} from "./models/vci-offer-create-request.js"; +export { + vciOfferCreateResponseSchema, + type VciOfferCreateResponse, +} from "./models/vci-offer-create-response.js"; +export { vciOfferInfoRequestSchema, type VciOfferInfoRequest } from "./models/vci-offer-info-request.js"; +export { vciOfferInfoResponseSchema, type VciOfferInfoResponse } from "./models/vci-offer-info-response.js"; +export { + vciSingleIssueRequestSchema, + type VciSingleIssueRequest, +} from "./models/vci-single-issue-request.js"; +export { + vciSingleIssueResponseSchema, + type VciSingleIssueResponse, +} from "./models/vci-single-issue-response.js"; +export { + vciSingleParseRequestSchema, + type VciSingleParseRequest, +} from "./models/vci-single-parse-request.js"; +export { + vciSingleParseResponseSchema, + type VciSingleParseResponse, +} from "./models/vci-single-parse-response.js"; +export { + VerifiedClaimsValidationSchema, + verifiedClaimsValidationSchemaSchema, +} from "./models/verified-claims-validation-schema.js"; + +export { + CoreError as AuthleteApiError, + ResponseError, + DecodeError, + EncodeError, + ConnectionError, + TimeoutError, + AuthError, + ConfigurationError, +} from "./core/errors.js"; +export { ApiError } from "./core/api-error.js"; +export { SchemaError } from "./core/validation/schema-error.js"; +export { + defaultLoggingOptions, + type LoggingOptions, + type RequestLoggingOptions, + type Logger, + type LogDetails, + type HttpRequestDetails, + type RequestHeadersDetails, + type RequestBodyDetails, + type HttpResponseDetails, + type ResponseHeadersDetails, + type HttpRetryingDetails, + type HttpFailedDetails, + type LogLevel, +} from "./core/logging.js"; +export type { ApiPromise, ApiResult } from "./core/api-promise.js"; +export type { HttpMethod, RequestOptions } from "./core/api-request.js"; +export type { RetryOptions, RequestRetryOptions, RetryAttempt, RetryReason } from "./core/retry.js"; +export type { BinaryContent, BinaryData, BinaryErrorContent, FileData, FileInput } from "./core/binary.js"; +export type { ErrorKind } from "./core/errors.js"; +export type { Declared, Undeclared } from "./core/api-error.js"; +export type { Schema, EnumSchema, Encoded } from "./core/validation/schema.js"; diff --git a/plugins/authlete/sdk/typescript/src/models/access-token.ts b/plugins/authlete/sdk/typescript/src/models/access-token.ts new file mode 100644 index 00000000..49de1ab6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/access-token.ts @@ -0,0 +1,46 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { grantTypeSchema, type GrantType } from "./grant-type.js"; +import { propertySchema, type Property } from "./property.js"; + +export type AccessToken = { + /** The hash of the access token. */ + accessTokenHash?: string; + /** The timestamp at which the access token will expire. */ + accessTokenExpiresAt?: number; + /** The hash of the refresh token. */ + refreshTokenHash?: string; + /** The timestamp at which the refresh token will expire. */ + refreshTokenExpiresAt?: number; + /** The timestamp at which the access token was first created. */ + createdAt?: number; + /** The timestamp at which the access token was last refreshed using the refresh token. */ + lastRefreshedAt?: number; + /** The ID of the client associated with the access token. */ + clientId?: number; + /** The subject (= unique user ID) associated with the access token. */ + subject?: string; + /** The grant type of the access token when the access token was created. */ + grantType?: GrantType; + /** The scopes associated with the access token. */ + scopes?: string[]; + /** The properties associated with the access token. */ + properties?: Property[]; + /** The scopes associated with the refresh token. */ + refreshTokenScopes?: string[]; +}; + +export const accessTokenSchema: Schema = s.object({ + accessTokenHash: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + refreshTokenHash: s.optional(s.string()), + refreshTokenExpiresAt: s.optional(s.int()), + createdAt: s.optional(s.int()), + lastRefreshedAt: s.optional(s.int()), + clientId: s.optional(s.int()), + subject: s.optional(s.string()), + grantType: s.optional(s.lazy(() => grantTypeSchema)), + scopes: s.optional(s.array(s.string())), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + refreshTokenScopes: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/action.ts b/plugins/authlete/sdk/typescript/src/models/action.ts new file mode 100644 index 00000000..57f117ec --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Location: "LOCATION", + Form: "FORM", + NoInteraction: "NO_INTERACTION", + Interaction: "INTERACTION", +} as const; +export type Action = (typeof Action)[keyof typeof Action] | (string & {}); + +export const actionSchema: EnumSchema = s.enumOf(Action); diff --git a/plugins/authlete/sdk/typescript/src/models/action1.ts b/plugins/authlete/sdk/typescript/src/models/action1.ts new file mode 100644 index 00000000..890dc32c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action1.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action1 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Location: "LOCATION", + Form: "FORM", +} as const; +export type Action1 = (typeof Action1)[keyof typeof Action1] | (string & {}); + +export const action1Schema: EnumSchema = s.enumOf(Action1); diff --git a/plugins/authlete/sdk/typescript/src/models/action11.ts b/plugins/authlete/sdk/typescript/src/models/action11.ts new file mode 100644 index 00000000..5569b22f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action11.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action11 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + Json: "JSON", + Jwt: "JWT", +} as const; +export type Action11 = (typeof Action11)[keyof typeof Action11] | (string & {}); + +export const action11Schema: EnumSchema = s.enumOf(Action11); diff --git a/plugins/authlete/sdk/typescript/src/models/action12.ts b/plugins/authlete/sdk/typescript/src/models/action12.ts new file mode 100644 index 00000000..6d9a2060 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action12.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the implementation of the token endpoint should take. */ +export const Action12 = { + Ok: "OK", + InternalServerError: "INTERNAL_SERVER_ERROR", + CallerError: "CALLER_ERROR", +} as const; +export type Action12 = (typeof Action12)[keyof typeof Action12] | (string & {}); + +export const action12Schema: EnumSchema = s.enumOf(Action12); diff --git a/plugins/authlete/sdk/typescript/src/models/action13.ts b/plugins/authlete/sdk/typescript/src/models/action13.ts new file mode 100644 index 00000000..8fdad0ec --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action13.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action13 = { + Ok: "OK", + NoContent: "NO_CONTENT", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + NotFound: "NOT_FOUND", + CallerError: "CALLER_ERROR", + AuthleteError: "AUTHLETE_ERROR", +} as const; +export type Action13 = (typeof Action13)[keyof typeof Action13] | (string & {}); + +export const action13Schema: EnumSchema = s.enumOf(Action13); diff --git a/plugins/authlete/sdk/typescript/src/models/action14.ts b/plugins/authlete/sdk/typescript/src/models/action14.ts new file mode 100644 index 00000000..2975947f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action14.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action14 = { + BadRequest: "BAD_REQUEST", + Created: "CREATED", + Deleted: "DELETED", + InternalServerError: "INTERNAL_SERVER_ERROR", + Ok: "OK", + Unauthorized: "UNAUTHORIZED", + Updated: "UPDATED", +} as const; +export type Action14 = (typeof Action14)[keyof typeof Action14] | (string & {}); + +export const action14Schema: EnumSchema = s.enumOf(Action14); diff --git a/plugins/authlete/sdk/typescript/src/models/action15.ts b/plugins/authlete/sdk/typescript/src/models/action15.ts new file mode 100644 index 00000000..62210f04 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action15.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action15 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + UserIdentification: "USER_IDENTIFICATION", +} as const; +export type Action15 = (typeof Action15)[keyof typeof Action15] | (string & {}); + +export const action15Schema: EnumSchema = s.enumOf(Action15); diff --git a/plugins/authlete/sdk/typescript/src/models/action16.ts b/plugins/authlete/sdk/typescript/src/models/action16.ts new file mode 100644 index 00000000..1dc37fda --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action16.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action16 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + InvalidTicket: "INVALID_TICKET", + Ok: "OK", +} as const; +export type Action16 = (typeof Action16)[keyof typeof Action16] | (string & {}); + +export const action16Schema: EnumSchema = s.enumOf(Action16); diff --git a/plugins/authlete/sdk/typescript/src/models/action17.ts b/plugins/authlete/sdk/typescript/src/models/action17.ts new file mode 100644 index 00000000..ccc771cf --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action17.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action17 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Forbidden: "FORBIDDEN", +} as const; +export type Action17 = (typeof Action17)[keyof typeof Action17] | (string & {}); + +export const action17Schema: EnumSchema = s.enumOf(Action17); diff --git a/plugins/authlete/sdk/typescript/src/models/action18.ts b/plugins/authlete/sdk/typescript/src/models/action18.ts new file mode 100644 index 00000000..23182d7e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action18.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action18 = { + ServerError: "SERVER_ERROR", + NoAction: "NO_ACTION", + Notification: "NOTIFICATION", +} as const; +export type Action18 = (typeof Action18)[keyof typeof Action18] | (string & {}); + +export const action18Schema: EnumSchema = s.enumOf(Action18); diff --git a/plugins/authlete/sdk/typescript/src/models/action19.ts b/plugins/authlete/sdk/typescript/src/models/action19.ts new file mode 100644 index 00000000..19e4ac50 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action19.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the API caller should take. */ +export const Action19 = { + Ok: "OK", + ServerError: "SERVER_ERROR", + CallerError: "CALLER_ERROR", +} as const; +export type Action19 = (typeof Action19)[keyof typeof Action19] | (string & {}); + +export const action19Schema: EnumSchema = s.enumOf(Action19); diff --git a/plugins/authlete/sdk/typescript/src/models/action20.ts b/plugins/authlete/sdk/typescript/src/models/action20.ts new file mode 100644 index 00000000..7d1956ef --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action20.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action20 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Ok: "OK", +} as const; +export type Action20 = (typeof Action20)[keyof typeof Action20] | (string & {}); + +export const action20Schema: EnumSchema = s.enumOf(Action20); diff --git a/plugins/authlete/sdk/typescript/src/models/action21.ts b/plugins/authlete/sdk/typescript/src/models/action21.ts new file mode 100644 index 00000000..69763ce8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action21.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action21 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + NotExist: "NOT_EXIST", + Expired: "EXPIRED", + Valid: "VALID", +} as const; +export type Action21 = (typeof Action21)[keyof typeof Action21] | (string & {}); + +export const action21Schema: EnumSchema = s.enumOf(Action21); diff --git a/plugins/authlete/sdk/typescript/src/models/action22.ts b/plugins/authlete/sdk/typescript/src/models/action22.ts new file mode 100644 index 00000000..480b666f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action22.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action22 = { + ServerError: "SERVER_ERROR", + UserCodeNotExist: "USER_CODE_NOT_EXIST", + UserCodeExpired: "USER_CODE_EXPIRED", + InvalidRequest: "INVALID_REQUEST", + Success: "SUCCESS", +} as const; +export type Action22 = (typeof Action22)[keyof typeof Action22] | (string & {}); + +export const action22Schema: EnumSchema = s.enumOf(Action22); diff --git a/plugins/authlete/sdk/typescript/src/models/action23.ts b/plugins/authlete/sdk/typescript/src/models/action23.ts new file mode 100644 index 00000000..518c9e45 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action23.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action23 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Forbidden: "FORBIDDEN", + Ok: "OK", +} as const; +export type Action23 = (typeof Action23)[keyof typeof Action23] | (string & {}); + +export const action23Schema: EnumSchema = s.enumOf(Action23); diff --git a/plugins/authlete/sdk/typescript/src/models/action24.ts b/plugins/authlete/sdk/typescript/src/models/action24.ts new file mode 100644 index 00000000..27e9b0ef --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action24.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action24 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Forbidden: "FORBIDDEN", + NotFound: "NOT_FOUND", + Ok: "OK", +} as const; +export type Action24 = (typeof Action24)[keyof typeof Action24] | (string & {}); + +export const action24Schema: EnumSchema = s.enumOf(Action24); diff --git a/plugins/authlete/sdk/typescript/src/models/action25.ts b/plugins/authlete/sdk/typescript/src/models/action25.ts new file mode 100644 index 00000000..b58008ca --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action25.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action25 = { + Ok: "OK", + NotFound: "NOT_FOUND", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action25 = (typeof Action25)[keyof typeof Action25] | (string & {}); + +export const action25Schema: EnumSchema = s.enumOf(Action25); diff --git a/plugins/authlete/sdk/typescript/src/models/action26.ts b/plugins/authlete/sdk/typescript/src/models/action26.ts new file mode 100644 index 00000000..2b15a30d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action26.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action26 = { + Ok: "OK", + BadRequest: "BAD_REQUEST", + NotFound: "NOT_FOUND", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action26 = (typeof Action26)[keyof typeof Action26] | (string & {}); + +export const action26Schema: EnumSchema = s.enumOf(Action26); diff --git a/plugins/authlete/sdk/typescript/src/models/action27.ts b/plugins/authlete/sdk/typescript/src/models/action27.ts new file mode 100644 index 00000000..b09a30b3 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action27.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** Result of the API call. */ +export const Action27 = { + Success: "SUCCESS", + InvalidRequest: "INVALID_REQUEST", + NotFound: "NOT_FOUND", + ServerError: "SERVER_ERROR", +} as const; +export type Action27 = (typeof Action27)[keyof typeof Action27] | (string & {}); + +export const action27Schema: EnumSchema = s.enumOf(Action27); diff --git a/plugins/authlete/sdk/typescript/src/models/action28.ts b/plugins/authlete/sdk/typescript/src/models/action28.ts new file mode 100644 index 00000000..0b1dc44f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action28.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** Result of the API call */ +export const Action28 = { + Success: "SUCCESS", + InvalidRequest: "INVALID_REQUEST", + NotFound: "NOT_FOUND", + ServerError: "SERVER_ERROR", +} as const; +export type Action28 = (typeof Action28)[keyof typeof Action28] | (string & {}); + +export const action28Schema: EnumSchema = s.enumOf(Action28); diff --git a/plugins/authlete/sdk/typescript/src/models/action3.ts b/plugins/authlete/sdk/typescript/src/models/action3.ts new file mode 100644 index 00000000..56b79e54 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action3.ts @@ -0,0 +1,18 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The next action that the authorization server implementation should take. Any other value other + * than "CREATED" should be handled as unsuccessful result. + */ +export const Action3 = { + Created: "CREATED", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + PayloadTooLarge: "PAYLOAD_TOO_LARGE", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action3 = (typeof Action3)[keyof typeof Action3] | (string & {}); + +export const action3Schema: EnumSchema = s.enumOf(Action3); diff --git a/plugins/authlete/sdk/typescript/src/models/action30.ts b/plugins/authlete/sdk/typescript/src/models/action30.ts new file mode 100644 index 00000000..c8b857b8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action30.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** Result of the API call */ +export const Action30 = { + Success: "SUCCESS", + InvalidRequest: "INVALID_REQUEST", + ServerError: "SERVER_ERROR", +} as const; +export type Action30 = (typeof Action30)[keyof typeof Action30] | (string & {}); + +export const action30Schema: EnumSchema = s.enumOf(Action30); diff --git a/plugins/authlete/sdk/typescript/src/models/action31.ts b/plugins/authlete/sdk/typescript/src/models/action31.ts new file mode 100644 index 00000000..ce15a2a7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action31.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The next action that the implementation of the credential issuer metadata endpoint + * (`/.well-known/openid-credential-issuer`) should take after getting a response from Authlete's + * `/vci/metadata` API. + */ +export const Action31 = { + Ok: "OK", + NotFound: "NOT_FOUND", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action31 = (typeof Action31)[keyof typeof Action31] | (string & {}); + +export const action31Schema: EnumSchema = s.enumOf(Action31); diff --git a/plugins/authlete/sdk/typescript/src/models/action32.ts b/plugins/authlete/sdk/typescript/src/models/action32.ts new file mode 100644 index 00000000..69a12718 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action32.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The next action that the implementation of the JWT issuer metadata endpoint + * (`/.well-known/jwt-issuer`) should take after getting a response from Authlete's `/vci/jwtissuer` + * API. + */ +export const Action32 = { + Ok: "OK", + NotFound: "NOT_FOUND", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action32 = (typeof Action32)[keyof typeof Action32] | (string & {}); + +export const action32Schema: EnumSchema = s.enumOf(Action32); diff --git a/plugins/authlete/sdk/typescript/src/models/action33.ts b/plugins/authlete/sdk/typescript/src/models/action33.ts new file mode 100644 index 00000000..9cd31490 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action33.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The next action that the implementation of the JWK Set document endpoint of the credential issuer + * should take after getting a response from Authlete's `/vci/jwks` API. + */ +export const Action33 = { + Ok: "OK", + NotFound: "NOT_FOUND", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action33 = (typeof Action33)[keyof typeof Action33] | (string & {}); + +export const action33Schema: EnumSchema = s.enumOf(Action33); diff --git a/plugins/authlete/sdk/typescript/src/models/action34.ts b/plugins/authlete/sdk/typescript/src/models/action34.ts new file mode 100644 index 00000000..043da0f1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action34.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The result of the `/vci/offer/create` API call. */ +export const Action34 = { + Created: "CREATED", + Forbidden: "FORBIDDEN", + CallerError: "CALLER_ERROR", + AuthleteError: "AUTHLETE_ERROR", +} as const; +export type Action34 = (typeof Action34)[keyof typeof Action34] | (string & {}); + +export const action34Schema: EnumSchema = s.enumOf(Action34); diff --git a/plugins/authlete/sdk/typescript/src/models/action35.ts b/plugins/authlete/sdk/typescript/src/models/action35.ts new file mode 100644 index 00000000..cc77439b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action35.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The result of the `/vci/offer/info` API call. */ +export const Action35 = { + Ok: "OK", + Forbidden: "FORBIDDEN", + NotFound: "NOT_FOUND", + CallerError: "CALLER_ERROR", + AuthleteError: "AUTHLETE_ERROR", +} as const; +export type Action35 = (typeof Action35)[keyof typeof Action35] | (string & {}); + +export const action35Schema: EnumSchema = s.enumOf(Action35); diff --git a/plugins/authlete/sdk/typescript/src/models/action36.ts b/plugins/authlete/sdk/typescript/src/models/action36.ts new file mode 100644 index 00000000..2ab90024 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action36.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the credential endpoint should take. */ +export const Action36 = { + Ok: "OK", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action36 = (typeof Action36)[keyof typeof Action36] | (string & {}); + +export const action36Schema: EnumSchema = s.enumOf(Action36); diff --git a/plugins/authlete/sdk/typescript/src/models/action37.ts b/plugins/authlete/sdk/typescript/src/models/action37.ts new file mode 100644 index 00000000..e9544a89 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action37.ts @@ -0,0 +1,18 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the implementation of the credential endpoint should take. */ +export const Action37 = { + Ok: "OK", + OkJwt: "OK_JWT", + Accepted: "ACCEPTED", + AcceptedJwt: "ACCEPTED_JWT", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", + CallerError: "CALLER_ERROR", +} as const; +export type Action37 = (typeof Action37)[keyof typeof Action37] | (string & {}); + +export const action37Schema: EnumSchema = s.enumOf(Action37); diff --git a/plugins/authlete/sdk/typescript/src/models/action38.ts b/plugins/authlete/sdk/typescript/src/models/action38.ts new file mode 100644 index 00000000..998d4179 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action38.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the batch credential endpoint should take. */ +export const Action38 = { + Ok: "OK", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action38 = (typeof Action38)[keyof typeof Action38] | (string & {}); + +export const action38Schema: EnumSchema = s.enumOf(Action38); diff --git a/plugins/authlete/sdk/typescript/src/models/action39.ts b/plugins/authlete/sdk/typescript/src/models/action39.ts new file mode 100644 index 00000000..82d3b476 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action39.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the implementation of the batch credential endpoint should take. */ +export const Action39 = { + Ok: "OK", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", + CallerError: "CALLER_ERROR", +} as const; +export type Action39 = (typeof Action39)[keyof typeof Action39] | (string & {}); + +export const action39Schema: EnumSchema = s.enumOf(Action39); diff --git a/plugins/authlete/sdk/typescript/src/models/action4.ts b/plugins/authlete/sdk/typescript/src/models/action4.ts new file mode 100644 index 00000000..41b2d4f5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action4.ts @@ -0,0 +1,18 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action4 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + InvalidClient: "INVALID_CLIENT", + BadRequest: "BAD_REQUEST", + Password: "PASSWORD", + Ok: "OK", + TokenExchange: "TOKEN_EXCHANGE", + JwtBearer: "JWT_BEARER", + NativeSso: "NATIVE_SSO", + IdTokenReissuable: "ID_TOKEN_REISSUABLE", +} as const; +export type Action4 = (typeof Action4)[keyof typeof Action4] | (string & {}); + +export const action4Schema: EnumSchema = s.enumOf(Action4); diff --git a/plugins/authlete/sdk/typescript/src/models/action40.ts b/plugins/authlete/sdk/typescript/src/models/action40.ts new file mode 100644 index 00000000..03a114ef --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action40.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the deferred credential endpoint should take. */ +export const Action40 = { + Ok: "OK", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", +} as const; +export type Action40 = (typeof Action40)[keyof typeof Action40] | (string & {}); + +export const action40Schema: EnumSchema = s.enumOf(Action40); diff --git a/plugins/authlete/sdk/typescript/src/models/action41.ts b/plugins/authlete/sdk/typescript/src/models/action41.ts new file mode 100644 index 00000000..e4094bf4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action41.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the implementation of the deferred credential endpoint should take. */ +export const Action41 = { + Ok: "OK", + Forbidden: "FORBIDDEN", + InternalServerError: "INTERNAL_SERVER_ERROR", + CallerError: "CALLER_ERROR", +} as const; +export type Action41 = (typeof Action41)[keyof typeof Action41] | (string & {}); + +export const action41Schema: EnumSchema = s.enumOf(Action41); diff --git a/plugins/authlete/sdk/typescript/src/models/action42.ts b/plugins/authlete/sdk/typescript/src/models/action42.ts new file mode 100644 index 00000000..dc4a9b87 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action42.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The result of the `/auth/authorization/ticket/info` API call. */ +export const Action42 = { + Ok: "OK", + NotFound: "NOT_FOUND", + CallerError: "CALLER_ERROR", + AuthleteError: "AUTHLETE_ERROR", +} as const; +export type Action42 = (typeof Action42)[keyof typeof Action42] | (string & {}); + +export const action42Schema: EnumSchema = s.enumOf(Action42); diff --git a/plugins/authlete/sdk/typescript/src/models/action43.ts b/plugins/authlete/sdk/typescript/src/models/action43.ts new file mode 100644 index 00000000..06f8199b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action43.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The result of the /auth/authorization/ticket/info API call. */ +export const Action43 = { + Ok: "OK", + NotFound: "NOT_FOUND", + CallerError: "CALLER_ERROR", + AuthleteError: "AUTHLETE_ERROR", +} as const; +export type Action43 = (typeof Action43)[keyof typeof Action43] | (string & {}); + +export const action43Schema: EnumSchema = s.enumOf(Action43); diff --git a/plugins/authlete/sdk/typescript/src/models/action5.ts b/plugins/authlete/sdk/typescript/src/models/action5.ts new file mode 100644 index 00000000..a75f6660 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action5.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action5 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", +} as const; +export type Action5 = (typeof Action5)[keyof typeof Action5] | (string & {}); + +export const action5Schema: EnumSchema = s.enumOf(Action5); diff --git a/plugins/authlete/sdk/typescript/src/models/action6.ts b/plugins/authlete/sdk/typescript/src/models/action6.ts new file mode 100644 index 00000000..0f758039 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action6.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action6 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + Ok: "OK", +} as const; +export type Action6 = (typeof Action6)[keyof typeof Action6] | (string & {}); + +export const action6Schema: EnumSchema = s.enumOf(Action6); diff --git a/plugins/authlete/sdk/typescript/src/models/action7.ts b/plugins/authlete/sdk/typescript/src/models/action7.ts new file mode 100644 index 00000000..08352dbf --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action7.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action7 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Unauthorized: "UNAUTHORIZED", + Forbidden: "FORBIDDEN", + Ok: "OK", +} as const; +export type Action7 = (typeof Action7)[keyof typeof Action7] | (string & {}); + +export const action7Schema: EnumSchema = s.enumOf(Action7); diff --git a/plugins/authlete/sdk/typescript/src/models/action8.ts b/plugins/authlete/sdk/typescript/src/models/action8.ts new file mode 100644 index 00000000..73c32d32 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action8.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action8 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + BadRequest: "BAD_REQUEST", + Ok: "OK", + Jwt: "JWT", +} as const; +export type Action8 = (typeof Action8)[keyof typeof Action8] | (string & {}); + +export const action8Schema: EnumSchema = s.enumOf(Action8); diff --git a/plugins/authlete/sdk/typescript/src/models/action9.ts b/plugins/authlete/sdk/typescript/src/models/action9.ts new file mode 100644 index 00000000..644e28ae --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/action9.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The next action that the authorization server implementation should take. */ +export const Action9 = { + InternalServerError: "INTERNAL_SERVER_ERROR", + InvalidClient: "INVALID_CLIENT", + BadRequest: "BAD_REQUEST", + Ok: "OK", +} as const; +export type Action9 = (typeof Action9)[keyof typeof Action9] | (string & {}); + +export const action9Schema: EnumSchema = s.enumOf(Action9); diff --git a/plugins/authlete/sdk/typescript/src/models/api-client-registration-delete-request.ts b/plugins/authlete/sdk/typescript/src/models/api-client-registration-delete-request.ts new file mode 100644 index 00000000..f3606d6f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/api-client-registration-delete-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ApiClientRegistrationDeleteRequest = { + /** + * Client metadata in JSON format that complies with [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration + * Protocol). + */ + json?: string; + /** The client registration access token. Used only for GET, UPDATE, and DELETE requests. */ + token: string; + /** The client's identifier. Used for GET, UPDATE, and DELETE requests */ + clientId: string; +}; + +export const apiClientRegistrationDeleteRequestSchema: Schema = + s.object({ + json: s.optional(s.string()), + token: s.string(), + clientId: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/api-client-registration-get-request.ts b/plugins/authlete/sdk/typescript/src/models/api-client-registration-get-request.ts new file mode 100644 index 00000000..9311aecf --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/api-client-registration-get-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ApiClientRegistrationGetRequest = { + /** + * Client metadata in JSON format that complies with [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration + * Protocol). + */ + json?: string; + /** The client registration access token. Used only for GET, UPDATE, and DELETE requests. */ + token: string; + /** The client's identifier. Used for GET, UPDATE, and DELETE requests */ + clientId: string; +}; + +export const apiClientRegistrationGetRequestSchema: Schema = + s.object({ + json: s.optional(s.string()), + token: s.string(), + clientId: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/api-client-registration-request.ts b/plugins/authlete/sdk/typescript/src/models/api-client-registration-request.ts new file mode 100644 index 00000000..954cac33 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/api-client-registration-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ApiClientRegistrationRequest = { + /** + * Client metadata in JSON format that complies with [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration + * Protocol). + */ + json: string; + /** The client registration access token. Used only for GET, UPDATE, and DELETE requests. */ + token?: string; + /** The client's identifier. Used for GET, UPDATE, and DELETE requests */ + clientId?: string; +}; + +export const apiClientRegistrationRequestSchema: Schema = + s.object({ + json: s.string(), + token: s.optional(s.string()), + clientId: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/api-client-registration-update-request.ts b/plugins/authlete/sdk/typescript/src/models/api-client-registration-update-request.ts new file mode 100644 index 00000000..a20bbcdf --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/api-client-registration-update-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ApiClientRegistrationUpdateRequest = { + /** + * Client metadata in JSON format that complies with [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration + * Protocol). + */ + json: string; + /** The client registration access token. Used only for GET, UPDATE, and DELETE requests. */ + token: string; + /** The client's identifier. Used for GET, UPDATE, and DELETE requests */ + clientId: string; +}; + +export const apiClientRegistrationUpdateRequestSchema: Schema = + s.object({ + json: s.string(), + token: s.string(), + clientId: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/api-federation-configuration-request.ts b/plugins/authlete/sdk/typescript/src/models/api-federation-configuration-request.ts new file mode 100644 index 00000000..25d9fb6a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/api-federation-configuration-request.ts @@ -0,0 +1,18 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { entityTypeSchema, type EntityType } from "./entity-type.js"; + +export type ApiFederationConfigurationRequest = { + /** + * The entity types for which the entity configuration is requested. When omitted or empty, it + * defaults to `OPENID_PROVIDER` only (backward compatibility). Unsupported entity types are + * ignored. `OPENID_CREDENTIAL_ISSUER` requires the Verifiable Credentials feature (Authlete + * 3.0+). + */ + entityTypes?: EntityType[]; +}; + +export const apiFederationConfigurationRequestSchema: Schema = + s.object({ + entityTypes: s.optional(s.array(s.lazy(() => entityTypeSchema))), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/application-type.ts b/plugins/authlete/sdk/typescript/src/models/application-type.ts new file mode 100644 index 00000000..1f2c968d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/application-type.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The application type. The value of this property affects the validation steps for a redirect URI. + * See the description about `redirectUris` property for more details. + */ +export const ApplicationType = { + Web: "WEB", + Native: "NATIVE", +} as const; +export type ApplicationType = (typeof ApplicationType)[keyof typeof ApplicationType] | (string & {}); + +export const applicationTypeSchema: EnumSchema = s.enumOf(ApplicationType); diff --git a/plugins/authlete/sdk/typescript/src/models/attachment-type.ts b/plugins/authlete/sdk/typescript/src/models/attachment-type.ts new file mode 100644 index 00000000..279ee3aa --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/attachment-type.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * Supported attachment types. This property corresponds to the `attachments_supported` server + * metadata which was added by the third implementer's draft of OpenID Connect for Identity + * Assurance 1.0. + */ +export const AttachmentType = { + Embedded: "EMBEDDED", + External: "EXTERNAL", +} as const; +export type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType] | (string & {}); + +export const attachmentTypeSchema: EnumSchema = s.enumOf(AttachmentType); diff --git a/plugins/authlete/sdk/typescript/src/models/audit-entries-get-response.ts b/plugins/authlete/sdk/typescript/src/models/audit-entries-get-response.ts new file mode 100644 index 00000000..9d9d9a67 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/audit-entries-get-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { auditLogEntrySchema, type AuditLogEntry } from "./audit-log-entry.js"; + +export type AuditEntriesGetResponse = { + /** + * The audit log entries on this page, in reverse chronological order. Entries sharing the same + * timestamp are never split across pages, so a page may contain more than `limit` entries. + */ + entries?: AuditLogEntry[]; + /** + * An opaque cursor pointing at the next page. Pass this value as the `cursor` parameter of the + * next request to fetch the following page. Absent or `null` on the last page. + */ + nextCursor?: string | null; + /** + * The effective page size limit applied to this response. May differ from the requested `limit` + * if the requested value was outside the allowed range. + */ + limit?: number; +}; + +export const auditEntriesGetResponseSchema: Schema = + s.object({ + entries: s.optional(s.array(s.lazy(() => auditLogEntrySchema))), + nextCursor: s.optionalNullable(s.string()), + limit: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/audit-log-entry.ts b/plugins/authlete/sdk/typescript/src/models/audit-log-entry.ts new file mode 100644 index 00000000..ab3c7532 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/audit-log-entry.ts @@ -0,0 +1,39 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { statusSchema, type Status } from "./status.js"; + +export type AuditLogEntry = { + /** The audit event type (e.g. service.create, security.login). */ + event?: string; + /** Whether the action completed successfully or failed. */ + status?: Status; + /** + * When the event occurred (ISO 8601). May be null for entries without a recorded timestamp; such + * entries are only returned on the first page of results. + */ + timestamp?: Date | null; + /** The cluster or region where the event occurred. */ + cluster?: string; + /** The API path that triggered the event. */ + path?: string; + /** The IP address of the client that performed the action. */ + remoteAddr?: string; + /** The user agent of the client. */ + userAgent?: string; + /** The user or token that performed the action. */ + user?: string; + /** Additional event-specific metadata. */ + details?: Record; +}; + +export const auditLogEntrySchema: Schema = s.object({ + event: s.optional(s.string()), + status: s.optional(s.lazy(() => statusSchema)), + timestamp: s.optionalNullable(s.dateTime()), + cluster: s.optional(s.string()), + path: s.optional(s.string()), + remoteAddr: s.optional(s.string()), + userAgent: s.optional(s.string()), + user: s.optional(s.string()), + details: s.optional(s.record(s.string(), s.unknown())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-details-element.ts b/plugins/authlete/sdk/typescript/src/models/authorization-details-element.ts new file mode 100644 index 00000000..69fbfc48 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-details-element.ts @@ -0,0 +1,72 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type AuthorizationDetailsElement = { + /** + * The type of this element. + * + * From _"OAuth 2.0 Rich Authorization Requests"_: _"The type of authorization data as a string. + * This field MAY define which other elements are allowed in the request. This element is + * REQUIRED."_ + * + * This property is always NOT `null`. + */ + type: string; + /** + * The resources and/or resource servers. This property may be `null`. + * + * From _"OAuth 2.0 Rich Authorization Requests"_: _"An array of strings representing the location + * of the resource or resource server. This is typically composed of URIs."_ + * + * This property may be `null`. + */ + locations?: string[]; + /** + * The actions. + * + * From _"OAuth 2.0 Rich Authorization Requests"_: _"An array of strings representing the kinds of + * actions to be taken at the resource. The values of the strings are determined by the API being + * protected."_ + * + * This property may be `null`. + */ + actions?: string[]; + /** + * From _"OAuth 2.0 Rich Authorization Requests"_: _"An array of strings representing the kinds of + * data being requested from the resource."_ + * + * This property may be `null`. + */ + dataTypes?: string[]; + /** + * The identifier of a specific resource. From _"OAuth 2.0 Rich Authorization Requests"_: _"A + * string identifier indicating a specific resource available at the API."_ + * + * This property may be `null`. + */ + identifier?: string; + /** + * The types or levels of privilege. From "OAuth 2.0 Rich Authorization Requests": _"An array of + * strings representing the types or levels of privilege being requested at the resource."_ + * + * This property may be `null`. + */ + privileges?: string[]; + /** + * The RAR request in the JSON format excluding the pre-defined attributes such as `type` and + * `locations`. The content and semantics are specific to the deployment and the use case + * implemented. + */ + otherFields?: string; +}; + +export const authorizationDetailsElementSchema: Schema = + s.object({ + type: s.string(), + locations: s.optional(s.array(s.string())), + actions: s.optional(s.array(s.string())), + dataTypes: s.optional(s.array(s.string())), + identifier: s.optional(s.string()), + privileges: s.optional(s.array(s.string())), + otherFields: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-fail-request.ts b/plugins/authlete/sdk/typescript/src/models/authorization-fail-request.ts new file mode 100644 index 00000000..ecb7df1c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-fail-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { reasonSchema, type Reason } from "./reason.js"; + +export type AuthorizationFailRequest = { + /** The ticket issued from Authlete `/auth/authorization` API. */ + ticket: string; + /** + * The reason of the failure of the authorization request. For more details, see [NO_INTERACTION] + * in the description of `/auth/authorization` API. + */ + reason: Reason; + /** The custom description about the authorization failure. */ + description?: string; +}; + +export const authorizationFailRequestSchema: Schema = + s.object({ + ticket: s.string(), + reason: reasonSchema, + description: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-fail-response.ts b/plugins/authlete/sdk/typescript/src/models/authorization-fail-response.ts new file mode 100644 index 00000000..a9ec0bfc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-fail-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action1Schema, type Action1 } from "./action1.js"; + +export type AuthorizationFailResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action1; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; +}; + +export const authorizationFailResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action1Schema)), + responseContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/authorization-issue-request.ts new file mode 100644 index 00000000..0c6c1b11 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-issue-request.ts @@ -0,0 +1,114 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { propertySchema, type Property } from "./property.js"; + +export type AuthorizationIssueRequest = { + /** The ticket issued from Authlete `/auth/authorization` API. */ + ticket: string; + /** + * The subject (= a user account managed by the service) who has granted authorization to the + * client application. + */ + subject: string; + /** + * The time when the authentication of the end-user occurred. Its value is the number of seconds + * from `1970-01-01`. + */ + authTime?: number; + /** The Authentication Context Class Reference performed for the end-user authentication. */ + acr?: string; + /** + * The claims of the end-user (= pieces of information about the end-user) in JSON format. See + * [OpenID Connect Core 1.0, 5.1. Standard + * Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) for details about + * the format. + */ + claims?: string; + /** Extra properties to associate with an access token and/or an authorization code. */ + properties?: Property[]; + /** + * Scopes to associate with an access token and/or an authorization code. If a non-empty string + * array is given, it replaces the scopes specified by the original authorization request. + */ + scopes?: string[]; + /** + * The value of the `sub` claim to embed in an ID token. If this request parameter is `null` or + * empty, the value of the `subject` request parameter is used as the value of the `sub` claim. + */ + sub?: string; + /** + * JSON that represents additional JWS header parameters for ID tokens that may be issued based on + * the authorization request. + */ + idtHeaderParams?: string; + /** Claim key-value pairs that are used to compute transformed claims. */ + claimsForTx?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the access + * token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The session ID of the user's authentication session. The specified value will be embedded in + * the ID token as the value of the `sid` claim. This parameter needs to be provided only if you + * want to support the [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (a.k.a. "Native + * SSO"). To enable support for the Native SSO specification, the `nativeSsoSupported` property of + * your service must be set to `true`. + */ + sessionId?: string; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; + /** Values of verified claims requested indirectly by "transformed claims". */ + verifiedClaimsForTx?: string[]; +}; + +export const authorizationIssueRequestSchema: Schema = + s.object({ + ticket: s.string(), + subject: s.string(), + authTime: s.optional(s.int()), + acr: s.optional(s.string()), + claims: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + scopes: s.optional(s.array(s.string())), + sub: s.optional(s.string()), + idtHeaderParams: s.optional(s.string()), + claimsForTx: s.optional(s.string()), + consentedClaims: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + jwtAtClaims: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + sessionId: s.optional(s.string()), + idTokenAudType: s.optional(s.string()), + verifiedClaimsForTx: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/authorization-issue-response.ts new file mode 100644 index 00000000..fcf7703e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-issue-response.ts @@ -0,0 +1,64 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action1Schema, type Action1 } from "./action1.js"; +import { authorizationTicketInfoSchema, type AuthorizationTicketInfo } from "./authorization-ticket-info.js"; + +export type AuthorizationIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action1; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * The newly issued access token. Note that an access token is issued from an authorization + * endpoint only when `response_type` contains token. + */ + accessToken?: string; + /** + * The datetime at which the newly issued access token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + accessTokenExpiresAt?: number; + /** The duration of the newly issued access token in seconds. */ + accessTokenDuration?: number; + /** + * The newly issued ID token. Note that an ID token is issued from an authorization endpoint only + * when `response_type` contains `id_token`. + */ + idToken?: string; + /** + * The newly issued authorization code. Note that an authorization code is issued only when + * `response_type` contains code. + */ + authorizationCode?: string; + /** + * The newly issued access token in JWT format. If the service is not configured to issue + * JWT-based access tokens, this property is always set to `null`. + */ + jwtAccessToken?: string; + ticketInfo?: AuthorizationTicketInfo; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; +}; + +export const authorizationIssueResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action1Schema)), + responseContent: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + accessTokenDuration: s.optional(s.int()), + idToken: s.optional(s.string()), + authorizationCode: s.optional(s.string()), + jwtAccessToken: s.optional(s.string()), + ticketInfo: s.optional(s.lazy(() => authorizationTicketInfoSchema)), + consentedClaims: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-request.ts b/plugins/authlete/sdk/typescript/src/models/authorization-request.ts new file mode 100644 index 00000000..7eb83160 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-request.ts @@ -0,0 +1,42 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cimdOptionsSchema, type CimdOptions } from "./cimd-options.js"; + +export type AuthorizationRequest = { + /** + * OAuth 2.0 authorization request parameters which are the request parameters that the OAuth 2.0 + * authorization endpoint of the authorization server implementation received from the client + * application. + * + * The value of parameters is either (1) the entire query string when the HTTP method of the + * request from the client application is `GET` or (2) the entire entity body (which is formatted + * in `application/x-www-form-urlencoded`) when the HTTP method of the request from the client + * application is `POST`. + */ + parameters: string; + /** + * The arbitrary text to be attached to the ticket that will be issued from the + * `/auth/authorization` API. + * + * The text can be retrieved later by the `/auth/authorization/ticket/info` API and can be updated + * by the `/auth/authorization/ticket/update` API. + * + * The text will be compressed and encrypted when it is saved in the Authlete database. + */ + context?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; +}; + +export const authorizationRequestSchema: Schema = s.object({ + parameters: s.string(), + context: s.optional(s.string()), + cimdOptions: s.optional(s.lazy(() => cimdOptionsSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-response.ts b/plugins/authlete/sdk/typescript/src/models/authorization-response.ts new file mode 100644 index 00000000..510b31a2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-response.ts @@ -0,0 +1,391 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { actionSchema, type Action } from "./action.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { + clientLimitedAuthorizationSchema, + type ClientLimitedAuthorization, +} from "./client-limited-authorization.js"; +import { credentialOfferInfoSchema, type CredentialOfferInfo } from "./credential-offer-info.js"; +import { displaySchema, type Display } from "./display.js"; +import { dynamicScopeSchema, type DynamicScope } from "./dynamic-scope.js"; +import { grantManagementActionSchema, type GrantManagementAction } from "./grant-management-action.js"; +import { grantSchema, type Grant } from "./grant.js"; +import { promptSchema, type Prompt } from "./prompt.js"; +import { scopeSchema, type Scope } from "./scope.js"; +import { serviceSchema, type Service } from "./service.js"; + +export type AuthorizationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action; + client?: ClientLimitedAuthorization; + /** + * The display mode which the client application requests by `display` request parameter. When the + * authorization request does not have `display` request parameter, `PAGE` is set as the default + * value. + * + * It is ensured that the value of `display` is one of the supported display modes which are + * specified by `supportedDisplays` configuration parameter of the service. If the display mode + * specified by the authorization request is not supported, an error is raised. + * + * Values for this property correspond to the values listed in "[OpenID Connect Core 1.0, 3.1.2.1. + * Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), + * display". + */ + display?: Display; + /** + * The maximum authentication age. This value comes from `max_age` request parameter, or + * `defaultMaxAge` configuration parameter of the client application when the authorization + * request does not contain `max_age` request parameter. + * + * See "[OpenID Connect Core 1.0, 3.1.2.1. Authentication + * Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), max_age" for + * `max_age` request parameter, and see "[OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata), + * default_max_age" for `defaultMaxAge` configuration parameter. + */ + maxAge?: number; + service?: Service; + /** + * The scopes that the client application requests. This value comes from `scope` request + * parameter. If the request does not contain `scope` parameter, this parameter is a list of + * scopes which are registered as default. If the authorization request does not have `scope` + * request parameter and the service has not registered any default scope, the value of this + * parameter is `null`. It is ensured that scopes listed by this parameters are contained in the + * list of supported scopes which are specified by `supportedScopes` configuration parameter of + * the service. Unsupported scopes in the authorization request do not cause an error and are just + * ignored. Note that, if `response_type` request parameter does not contain code, + * `offline_access` scope is removed from this list even when scope request parameter contains + * `offline_access`. This behavior is a requirement written in [OpenID Connect Core 1.0, 11. + * Offline Access](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess). OpenID + * Connect defines some scope names which need to be treated specially. The table below lists the + * special scope names. + * | Name | Description | + * | --- | --- | + * | `openid` | This scope must be contained in `scope` request parameter to promote an OAuth 2.0 authorization request to an OpenID Connect request. It is described in "[OpenID Connect Core 1.0, 3.1.2.1. Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), scope". | + * | `profile` | This scope is used to request some claims to be embedded in the ID token. The claims are `name`, `family_name`, `given_name`, `middle_name`, `nickname`, `preferred_username`, `profile`, `picture`, `website`, `gender`, `birthdate`, `zoneinfo`, `locale`, and `updated_at`. It is described in [OpenID Connect Core 1.0, 5.4. Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims). | + * | `email` | This scope is used to request some claims to be embedded in the ID token. The claims are `email` and `email_verified`. It is described in [OpenID Connect Core 1.0, 5.4. Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims). | + * | `address` | This scope is used to request `address` claim to be embedded in the ID token. It is described in [OpenID Connect Core 1.0, 5.4. Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims). The format of `address` claim is not a simple string. It is described in [OpenID Connect Core 1.0, 5.1.1. Address Claim](https://openid.net/specs/openid-connect-core-1_0.html#AddressClaim). | + * | `phone` | This scope is used to request some claims to be embedded in the ID token. The claims are `phone_number` and `phone_number_verified`. It is described in [OpenID Connect Core 1.0, 5.4. Requesting Claims using Scope Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims). | + * | `offline_access` | The following is an excerpt about this scope from [OpenID Connect Core 1.0, 11. Offline Access](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess): "This scope value requests that an OAuth 2.0 Refresh Token be issued that can be used to obtain an Access Token that grants access to the end-user's userinfo endpoint even when the end-user is not present (not logged in)." | + */ + scopes?: Scope[]; + /** + * The locales that the client application presented as candidates to be used for UI. This value + * comes from `ui_locales` request parameter. The format of `ui_locales` is a space-separated list + * of language tag values defined in [RFC5646](https://datatracker.ietf.org/doc/html/rfc5646). See + * "[OpenID Connect Core 1.0, 3.1.2.1. Authentication + * Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), ui_locales" for + * details. + * + * It is ensured that locales listed by this parameters are contained in the list of supported UI + * locales which are specified by `supportedUiLocales` configuration parameter of the service. + * Unsupported UI locales in the authorization request do not cause an error and are just ignored. + */ + uiLocales?: string[]; + /** + * End-user's preferred languages and scripts for claims. This value comes from `claims_locales` + * request parameter. The format of `claims_locales` is a space-separated list of language tag + * values defined in [RFC5646](https://datatracker.ietf.org/doc/html/rfc5646). See "[OpenID + * Connect Core 1.0, 5.2. Claims Languages and + * Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts)" for + * details. + * + * It is ensured that locales listed by this parameters are contained in the list of supported + * claim locales which are specified by `supportedClaimsLocales` configuration parameter of the + * service. Unsupported claim locales in the authorization request do not cause an error and are + * just ignored. + */ + claimsLocales?: string[]; + /** + * The list of claims that the client application requests to be embedded in the ID token. The + * value comes from (1) `id_token` in `claims` request parameter [1] and/or (2) special scopes + * (`profile`, `email`, `address` and `phone`) which are expanded to claims. + * + * See [OpenID Connect Core 1.0, 5.5. Requesting Claims using the "claims" Request + * Parameter](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter) for `claims` + * request parameter, and see [OpenID Connect Core 1.0, 5.4. Requesting Claims using Scope + * Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) for the special + * scopes. + */ + claims?: string[]; + /** + * This boolean value indicates whether the authentication of the end-user must be one of the ACRs + * (Authentication Context Class References) listed in `acrs` parameter. This parameter becomes + * `true` only when (1) the authorization request contains `claims` request parameter and (2) + * `acr` claim is in it, and (3) `essential` property of the `acr` claim is `true`. See [OpenID + * Connect Core 1.0, 5.5.1.1. Requesting the "acr" + * Claim](https://openid.net/specs/openid-connect-core-1_0.html#acrSemantics) for details. + */ + acrEssential?: boolean; + /** + * `true` if the value of the `client_id` request parameter included in the authorization request + * is the client ID alias. `false` if the value is the original numeric client ID. + */ + clientIdAliasUsed?: boolean; + /** + * The list of ACRs (Authentication Context Class References) one of which the client application + * requests to be satisfied for the authentication of the end-user. This value comes from + * `acr_values` request parameter or `defaultAcrs` configuration parameter of the client + * application. + * + * See "[OpenID Connect Core 1.0, 3.1.2.1. Authentication + * Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), acr_values" for + * `acr_values` request parameter, and see "[OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata), + * default_acr_values" for `defaultAcrs` configuration parameter. + */ + acrs?: string[]; + /** + * The subject (= unique user ID managed by the authorization server implementation) that the + * client application expects to grant authorization. The value comes from `sub` claim in `claims` + * request parameter. + */ + subject?: string; + /** + * A hint about the login identifier of the end-user. The value comes from `login_hint` request + * parameter. + */ + loginHint?: string; + /** + * The list of values of prompt request parameter. See "[OpenID Connect Core 1.0, 3.1.2.1. + * Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), + * prompt" for prompt request parameter. + */ + prompts?: Prompt[]; + /** + * The prompt that the UI displayed to the end-user must satisfy as the minimum level. This value + * comes from `prompt` request parameter. + * + * When the authorization request does not contain `prompt` request parameter, `CONSENT` is used + * as the default value. + * + * See "[OpenID Connect Core 1.0, 3.1.2.1. Authentication + * Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), prompt" for + * `prompt` request parameter. + */ + lowestPrompt?: Prompt; + /** + * The payload part of the request object. The value of this proprty is `null` if the + * authorization request does not include a request object. + */ + requestObjectPayload?: string; + /** + * The value of the `id_token` property in the claims request parameter or in the claims property + * in a request object. + */ + idTokenClaims?: string; + /** + * The value of the `userinfo` property in the `claims` request parameter or in the `claims` + * property in a request object. + */ + userInfoClaims?: string; + /** + * The resources specified by the `resource` request parameters or by the `resource` property in + * the request object. If both are given, the values in the request object should be set. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** + * The `purpose` request parameter is defined in [9. Transaction-specific + * Purpose](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#name-transaction-specific-purpos) + * of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) as follows: + * + * > purpose: OPTIONAL. String describing the purpose for obtaining certain user data from the OP. + * The purpose MUST NOT be shorter than 3 characters and MUST NOT be longer than 300 characters. + * If these rules are violated, the authentication request MUST fail and the OP returns an error + * invalid_request to the RP. + */ + purpose?: string; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * A ticket issued by Authlete to the service implementation. This is needed when the service + * implementation calls either `/auth/authorization/fail` API or `/auth/authorization/issue` API. + */ + ticket?: string; + /** The dynamic scopes which the client application requested by the scope request parameter. */ + dynamicScopes?: DynamicScope[]; + /** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ + gmAction?: GrantManagementAction; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + grant?: Grant; + /** + * The subject identifying the user who has given the grant identified by the `grant_id` request + * parameter of the device authorization request. Authlete 2.3 and newer versions support [Grant + * Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). An + * authorization request may contain a `grant_id` request parameter which is defined in the + * specification. If the value of the request parameter is valid, {@link + * #getGrantSubject()} will return the subject of the user who has given the grant to the + * client application. Authorization server implementations may use the value returned from + * {@link #getGrantSubject()} in order to determine the user to authenticate. The user + * your system will authenticate during the authorization process (or has already authenticated) + * may be different from the user of the grant. The first implementer's draft of "Grant Management + * for OAuth 2.0" does not mention anything about the case, so the behavior in the case is left to + * implementations. Authlete will not perform the grant management action when the `subject` + * passed to Authlete does not match the user of the grant. + */ + grantSubject?: string; + /** + * Names of claims that are requested indirectly by *"transformed claims"*. A client application + * can request *"transformed claims"* by adding names of transformed claims in the `claims` + * request parameter. The following is an example of the `claims` request parameter that requests + * a predefined transformed claim named `18_or_over` and a transformed claim named + * `nationality_usa` to be embedded in the response from the userinfo endpoint. + * ```json + * { + * "transformed_claims": { + * "nationality_usa": { + * "claim": "nationalities", + * "fn": [ + * [ "eq", "USA" ], + * "any" + * ] + * } + * }, + * "userinfo": { + * "::18_or_over": null, + * ":nationality_usa": null + * } + * } + * ``` + * The example above assumes that a transformed claim named `18_or_over` is predefined by the + * authorization server like below. + * ```json + * { + * "18_or_over": { + * "claim": "birthdate", + * "fn": [ + * "years_ago", + * [ "gte", 18 ] + * ] + * } + * } + * ``` + * In the example, the `nationalities` claim is requested indirectly by the `nationality_usa` + * transformed claim. Likewise, the `birthdate` claim is requested indirectly by the `18_or_over` + * transformed claim. When the `claims` request parameter of an authorization request is like the + * example above, this `requestedClaimsForTx` property will hold the following value. + * ```json + * [ "birthdate", "nationalities" ] + * ``` + * It is expected that the authorization server implementation prepares values of the listed + * claims and passes them as the value of the `claimsForTx` request parameter when it calls the + * `/api/auth/userinfo/issue` API. The following is an example of the value of the `claimsForTx` + * request parameter. + * ```json + * { + * "birthdate": "1970-01-23", + * "nationalities": [ "DEU", "USA" ] + * } + * ``` + */ + requestedClaimsForTx?: string[]; + /** Names of verified claims that will be referenced when transformed claims are computed. */ + requestedVerifiedClaimsForTx?: string[][]; + /** + * the value of the `transformed_claims` property in the `claims` request parameter of an + * authorization request or in the `claims` property in a request object. + */ + transformedClaims?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The list of claims that the client application requests to be embedded in userinfo responses. + * The value comes from the `"scope"` and `"claims"` request parameters of the original + * authorization request. + */ + claimsAtUserInfo?: string[]; + credentialOfferInfo?: CredentialOfferInfo; + /** + * The information about the **issuable credentials** that can be obtained by presenting the + * access token that will be issued as a result of the authorization request. + */ + issuableCredentials?: string; + /** + * Flag which indicates whether [Native + * SSO](https://openid.net/specs/openid-connect-native-sso-1_0.html) is requested. This property + * should be set to `true` when all the following conditions are satisfied: + */ + nativeSsoRequested?: boolean; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const authorizationResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => actionSchema)), + client: s.optional(s.lazy(() => clientLimitedAuthorizationSchema)), + display: s.optional(s.lazy(() => displaySchema)), + maxAge: s.optional(s.int()), + service: s.optional(s.lazy(() => serviceSchema)), + scopes: s.optional(s.array(s.lazy(() => scopeSchema))), + uiLocales: s.optional(s.array(s.string())), + claimsLocales: s.optional(s.array(s.string())), + claims: s.optional(s.array(s.string())), + acrEssential: s.optional(s.boolean()), + clientIdAliasUsed: s.optional(s.boolean()), + acrs: s.optional(s.array(s.string())), + subject: s.optional(s.string()), + loginHint: s.optional(s.string()), + prompts: s.optional(s.array(s.lazy(() => promptSchema))), + lowestPrompt: s.optional(s.lazy(() => promptSchema)), + requestObjectPayload: s.optional(s.string()), + idTokenClaims: s.optional(s.string()), + userInfoClaims: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + purpose: s.optional(s.string()), + responseContent: s.optional(s.string()), + ticket: s.optional(s.string()), + dynamicScopes: s.optional(s.array(s.lazy(() => dynamicScopeSchema))), + gmAction: s.optional(s.lazy(() => grantManagementActionSchema)), + grantId: s.optional(s.string()), + grant: s.optional(s.lazy(() => grantSchema)), + grantSubject: s.optional(s.string()), + requestedClaimsForTx: s.optional(s.array(s.string())), + requestedVerifiedClaimsForTx: s.optional(s.array(s.array(s.string()))), + transformedClaims: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + claimsAtUserInfo: s.optional(s.array(s.string())), + credentialOfferInfo: s.optional(s.lazy(() => credentialOfferInfoSchema)), + issuableCredentials: s.optional(s.string()), + nativeSsoRequested: s.optional(s.boolean()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-request.ts b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-request.ts new file mode 100644 index 00000000..e777a02d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-request.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type AuthorizationTicketInfoRequest = { + /** The ticket that has been issued from the `/auth/authorization` API. */ + ticket: string; +}; + +export const authorizationTicketInfoRequestSchema: Schema = + s.object({ + ticket: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-response.ts b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-response.ts new file mode 100644 index 00000000..54453ebf --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action42Schema, type Action42 } from "./action42.js"; +import { authorizationTicketInfoSchema, type AuthorizationTicketInfo } from "./authorization-ticket-info.js"; + +export type AuthorizationTicketInfoResponse = { + info?: AuthorizationTicketInfo; + /** The result of the `/auth/authorization/ticket/info` API call. */ + action?: Action42; + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; +}; + +export const authorizationTicketInfoResponseSchema: Schema = + s.object({ + info: s.optional(s.lazy(() => authorizationTicketInfoSchema)), + action: s.optional(s.lazy(() => action42Schema)), + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info.ts b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info.ts new file mode 100644 index 00000000..44d449cc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-info.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type AuthorizationTicketInfo = { + /** The arbitrary text attached to the ticket. */ + context?: string; +}; + +export const authorizationTicketInfoSchema: Schema = + s.object({ + context: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-request.ts b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-request.ts new file mode 100644 index 00000000..daa43044 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { authorizationTicketInfoSchema, type AuthorizationTicketInfo } from "./authorization-ticket-info.js"; + +export type AuthorizationTicketUpdateRequest = { + /** The ticket. */ + ticket: string; + info: AuthorizationTicketInfo; +}; + +export const authorizationTicketUpdateRequestSchema: Schema = + s.object({ + ticket: s.string(), + info: authorizationTicketInfoSchema, + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-response.ts b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-response.ts new file mode 100644 index 00000000..0e04448f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authorization-ticket-update-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action43Schema, type Action43 } from "./action43.js"; +import { authorizationTicketInfoSchema, type AuthorizationTicketInfo } from "./authorization-ticket-info.js"; + +export type AuthorizationTicketUpdateResponse = { + info?: AuthorizationTicketInfo; + /** The result of the /auth/authorization/ticket/info API call. */ + action?: Action43; + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; +}; + +export const authorizationTicketUpdateResponseSchema: Schema = + s.object({ + info: s.optional(s.lazy(() => authorizationTicketInfoSchema)), + action: s.optional(s.lazy(() => action43Schema)), + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/authz-details.ts b/plugins/authlete/sdk/typescript/src/models/authz-details.ts new file mode 100644 index 00000000..f801436f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/authz-details.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + authorizationDetailsElementSchema, + type AuthorizationDetailsElement, +} from "./authorization-details-element.js"; + +/** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ +export type AuthzDetails = { + /** Elements of this authorization details. */ + elements?: AuthorizationDetailsElement[]; +}; + +export const authzDetailsSchema: Schema = s.object({ + elements: s.optional(s.array(s.lazy(() => authorizationDetailsElementSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-request.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-request.ts new file mode 100644 index 00000000..adf3da56 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-request.ts @@ -0,0 +1,108 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { propertySchema, type Property } from "./property.js"; +import { result1Schema, type Result1 } from "./result1.js"; + +export type BackchannelAuthenticationCompleteRequest = { + /** The ticket issued by Authlete's `/backchannel/authentication` API. */ + ticket: string; + /** + * The result of the end-user authentication and authorization. One of the following. Details are + * described in the description. + */ + result: Result1; + /** The subject (= unique identifier) of the end-user. */ + subject: string; + /** The value of the sub claim that should be used in the ID token. */ + sub?: string; + /** + * The time at which the end-user was authenticated. Its value is the number of seconds from + * `1970-01-01`. + */ + authTime?: number; + /** + * The reference of the authentication context class which the end-user authentication satisfied. + */ + acr?: string; + /** Additional claims which will be embedded in the ID token. */ + claims?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** + * Scopes to replace the scopes specified in the original backchannel authentication request with. + * When nothing is specified for this parameter, replacement is not performed. + */ + scopes?: string[]; + /** JSON that represents additional JWS header parameters for ID tokens. */ + idtHeaderParams?: string; + /** + * The description of the error. If this optional request parameter is given, its value is used as + * the value of the `error_description` property, but it is used only when the result is not + * `AUTHORIZED`. To comply with the specification strictly, the description must not include + * characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. This corresponds to the `error_uri` + * property in the response to the client. + */ + errorUri?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the access + * token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the refresh + * token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; +}; + +export const backchannelAuthenticationCompleteRequestSchema: Schema = + s.object({ + ticket: s.string(), + result: result1Schema, + subject: s.string(), + sub: s.optional(s.string()), + authTime: s.optional(s.int()), + acr: s.optional(s.string()), + claims: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + scopes: s.optional(s.array(s.string())), + idtHeaderParams: s.optional(s.string()), + errorDescription: s.optional(s.string()), + errorUri: s.optional(s.string()), + consentedClaims: s.optional(s.array(s.string())), + jwtAtClaims: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + idTokenAudType: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-response.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-response.ts new file mode 100644 index 00000000..c2a474cb --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-complete-response.ts @@ -0,0 +1,147 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action18Schema, type Action18 } from "./action18.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { deliveryModeSchema, type DeliveryMode } from "./delivery-mode.js"; +import { pairSchema, type Pair } from "./pair.js"; + +export type BackchannelAuthenticationCompleteResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action18; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * The client ID of the client application that has made the backchannel authentication request. + */ + clientId?: number; + /** + * The client ID alias of the client application that has made the backchannel authentication + * request. + */ + clientIdAlias?: string; + /** + * `true` if the value of the client_id request parameter included in the backchannel + * authentication request is the client ID alias. `false` if the value is the original numeric + * client ID. + */ + clientIdAliasUsed?: boolean; + /** The name of the client application which has made the backchannel authentication request. */ + clientName?: string; + deliveryMode?: DeliveryMode; + /** + * The client notification endpoint to which a notification needs to be sent. This corresponds to + * the `client_notification_endpoint` metadata of the client application. + */ + clientNotificationEndpoint?: string; + /** + * The client notification token which needs to be embedded as a Bearer token in the Authorization + * header in the notification. This is the value of the `client_notification_token` request + * parameter included in the backchannel authentication request. + */ + clientNotificationToken?: string; + /** The newly issued authentication request ID. */ + authReqId?: string; + /** The issued access token. */ + accessToken?: string; + /** The issued refresh token. */ + refreshToken?: string; + /** The issued ID token. */ + idToken?: string; + /** The duration of the access token in seconds. */ + accessTokenDuration?: number; + /** The duration of the refresh token in seconds. */ + refreshTokenDuration?: number; + /** The duration of the ID token in seconds. */ + idTokenDuration?: number; + /** The issued access token in JWT format. */ + jwtAccessToken?: string; + /** + * The resources specified by the `resource` request parameters or by the `resource` property in + * the request object. If both are given, the values in the request object should be set. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; +}; + +export const backchannelAuthenticationCompleteResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action18Schema)), + responseContent: s.optional(s.string()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + clientName: s.optional(s.string()), + deliveryMode: s.optional(s.lazy(() => deliveryModeSchema)), + clientNotificationEndpoint: s.optional(s.string()), + clientNotificationToken: s.optional(s.string()), + authReqId: s.optional(s.string()), + accessToken: s.optional(s.string()), + refreshToken: s.optional(s.string()), + idToken: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + idTokenDuration: s.optional(s.int()), + jwtAccessToken: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + grantId: s.optional(s.string()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), + consentedClaims: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-request.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-request.ts new file mode 100644 index 00000000..e98e2ade --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-request.ts @@ -0,0 +1,37 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { reason2Schema, type Reason2 } from "./reason2.js"; + +export type BackchannelAuthenticationFailRequest = { + /** + * The ticket which should be deleted on a call of Authlete's `/backchannel/authentication/fail` + * API. This request parameter is not mandatory but optional. If this request parameter is given + * and the ticket belongs to the service, the specified ticket is deleted from the database. + * Giving this parameter is recommended to clean up the storage area for the service. + */ + ticket: string; + /** + * The reason of the failure of the backchannel authentication request. This request parameter is + * not mandatory but optional. However, giving this parameter is recommended. If omitted, + * `SERVER_ERROR` is used as a reason. + */ + reason: Reason2; + /** + * The description of the error. This corresponds to the `error_description` property in the + * response to the client. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. If this optional request parameter + * is given, its value is used as the value of the `error_uri` property. + */ + errorUri?: string; +}; + +export const backchannelAuthenticationFailRequestSchema: Schema = + s.object({ + ticket: s.string(), + reason: reason2Schema, + errorDescription: s.optional(s.string()), + errorUri: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-response.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-response.ts new file mode 100644 index 00000000..ba845e7a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-fail-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action17Schema, type Action17 } from "./action17.js"; + +export type BackchannelAuthenticationFailResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action17; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; +}; + +export const backchannelAuthenticationFailResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action17Schema)), + responseContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-request.ts new file mode 100644 index 00000000..d059abc2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-request.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type BackchannelAuthenticationIssueRequest = { + /** The ticket issued from Authlete's `/backchannel/authentication` API. */ + ticket: string; +}; + +export const backchannelAuthenticationIssueRequestSchema: Schema = + s.object({ + ticket: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-response.ts new file mode 100644 index 00000000..0afe6267 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-issue-response.ts @@ -0,0 +1,37 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action16Schema, type Action16 } from "./action16.js"; + +export type BackchannelAuthenticationIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action16; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** The newly issued authentication request ID. */ + authReqId?: string; + /** The duration of the issued authentication request ID in seconds. */ + expiresIn?: number; + /** + * The minimum amount of time in seconds that the client must wait for between polling requests to + * the token endpoint. + */ + interval?: number; +}; + +export const backchannelAuthenticationIssueResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action16Schema)), + responseContent: s.optional(s.string()), + authReqId: s.optional(s.string()), + expiresIn: s.optional(s.int()), + interval: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-request.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-request.ts new file mode 100644 index 00000000..044459bd --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-request.ts @@ -0,0 +1,78 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cimdOptionsSchema, type CimdOptions } from "./cimd-options.js"; + +export type BackchannelAuthenticationRequest = { + /** + * Parameters of a backchannel authentication request which are the request parameters that the + * backchannel authentication endpoint of the OpenID provider implementation received from the + * client application. + * + * The value of `parameters` is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from Authorization header of the backchannel authentication request + * from the client application. + * + * If the backchannel authentication endpoint of the OpenID provider implementation supports Basic + * Authentication as a means of client authentication, and the request from the client application + * contained its client ID in Authorization header, the value should be extracted and set to this + * parameter. + */ + clientId?: string; + /** + * The client secret extracted from Authorization header of the backchannel authentication request + * from the client application. + * + * If the backchannel authentication endpoint of the OpenID provider implementation supports Basic + * Authentication as a means of client authentication, and the request from the client application + * contained its client secret in Authorization header, the value should be extracted and set to + * this parameter. + */ + clientSecret?: string; + /** + * The client certification used in the TLS connection between the client application and the + * backchannel authentication endpoint of the OpenID provider. + */ + clientCertificate?: string; + /** + * The client certificate path presented by the client during client authentication. Each element + * is a string in PEM format. + */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification + * of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; +}; + +export const backchannelAuthenticationRequestSchema: Schema = + s.object({ + parameters: s.string(), + clientId: s.optional(s.string()), + clientSecret: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + clientCertificatePath: s.optional(s.array(s.string())), + oauthClientAttestation: s.optional(s.string()), + oauthClientAttestationPop: s.optional(s.string()), + cimdOptions: s.optional(s.lazy(() => cimdOptionsSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-response.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-response.ts new file mode 100644 index 00000000..0e673033 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-authentication-response.ts @@ -0,0 +1,222 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action15Schema, type Action15 } from "./action15.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { deliveryModeSchema, type DeliveryMode } from "./delivery-mode.js"; +import { dynamicScopeSchema, type DynamicScope } from "./dynamic-scope.js"; +import { grantManagementActionSchema, type GrantManagementAction } from "./grant-management-action.js"; +import { grantSchema, type Grant } from "./grant.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { scopeSchema, type Scope } from "./scope.js"; + +export type BackchannelAuthenticationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action15; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * The client ID of the client application that has made the backchannel authentication request. + */ + clientId?: number; + /** + * The client ID alias of the client application that has made the backchannel authentication + * request. + */ + clientIdAlias?: string; + /** + * `true` if the value of the client_id request parameter included in the backchannel + * authentication request is the client ID alias. `false` if the value is the original numeric + * client ID. + */ + clientIdAliasUsed?: boolean; + /** The name of the client application which has made the backchannel authentication request. */ + clientName?: string; + /** The scopes requested by the backchannel authentication request. */ + scopes?: Scope[]; + /** + * The names of the claims which were requested indirectly via some special scopes. See [5.4. + * Requesting Claims using Scope + * Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) in OpenID Connect + * Core 1.0 for details. + */ + claimNames?: string[]; + /** The client notification token included in the backchannel authentication request. */ + clientNotificationToken?: string; + /** + * The list of ACR values requested by the backchannel authentication request. + * + * Basically, this property holds the value of the `acr_values` request parameter in the + * backchannel authentication request. However, because unsupported ACR values are dropped on + * Authlete side, if the `acr_values` request parameter contains unrecognized ACR values, the list + * returned by this property becomes different from the value of the `acr_values` request + * parameter. + */ + acrs?: string[]; + /** + * The type of the hint for end-user identification which was included in the backchannel + * authentication request. + */ + hintType?: string; + /** The value of the hint for end-user identification. */ + hint?: string; + /** + * The value of the `sub` claim contained in the ID token hint included in the backchannel + * authentication request. + */ + sub?: string; + /** The binding message included in the backchannel authentication request. */ + bindingMessage?: string; + /** The binding message included in the backchannel authentication request. */ + userCode?: string; + /** + * The flag which indicates whether a user code is required. + * + * `true` when both the `backchannel_user_code_parameter` metadata of the client (= Client's + * `bcUserCodeRequired` property) and the `backchannel_user_code_parameter_supported` metadata of + * the service (= Service's `backchannelUserCodeParameterSupported` property) are `true`. + */ + userCodeRequired?: boolean; + /** The requested expiry for the authentication request ID (`auth_req_id`). */ + requestedExpiry?: number; + /** + * The request context of the backchannel authentication request. + * + * It is the value of the request_context claim in the signed authentication request and its + * format is JSON. request_context is a new claim added by the FAPI-CIBA profile. + */ + requestContext?: string; + /** The warnings raised during processing the backchannel authentication request. */ + warnings?: string[]; + /** + * The ticket which is necessary to call Authlete's `/auth/token/fail` API or `/auth/token/issue` + * API. + * + * This parameter has a value only if the value of `grant_type` request parameter is `password` + * and the token request is valid. + */ + ticket?: string; + /** + * The resources specified by the `resource` request parameters or by the `resource` property in + * the request object. If both are given, the values in the request object should be set. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** The dynamic scopes which the client application requested by the scope request parameter. */ + dynamicScopes?: DynamicScope[]; + deliveryMode?: DeliveryMode; + /** The client authentication method that was performed. */ + clientAuthMethod?: string; + /** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ + gmAction?: GrantManagementAction; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + grant?: Grant; + /** + * The subject identifying the user who has given the grant identified by the `grant_id` request + * parameter of the device authorization request. Authlete 2.3 and newer versions support [Grant + * Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). An + * authorization request may contain a `grant_id` request parameter which is defined in the + * specification. If the value of the request parameter is valid, {@link + * #getGrantSubject()} will return the subject of the user who has given the grant to the + * client application. Authorization server implementations may use the value returned from + * {@link #getGrantSubject()} in order to determine the user to authenticate. The user + * your system will authenticate during the authorization process (or has already authenticated) + * may be different from the user of the grant. The first implementer's draft of "Grant Management + * for OAuth 2.0" does not mention anything about the case, so the behavior in the case is left to + * implementations. Authlete will not perform the grant management action when the `subject` + * passed to Authlete does not match the user of the grant. + */ + grantSubject?: string; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const backchannelAuthenticationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action15Schema)), + responseContent: s.optional(s.string()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + clientName: s.optional(s.string()), + scopes: s.optional(s.array(s.lazy(() => scopeSchema))), + claimNames: s.optional(s.array(s.string())), + clientNotificationToken: s.optional(s.string()), + acrs: s.optional(s.array(s.string())), + hintType: s.optional(s.string()), + hint: s.optional(s.string()), + sub: s.optional(s.string()), + bindingMessage: s.optional(s.string()), + userCode: s.optional(s.string()), + userCodeRequired: s.optional(s.boolean()), + requestedExpiry: s.optional(s.int()), + requestContext: s.optional(s.string()), + warnings: s.optional(s.array(s.string())), + ticket: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + dynamicScopes: s.optional(s.array(s.lazy(() => dynamicScopeSchema))), + deliveryMode: s.optional(s.lazy(() => deliveryModeSchema)), + clientAuthMethod: s.optional(s.string()), + gmAction: s.optional(s.lazy(() => grantManagementActionSchema)), + grantId: s.optional(s.string()), + grant: s.optional(s.lazy(() => grantSchema)), + grantSubject: s.optional(s.string()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-request.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-request.ts new file mode 100644 index 00000000..385f1771 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-request.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type BackchannelLogoutTokenRequest = { + /** The identifier of the client application. Either a client ID or a client alias. */ + clientIdentifier: string; + /** + * The subject (end-user) identifier. The logout token will be issued for this subject. At least + * one of `subject` or `sessionId` must be provided. + */ + subject?: string; + /** + * The session ID (`sid`) identifying the user session to log out. At least one of `subject` or + * `sessionId` must be provided. + */ + sessionId?: string; +}; + +export const backchannelLogoutTokenRequestSchema: Schema = + s.object({ + clientIdentifier: s.string(), + subject: s.optional(s.string()), + sessionId: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-response.ts b/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-response.ts new file mode 100644 index 00000000..9597a252 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/backchannel-logout-token-response.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action19Schema, type Action19 } from "./action19.js"; + +export type BackchannelLogoutTokenResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the API caller should take. */ + action?: Action19; + /** + * The logout token issued for the client. The caller should deliver this token to the client's + * `backchannelLogoutUri`. + */ + logoutToken?: string; + /** + * The backchannel logout URI of the client. The caller should POST the `logoutToken` to this URI. + */ + backchannelLogoutUri?: string; +}; + +export const backchannelLogoutTokenResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action19Schema)), + logoutToken: s.optional(s.string()), + backchannelLogoutUri: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/cimd-options.ts b/plugins/authlete/sdk/typescript/src/models/cimd-options.ts new file mode 100644 index 00000000..17cbfe50 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/cimd-options.ts @@ -0,0 +1,74 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +/** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over service-level + * configuration when provided. + */ +export type CimdOptions = { + /** + * Whether to always retrieve client metadata in the CIMD context regardless of the cache's + * validity. + * + * Under normal circumstances, client metadata retrieved from the location referenced by the + * client ID is stored in the database with an expiration time calculated using HTTP caching + * mechanisms (see [RFC 9111 HTTP Caching](https://www.rfc-editor.org/rfc/rfc9111.html)). Until + * that expiration time is reached, Authlete does not attempt to retrieve the client metadata + * again. + * + * When this flag is set to `true`, Authlete retrieves the client metadata regardless of the + * cache's validity. + * + * If this flag is included in an Authlete API call and its value is `true`, it takes precedence + * over the corresponding service configuration (see `Service.cimdAlwaysRetrieved`). + * + * This flag is effective only when the service supports CIMD (see + * `Service.clientIdMetadataDocumentSupported`) and CIMD is actually used to resolve client + * metadata. For example, if the client ID in a request does not appear to be a valid URI, CIMD + * will not be used even if the service is configured to support it. In such cases, this flag has + * no effect. + * + * Client metadata retrieval is performed only in the initiating request of an authorization flow, + * and not in any subsequent requests. For example, in the authorization code flow, metadata may + * be retrieved during the authorization request, but not during the subsequent token request. In + * contrast, in the client credentials flow, metadata retrieval may occur because the token + * request itself is the initiating request in the flow. + */ + alwaysRetrieved?: boolean; + /** + * Whether to allow the `http` scheme in client IDs in the CIMD context. + * + * The specification requires the `https` scheme, but if this flag is set to `true`, Authlete also + * allows the `http` scheme. The main purpose of this option is to make development easier for + * developers who run CIMD-enabled servers and a web server publishing client metadata on their + * local machines without TLS. + * + * Given this purpose, it is not recommended to enable this option in production environments + * unless an allowlist is used (see `Service.cimdAllowlistEnabled`). + * + * If this flag is included in an Authlete API call and its value is `true`, it takes precedence + * over the corresponding service configuration (see `Service.cimdHttpPermitted`). + */ + httpPermitted?: boolean; + /** + * Whether to allow a query component in client IDs in the CIMD context. + * + * Although the specification states that a client ID "SHOULD NOT include a query string + * component," it does technically allow it. However, query components are prone to misuse. + * Therefore, Authlete does not allow them by default. Setting this flag to `true` relaxes that + * restriction. + * + * If this flag is included in an Authlete API call and its value is `true`, it takes precedence + * over the corresponding service configuration (see `Service.cimdQueryPermitted`). + */ + queryPermitted?: boolean; +}; + +export const cimdOptionsSchema: Schema = s.object({ + alwaysRetrieved: s.optional(s.boolean()), + httpPermitted: s.optional(s.boolean()), + queryPermitted: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/claim-type.ts b/plugins/authlete/sdk/typescript/src/models/claim-type.ts new file mode 100644 index 00000000..f1808528 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/claim-type.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const ClaimType = { + Normal: "NORMAL", + Aggregated: "AGGREGATED", + Distributed: "DISTRIBUTED", +} as const; +export type ClaimType = (typeof ClaimType)[keyof typeof ClaimType] | (string & {}); + +export const claimTypeSchema: EnumSchema = s.enumOf(ClaimType); diff --git a/plugins/authlete/sdk/typescript/src/models/client-auth-method.ts b/plugins/authlete/sdk/typescript/src/models/client-auth-method.ts new file mode 100644 index 00000000..1f8e7a37 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-auth-method.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The client authentication method that the client application declares that it uses at the token + * endpoint. This property corresponds to `token_endpoint_auth_method` in [OpenID Connect Dynamic + * Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ +export const ClientAuthMethod = { + None: "NONE", + ClientSecretBasic: "CLIENT_SECRET_BASIC", + ClientSecretPost: "CLIENT_SECRET_POST", + ClientSecretJwt: "CLIENT_SECRET_JWT", + PrivateKeyJwt: "PRIVATE_KEY_JWT", + TlsClientAuth: "TLS_CLIENT_AUTH", + SelfSignedTlsClientAuth: "SELF_SIGNED_TLS_CLIENT_AUTH", + AttestJwtClientAuth: "ATTEST_JWT_CLIENT_AUTH", + SpiffeJwt: "SPIFFE_JWT", +} as const; +export type ClientAuthMethod = (typeof ClientAuthMethod)[keyof typeof ClientAuthMethod] | (string & {}); + +export const clientAuthMethodSchema: EnumSchema = + s.enumOf(ClientAuthMethod); diff --git a/plugins/authlete/sdk/typescript/src/models/client-authorization-delete-response.ts b/plugins/authlete/sdk/typescript/src/models/client-authorization-delete-response.ts new file mode 100644 index 00000000..90596bdc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-authorization-delete-response.ts @@ -0,0 +1,41 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientAuthorizationDeleteResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** A short message which explains the result of the API call. */ + serviceApiKey?: number; + /** Get the client ID. */ + clientId?: number; + /** + * Get the subject (= unique identifier) of the user who has granted authorization to the client. + */ + subject?: string; + /** + * Get the scopes granted to the client application by the last authorization process by the user + * (who is identified by the subject). + */ + latestGrantedScopes?: string[]; + /** + * Get the scopes granted to the client application by all the past authorization processes. Note + * that revoked scopes are not included. + */ + mergedGrantedScopes?: string[]; + /** Get the timestamp in milliseconds since Unix epoch at which this record was modified. */ + modifiedAt?: number; +}; + +export const clientAuthorizationDeleteResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + serviceApiKey: s.optional(s.int()), + clientId: s.optional(s.int()), + subject: s.optional(s.string()), + latestGrantedScopes: s.optional(s.array(s.string())), + mergedGrantedScopes: s.optional(s.array(s.string())), + modifiedAt: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-request.ts b/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-request.ts new file mode 100644 index 00000000..538ba6cb --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-request.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientAuthorizationGetListRequest = { + /** Unique user ID of an end-user. */ + subject: string; + /** Unique ID of a client developer. */ + developer?: string; + /** Start index of search results (inclusive). */ + start?: number; + /** End index of search results (exclusive). */ + end?: number; +}; + +export const clientAuthorizationGetListRequestSchema: Schema = + s.object({ + subject: s.string(), + developer: s.optional(s.string()), + start: s.optional(s.int()), + end: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-response.ts b/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-response.ts new file mode 100644 index 00000000..b9c4f639 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-authorization-get-list-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientLimitedSchema, type ClientLimited } from "./client-limited.js"; + +export type ClientAuthorizationGetListResponse = { + /** Start index of search results (inclusive). */ + start?: number; + /** End index of search results (exclusive). */ + end?: number; + /** Unique ID of a client developer. */ + developer?: string; + /** Unique user ID of an end-user. */ + subject?: string; + /** Unique ID of a client developer. */ + totalCount?: number; + /** An array of clients. */ + clients?: ClientLimited[]; +}; + +export const clientAuthorizationGetListResponseSchema: Schema = + s.object({ + start: s.optional(s.int()), + end: s.optional(s.int()), + developer: s.optional(s.string()), + subject: s.optional(s.string()), + totalCount: s.optional(s.int()), + clients: s.optional(s.array(s.lazy(() => clientLimitedSchema))), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-authorization-update-request.ts b/plugins/authlete/sdk/typescript/src/models/client-authorization-update-request.ts new file mode 100644 index 00000000..61aeac2c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-authorization-update-request.ts @@ -0,0 +1,23 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientAuthorizationUpdateRequest = { + /** + * The subject (= unique identifier) of the end-user who has granted authorization to the client + * application. + */ + subject: string; + /** + * An array of new scopes. Optional. If a non-null value is given, the new scopes are set to all + * existing access tokens. If an API call is made using `"Content-Type: + * application/x-www-form-urlencoded"`, scope names listed in this request parameter should be + * delimited by spaces (after form encoding, spaces are converted to `+`). + */ + scopes?: string[]; +}; + +export const clientAuthorizationUpdateRequestSchema: Schema = + s.object({ + subject: s.string(), + scopes: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-cursor-list-response.ts b/plugins/authlete/sdk/typescript/src/models/client-cursor-list-response.ts new file mode 100644 index 00000000..ccb16435 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-cursor-list-response.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientSchema, type Client } from "./client.js"; + +export type ClientCursorListResponse = { + /** The clamped requested limit of clients to return from the request. */ + limit?: number; + /** + * If there are more entries to retrieve this value will be generated and returned. The next call + * to this endpoint should use this value as the `cursor` to continue to continue to retrieve the + * remaining clients from the correct point. If there are no further clients to list this property + * will not be set. + */ + nextCursor?: string; + /** + * The total number of clients that belong to the service. This doesn't mean the number of clients + * contained in the response. + */ + totalCount?: number; + /** The list of clients. */ + entities?: Client[]; +}; + +export const clientCursorListResponseSchema: Schema = + s.object({ + limit: s.optional(s.int()), + nextCursor: s.optional(s.string()), + totalCount: s.optional(s.int()), + entities: s.optional(s.array(s.lazy(() => clientSchema))), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-get-response.ts b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-get-response.ts new file mode 100644 index 00000000..c8a8438a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-get-response.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientExtensionRequestableScopesGetResponse = { + /** + * The requestable scopes for this client. This is null if the Requestable Scopes per Client + * feature is not enabled for this client. + */ + requestableScopes?: string[] | null; +}; + +export const clientExtensionRequestableScopesGetResponseSchema: Schema = + s.object({ + requestableScopes: s.optionalNullable(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-request.ts b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-request.ts new file mode 100644 index 00000000..180f98a4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-request.ts @@ -0,0 +1,23 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientExtensionRequestableScopesUpdateRequest = { + /** + * The set of scopes that the client application is allowed to request. This parameter will be one + * of the following. Details are described in the description. + * + * + * - an empty set + * - a set with at least one element + * + * If this parameter contains scopes that the service does not support, those scopes are just + * ignored. Also, if this parameter is `null` or is not included in the request, it is equivalent + * to calling `/client/extension/requestable_scopes/delete` API. + */ + requestableScopes?: string[]; +}; + +export const clientExtensionRequestableScopesUpdateRequestSchema: Schema = + s.object({ + requestableScopes: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-response.ts b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-response.ts new file mode 100644 index 00000000..34177ed8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-extension-requestable-scopes-update-response.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientExtensionRequestableScopesUpdateResponse = { + requestableScopes?: string[]; +}; + +export const clientExtensionRequestableScopesUpdateResponseSchema: Schema = + s.object({ + requestableScopes: s.optional(s.array(s.string())), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-extension.ts b/plugins/authlete/sdk/typescript/src/models/client-extension.ts new file mode 100644 index 00000000..7623abf4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-extension.ts @@ -0,0 +1,63 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientExtension = { + /** + * The set of scopes that the client application is allowed to request. This paramter will be one + * of the following. + */ + requestableScopes?: string[]; + /** + * The flag to indicate whether "Requestable Scopes per Client" is enabled or not. If `true`, you + * can define the set of scopes which this client application can request. If `false`, this client + * application can request any scope which is supported by the authorization server. + */ + requestableScopesEnabled?: boolean; + /** + * The value of the duration of access tokens per client in seconds. In normal cases, the value of + * the service's `accessTokenDuration` property is used as the duration of access tokens issued by + * the service. However, if this `accessTokenDuration` property holds a non-zero positive number + * and its value is less than the duration configured by the service, the value is used as the + * duration of access tokens issued to the client application. + * + * Note that the duration of access tokens can be controlled by the scope attribute + * `access_token.duration`, too. Authlete chooses the minimum value among the candidates. + */ + accessTokenDuration?: number; + /** + * The value of the duration of refresh tokens per client in seconds. In normal cases, the value + * of the service's `refreshTokenDuration` property is used as the duration of refresh tokens + * issued by the service. However, if this `refreshTokenDuration` property holds a non-zero + * positive number and its value is less than the duration configured by the service, the value is + * used as the duration of refresh tokens issued to the client application. + * + * Note that the duration of refresh tokens can be controlled by the scope attribute + * `refresh_token.duration`, too. Authlete chooses the minimum value among the candidates. + */ + refreshTokenDuration?: number; + /** + * The value of the duration of ID tokens per client in seconds. In normal cases, the value of the + * service's `idTokenDuration` property is used as the duration of ID tokens issued by the + * service. However, if this `idTokenDuration` property holds a non-zero positive number and its + * value is less than the duration configured by the service, the value is used as the duration of + * ID tokens issued to the client application. + * + * Note that the duration of refresh tokens can be controlled by the scope attribute + * `id_token.duration`, too. Authlete chooses the minimum value among the candidates. + */ + idTokenDuration?: number; + /** + * Get the flag indicating whether the client is explicitly given a permission to make token + * exchange requests ([RFC 8693][https://www.rfc-editor.org/rfc/rfc8693.html]) + */ + tokenExchangePermitted?: boolean; +}; + +export const clientExtensionSchema: Schema = s.object({ + requestableScopes: s.optional(s.array(s.string())), + requestableScopesEnabled: s.optional(s.boolean()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + idTokenDuration: s.optional(s.int()), + tokenExchangePermitted: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/client-flag-update-request.ts b/plugins/authlete/sdk/typescript/src/models/client-flag-update-request.ts new file mode 100644 index 00000000..a18864f8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-flag-update-request.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientFlagUpdateRequest = { + /** The flag value to be set */ + clientLocked: boolean; +}; + +export const clientFlagUpdateRequestSchema: Schema = + s.object({ + clientLocked: s.boolean(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-flag-update-response.ts b/plugins/authlete/sdk/typescript/src/models/client-flag-update-response.ts new file mode 100644 index 00000000..37bab225 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-flag-update-response.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientFlagUpdateResponse = { + /** The code which represents the result of the API call. */ + resultCode: string; + /** A short message which explains the result of the API call. */ + resultMessage: string; +}; + +export const clientFlagUpdateResponseSchema: Schema = + s.object({ + resultCode: s.string(), + resultMessage: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-get-list-response.ts b/plugins/authlete/sdk/typescript/src/models/client-get-list-response.ts new file mode 100644 index 00000000..28617400 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-get-list-response.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientSchema, type Client } from "./client.js"; + +export type ClientGetListResponse = { + /** Start index (inclusive) of the result set of the query. */ + start?: number; + /** End index (exclusive) of the result set of the query. */ + end?: number; + /** + * Total number of clients that belong to the service. This doesn't mean the number of clients + * contained in the response. + */ + totalCount?: number; + /** An array of clients. */ + clients?: Client[]; +}; + +export const clientGetListResponseSchema: Schema = s.object({ + start: s.optional(s.int()), + end: s.optional(s.int()), + totalCount: s.optional(s.int()), + clients: s.optional(s.array(s.lazy(() => clientSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/client-limited-authorization.ts b/plugins/authlete/sdk/typescript/src/models/client-limited-authorization.ts new file mode 100644 index 00000000..11e3c511 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-limited-authorization.ts @@ -0,0 +1,309 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientSourceSchema, type ClientSource } from "./client-source.js"; +import { clientTypeSchema, type ClientType } from "./client-type.js"; +import { taggedValueSchema, type TaggedValue } from "./tagged-value.js"; + +export type ClientLimitedAuthorization = { + /** The sequential number of the client. The value of this property is assigned by Authlete. */ + number?: number; + /** + * The name of the client application. This property corresponds to `client_name` in [OpenID + * Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientName?: string; + /** + * Client names with language tags. If the client application has different names for different + * languages, this property can be used to register the names. + */ + clientNames?: TaggedValue[]; + /** The description about the client application. */ + description?: string; + /** + * Descriptions about the client application with language tags. If the client application has + * different descriptions for different languages, this property can be used to register the + * descriptions. + */ + descriptions?: TaggedValue[]; + /** + * The client identifier used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + clientId?: number; + /** + * The value of the client's `client_id` property used in OAuth and OpenID Connect calls. By + * default, this is a string version of the `clientId` property. + */ + clientIdAlias?: string; + /** Deprecated. Always set to `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client + * Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. + */ + clientType?: ClientType; + /** + * The URL pointing to the logo image of the client application. + * + * This property corresponds to `logo_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + logoUri?: string; + /** + * Logo image URLs with language tags. If the client application has different logo images for + * different languages, this property can be used to register URLs of the images. + */ + logoUris?: TaggedValue[]; + /** + * The URL pointing to the "Terms Of Service" page. + * + * This property corresponds to `tos_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + tosUri?: string; + /** + * URLs of "Terms Of Service" pages with language tags. + * + * If the client application has different "Terms Of Service" pages for different languages, this + * property can be used to register the URLs. + */ + tosUris?: TaggedValue[]; + /** + * The URL pointing to the page which describes the policy as to how end-user's profile data is + * used. + * + * This property corresponds to `policy_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + policyUri?: string; + /** + * URLs of policy pages with language tags. If the client application has different policy pages + * for different languages, this property can be used to register the URLs. + */ + policyUris?: TaggedValue[]; + /** + * The sequential number of the service of the client application. The value of this property is + * assigned by Authlete. + */ + serviceNumber?: number; + /** + * The default maximum authentication age in seconds. This value is used when an authorization + * request from the client application does not have `max_age` request parameter. + * + * This property corresponds to `default_max_age` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + defaultMaxAge?: number; + /** + * The flag to indicate whether this client requires `auth_time` claim to be embedded in the ID + * token. + * + * This property corresponds to `require_auth_time` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + authTimeRequired?: boolean; + /** + * The time at which this client was created. The value is represented as milliseconds since the + * UNIX epoch (1970-01-01). + */ + createdAt?: number; + /** + * The time at which this client was last modified. The value is represented as milliseconds since + * the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** The flag to indicate whether this client use TLS client certificate bound access tokens. */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The boolean flag to indicate whether a user code is required when this client makes a + * backchannel authentication request. + * + * This property corresponds to the `backchannel_user_code_parameter` metadata. + */ + bcUserCodeRequired?: boolean; + /** + * The flag indicating whether the client requires that a `sid` (session ID) claim be included in + * the logout token sent to `backchannelLogoutUri`. + */ + backchannelLogoutSessionRequired?: boolean; + /** + * The flag to indicate whether this client has been registered dynamically. For more details, see + * [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + dynamicallyRegistered?: boolean; + /** + * The flag to indicate whether this client is required to use the pushed authorization request + * endpoint. This property corresponds to the `require_pushed_authorization_requests` client + * metadata defined in "OAuth 2.0 Pushed Authorization Requests". + */ + parRequired?: boolean; + /** + * The flag to indicate whether authorization requests from this client are always required to + * utilize a request object by using either `request` or `request_uri` request parameter. + * + * If this flag is set to `true` and the service's `traditionalRequestObjectProcessingApplied` is + * set to `false`, authorization requests from this client are processed as if + * `require_signed_request_object` client metadata of this client is `true`. The metadata is + * defined in "JAR (JWT Secured Authorization Request)". + */ + requestObjectRequired?: boolean; + /** + * The flag indicating whether encryption of request object is required when the request object is + * passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE enc of encrypted request object must match the + * `request_object_encryption_enc` client metadata. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * If `Enabled` is selected, an attempt to issue a new access token invalidates existing access + * tokens that are associated with the same combination of subject and client. + * + * Note that, however, attempts by Client Credentials Flow do not invalidate existing access + * tokens because access tokens issued by Client Credentials Flow are not associated with any + * end-user's subject. + * + * Even if `Disabled` is selected here, single access token per subject is effective if + * `singleAccessTokenPerSubject` of the `Service` this client belongs to is Enabled. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** The flag indicating whether this service signs responses from the resource server. */ + rsRequestSigned?: boolean; + /** If the DPoP is required for this client */ + dpopRequired?: boolean; + /** The flag which indicates whether this client is locked. */ + locked?: boolean; + /** + * The flag indicating whether the client intends to prefer mutual TLS endpoints over non-MTLS + * endpoints. + * + * This property corresponds to the `use_mtls_endpoint_aliases` client metadata that is defined in + * [FAPI 2.0 Security Profile, 8.1.1. + * use_mtls_endpoint_aliases](https://openid.bitbucket.io/fapi/fapi-2_0-security-profile.html#section-8.1.1). + */ + mtlsEndpointAliasesUsed?: boolean; + /** The flag indicating whether this client is in scope for token migration operations. */ + inScopeForTokenMigration?: boolean; + /** + * the expiration time of the trust chain that was used when this client was registered or updated + * by the mechanism defined in OpenID Connect Federation 1.0. The value is represented as + * milliseconds elapsed since the Unix epoch (1970-01-01). + */ + trustChainExpiresAt?: number; + /** + * the time at which the trust chain was updated by the mechanism defined in OpenID Connect + * Federation 1.0 + */ + trustChainUpdatedAt?: number; + /** + * The flag indicating whether this client was registered by the "automatic" client registration + * of OIDC Federation. + */ + automaticallyRegistered?: boolean; + /** + * The flag indicating whether this client was registered by the "explicit" client registration of + * OIDC Federation. + */ + explicitlyRegistered?: boolean; + /** True if credential responses to this client must be always encrypted. */ + credentialResponseEncryptionRequired?: boolean; + /** + * Location of the Client ID Metadata Document that was used for this client. This property is set + * when client metadata was retrieved via the OAuth Client ID Metadata Document (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** Expiration time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentExpiresAt?: number; + /** Last-updated time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentUpdatedAt?: number; + /** Indicates whether this client was discovered via a Client ID Metadata Document. */ + discoveredByMetadataDocument?: boolean; + /** Source of this client record. */ + clientSource?: ClientSource; + /** the entity ID of this client. */ + entityId?: string; +}; + +export const clientLimitedAuthorizationSchema: Schema = + s.object({ + number: s.optional(s.int()), + clientName: s.optional(s.string()), + clientNames: s.optional(s.array(s.lazy(() => taggedValueSchema))), + description: s.optional(s.string()), + descriptions: s.optional(s.array(s.lazy(() => taggedValueSchema))), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasEnabled: s.optional(s.boolean()), + clientType: s.optional(s.lazy(() => clientTypeSchema)), + logoUri: s.optional(s.string()), + logoUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + tosUri: s.optional(s.string()), + tosUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + policyUri: s.optional(s.string()), + policyUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + serviceNumber: s.optional(s.int()), + defaultMaxAge: s.optional(s.int()), + authTimeRequired: s.optional(s.boolean()), + createdAt: s.optional(s.int()), + modifiedAt: s.optional(s.int()), + tlsClientCertificateBoundAccessTokens: s.optional(s.boolean()), + bcUserCodeRequired: s.optional(s.boolean()), + backchannelLogoutSessionRequired: s.optional(s.boolean()), + dynamicallyRegistered: s.optional(s.boolean()), + parRequired: s.optional(s.boolean()), + requestObjectRequired: s.optional(s.boolean()), + frontChannelRequestObjectEncryptionRequired: s.optional(s.boolean()), + requestObjectEncryptionAlgMatchRequired: s.optional(s.boolean()), + requestObjectEncryptionEncMatchRequired: s.optional(s.boolean()), + singleAccessTokenPerSubject: s.optional(s.boolean()), + pkceRequired: s.optional(s.boolean()), + pkceS256Required: s.optional(s.boolean()), + rsRequestSigned: s.optional(s.boolean()), + dpopRequired: s.optional(s.boolean()), + locked: s.optional(s.boolean()), + mtlsEndpointAliasesUsed: s.optional(s.boolean()), + inScopeForTokenMigration: s.optional(s.boolean()), + trustChainExpiresAt: s.optional(s.int()), + trustChainUpdatedAt: s.optional(s.int()), + automaticallyRegistered: s.optional(s.boolean()), + explicitlyRegistered: s.optional(s.boolean()), + credentialResponseEncryptionRequired: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentExpiresAt: s.optional(s.int()), + metadataDocumentUpdatedAt: s.optional(s.int()), + discoveredByMetadataDocument: s.optional(s.boolean()), + clientSource: s.optional(s.lazy(() => clientSourceSchema)), + entityId: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-limited.ts b/plugins/authlete/sdk/typescript/src/models/client-limited.ts new file mode 100644 index 00000000..872d86d6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-limited.ts @@ -0,0 +1,57 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientTypeSchema, type ClientType } from "./client-type.js"; +import { taggedValueSchema, type TaggedValue } from "./tagged-value.js"; + +export type ClientLimited = { + /** The sequential number of the client. The value of this property is assigned by Authlete. */ + number?: number; + /** + * The name of the client application. This property corresponds to `client_name` in [OpenID + * Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientName?: string; + /** + * Client names with language tags. If the client application has different names for different + * languages, this property can be used to register the names. + */ + clientNames?: TaggedValue[]; + /** The description about the client application. */ + description?: string; + /** + * Descriptions about the client application with language tags. If the client application has + * different descriptions for different languages, this property can be used to register the + * descriptions. + */ + descriptions?: TaggedValue[]; + /** + * The client identifier used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + clientId?: number; + /** + * The value of the client's `client_id` property used in OAuth and OpenID Connect calls. By + * default, this is a string version of the `clientId` property. + */ + clientIdAlias?: string; + /** Deprecated. Always set to `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client + * Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. + */ + clientType?: ClientType; +}; + +export const clientLimitedSchema: Schema = s.object({ + number: s.optional(s.int()), + clientName: s.optional(s.string()), + clientNames: s.optional(s.array(s.lazy(() => taggedValueSchema))), + description: s.optional(s.string()), + descriptions: s.optional(s.array(s.lazy(() => taggedValueSchema))), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasEnabled: s.optional(s.boolean()), + clientType: s.optional(s.lazy(() => clientTypeSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/client-registration-request.ts b/plugins/authlete/sdk/typescript/src/models/client-registration-request.ts new file mode 100644 index 00000000..a965d85d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-registration-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientRegistrationRequest = { + /** + * Client metadata in JSON format that complies with [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic Client Registration + * Protocol). + */ + json?: string; + /** The client registration access token. Used only for GET, UPDATE, and DELETE requests. */ + token?: string; + /** The client's identifier. Used for GET, UPDATE, and DELETE requests */ + clientId?: string; +}; + +export const clientRegistrationRequestSchema: Schema = + s.object({ + json: s.optional(s.string()), + token: s.optional(s.string()), + clientId: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-registration-response.ts b/plugins/authlete/sdk/typescript/src/models/client-registration-response.ts new file mode 100644 index 00000000..57c96e55 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-registration-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action14Schema, type Action14 } from "./action14.js"; +import { clientSchema, type Client } from "./client.js"; + +export type ClientRegistrationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action14; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + client?: Client; +}; + +export const clientRegistrationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action14Schema)), + responseContent: s.optional(s.string()), + client: s.optional(s.lazy(() => clientSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-registration-type.ts b/plugins/authlete/sdk/typescript/src/models/client-registration-type.ts new file mode 100644 index 00000000..40298d36 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-registration-type.ts @@ -0,0 +1,18 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * Values for the `client_registration_types` RP metadata and the + * `client_registration_types_supported` OP metadata that are defined in [OpenID Connect Federation + * 1.0](https://openid.net/specs/openid-connect-federation-1_0.html). + */ +export const ClientRegistrationType = { + Automatic: "AUTOMATIC", + Explicit: "EXPLICIT", +} as const; +export type ClientRegistrationType = + | (typeof ClientRegistrationType)[keyof typeof ClientRegistrationType] + | (string & {}); + +export const clientRegistrationTypeSchema: EnumSchema = + s.enumOf(ClientRegistrationType); diff --git a/plugins/authlete/sdk/typescript/src/models/client-search-response.ts b/plugins/authlete/sdk/typescript/src/models/client-search-response.ts new file mode 100644 index 00000000..0126974b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-search-response.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { clientSchema, type Client } from "./client.js"; + +export type ClientSearchResponse = { + /** The clamped requested limit of clients to return from the request. */ + limit?: number; + /** + * If there are more matching entries to retrieve this value will be generated and returned. The + * next call to this endpoint should use this value as the `cursor` to continue to retrieve the + * remaining clients from the correct point. If there are no further matching clients, or the + * search pagination limit has been reached, this property will not be set. + */ + nextCursor?: string; + /** + * The total number of clients in the service that match the search query. This doesn't mean the + * number of clients contained in the response. Only set when `includeTotalCount` is `true` in the + * request. + */ + totalCount?: number; + /** The list of clients matching the search query. */ + entities?: Client[]; +}; + +export const clientSearchResponseSchema: Schema = s.object({ + limit: s.optional(s.int()), + nextCursor: s.optional(s.string()), + totalCount: s.optional(s.int()), + entities: s.optional(s.array(s.lazy(() => clientSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/client-secret-refresh-response.ts b/plugins/authlete/sdk/typescript/src/models/client-secret-refresh-response.ts new file mode 100644 index 00000000..f491168c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-secret-refresh-response.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientSecretRefreshResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The new client secret. */ + newClientSecret?: string; + /** The old client secret. */ + oldClientSecret?: string; +}; + +export const clientSecretRefreshResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + newClientSecret: s.optional(s.string()), + oldClientSecret: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-secret-update-request.ts b/plugins/authlete/sdk/typescript/src/models/client-secret-update-request.ts new file mode 100644 index 00000000..af0e5856 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-secret-update-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ClientSecretUpdateRequest = { + /** + * The new value of the client secret. Valid characters for a client secret are `A-Z`, `a-z`, + * `0-9`, `-`, and `_`. The maximum length of a client secret is 86. + */ + clientSecret: string; +}; + +export const clientSecretUpdateRequestSchema: Schema = + s.object({ + clientSecret: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/client-source.ts b/plugins/authlete/sdk/typescript/src/models/client-source.ts new file mode 100644 index 00000000..92c4459c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-source.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** Source of this client record. */ +export const ClientSource = { + DynamicRegistration: "DYNAMIC_REGISTRATION", + AutomaticRegistration: "AUTOMATIC_REGISTRATION", + ExplicitRegistration: "EXPLICIT_REGISTRATION", + MetadataDocument: "METADATA_DOCUMENT", + StaticRegistration: "STATIC_REGISTRATION", +} as const; +export type ClientSource = (typeof ClientSource)[keyof typeof ClientSource] | (string & {}); + +export const clientSourceSchema: EnumSchema = s.enumOf(ClientSource); diff --git a/plugins/authlete/sdk/typescript/src/models/client-type.ts b/plugins/authlete/sdk/typescript/src/models/client-type.ts new file mode 100644 index 00000000..6ff803b3 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client-type.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client + * Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. + */ +export const ClientType = { + Public: "PUBLIC", + Confidential: "CONFIDENTIAL", +} as const; +export type ClientType = (typeof ClientType)[keyof typeof ClientType] | (string & {}); + +export const clientTypeSchema: EnumSchema = s.enumOf(ClientType); diff --git a/plugins/authlete/sdk/typescript/src/models/client.ts b/plugins/authlete/sdk/typescript/src/models/client.ts new file mode 100644 index 00000000..89195578 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/client.ts @@ -0,0 +1,858 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { applicationTypeSchema, type ApplicationType } from "./application-type.js"; +import { clientAuthMethodSchema, type ClientAuthMethod } from "./client-auth-method.js"; +import { clientExtensionSchema, type ClientExtension } from "./client-extension.js"; +import { clientRegistrationTypeSchema, type ClientRegistrationType } from "./client-registration-type.js"; +import { clientSourceSchema, type ClientSource } from "./client-source.js"; +import { clientTypeSchema, type ClientType } from "./client-type.js"; +import { fapiModeSchema, type FapiMode } from "./fapi-mode.js"; +import { grantTypeSchema, type GrantType } from "./grant-type.js"; +import { jweAlgSchema, type JweAlg } from "./jwe-alg.js"; +import { jweEncSchema, type JweEnc } from "./jwe-enc.js"; +import { jwsAlgSchema, type JwsAlg } from "./jws-alg.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { responseModeSchema, type ResponseMode } from "./response-mode.js"; +import { responseTypeSchema, type ResponseType } from "./response-type.js"; +import { subjectTypeSchema, type SubjectType } from "./subject-type.js"; +import { taggedValueSchema, type TaggedValue } from "./tagged-value.js"; + +export type Client = { + /** The sequential number of the client. The value of this property is assigned by Authlete. */ + number?: number; + /** + * The sequential number of the service of the client application. The value of this property is + * assigned by Authlete. + */ + serviceNumber?: number; + /** + * The name of the client application. This property corresponds to `client_name` in [OpenID + * Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientName?: string; + /** + * Client names with language tags. If the client application has different names for different + * languages, this property can be used to register the names. + */ + clientNames?: TaggedValue[]; + /** The description about the client application. */ + description?: string; + /** + * Descriptions about the client application with language tags. If the client application has + * different descriptions for different languages, this property can be used to register the + * descriptions. + */ + descriptions?: TaggedValue[]; + /** + * The client identifier used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + clientId?: number; + /** + * The client secret. A random 512-bit value encoded by base64url (86 letters). The value of this + * property is assigned by Authlete. + */ + clientSecret?: string; + /** + * The value of the client's `client_id` property used in OAuth and OpenID Connect calls. By + * default, this is a string version of the `clientId` property. + */ + clientIdAlias?: string; + /** Deprecated. Always set to `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client + * Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. + */ + clientType?: ClientType; + /** + * The application type. The value of this property affects the validation steps for a redirect + * URI. See the description about `redirectUris` property for more details. + */ + applicationType?: ApplicationType; + /** + * The URL pointing to the logo image of the client application. + * + * This property corresponds to `logo_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + logoUri?: string; + /** + * Logo image URLs with language tags. If the client application has different logo images for + * different languages, this property can be used to register URLs of the images. + */ + logoUris?: TaggedValue[]; + /** + * An array of email addresses of people responsible for the client application. + * + * This property corresponds to contacts in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + contacts?: string[]; + /** The flag to indicate whether this client use TLS client certificate bound access tokens. */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The flag to indicate whether this client has been registered dynamically. For more details, see + * [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + dynamicallyRegistered?: boolean; + /** + * The unique identifier string assigned by the client developer or software publisher used by + * registration endpoints to identify the client software to be dynamically registered. + * + * This property corresponds to the `software_id metadata` defined in [2. Client + * Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + softwareId?: string; + /** The unique identifier of the developer who created this client application. */ + developer?: string; + /** + * The version identifier string for the client software identified by the software ID. + * + * This property corresponds to the software_version metadata defined in [2. Client + * Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + softwareVersion?: string; + /** The hash of the registration access token for this client. */ + registrationAccessTokenHash?: string; + /** + * The time at which this client was created. The value is represented as milliseconds since the + * UNIX epoch (1970-01-01). + */ + createdAt?: number; + /** + * The time at which this client was last modified. The value is represented as milliseconds since + * the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** + * A string array of grant types which the client application declares that it will restrict + * itself to using. This property corresponds to `grant_types` in [OpenID Connect Dynamic Client + * Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + grantTypes?: GrantType[]; + /** + * A string array of response types which the client application declares that it will restrict + * itself to using. This property corresponds to `response_types` in [OpenID Connect Dynamic + * Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + responseTypes?: ResponseType[]; + /** + * Redirect URIs that the client application uses to receive a response from the authorization + * endpoint. Requirements for a redirect URI are as follows. + */ + redirectUris?: string[]; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + authorizationSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for + * transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + authorizationEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. For + * instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server + * when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the + * client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + authorizationEncryptionEnc?: JweEnc | null; + /** + * The client authentication method that the client application declares that it uses at the token + * endpoint. This property corresponds to `token_endpoint_auth_method` in [OpenID Connect Dynamic + * Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + tokenAuthMethod?: ClientAuthMethod; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + tokenAuthSignAlg?: JwsAlg | null; + /** The key ID of a JWK containing a self-signed certificate of this client. */ + selfSignedCertificateKeyId?: string; + /** + * The string representation of the expected subject distinguished name of the certificate this + * client will use in mutual TLS authentication. + * + * See `tls_client_auth_subject_dn` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSubjectDn?: string; + /** + * The string representation of the expected DNS subject alternative name of the certificate this + * client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_dns` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanDns?: string; + /** + * The string representation of the expected URI subject alternative name of the certificate this + * client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_uri` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanUri?: string; + /** + * The string representation of the expected IP address subject alternative name of the + * certificate this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_ip` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanIp?: string; + /** + * The string representation of the expected email address subject alternative name of the + * certificate this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_email` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanEmail?: string; + /** + * The flag to indicate whether this client is required to use the pushed authorization request + * endpoint. This property corresponds to the `require_pushed_authorization_requests` client + * metadata defined in "OAuth 2.0 Pushed Authorization Requests". + */ + parRequired?: boolean; + /** + * The flag to indicate whether authorization requests from this client are always required to + * utilize a request object by using either `request` or `request_uri` request parameter. + * + * If this flag is set to `true` and the service's `traditionalRequestObjectProcessingApplied` is + * set to `false`, authorization requests from this client are processed as if + * `require_signed_request_object` client metadata of this client is `true`. The metadata is + * defined in "JAR (JWT Secured Authorization Request)". + */ + requestObjectRequired?: boolean; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + requestSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for + * transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + requestEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. For + * instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server + * when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the + * client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + requestEncryptionEnc?: JweEnc | null; + /** + * An array of URLs each of which points to a request object. + * + * Authlete requires that URLs used as values for `request_uri` request parameter be + * pre-registered. This property is used for the pre-registration. See [OpenID Connect Core 1.0, + * 6.2. Passing a Request Object by + * Reference](https://openid.net/specs/openid-connect-core-1_0.html#RequestUriParameter) for + * details. + */ + requestUris?: string[]; + /** + * The default maximum authentication age in seconds. This value is used when an authorization + * request from the client application does not have `max_age` request parameter. + * + * This property corresponds to `default_max_age` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + defaultMaxAge?: number; + /** + * The default ACRs (Authentication Context Class References). This value is used when an + * authorization request from the client application has neither `acr_values` request parameter + * nor `acr` claim in claims request parameter. + */ + defaultAcrs?: string[]; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + idTokenSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for + * transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + idTokenEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. For + * instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server + * when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the + * client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + idTokenEncryptionEnc?: JweEnc | null; + /** + * The flag to indicate whether this client requires `auth_time` claim to be embedded in the ID + * token. + * + * This property corresponds to `require_auth_time` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + authTimeRequired?: boolean; + /** + * The subject type that the client application requests. Details about the subject type are + * described in [OpenID Connect Core 1.0, 8. Subjct Identifier + * Types](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes). + * + * This property corresponds to `subject_type` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + subjectType?: SubjectType; + /** + * The value of the sector identifier URI. This represents the `sector_identifier_uri` client + * metadata which is defined in [OpenID Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata) + */ + sectorIdentifierUri?: string; + /** + * The sector identifier host component as derived from either the `sector_identifier_uri` or the + * registered redirect URI. If no `sector_identifier_uri` is registered and multiple redirect URIs + * are also registered, the value of this property is `null`. + */ + derivedSectorIdentifier?: string; + /** + * The URL pointing to the JWK Set of the client application. The content pointed to by the URL is + * JSON which complies with the format described in [JSON Web Key (JWK), 5. JWK Set + * Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The JWK Set must not include + * private keys of the client application. + */ + jwksUri?: string; + /** + * The content of the JWK Set of the client application. The format is described in [JSON Web Key + * (JWK), 5. JWK Set Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The JWK Set + * must not include private keys of the client application. + */ + jwks?: string; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + userInfoSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for + * transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + userInfoEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. For + * instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server + * when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the + * client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + userInfoEncryptionEnc?: JweEnc | null; + /** + * The URL which a third party can use to initiate a login by the client application. + * + * This property corresponds to `initiate_login_uri` in [OpenID Connect Dynamic Client + * Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + loginUri?: string; + /** + * The URL pointing to the "Terms Of Service" page. + * + * This property corresponds to `tos_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + tosUri?: string; + /** + * URLs of "Terms Of Service" pages with language tags. + * + * If the client application has different "Terms Of Service" pages for different languages, this + * property can be used to register the URLs. + */ + tosUris?: TaggedValue[]; + /** + * The URL pointing to the page which describes the policy as to how end-user's profile data is + * used. + * + * This property corresponds to `policy_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + policyUri?: string; + /** + * URLs of policy pages with language tags. If the client application has different policy pages + * for different languages, this property can be used to register the URLs. + */ + policyUris?: TaggedValue[]; + /** + * The URL pointing to the home page of the client application. + * + * This property corresponds to `client_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientUri?: string; + /** + * Home page URLs with language tags. If the client application has different home pages for + * different languages, this property can be used to register the URLs. + */ + clientUris?: TaggedValue[]; + /** + * The backchannel token delivery mode. + * + * This property corresponds to the `backchannel_token_delivery_mode` metadata. The backchannel + * token delivery mode is defined in the specification of "CIBA (Client Initiated Backchannel + * Authentication)". + */ + bcDeliveryMode?: string; + /** + * The backchannel client notification endpoint. + * + * This property corresponds to the `backchannel_client_notification_endpoint` metadata. The + * backchannel token delivery mode is defined in the specification of "CIBA (Client Initiated + * Backchannel Authentication)". + */ + bcNotificationEndpoint?: string; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + bcRequestSignAlg?: JwsAlg | null; + /** + * The boolean flag to indicate whether a user code is required when this client makes a + * backchannel authentication request. + * + * This property corresponds to the `backchannel_user_code_parameter` metadata. + */ + bcUserCodeRequired?: boolean; + /** + * The backchannel logout URI for this client. Used by the service to deliver logout tokens when + * OpenID Connect Back-Channel Logout 1.0 is triggered. + */ + backchannelLogoutUri?: string; + /** + * The flag indicating whether the client requires that a `sid` (session ID) claim be included in + * the logout token sent to `backchannelLogoutUri`. + */ + backchannelLogoutSessionRequired?: boolean; + /** The attributes of this client. */ + attributes?: Pair[]; + extension?: ClientExtension; + /** + * The authorization details types that this client may use as values of the `type` field in + * `authorization_details`. + * + * This property corresponds to the `authorization_details_types` metadata. See [OAuth 2.0 Rich + * Authorization Requests (RAR)](https://datatracker.ietf.org/doc/draft-ietf-oauth-rar/) for + * details. + * + * Note that the property name was renamed from authorizationDataTypes to + * authorizationDetailsTypes to align with the change made by the 5th draft of the RAR + * specification. + */ + authorizationDetailsTypes?: string[]; + /** The custom client metadata in JSON format. */ + customMetadata?: string; + /** + * The flag indicating whether encryption of request object is required when the request object is + * passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE enc of encrypted request object must match the + * `request_object_encryption_enc` client metadata. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * The digest algorithm that this client requests the server to use when it computes digest values + * of [external + * attachments](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#name-external-attachments), + * which may be referenced from within ID tokens or userinfo responses (or any place that can have + * the `verified_claims` claim). Possible values are listed in the [Hash Algorithm + * Registry](https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg) + * of IANA (Internet Assigned Numbers Authority), but the server does not necessarily support all + * the values there. When this property is omitted, `sha-256` is used as the default algorithm. + * This property corresponds to the `digest_algorithm` client metadata which was defined by the + * third implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html). + */ + digestAlgorithm?: string; + /** + * If `Enabled` is selected, an attempt to issue a new access token invalidates existing access + * tokens that are associated with the same combination of subject and client. + * + * Note that, however, attempts by Client Credentials Flow do not invalidate existing access + * tokens because access tokens issued by Client Credentials Flow are not associated with any + * end-user's subject. + * + * Even if `Disabled` is selected here, single access token per subject is effective if + * `singleAccessTokenPerSubject` of the `Service` this client belongs to is Enabled. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** If the DPoP is required for this client */ + dpopRequired?: boolean; + /** + * The flag indicating whether this client was registered by the "automatic" client registration + * of OIDC Federation. + */ + automaticallyRegistered?: boolean; + /** + * The flag indicating whether this client was registered by the "explicit" client registration of + * OIDC Federation. + */ + explicitlyRegistered?: boolean; + /** The flag indicating whether this service signs responses from the resource server. */ + rsRequestSigned?: boolean; + /** + * The key ID of a JWK containing the public key used by this client to sign requests to the + * resource server. + */ + rsSignedRequestKeyId?: string; + /** The client registration types that the client has declared it may use. */ + clientRegistrationTypes?: ClientRegistrationType[]; + /** + * The human-readable name representing the organization that manages this client. This property + * corresponds to the organization_name client metadata that is defined in OpenID Connect + * Federation 1.0. + */ + organizationName?: string; + /** + * The URI of the endpoint that returns this client's JWK Set document in the JWT format. This + * property corresponds to the `signed_jwks_uri` client metadata defined in OpenID Connect + * Federation 1.0. + */ + signedJwksUri?: string; + /** the entity ID of this client. */ + entityId?: string; + /** + * The entity ID of the trust anchor of the trust chain that was used when this client was + * registered or updated by the mechanism defined in OpenID Connect Federation 1.0 + */ + trustAnchorId?: string; + /** + * The trust chain that was used when this client was registered or updated by the mechanism + * defined in OpenID Connect Federation 1.0 + */ + trustChain?: string[]; + /** + * the expiration time of the trust chain that was used when this client was registered or updated + * by the mechanism defined in OpenID Connect Federation 1.0. The value is represented as + * milliseconds elapsed since the Unix epoch (1970-01-01). + */ + trustChainExpiresAt?: number; + /** + * the time at which the trust chain was updated by the mechanism defined in OpenID Connect + * Federation 1.0 + */ + trustChainUpdatedAt?: number; + /** The flag which indicates whether this client is locked. */ + locked?: boolean; + /** + * The URL of the credential offer endpoint at which this client (wallet) receives a credential + * offer from the credential issuer. + */ + credentialOfferEndpoint?: string; + /** The FAPI modes for this client. */ + fapiModes?: FapiMode[]; + /** The response modes that this client may use. */ + responseModes?: ResponseMode[]; + /** True if credential responses to this client must be always encrypted. */ + credentialResponseEncryptionRequired?: boolean; + /** + * The flag indicating whether the client intends to prefer mutual TLS endpoints over non-MTLS + * endpoints. + * + * This property corresponds to the `use_mtls_endpoint_aliases` client metadata that is defined in + * [FAPI 2.0 Security Profile, 8.1.1. + * use_mtls_endpoint_aliases](https://openid.bitbucket.io/fapi/fapi-2_0-security-profile.html#section-8.1.1). + */ + mtlsEndpointAliasesUsed?: boolean; + /** The flag indicating whether this client is in scope for token migration operations. */ + inScopeForTokenMigration?: boolean; + /** Location of the Client ID Metadata Document that was used for this client. */ + metadataDocumentLocation?: string; + /** Expiration time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentExpiresAt?: number; + /** Last-updated time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentUpdatedAt?: number; + /** Indicates whether this client was discovered via a Client ID Metadata Document. */ + discoveredByMetadataDocument?: boolean; + /** Source of this client record. */ + clientSource?: ClientSource; + /** + * The SPIFFE ID of the client. Used for SPIFFE-based client authentication (`SPIFFE_JWT`). + * Corresponds to the `spiffe_id` client metadata parameter. + */ + spiffeId?: string; + /** + * The endpoint URL of the SPIFFE bundle for this client. Used to fetch the SPIFFE trust bundle + * for validating JWT-SVIDs. Corresponds to the `spiffe_bundle_endpoint` client metadata + * parameter. + */ + spiffeBundleEndpoint?: string; +}; + +export const clientSchema: Schema = s.object({ + number: s.optional(s.int()), + serviceNumber: s.optional(s.int()), + clientName: s.optional(s.string()), + clientNames: s.optional(s.array(s.lazy(() => taggedValueSchema))), + description: s.optional(s.string()), + descriptions: s.optional(s.array(s.lazy(() => taggedValueSchema))), + clientId: s.optional(s.int()), + clientSecret: s.optional(s.string()), + clientIdAlias: s.optional(s.string()), + clientIdAliasEnabled: s.optional(s.boolean()), + clientType: s.optional(s.lazy(() => clientTypeSchema)), + applicationType: s.optional(s.lazy(() => applicationTypeSchema)), + logoUri: s.optional(s.string()), + logoUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + contacts: s.optional(s.array(s.string())), + tlsClientCertificateBoundAccessTokens: s.optional(s.boolean()), + dynamicallyRegistered: s.optional(s.boolean()), + softwareId: s.optional(s.string()), + developer: s.optional(s.string()), + softwareVersion: s.optional(s.string()), + registrationAccessTokenHash: s.optional(s.string()), + createdAt: s.optional(s.int()), + modifiedAt: s.optional(s.int()), + grantTypes: s.optional(s.array(s.lazy(() => grantTypeSchema))), + responseTypes: s.optional(s.array(s.lazy(() => responseTypeSchema))), + redirectUris: s.optional(s.array(s.string())), + authorizationSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + authorizationEncryptionAlg: s.optionalNullable(s.lazy(() => jweAlgSchema)), + authorizationEncryptionEnc: s.optionalNullable(s.lazy(() => jweEncSchema)), + tokenAuthMethod: s.optional(s.lazy(() => clientAuthMethodSchema)), + tokenAuthSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + selfSignedCertificateKeyId: s.optional(s.string()), + tlsClientAuthSubjectDn: s.optional(s.string()), + tlsClientAuthSanDns: s.optional(s.string()), + tlsClientAuthSanUri: s.optional(s.string()), + tlsClientAuthSanIp: s.optional(s.string()), + tlsClientAuthSanEmail: s.optional(s.string()), + parRequired: s.optional(s.boolean()), + requestObjectRequired: s.optional(s.boolean()), + requestSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + requestEncryptionAlg: s.optionalNullable(s.lazy(() => jweAlgSchema)), + requestEncryptionEnc: s.optionalNullable(s.lazy(() => jweEncSchema)), + requestUris: s.optional(s.array(s.string())), + defaultMaxAge: s.optional(s.int()), + defaultAcrs: s.optional(s.array(s.string())), + idTokenSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + idTokenEncryptionAlg: s.optionalNullable(s.lazy(() => jweAlgSchema)), + idTokenEncryptionEnc: s.optionalNullable(s.lazy(() => jweEncSchema)), + authTimeRequired: s.optional(s.boolean()), + subjectType: s.optional(s.lazy(() => subjectTypeSchema)), + sectorIdentifierUri: s.optional(s.string()), + derivedSectorIdentifier: s.optional(s.string()), + jwksUri: s.optional(s.string()), + jwks: s.optional(s.string()), + userInfoSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + userInfoEncryptionAlg: s.optionalNullable(s.lazy(() => jweAlgSchema)), + userInfoEncryptionEnc: s.optionalNullable(s.lazy(() => jweEncSchema)), + loginUri: s.optional(s.string()), + tosUri: s.optional(s.string()), + tosUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + policyUri: s.optional(s.string()), + policyUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + clientUri: s.optional(s.string()), + clientUris: s.optional(s.array(s.lazy(() => taggedValueSchema))), + bcDeliveryMode: s.optional(s.string()), + bcNotificationEndpoint: s.optional(s.string()), + bcRequestSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + bcUserCodeRequired: s.optional(s.boolean()), + backchannelLogoutUri: s.optional(s.string()), + backchannelLogoutSessionRequired: s.optional(s.boolean()), + attributes: s.optional(s.array(s.lazy(() => pairSchema))), + extension: s.optional(s.lazy(() => clientExtensionSchema)), + authorizationDetailsTypes: s.optional(s.array(s.string())), + customMetadata: s.optional(s.string()), + frontChannelRequestObjectEncryptionRequired: s.optional(s.boolean()), + requestObjectEncryptionAlgMatchRequired: s.optional(s.boolean()), + requestObjectEncryptionEncMatchRequired: s.optional(s.boolean()), + digestAlgorithm: s.optional(s.string()), + singleAccessTokenPerSubject: s.optional(s.boolean()), + pkceRequired: s.optional(s.boolean()), + pkceS256Required: s.optional(s.boolean()), + dpopRequired: s.optional(s.boolean()), + automaticallyRegistered: s.optional(s.boolean()), + explicitlyRegistered: s.optional(s.boolean()), + rsRequestSigned: s.optional(s.boolean()), + rsSignedRequestKeyId: s.optional(s.string()), + clientRegistrationTypes: s.optional(s.array(s.lazy(() => clientRegistrationTypeSchema))), + organizationName: s.optional(s.string()), + signedJwksUri: s.optional(s.string()), + entityId: s.optional(s.string()), + trustAnchorId: s.optional(s.string()), + trustChain: s.optional(s.array(s.string())), + trustChainExpiresAt: s.optional(s.int()), + trustChainUpdatedAt: s.optional(s.int()), + locked: s.optional(s.boolean()), + credentialOfferEndpoint: s.optional(s.string()), + fapiModes: s.optional(s.array(s.lazy(() => fapiModeSchema))), + responseModes: s.optional(s.array(s.lazy(() => responseModeSchema))), + credentialResponseEncryptionRequired: s.optional(s.boolean()), + mtlsEndpointAliasesUsed: s.optional(s.boolean()), + inScopeForTokenMigration: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentExpiresAt: s.optional(s.int()), + metadataDocumentUpdatedAt: s.optional(s.int()), + discoveredByMetadataDocument: s.optional(s.boolean()), + clientSource: s.optional(s.lazy(() => clientSourceSchema)), + spiffeId: s.optional(s.string()), + spiffeBundleEndpoint: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/credential-issuance-order.ts b/plugins/authlete/sdk/typescript/src/models/credential-issuance-order.ts new file mode 100644 index 00000000..6c1537a2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/credential-issuance-order.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CredentialIssuanceOrder = { + /** The identifier of a credential request. */ + requestIdentifier?: string; + /** The additional payload that will be added into a credential to be issued. */ + credentialPayload?: string; + /** The flag indicating whether to defer credential issuance. */ + issuanceDeferred?: boolean; + /** The duration of a credential to be issued. */ + credentialDuration?: number; + /** The key ID of a private key that should be used for signing a credential to be issued. */ + signingKeyId?: string; +}; + +export const credentialIssuanceOrderSchema: Schema = + s.object({ + requestIdentifier: s.optional(s.string()), + credentialPayload: s.optional(s.string()), + issuanceDeferred: s.optional(s.boolean()), + credentialDuration: s.optional(s.int()), + signingKeyId: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/credential-issuer-metadata.ts b/plugins/authlete/sdk/typescript/src/models/credential-issuer-metadata.ts new file mode 100644 index 00000000..cb20e473 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/credential-issuer-metadata.ts @@ -0,0 +1,76 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CredentialIssuerMetadata = { + /** + * The identifiers of the authorization servers that the credential issuer relies on for + * authorization. This property corresponds to the authorization_servers metadata. When the + * credential issuer works as an authorization server for itself, this property should be omitted. + */ + authorizationServers?: string[]; + /** The identifier of a credential request. */ + credentialIssuer?: string; + /** The URL of the credential endpoint of the credential issuer. */ + credentialEndpoint?: string; + /** The URL of the batch credential endpoint of the credential issuer. */ + batchCredentialEndpoint?: string; + /** The URL of the deferred credential endpoint of the credential issuer. */ + deferredCredentialEndpoint?: string; + /** + * A JSON object describing supported credential configurations. This property corresponds to the + * credential_configurations_supported metadata. Note: Due to a breaking change in December 2023, + * this was changed from a JSON array to a JSON object. + */ + credentialsSupported?: string; + /** + * The supported JWE `alg` algorithms for credential response encryption. This property + * corresponds to the `credential_response_encryption.alg_values_supported` metadata. + */ + credentialResponseEncryptionAlgValuesSupported?: string[]; + /** + * The supported JWE `enc` algorithms for credential response encryption. This property + * corresponds to the `credential_response_encryption.enc_values_supported` metadata. + */ + credentialResponseEncryptionEncValuesSupported?: string[]; + /** + * The boolean flag indicating whether credential response encryption is required. This property + * corresponds to the `credential_response_encryption.encryption_required metadata`. If this flag + * is `true`, every credential request to the credential issuer must include the + * `credential_response_encryption` property. + */ + requireCredentialResponseEncryption?: boolean; + /** + * The boolean flag indicating whether credential request encryption is required. This property + * corresponds to the `credential_request_encryption.encryption_required` metadata. If this flag + * is `true`, every credential request to the credential issuer must be encrypted. + */ + requireCredentialRequestEncryption?: boolean; + /** + * The supported JWE `zip` (compression) algorithms for credential response encryption. This + * property corresponds to the `credential_response_encryption.zip_values_supported` metadata. + */ + credentialResponseEncryptionZipValuesSupported?: string[]; + /** + * The maximum array size for the `proofs` parameter in a credential request. This property + * corresponds to the `batch_credential_issuance.batch_size` metadata. If the value of this + * property is 2 or greater, the `batch_credential_issuance` parameter will appear in the response + * from the credential issuer metadata endpoint. + */ + batchSize?: number; +}; + +export const credentialIssuerMetadataSchema: Schema = + s.object({ + authorizationServers: s.optional(s.array(s.string())), + credentialIssuer: s.optional(s.string()), + credentialEndpoint: s.optional(s.string()), + batchCredentialEndpoint: s.optional(s.string()), + deferredCredentialEndpoint: s.optional(s.string()), + credentialsSupported: s.optional(s.string()), + credentialResponseEncryptionAlgValuesSupported: s.optional(s.array(s.string())), + credentialResponseEncryptionEncValuesSupported: s.optional(s.array(s.string())), + requireCredentialResponseEncryption: s.optional(s.boolean()), + requireCredentialRequestEncryption: s.optional(s.boolean()), + credentialResponseEncryptionZipValuesSupported: s.optional(s.array(s.string())), + batchSize: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/credential-offer-info.ts b/plugins/authlete/sdk/typescript/src/models/credential-offer-info.ts new file mode 100644 index 00000000..cf172c35 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/credential-offer-info.ts @@ -0,0 +1,86 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { propertySchema, type Property } from "./property.js"; + +export type CredentialOfferInfo = { + /** The identifier of the credential offer. */ + identifier?: string; + /** The credential offer in the JSON format. */ + credentialOffer?: string; + /** The identifier of the credential issuer. */ + credentialIssuer?: string; + /** + * The flag indicating whether the `authorization_code` object is included in the `grants` object. + */ + authorizationCodeGrantIncluded?: boolean; + /** + * The flag indicating whether the `issuer_state` property is included in the `authorization_code` + * object in the `grants` object. + */ + issuerStateIncluded?: boolean; + /** + * The value of the `issuer_state` property in the `authorization_code` object in the `grants` + * object. + */ + issuerState?: string; + /** + * The flag indicating whether the `urn:ietf:params:oauth:grant-type:pre-authorized_code` object + * is included in the `grants` object. + */ + preAuthorizedCodeGrantIncluded?: boolean; + /** + * The value of the `pre-authorized_code` property in the + * `urn:ietf:params:oauth:grant-type:pre-authorized_code` object in the `grants` object. + */ + preAuthorizedCode?: string; + /** The subject associated with the credential offer. */ + subject?: string; + /** The time at which the credential offer will expire. */ + expiresAt?: number; + /** The general-purpose arbitrary string. */ + context?: string; + /** Extra properties to associate with the credential offer. */ + properties?: Property[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The time at which the user authentication was performed during the course of issuing the + * credential offer. + */ + authTime?: number; + /** + * The Authentication Context Class Reference of the user authentication performed during the + * course of issuing the credential offer. + */ + acr?: string; + /** The value of the `credential_configuration_ids` property of the credential offer. */ + credentialConfigurationIds?: string[]; + /** The transaction code. */ + txCode?: string; + /** The input mode of the transaction code. */ + txCodeInputMode?: string; + /** The description of the transaction code. */ + txCodeDescription?: string; +}; + +export const credentialOfferInfoSchema: Schema = s.object({ + identifier: s.optional(s.string()), + credentialOffer: s.optional(s.string()), + credentialIssuer: s.optional(s.string()), + authorizationCodeGrantIncluded: s.optional(s.boolean()), + issuerStateIncluded: s.optional(s.boolean()), + issuerState: s.optional(s.string()), + preAuthorizedCodeGrantIncluded: s.optional(s.boolean()), + preAuthorizedCode: s.optional(s.string()), + subject: s.optional(s.string()), + expiresAt: s.optional(s.int()), + context: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + jwtAtClaims: s.optional(s.string()), + authTime: s.optional(s.int()), + acr: s.optional(s.string()), + credentialConfigurationIds: s.optional(s.array(s.string())), + txCode: s.optional(s.string()), + txCodeInputMode: s.optional(s.string()), + txCodeDescription: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/credential-request-info.ts b/plugins/authlete/sdk/typescript/src/models/credential-request-info.ts new file mode 100644 index 00000000..b718641e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/credential-request-info.ts @@ -0,0 +1,26 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CredentialRequestInfo = { + /** The identifier of the credential offer. */ + identifier?: string; + /** The value of the format parameter in the credential request. */ + format?: string; + /** The identifier of the credential configuration referenced by the credential request. */ + credentialConfigurationId?: string; + /** The binding key specified by the proof in the credential request. */ + bindingKey?: string; + /** The binding keys specified by the proofs in the credential request. */ + bindingKeys?: string[]; + /** The details about the credential request. */ + details?: string; +}; + +export const credentialRequestInfoSchema: Schema = s.object({ + identifier: s.optional(s.string()), + format: s.optional(s.string()), + credentialConfigurationId: s.optional(s.string()), + bindingKey: s.optional(s.string()), + bindingKeys: s.optional(s.array(s.string())), + details: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/delivery-mode.ts b/plugins/authlete/sdk/typescript/src/models/delivery-mode.ts new file mode 100644 index 00000000..4cb82b19 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/delivery-mode.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const DeliveryMode = { + Ping: "PING", + Poll: "POLL", + Push: "PUSH", +} as const; +export type DeliveryMode = (typeof DeliveryMode)[keyof typeof DeliveryMode] | (string & {}); + +export const deliveryModeSchema: EnumSchema = s.enumOf(DeliveryMode); diff --git a/plugins/authlete/sdk/typescript/src/models/device-authorization-request.ts b/plugins/authlete/sdk/typescript/src/models/device-authorization-request.ts new file mode 100644 index 00000000..e79a56f8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-authorization-request.ts @@ -0,0 +1,78 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cimdOptionsSchema, type CimdOptions } from "./cimd-options.js"; + +export type DeviceAuthorizationRequest = { + /** + * Parameters of a device authorization request which are the request parameters that the device + * authorization endpoint of the authorization server implementation received from the client + * application. + * + * The value of `parameters` is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from Authorization header of the device authorization request from the + * client application. + * + * If the device authorization endpoint of the authorization server implementation supports Basic + * `Authentication` as a means of client authentication, and the request from the client + * application contained its client ID in `Authorization` header, the value should be extracted + * and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the device authorization request + * from the client application. + * + * If the device authorization endpoint of the authorization server implementation supports Basic + * Authentication as a means of client authentication, and the request from the client application + * contained its client secret in `Authorization` header, the value should be extracted and set to + * this parameter. + */ + clientSecret?: string; + /** + * The client certificate used in the TLS connection between the client application and the device + * authorization endpoint of the authorization server. + */ + clientCertificate?: string; + /** + * The client certificate path presented by the client during client authentication. Each element + * is a string in PEM format. + */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification + * of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; +}; + +export const deviceAuthorizationRequestSchema: Schema = + s.object({ + parameters: s.string(), + clientId: s.optional(s.string()), + clientSecret: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + clientCertificatePath: s.optional(s.array(s.string())), + oauthClientAttestation: s.optional(s.string()), + oauthClientAttestationPop: s.optional(s.string()), + cimdOptions: s.optional(s.lazy(() => cimdOptionsSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/device-authorization-response.ts b/plugins/authlete/sdk/typescript/src/models/device-authorization-response.ts new file mode 100644 index 00000000..95cddc36 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-authorization-response.ts @@ -0,0 +1,198 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action20Schema, type Action20 } from "./action20.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { dynamicScopeSchema, type DynamicScope } from "./dynamic-scope.js"; +import { grantManagementActionSchema, type GrantManagementAction } from "./grant-management-action.js"; +import { grantSchema, type Grant } from "./grant.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { scopeSchema, type Scope } from "./scope.js"; + +export type DeviceAuthorizationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action20; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** The client ID of the client application that has made the device authorization request. */ + clientId?: number; + /** + * The client ID alias of the client application that has made the device authorization request. + */ + clientIdAlias?: string; + /** + * `true` if the value of the client_id request parameter included in the device authorization + * request is the client ID alias. `false` if the value is the original numeric client ID. + */ + clientIdAliasUsed?: boolean; + /** The name of the client application which has made the device authorization request. */ + clientName?: string; + /** + * The client authentication method that should be performed at the device authorization endpoint. + */ + clientAuthMethod?: string; + /** The scopes requested by the device authorization request. */ + scopes?: Scope[]; + /** + * The names of the claims which were requested indirectly via some special scopes. See [5.4. + * Requesting Claims using Scope + * Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) in OpenID Connect + * Core 1.0 for details. + */ + claimNames?: string[]; + /** + * The list of ACR values requested by the device authorization request. + * + * Basically, this property holds the value of the `acr_values` request parameter in the device + * authorization request. However, because unsupported ACR values are dropped on Authlete side, if + * the `acr_values` request parameter contains unrecognized ACR values, the list returned by this + * property becomes different from the value of the `acr_values` request parameter. + */ + acrs?: string[]; + /** + * The device verification code. This corresponds to the `device_code` property in the response to + * the client. + */ + deviceCode?: string; + /** + * The end-user verification code. This corresponds to the `user_code` property in the response to + * the client. + */ + userCode?: string; + /** + * The end-user verification URI. This corresponds to the `verification_uri` property in the + * response to the client. + */ + verificationUri?: string; + /** + * The end-user verification URI that includes the end-user verification code. This corresponds to + * the `verification_uri_complete` property in the response to the client. + */ + verificationUriComplete?: string; + /** + * The duration of the device verification code in seconds. This corresponds to the `expires_in` + * property in the response to the client. + */ + expiresIn?: number; + /** + * The minimum amount of time in seconds that the client must wait for between polling requests to + * the token endpoint. This corresponds to the `interval` property in the response to the client. + */ + interval?: number; + /** The warnings raised during processing the backchannel authentication request. */ + warnings?: string[]; + /** + * The resources specified by the `resource` request parameters. See "Resource Indicators for + * OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** The dynamic scopes which the client application requested by the scope request parameter. */ + dynamicScopes?: DynamicScope[]; + /** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ + gmAction?: GrantManagementAction; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + grant?: Grant; + /** + * The subject identifying the user who has given the grant identified by the `grant_id` request + * parameter of the device authorization request. Authlete 2.3 and newer versions support [Grant + * Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). An + * authorization request may contain a `grant_id` request parameter which is defined in the + * specification. If the value of the request parameter is valid, {@link + * #getGrantSubject()} will return the subject of the user who has given the grant to the + * client application. Authorization server implementations may use the value returned from + * {@link #getGrantSubject()} in order to determine the user to authenticate. The user + * your system will authenticate during the authorization process (or has already authenticated) + * may be different from the user of the grant. The first implementer's draft of "Grant Management + * for OAuth 2.0" does not mention anything about the case, so the behavior in the case is left to + * implementations. Authlete will not perform the grant management action when the `subject` + * passed to Authlete does not match the user of the grant. + */ + grantSubject?: string; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const deviceAuthorizationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action20Schema)), + responseContent: s.optional(s.string()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + clientName: s.optional(s.string()), + clientAuthMethod: s.optional(s.string()), + scopes: s.optional(s.array(s.lazy(() => scopeSchema))), + claimNames: s.optional(s.array(s.string())), + acrs: s.optional(s.array(s.string())), + deviceCode: s.optional(s.string()), + userCode: s.optional(s.string()), + verificationUri: s.optional(s.string()), + verificationUriComplete: s.optional(s.string()), + expiresIn: s.optional(s.int()), + interval: s.optional(s.int()), + warnings: s.optional(s.array(s.string())), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + dynamicScopes: s.optional(s.array(s.lazy(() => dynamicScopeSchema))), + gmAction: s.optional(s.lazy(() => grantManagementActionSchema)), + grantId: s.optional(s.string()), + grant: s.optional(s.lazy(() => grantSchema)), + grantSubject: s.optional(s.string()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/device-complete-request.ts b/plugins/authlete/sdk/typescript/src/models/device-complete-request.ts new file mode 100644 index 00000000..9413b7c1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-complete-request.ts @@ -0,0 +1,102 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { propertySchema, type Property } from "./property.js"; +import { result1Schema, type Result1 } from "./result1.js"; + +export type DeviceCompleteRequest = { + /** A user code. */ + userCode: string; + /** + * The result of the end-user authentication and authorization. One of the following. Details are + * described in the description. + */ + result: Result1; + /** The subject (= unique identifier) of the end-user. */ + subject: string; + /** The value of the sub claim that should be used in the ID token. */ + sub?: string; + /** + * The time at which the end-user was authenticated. Its value is the number of seconds from + * `1970-01-01`. + */ + authTime?: number; + /** + * The reference of the authentication context class which the end-user authentication satisfied. + */ + acr?: string; + /** Additional claims which will be embedded in the ID token. */ + claims?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** + * Scopes to replace the scopes specified in the original device authorization request with. When + * nothing is specified for this parameter, replacement is not performed. + */ + scopes?: string[]; + /** + * The description of the error. If this optional request parameter is given, its value is used as + * the value of the `error_description` property, but it is used only when the result is not + * `AUTHORIZED`. To comply with the specification strictly, the description must not include + * characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. This corresponds to the `error_uri` + * property in the response to the client. + */ + errorUri?: string; + /** JSON that represents additional JWS header parameters for ID tokens. */ + idtHeaderParams?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the access + * token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the refresh + * token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; +}; + +export const deviceCompleteRequestSchema: Schema = s.object({ + userCode: s.string(), + result: result1Schema, + subject: s.string(), + sub: s.optional(s.string()), + authTime: s.optional(s.int()), + acr: s.optional(s.string()), + claims: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + scopes: s.optional(s.array(s.string())), + errorDescription: s.optional(s.string()), + errorUri: s.optional(s.string()), + idtHeaderParams: s.optional(s.string()), + consentedClaims: s.optional(s.array(s.string())), + jwtAtClaims: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + idTokenAudType: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/device-complete-response.ts b/plugins/authlete/sdk/typescript/src/models/device-complete-response.ts new file mode 100644 index 00000000..efa484bc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-complete-response.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action22Schema, type Action22 } from "./action22.js"; + +export type DeviceCompleteResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action22; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; +}; + +export const deviceCompleteResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action22Schema)), + consentedClaims: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/device-verification-request.ts b/plugins/authlete/sdk/typescript/src/models/device-verification-request.ts new file mode 100644 index 00000000..c4f618b5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-verification-request.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type DeviceVerificationRequest = { + /** A user code. */ + userCode: string; +}; + +export const deviceVerificationRequestSchema: Schema = + s.object({ + userCode: s.string(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/device-verification-response.ts b/plugins/authlete/sdk/typescript/src/models/device-verification-response.ts new file mode 100644 index 00000000..cca67abd --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/device-verification-response.ts @@ -0,0 +1,156 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action21Schema, type Action21 } from "./action21.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { dynamicScopeSchema, type DynamicScope } from "./dynamic-scope.js"; +import { grantManagementActionSchema, type GrantManagementAction } from "./grant-management-action.js"; +import { grantSchema, type Grant } from "./grant.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { scopeSchema, type Scope } from "./scope.js"; + +export type DeviceVerificationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action21; + /** The client ID of the client application to which the user code has been issued. */ + clientId?: number; + /** The client ID alias of the client application to which the user code has been issued. */ + clientIdAlias?: string; + /** + * `true` if the value of the `client_id` request parameter included in the device authorization + * request is the client ID alias. `false` if the value is the original numeric client ID. + */ + clientIdAliasUsed?: boolean; + /** The name of the client application to which the user code has been issued. */ + clientName?: string; + /** + * The scopes requested by the device authorization request. + * + * Note that `description` property and `descriptions` property of each scope object in the array + * contained in this property is always null even if descriptions of the scopes are registered. + */ + scopes?: Scope[]; + /** + * The names of the claims which were requested indirectly via some special scopes. See [5.4. + * Requesting Claims using Scope + * Values](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) in OpenID Connect + * Core 1.0 for details. + * + * This property is always `null` if the `scope` request parameter of the device authorization + * request does not include the `openid` scope even if special scopes (such as `profile`) are + * included in the request (unless the openid scope is included in the default set of scopes which + * is used when the `scope` request parameter is omitted). + */ + claimNames?: string[]; + /** The list of ACR values requested by the device authorization request. */ + acrs?: string[]; + /** + * The resources specified by the `resource` request parameters or by the `resource` property in + * the request object. If both are given, the values in the request object should be set. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** The dynamic scopes which the client application requested by the scope request parameter. */ + dynamicScopes?: DynamicScope[]; + /** + * Get the date in milliseconds since the Unix epoch (1970-01-01) at which the user code will + * expire. + */ + expiresAt?: number; + /** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ + gmAction?: GrantManagementAction; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + grant?: Grant; + /** + * The subject identifying the user who has given the grant identified by the `grant_id` request + * parameter of the device authorization request. Authlete 2.3 and newer versions support [Grant + * Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html). An + * authorization request may contain a `grant_id` request parameter which is defined in the + * specification. If the value of the request parameter is valid, {@link + * #getGrantSubject()} will return the subject of the user who has given the grant to the + * client application. Authorization server implementations may use the value returned from + * {@link #getGrantSubject()} in order to determine the user to authenticate. The user + * your system will authenticate during the authorization process (or has already authenticated) + * may be different from the user of the grant. The first implementer's draft of "Grant Management + * for OAuth 2.0" does not mention anything about the case, so the behavior in the case is left to + * implementations. Authlete will not perform the grant management action when the `subject` + * passed to Authlete does not match the user of the grant. + */ + grantSubject?: string; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const deviceVerificationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action21Schema)), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + clientName: s.optional(s.string()), + scopes: s.optional(s.array(s.lazy(() => scopeSchema))), + claimNames: s.optional(s.array(s.string())), + acrs: s.optional(s.array(s.string())), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + dynamicScopes: s.optional(s.array(s.lazy(() => dynamicScopeSchema))), + expiresAt: s.optional(s.int()), + gmAction: s.optional(s.lazy(() => grantManagementActionSchema)), + grantId: s.optional(s.string()), + grant: s.optional(s.lazy(() => grantSchema)), + grantSubject: s.optional(s.string()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/display.ts b/plugins/authlete/sdk/typescript/src/models/display.ts new file mode 100644 index 00000000..20d2745a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/display.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The display mode which the client application requests by `display` request parameter. When the + * authorization request does not have `display` request parameter, `PAGE` is set as the default + * value. + * + * It is ensured that the value of `display` is one of the supported display modes which are + * specified by `supportedDisplays` configuration parameter of the service. If the display mode + * specified by the authorization request is not supported, an error is raised. + * + * Values for this property correspond to the values listed in "[OpenID Connect Core 1.0, 3.1.2.1. + * Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), + * display". + */ +export const Display = { + Page: "PAGE", + Popup: "POPUP", + Touch: "TOUCH", + Wap: "WAP", +} as const; +export type Display = (typeof Display)[keyof typeof Display] | (string & {}); + +export const displaySchema: EnumSchema = s.enumOf(Display); diff --git a/plugins/authlete/sdk/typescript/src/models/dynamic-scope.ts b/plugins/authlete/sdk/typescript/src/models/dynamic-scope.ts new file mode 100644 index 00000000..86870307 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/dynamic-scope.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type DynamicScope = { + /** The scope name. */ + name?: string; + /** The scope value. */ + value?: string; +}; + +export const dynamicScopeSchema: Schema = s.object({ + name: s.optional(s.string()), + value: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/entity-type.ts b/plugins/authlete/sdk/typescript/src/models/entity-type.ts new file mode 100644 index 00000000..52e367de --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/entity-type.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The entity type, as defined in OpenID Federation 1.0. */ +export const EntityType = { + OpenidRelyingParty: "OPENID_RELYING_PARTY", + OpenidProvider: "OPENID_PROVIDER", + OauthAuthorizationServer: "OAUTH_AUTHORIZATION_SERVER", + OauthClient: "OAUTH_CLIENT", + OauthResource: "OAUTH_RESOURCE", + FederationEntity: "FEDERATION_ENTITY", + OpenidCredentialIssuer: "OPENID_CREDENTIAL_ISSUER", +} as const; +export type EntityType = (typeof EntityType)[keyof typeof EntityType] | (string & {}); + +export const entityTypeSchema: EnumSchema = s.enumOf(EntityType); diff --git a/plugins/authlete/sdk/typescript/src/models/fapi-mode.ts b/plugins/authlete/sdk/typescript/src/models/fapi-mode.ts new file mode 100644 index 00000000..13244f53 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/fapi-mode.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const FapiMode = { + Fapi1Advanced: "FAPI1_ADVANCED", + Fapi1Baseline: "FAPI1_BASELINE", + Fapi2MessageSigningAuthReq: "FAPI2_MESSAGE_SIGNING_AUTH_REQ", + Fapi2MessageSigningAuthRes: "FAPI2_MESSAGE_SIGNING_AUTH_RES", + Fapi2MessageSigningIntrospectionRes: "FAPI2_MESSAGE_SIGNING_INTROSPECTION_RES", + Fapi2Security: "FAPI2_SECURITY", +} as const; +export type FapiMode = (typeof FapiMode)[keyof typeof FapiMode] | (string & {}); + +export const fapiModeSchema: EnumSchema = s.enumOf(FapiMode); diff --git a/plugins/authlete/sdk/typescript/src/models/federation-configuration-response.ts b/plugins/authlete/sdk/typescript/src/models/federation-configuration-response.ts new file mode 100644 index 00000000..0ac5251a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/federation-configuration-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action25Schema, type Action25 } from "./action25.js"; + +export type FederationConfigurationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action25; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; +}; + +export const federationConfigurationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action25Schema)), + responseContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/federation-registration-request.ts b/plugins/authlete/sdk/typescript/src/models/federation-registration-request.ts new file mode 100644 index 00000000..f8120484 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/federation-registration-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type FederationRegistrationRequest = { + /** The entity configuration of a relying party. */ + entityConfiguration?: string; + /** The trust chain of a relying party. */ + trustChain?: string; +}; + +export const federationRegistrationRequestSchema: Schema = + s.object({ + entityConfiguration: s.optional(s.string()), + trustChain: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/federation-registration-response.ts b/plugins/authlete/sdk/typescript/src/models/federation-registration-response.ts new file mode 100644 index 00000000..d9f1bf54 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/federation-registration-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action26Schema, type Action26 } from "./action26.js"; +import { clientSchema, type Client } from "./client.js"; + +export type FederationRegistrationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action26; + /** + * The content that the authorization server implementation can use as the value of + * `WWW-Authenticate` header on errors. + */ + responseContent?: string; + client?: Client; +}; + +export const federationRegistrationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action26Schema)), + responseContent: s.optional(s.string()), + client: s.optional(s.lazy(() => clientSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/gm-request.ts b/plugins/authlete/sdk/typescript/src/models/gm-request.ts new file mode 100644 index 00000000..300d7812 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/gm-request.ts @@ -0,0 +1,70 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { grantManagementActionSchema, type GrantManagementAction } from "./grant-management-action.js"; + +export type GmRequest = { + /** An access token to introspect. */ + accessToken?: string; + /** + * Client certificate in PEM format, used to validate binding against access tokens using the TLS + * client certificate confirmation method. + */ + clientCertificate?: string; + /** + * `DPoP` header presented by the client during the request to the resource server. + * + * The header contains a signed JWT which includes the public key that is paired with the private + * key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the request from the client to the protected resource endpoint. This field is + * used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htm?: string; + /** + * URL of the protected resource endpoint. This field is used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ + gmAction?: GrantManagementAction; + /** + * The value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + /** + * The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even if + * the service's `dpopNonceRequired` property is `false`, calling the `/auth/gm` API with this + * `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the DPoP + * proof JWT includes the expected `nonce` value. + */ + dpopNonceRequired?: boolean; +}; + +export const gmRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + dpop: s.optional(s.string()), + htm: s.optional(s.string()), + htu: s.optional(s.string()), + gmAction: s.optional(s.lazy(() => grantManagementActionSchema)), + grantId: s.optional(s.string()), + dpopNonceRequired: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/gm-response.ts b/plugins/authlete/sdk/typescript/src/models/gm-response.ts new file mode 100644 index 00000000..7c613f64 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/gm-response.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action13Schema, type Action13 } from "./action13.js"; + +export type GmResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action13; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * Get the expected nonce value for DPoP proof JWT, which should be used as the value of the + * `DPoP-Nonce` HTTP header. + */ + dpopNonce?: string; +}; + +export const gmResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action13Schema)), + responseContent: s.optional(s.string()), + dpopNonce: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/grant-management-action.ts b/plugins/authlete/sdk/typescript/src/models/grant-management-action.ts new file mode 100644 index 00000000..582031d4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/grant-management-action.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The grant management action of the device authorization request. + * + * The `grant_management_action` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html). + */ +export const GrantManagementAction = { + Create: "CREATE", + Query: "QUERY", + Replace: "REPLACE", + Revoke: "REVOKE", + Merge: "MERGE", +} as const; +export type GrantManagementAction = + | (typeof GrantManagementAction)[keyof typeof GrantManagementAction] + | (string & {}); + +export const grantManagementActionSchema: EnumSchema = + s.enumOf(GrantManagementAction); diff --git a/plugins/authlete/sdk/typescript/src/models/grant-scope.ts b/plugins/authlete/sdk/typescript/src/models/grant-scope.ts new file mode 100644 index 00000000..3a38e2ed --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/grant-scope.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type GrantScope = { + /** Space-delimited scopes. */ + scope?: string; + /** List of resource indicators. */ + resource?: string[]; +}; + +export const grantScopeSchema: Schema = s.object({ + scope: s.optional(s.string()), + resource: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/grant-type.ts b/plugins/authlete/sdk/typescript/src/models/grant-type.ts new file mode 100644 index 00000000..e13386be --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/grant-type.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The grant type of the access token when the access token was created. */ +export const GrantType = { + AuthorizationCode: "AUTHORIZATION_CODE", + Implicit: "IMPLICIT", + Password: "PASSWORD", + ClientCredentials: "CLIENT_CREDENTIALS", + RefreshToken: "REFRESH_TOKEN", + Ciba: "CIBA", + DeviceCode: "DEVICE_CODE", + TokenExchange: "TOKEN_EXCHANGE", + JwtBearer: "JWT_BEARER", + PreAuthorizedCode: "PRE_AUTHORIZED_CODE", +} as const; +export type GrantType = (typeof GrantType)[keyof typeof GrantType] | (string & {}); + +export const grantTypeSchema: EnumSchema = s.enumOf(GrantType); diff --git a/plugins/authlete/sdk/typescript/src/models/grant.ts b/plugins/authlete/sdk/typescript/src/models/grant.ts new file mode 100644 index 00000000..64901f53 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/grant.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { grantScopeSchema, type GrantScope } from "./grant-scope.js"; + +export type Grant = { + scopes?: GrantScope[]; + /** The claims associated with the Grant. */ + claims?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; +}; + +export const grantSchema: Schema = s.object({ + scopes: s.optional(s.array(s.lazy(() => grantScopeSchema))), + claims: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/hsk-create-request.ts b/plugins/authlete/sdk/typescript/src/models/hsk-create-request.ts new file mode 100644 index 00000000..35d288c9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/hsk-create-request.ts @@ -0,0 +1,39 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type HskCreateRequest = { + /** The key type (EC or RSA) */ + kty?: string; + /** + * The key on the HSM. When the key use is "sig" (signature), the private key on the HSM is used + * to sign data and the corresponding public key is used to verify the signature. When the key use + * is "enc" (encryption), the private key on the HSM is used to decrypt encrypted data which have + * been encrypted with the corresponding public key + */ + use?: string; + /** Key ID for the key on the HSM. */ + kid?: string; + /** + * The name of the HSM. The identifier for the HSM that sits behind the Authlete server. For + * example, "google". + */ + hsmName?: string; + /** + * The algorithm of the key on the HSM. When the key use is `"sig"`, the algorithm represents a + * signing algorithm such as `"ES256"`. When the key use is `"enc"`, the algorithm represents an + * encryption algorithm such as `"RSA-OAEP-256"`. + * + * It is rare that HSMs support all the algorithms listed in [RFC 7518 JSON Web Algorithms + * (JWA)](https://www.rfc-editor.org/rfc/rfc7518.html). When the specified algorithm is not + * supported by the HSM, the request to the `/hsk/create` API fails. + */ + alg?: string; +}; + +export const hskCreateRequestSchema: Schema = s.object({ + kty: s.optional(s.string()), + use: s.optional(s.string()), + kid: s.optional(s.string()), + hsmName: s.optional(s.string()), + alg: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/hsk-create-response.ts b/plugins/authlete/sdk/typescript/src/models/hsk-create-response.ts new file mode 100644 index 00000000..fb872e00 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/hsk-create-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action27Schema, type Action27 } from "./action27.js"; +import { hskSchema, type Hsk } from "./hsk.js"; + +export type HskCreateResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** Result of the API call. */ + action?: Action27; + /** Holds information about a key managed in an HSM (Hardware Security Module) */ + hsk?: Hsk; +}; + +export const hskCreateResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action27Schema)), + hsk: s.optional(s.lazy(() => hskSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/hsk-delete-response.ts b/plugins/authlete/sdk/typescript/src/models/hsk-delete-response.ts new file mode 100644 index 00000000..e639d491 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/hsk-delete-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action28Schema, type Action28 } from "./action28.js"; +import { hskSchema, type Hsk } from "./hsk.js"; + +export type HskDeleteResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** Result of the API call */ + action?: Action28; + /** Holds information about a key managed in an HSM (Hardware Security Module) */ + hsk?: Hsk; +}; + +export const hskDeleteResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action28Schema)), + hsk: s.optional(s.lazy(() => hskSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/hsk-get-list-response.ts b/plugins/authlete/sdk/typescript/src/models/hsk-get-list-response.ts new file mode 100644 index 00000000..5c9919e6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/hsk-get-list-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action30Schema, type Action30 } from "./action30.js"; +import { hskSchema, type Hsk } from "./hsk.js"; + +export type HskGetListResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** Result of the API call */ + action?: Action30; + /** List of HSK */ + hsks?: Hsk[]; +}; + +export const hskGetListResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action30Schema)), + hsks: s.optional(s.array(s.lazy(() => hskSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/hsk.ts b/plugins/authlete/sdk/typescript/src/models/hsk.ts new file mode 100644 index 00000000..143d6317 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/hsk.ts @@ -0,0 +1,45 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +/** Holds information about a key managed in an HSM (Hardware Security Module) */ +export type Hsk = { + /** The key type (EC or RSA) */ + kty?: string; + /** + * Get the use of the key on the HSM. When the key use is "sig" (signature), the private key on + * the HSM is used to sign data and the corresponding public key is used to verify the signature. + * When the key use is "enc" (encryption), the private key on the HSM is used to decrypt encrypted + * data which have been encrypted with the corresponding public key + */ + use?: string; + /** Key ID for the key on the HSM. */ + kid?: string; + /** + * The name of the HSM. The identifier for the HSM that sits behind the Authlete server. For + * example, "google". + */ + hsmName?: string; + /** + * The handle for the key on the HSM. A handle is a base64url-encoded 256-bit random value (43 + * letters) which is assigned by Authlete on the call of the /api/hsk/create API + */ + handle?: string; + /** The public key that corresponds to the key on the HSM. */ + publicKey?: string; + /** + * The algorithm of the key on the HSM. When the key use is `"sig"`, the algorithm represents a + * signing algorithm such as `"ES256"`. When the key use is `"enc"`, the algorithm represents an + * encryption algorithm such as `"RSA-OAEP-256"`. + */ + alg?: string; +}; + +export const hskSchema: Schema = s.object({ + kty: s.optional(s.string()), + use: s.optional(s.string()), + kid: s.optional(s.string()), + hsmName: s.optional(s.string()), + handle: s.optional(s.string()), + publicKey: s.optional(s.string()), + alg: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/idp-error-error.ts b/plugins/authlete/sdk/typescript/src/models/idp-error-error.ts new file mode 100644 index 00000000..6299f43d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/idp-error-error.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +/** + * Error response returned by the Authlete IdP server. Unlike the main API's + * `resultCode`/`resultMessage` format, IdP errors carry a human-readable `error` message, + * optionally accompanied by contextual fields (such as `organizationId` or `apiServerId`). Request + * validation failures instead return an `errors` array of per-field messages. + */ +export type IdpErrorError = { + /** A human-readable error message. */ + error?: string; + /** Per-field validation error messages, present for request validation failures. */ + errors?: string[]; +}; + +export const idpErrorErrorSchema: Schema = s.object({ + error: s.optional(s.string()), + errors: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/idp-error.ts b/plugins/authlete/sdk/typescript/src/models/idp-error.ts new file mode 100644 index 00000000..5ecdb545 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/idp-error.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +/** + * Error response returned by the Authlete IdP server. Unlike the main API's + * `resultCode`/`resultMessage` format, IdP errors carry a human-readable `error` message, + * optionally accompanied by contextual fields (such as `organizationId` or `apiServerId`). Request + * validation failures instead return an `errors` array of per-field messages. + */ +export type IdpError = { + /** A human-readable error message. */ + error?: string; + /** Per-field validation error messages, present for request validation failures. */ + errors?: string[]; +}; + +export const idpErrorSchema: Schema = s.object({ + error: s.optional(s.string()), + errors: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-request.ts b/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-request.ts new file mode 100644 index 00000000..e8298fa1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-request.ts @@ -0,0 +1,50 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type IdtokenReissueRequest = { + /** + * The value of this parameter should be (a) the value of the "`jwtAccessToken`" parameter in a + * response from the `/auth/token` API when the value is available, or (b) the value of the + * "`accessToken`" parameter in the response from the `/auth/token` API when the value of the + * "`jwtAccessToken`" parameter is not available. + */ + accessToken: string; + /** + * The value of this parameter should be the value of the "`refreshToken`" parameter in a response + * from the `/auth/token` API. + */ + refreshToken?: string; + /** + * The value that should be used as the value of the "`sub`" claim of the ID token. This parameter + * is optional. When omitted, the value of the subject associated with the access token is used. + */ + sub?: string; + /** + * Additional claims that should be embedded in the payload part of the ID token. The format is a + * JSON object. This parameter is optional. + */ + claims?: string; + /** + * Additional parameters that should be embedded in the JWS header of the ID token. The format is + * a JSON object. This parameter is optional. + */ + idtHeaderParams?: string; + /** + * The type of the "`aud`" claim of the ID token being issued. Valid values of this parameter are + * as follows. > | Value | Description | | --- | --- | | "`array`" | The type of the `aud` claim + * becomes an array of strings. | | "`string`" | The type of the `aud` claim becomes a single + * string. | This parameter is optional, and the default value on omission is "`array`". This + * parameter takes precedence over the `idTokenAudType` property of {@link Service} (cf. + * {@link Service#getIdTokenAudType()}). + */ + idTokenAudType?: string; +}; + +export const idtokenReissueRequestSchema: Schema = s.object({ + accessToken: s.string(), + refreshToken: s.optional(s.string()), + sub: s.optional(s.string()), + claims: s.optional(s.string()), + idtHeaderParams: s.optional(s.string()), + idTokenAudType: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-response.ts b/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-response.ts new file mode 100644 index 00000000..17cc4610 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/idtoken-reissue-response.ts @@ -0,0 +1,27 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action12Schema, type Action12 } from "./action12.js"; + +export type IdtokenReissueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the implementation of the token endpoint should take. */ + action?: Action12; + /** + * The response content that can be used as the message body of the token response that should be + * returned from the token endpoint. + */ + responseContent?: string; + /** The reissued ID token */ + idToken?: string; +}; + +export const idtokenReissueResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action12Schema)), + responseContent: s.optional(s.string()), + idToken: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/info-response.ts b/plugins/authlete/sdk/typescript/src/models/info-response.ts new file mode 100644 index 00000000..dfc8c736 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/info-response.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { serviceTokenCacheSchema, type ServiceTokenCache } from "./service-token-cache.js"; + +export type InfoResponse = { + /** The server version. */ + version: string; + /** the features that the server supports. */ + features: string[]; + /** Server configuration properties as key-value pairs. */ + configuration?: Record; + /** Cache statistics for service tokens. */ + serviceTokenCache?: ServiceTokenCache; +}; + +export const infoResponseSchema: Schema = s.object({ + version: s.string(), + features: s.array(s.string()), + configuration: s.optional(s.record(s.string(), s.string())), + serviceTokenCache: s.optional(s.lazy(() => serviceTokenCacheSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/introspection-request.ts b/plugins/authlete/sdk/typescript/src/models/introspection-request.ts new file mode 100644 index 00000000..764df387 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/introspection-request.ts @@ -0,0 +1,132 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { pairSchema, type Pair } from "./pair.js"; + +export type IntrospectionRequest = { + /** An access token to introspect. */ + token: string; + /** + * A string array listing names of scopes which the caller (= a protected resource endpoint of the + * service) requires. When the content type of the request from the service is + * `application/x-www-form-urlencoded`, the format of `scopes` is a space-separated list of scope + * names. + * + * If this parameter is a non-empty array and if it contains a scope which is not covered by the + * access token,`action=FORBIDDEN` with `error=insufficient_scope` is returned from Authlete. + */ + scopes?: string[]; + /** + * A subject (= a user account managed by the service) whom the caller (= a protected resource + * endpoint of the service) requires. + * + * If this parameter is not `null` and if the value does not match the subject who is associated + * with the access token, `action=FORBIDDEN` with `error=invalid_request` is returned from + * Authlete. + */ + subject?: string; + /** + * Client certificate in PEM format, used to validate binding against access tokens using the TLS + * client certificate confirmation method. + */ + clientCertificate?: string; + /** + * `DPoP` header presented by the client during the request to the resource server. + * + * The header contains a signed JWT which includes the public key that is paired with the private + * key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the request from the client to the protected resource endpoint. This field is + * used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htm?: string; + /** + * URL of the protected resource endpoint. This field is used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** + * The resources specified by the `resource` request parameters in the token request. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * Authentication Context Class Reference values one of which the user authentication performed + * during the course of issuing the access token must satisfy. + */ + acrValues?: string[]; + /** + * The maximum authentication age which is the maximum allowable elapsed time since the user + * authentication was performed during the course of issuing the access token. + */ + maxAge?: number; + /** + * HTTP Message Components required to be in the signature. If absent, defaults to [ "@method", + * "@target-uri", "authorization" ]. + */ + requiredComponents?: string[]; + /** The full URL of the userinfo endpoint. */ + uri?: string; + /** The HTTP message body of the request, if present. */ + message?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this must + * include the Signature and Signature-Input headers, as well as any additional headers covered by + * the signature. + */ + headers?: Pair[]; + /** The target URI of the resource request, including the query part, if any. */ + targetUri?: string; + /** + * The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` value. + * + * If this request parameter is set to `true` or if the service's `dpopNonceRequired` property is + * set to `true`, the `/auth/introspection` API checks if the DPoP proof JWT includes the expected + * `nonce` value. In this case, the response from the `/auth/introspection` API will include the + * `dpopNonce` response parameter, which should be used as the value of the DPoP-Nonce HTTP + * header. + */ + dpopNonceRequired?: boolean; + /** + * The flag indicating whether the resource request contains a request body. + * + * When the resource request must comply with the HTTP message signing requirements defined in the + * FAPI 2.0 Message Signing specification, the `"content-digest"` component identifier must be + * included in the signature base of the HTTP message signature (see [RFC 9421 HTTP Message + * Signatures](https://www.rfc-editor.org/rfc/rfc9421.html)) if the resource request contains a + * request body. + * + * When this `requestBodyContained` parameter is set to `true`, Authlete checks whether + * `"content-digest"` is included in the signature base, if the FAPI profile applies to the + * resource request. + */ + requestBodyContained?: boolean; +}; + +export const introspectionRequestSchema: Schema = s.object({ + token: s.string(), + scopes: s.optional(s.array(s.string())), + subject: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + dpop: s.optional(s.string()), + htm: s.optional(s.string()), + htu: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + acrValues: s.optional(s.array(s.string())), + maxAge: s.optional(s.int()), + requiredComponents: s.optional(s.array(s.string())), + uri: s.optional(s.string()), + message: s.optional(s.string()), + headers: s.optional(s.array(s.lazy(() => pairSchema))), + targetUri: s.optional(s.string()), + dpopNonceRequired: s.optional(s.boolean()), + requestBodyContained: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/introspection-response.ts b/plugins/authlete/sdk/typescript/src/models/introspection-response.ts new file mode 100644 index 00000000..95b44ee7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/introspection-response.ts @@ -0,0 +1,206 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action7Schema, type Action7 } from "./action7.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { grantTypeSchema, type GrantType } from "./grant-type.js"; +import { grantSchema, type Grant } from "./grant.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { propertySchema, type Property } from "./property.js"; +import { scopeSchema, type Scope } from "./scope.js"; + +export type IntrospectionResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action7; + /** + * The content that the authorization server implementation can use as the value of + * `WWW-Authenticate` header on errors. + */ + responseContent?: string; + /** The client ID. */ + clientId?: number; + /** + * The client ID alias when the token request was made. If the client did not have an alias, this + * parameter is `null`. Also, if the token request was invalid and it failed to identify a client, + * this parameter is `null`. + */ + clientIdAlias?: string; + /** + * The flag which indicates whether the client ID alias was used when the token request was made. + * `true` if the client ID alias was used when the token request was made. + */ + clientIdAliasUsed?: boolean; + /** + * The time at which the access token expires. The value is represented in milliseconds since the + * Unix epoch (1970-01-01). + */ + expiresAt?: number; + /** + * The subject who is associated with the access token. The value of this property is `null` if + * the access token was issued using the flow of [Client Credentials + * Grant](tools.ietf.org/html/rfc6749#section-4.4). + */ + subject?: string; + /** The scopes covered by the access token. */ + scopes?: string[]; + /** `true` if the access token exists. */ + existent?: boolean; + /** true` if the access token is usable (= exists and has not expired). */ + usable?: boolean; + /** `true` if the access token exists. */ + sufficient?: boolean; + /** + * `true` if the access token can be refreshed using the associated refresh token which had been + * issued along with the access token. `false` if the refresh token for the access token has + * expired or the access token has no associated refresh token. + */ + refreshable?: boolean; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** The client certificate thumbprint used to validate the access token. */ + certificateThumbprint?: string; + /** + * The target resources. This represents the resources specified by the `resource` request + * parameters or by the `resource` property in the request object. + * + * See "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The target resources this proeprty holds may be the same as or different from the ones + * `resource` property holds. + */ + accessTokenResources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** + * The scopes property of this class is a list of scope names. The property does not hold + * information about scope attributes. This scopeDetails property was newly created to convey + * information about scope attributes. + */ + scopeDetails?: Scope[]; + /** + * The value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + grant?: Grant; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** The grant type of the access token when the access token was created. */ + grantType?: GrantType; + /** + * The Authentication Context Class Reference of the user authentication that the authorization + * server performed during the course of issuing the access token. + */ + acr?: string; + /** + * The time when the user authentication was performed during the course of issuing the access + * token. + */ + authTime?: number; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** The flag indicating whether the token is for credential issuance. */ + forCredentialIssuance?: boolean; + /** The c_nonce */ + cnonce?: string; + /** The time at which the `c_nonce` expires. */ + cnonceExpiresAt?: number; + /** The credentials that can be obtained by presenting the access token. */ + issuableCredentials?: string; + /** + * The expected nonce value for DPoP proof JWT, which should be used as the value of the + * `DPoP-Nonce` HTTP header. + */ + dpopNonce?: string; + /** + * The flag indicating whether the HTTP response from the protected resource endpoint must include + * an HTTP message signature ([RFC 9421 HTTP Message + * Signatures](https://www.rfc-editor.org/rfc/rfc9421.html)) in compliance with [FAPI 2.0 Message + * Signing](https://openid.bitbucket.io/fapi/fapi-2_0-message-signing.html). + * + * When this response parameter is `true`, the HTTP response from the protected resource endpoint + * must include the `Signature` and `Signature-Input` HTTP fields. + */ + responseSigningRequired?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const introspectionResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action7Schema)), + responseContent: s.optional(s.string()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + expiresAt: s.optional(s.int()), + subject: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + existent: s.optional(s.boolean()), + usable: s.optional(s.boolean()), + sufficient: s.optional(s.boolean()), + refreshable: s.optional(s.boolean()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + certificateThumbprint: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + accessTokenResources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + scopeDetails: s.optional(s.array(s.lazy(() => scopeSchema))), + grantId: s.optional(s.string()), + grant: s.optional(s.lazy(() => grantSchema)), + forExternalAttachment: s.optional(s.boolean()), + consentedClaims: s.optional(s.array(s.string())), + grantType: s.optional(s.lazy(() => grantTypeSchema)), + acr: s.optional(s.string()), + authTime: s.optional(s.int()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + forCredentialIssuance: s.optional(s.boolean()), + cnonce: s.optional(s.string()), + cnonceExpiresAt: s.optional(s.int()), + issuableCredentials: s.optional(s.string()), + dpopNonce: s.optional(s.string()), + responseSigningRequired: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/jose-verify-request.ts b/plugins/authlete/sdk/typescript/src/models/jose-verify-request.ts new file mode 100644 index 00000000..dcf3c27b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/jose-verify-request.ts @@ -0,0 +1,29 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type JoseVerifyRequest = { + /** A JOSE object. */ + jose: string; + /** Mandatory claims that are required to be included in the JOSE object. */ + mandatoryClaims?: string[]; + /** Allowable clock skew in seconds. */ + clockSkew?: number; + /** + * The identifier of the client application whose keys are required for verification of the JOSE + * object. + */ + clientIdentifier?: string; + /** + * The flag which indicates whether the signature of the JOSE object has been signed by a client + * application with the client's private key or a shared symmetric key. + */ + signedByClient?: boolean; +}; + +export const joseVerifyRequestSchema: Schema = s.object({ + jose: s.string(), + mandatoryClaims: s.optional(s.array(s.string())), + clockSkew: s.optional(s.int()), + clientIdentifier: s.optional(s.string()), + signedByClient: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/jose-verify-response.ts b/plugins/authlete/sdk/typescript/src/models/jose-verify-response.ts new file mode 100644 index 00000000..b5433a54 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/jose-verify-response.ts @@ -0,0 +1,29 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type JoseVerifyResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The result of the verification on the JOSE object. */ + valid?: boolean; + /** The result of the signature verification. */ + signatureValid?: boolean; + /** The list of missing claims. */ + missingClaims?: string[]; + /** The list of invalid claims. */ + invalidClaims?: string[]; + /** The list of error messages. */ + errorDescriptions?: string[]; +}; + +export const joseVerifyResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + valid: s.optional(s.boolean()), + signatureValid: s.optional(s.boolean()), + missingClaims: s.optional(s.array(s.string())), + invalidClaims: s.optional(s.array(s.string())), + errorDescriptions: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/jwe-alg.ts b/plugins/authlete/sdk/typescript/src/models/jwe-alg.ts new file mode 100644 index 00000000..6fed9d7f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/jwe-alg.ts @@ -0,0 +1,38 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this refers + * to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server for + * transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key transport + * of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ +export const JweAlg = { + Rsa15: "RSA1_5", + RsaOaep: "RSA_OAEP", + RsaOaep256: "RSA_OAEP_256", + A128Kw: "A128KW", + A192Kw: "A192KW", + A256Kw: "A256KW", + Dir: "DIR", + EcdhEs: "ECDH_ES", + EcdhEsA128Kw: "ECDH_ES_A128KW", + EcdhEsA192Kw: "ECDH_ES_A192KW", + EcdhEsA256Kw: "ECDH_ES_A256KW", + A128Gcmkw: "A128GCMKW", + A192Gcmkw: "A192GCMKW", + A256Gcmkw: "A256GCMKW", + Pbes2Hs256A128Kw: "PBES2_HS256_A128KW", + Pbes2Hs384A192Kw: "PBES2_HS384_A192KW", + Pbes2Hs512A256Kw: "PBES2_HS512_A256KW", +} as const; +export type JweAlg = (typeof JweAlg)[keyof typeof JweAlg] | (string & {}); + +export const jweAlgSchema: EnumSchema = s.enumOf(JweAlg); diff --git a/plugins/authlete/sdk/typescript/src/models/jwe-enc.ts b/plugins/authlete/sdk/typescript/src/models/jwe-enc.ts new file mode 100644 index 00000000..fb1c086a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/jwe-enc.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. For + * instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by server + * when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by the + * client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ +export const JweEnc = { + A128CbcHs256: "A128CBC_HS256", + A192CbcHs384: "A192CBC_HS384", + A256CbcHs512: "A256CBC_HS512", + A128Gcm: "A128GCM", + A192Gcm: "A192GCM", + A256Gcm: "A256GCM", +} as const; +export type JweEnc = (typeof JweEnc)[keyof typeof JweEnc] | (string & {}); + +export const jweEncSchema: EnumSchema = s.enumOf(JweEnc); diff --git a/plugins/authlete/sdk/typescript/src/models/jws-alg.ts b/plugins/authlete/sdk/typescript/src/models/jws-alg.ts new file mode 100644 index 00000000..a6961fe5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/jws-alg.ts @@ -0,0 +1,38 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ +export const JwsAlg = { + None: "NONE", + Hs256: "HS256", + Hs384: "HS384", + Hs512: "HS512", + Rs256: "RS256", + Rs384: "RS384", + Rs512: "RS512", + Es256: "ES256", + Es384: "ES384", + Es512: "ES512", + Ps256: "PS256", + Ps384: "PS384", + Ps512: "PS512", + Es256K: "ES256K", + EdDsa: "EdDSA", +} as const; +export type JwsAlg = (typeof JwsAlg)[keyof typeof JwsAlg] | (string & {}); + +export const jwsAlgSchema: EnumSchema = s.enumOf(JwsAlg); diff --git a/plugins/authlete/sdk/typescript/src/models/named-uri.ts b/plugins/authlete/sdk/typescript/src/models/named-uri.ts new file mode 100644 index 00000000..50ff9d6f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/named-uri.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type NamedUri = { + name?: string; + uri?: string; +}; + +export const namedUriSchema: Schema = s.object({ + name: s.optional(s.string()), + uri: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/native-sso-logout-request.ts b/plugins/authlete/sdk/typescript/src/models/native-sso-logout-request.ts new file mode 100644 index 00000000..27ca5c32 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/native-sso-logout-request.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type NativeSsoLogoutRequest = { + /** The session ID of a user's authentication session. */ + sessionId: string; +}; + +export const nativeSsoLogoutRequestSchema: Schema = s.object({ + sessionId: s.string(), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/native-sso-logout-response.ts b/plugins/authlete/sdk/typescript/src/models/native-sso-logout-response.ts new file mode 100644 index 00000000..706dc49d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/native-sso-logout-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action19Schema, type Action19 } from "./action19.js"; + +export type NativeSsoLogoutResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the API caller should take. */ + action?: Action19; + /** The number of deleted access/refresh token records. */ + count?: number; +}; + +export const nativeSsoLogoutResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action19Schema)), + count: s.optional(s.int()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/native-sso-request.ts b/plugins/authlete/sdk/typescript/src/models/native-sso-request.ts new file mode 100644 index 00000000..8c79879b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/native-sso-request.ts @@ -0,0 +1,62 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type NativeSsoRequest = { + /** + * The value of this parameter should be: (a) the value of the `jwtAccessToken` parameter in a + * response from the `/auth/token` API when the value is available, or (b) the value of the + * `accessToken` parameter in the response from the `/auth/token` API when the `jwtAccessToken` + * parameter is not available. + */ + accessToken: string; + /** + * The value of this parameter should be the value of the `refreshToken` parameter in a response + * from the `/auth/token` API. + */ + refreshToken?: string; + /** + * The value that should be used as the value of the `sub` claim of the ID token. This parameter + * is optional. When omitted, the value of the subject associated with the access token is used. + */ + sub?: string; + /** + * Additional claims that should be embedded in the payload part of the ID token. The format is a + * JSON object. This parameter is optional. + */ + claims?: string; + /** + * Additional parameters that should be embedded in the JWS header of the ID token. The format is + * a JSON object. This parameter is optional. + */ + idtHeaderParams?: string; + /** + * The type of the `aud` claim of the ID token being issued. Valid values of this parameter are as + * follows: + */ + idTokenAudType?: string; + /** + * The device secret. The value of this parameter should be the value of the `deviceSecret` + * parameter in the response from the `/auth/token` API, if the parameter is present. Otherwise, + * the authorization server should generate a new device secret and specify it as the value of + * this parameter. + */ + deviceSecret: string; + /** + * The device secret hash. The specified device secret hash is included as the value of the + * `ds_hash` claim in the ID token generated by the `/nativesso` API. If the `deviceSecretHash` + * request parameter is omitted, the value of the `deviceSecret` request parameter is used to + * compute the hash. + */ + deviceSecretHash?: string; +}; + +export const nativeSsoRequestSchema: Schema = s.object({ + accessToken: s.string(), + refreshToken: s.optional(s.string()), + sub: s.optional(s.string()), + claims: s.optional(s.string()), + idtHeaderParams: s.optional(s.string()), + idTokenAudType: s.optional(s.string()), + deviceSecret: s.string(), + deviceSecretHash: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/native-sso-response.ts b/plugins/authlete/sdk/typescript/src/models/native-sso-response.ts new file mode 100644 index 00000000..98c9708a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/native-sso-response.ts @@ -0,0 +1,27 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action12Schema, type Action12 } from "./action12.js"; + +export type NativeSsoResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the implementation of the token endpoint should take. */ + action?: Action12; + /** + * The response content that can be used as the message body of the token response that should be + * returned from the token endpoint. + */ + responseContent?: string; + /** The issued ID token. */ + idToken?: string; +}; + +export const nativeSsoResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action12Schema)), + responseContent: s.optional(s.string()), + idToken: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/pair.ts b/plugins/authlete/sdk/typescript/src/models/pair.ts new file mode 100644 index 00000000..198587d1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/pair.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Pair = { + /** The key part. */ + key?: string; + /** The value part. */ + value?: string; +}; + +export const pairSchema: Schema = s.object({ + key: s.optional(s.string()), + value: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/prompt.ts b/plugins/authlete/sdk/typescript/src/models/prompt.ts new file mode 100644 index 00000000..0a509f06 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/prompt.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The prompt that the UI displayed to the end-user must satisfy as the minimum level. This value + * comes from `prompt` request parameter. + * + * When the authorization request does not contain `prompt` request parameter, `CONSENT` is used as + * the default value. + * + * See "[OpenID Connect Core 1.0, 3.1.2.1. Authentication + * Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest), prompt" for `prompt` + * request parameter. + */ +export const Prompt = { + None: "NONE", + Login: "LOGIN", + Consent: "CONSENT", + SelectAccount: "SELECT_ACCOUNT", + Create: "CREATE", +} as const; +export type Prompt = (typeof Prompt)[keyof typeof Prompt] | (string & {}); + +export const promptSchema: EnumSchema = s.enumOf(Prompt); diff --git a/plugins/authlete/sdk/typescript/src/models/property.ts b/plugins/authlete/sdk/typescript/src/models/property.ts new file mode 100644 index 00000000..87f8a983 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/property.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Property = { + /** The key part. */ + key?: string; + /** The value part. */ + value?: string; + /** + * The flag to indicate whether this property hidden from or visible to client applications. If + * `true`, this property is hidden from client applications. Otherwise, this property is visible + * to client applications. + */ + hidden?: boolean; +}; + +export const propertySchema: Schema = s.object({ + key: s.optional(s.string()), + value: s.optional(s.string()), + hidden: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/pushed-authorization-request.ts b/plugins/authlete/sdk/typescript/src/models/pushed-authorization-request.ts new file mode 100644 index 00000000..61efdbb2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/pushed-authorization-request.ts @@ -0,0 +1,72 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type PushedAuthorizationRequest = { + /** + * The pushed authorization request body received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the pushed request from the client + * application. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the pushed authorization request + * from the client application. + */ + clientSecret?: string; + /** + * The client certificate from the MTLS connection to pushed authorization endpoint from the + * client application. + */ + clientCertificate?: string; + /** + * The certificate path presented by the client during client authentication. These certificates + * are strings in PEM format. + */ + clientCertificatePath?: string[]; + /** DPoP Header */ + dpop?: string; + /** HTTP Method (for DPoP validation). */ + htm?: string; + /** HTTP URL base (for DPoP validation). */ + htu?: string; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification + * of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even if + * the service's `dpopNonceRequired` property is `false`, calling the `/pushed_auth_req` API with + * this `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the DPoP + * proof JWT includes the expected `nonce` value. + */ + dpopNonceRequired?: boolean; +}; + +export const pushedAuthorizationRequestSchema: Schema = + s.object({ + parameters: s.string(), + clientId: s.optional(s.string()), + clientSecret: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + clientCertificatePath: s.optional(s.array(s.string())), + dpop: s.optional(s.string()), + htm: s.optional(s.string()), + htu: s.optional(s.string()), + oauthClientAttestation: s.optional(s.string()), + oauthClientAttestationPop: s.optional(s.string()), + dpopNonceRequired: s.optional(s.boolean()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/pushed-authorization-response.ts b/plugins/authlete/sdk/typescript/src/models/pushed-authorization-response.ts new file mode 100644 index 00000000..b59cc153 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/pushed-authorization-response.ts @@ -0,0 +1,46 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action3Schema, type Action3 } from "./action3.js"; +import { clientAuthMethodSchema, type ClientAuthMethod } from "./client-auth-method.js"; + +export type PushedAuthorizationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** + * The next action that the authorization server implementation should take. Any other value other + * than "CREATED" should be handled as unsuccessful result. + */ + action?: Action3; + /** The request_uri created to the client to be used as request_uri on the authorize call. */ + requestUri?: string; + /** + * The content that the authorization server implementation is to return to the client + * application. + */ + responseContent?: string; + /** + * The client authentication method that the client application declares that it uses at the token + * endpoint. This property corresponds to `token_endpoint_auth_method` in [OpenID Connect Dynamic + * Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientAuthMethod?: ClientAuthMethod; + /** + * Get the expected nonce value for DPoP proof JWT, which should be used as the value of the + * `DPoP-Nonce` HTTP header. + */ + dpopNonce?: string; +}; + +export const pushedAuthorizationResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action3Schema)), + requestUri: s.optional(s.string()), + responseContent: s.optional(s.string()), + clientAuthMethod: s.optional(s.lazy(() => clientAuthMethodSchema)), + dpopNonce: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/reason.ts b/plugins/authlete/sdk/typescript/src/models/reason.ts new file mode 100644 index 00000000..b5014121 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/reason.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The reason of the failure of the authorization request. For more details, see [NO_INTERACTION] in + * the description of `/auth/authorization` API. + */ +export const Reason = { + Unknown: "UNKNOWN", + NotLoggedIn: "NOT_LOGGED_IN", + MaxAgeNotSupported: "MAX_AGE_NOT_SUPPORTED", + ExceedsMaxAge: "EXCEEDS_MAX_AGE", + DifferentSubject: "DIFFERENT_SUBJECT", + AcrNotSatisfied: "ACR_NOT_SATISFIED", + Denied: "DENIED", + ServerError: "SERVER_ERROR", + NotAuthenticated: "NOT_AUTHENTICATED", + AccountSelectionRequired: "ACCOUNT_SELECTION_REQUIRED", + ConsentRequired: "CONSENT_REQUIRED", + InteractionRequired: "INTERACTION_REQUIRED", + InvalidTarget: "INVALID_TARGET", + TemporarilyUnavailable: "TEMPORARILY_UNAVAILABLE", + InvalidScope: "INVALID_SCOPE", + UnauthorizedClient: "UNAUTHORIZED_CLIENT", +} as const; +export type Reason = (typeof Reason)[keyof typeof Reason] | (string & {}); + +export const reasonSchema: EnumSchema = s.enumOf(Reason); diff --git a/plugins/authlete/sdk/typescript/src/models/reason1.ts b/plugins/authlete/sdk/typescript/src/models/reason1.ts new file mode 100644 index 00000000..56de4418 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/reason1.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The reason of the failure of the token request. */ +export const Reason1 = { + Unknown: "UNKNOWN", + InvalidResourceOwnerCredentials: "INVALID_RESOURCE_OWNER_CREDENTIALS", + InvalidTarget: "INVALID_TARGET", + InvalidClient: "INVALID_CLIENT", + UnauthorizedClient: "UNAUTHORIZED_CLIENT", + InvalidScope: "INVALID_SCOPE", +} as const; +export type Reason1 = (typeof Reason1)[keyof typeof Reason1] | (string & {}); + +export const reason1Schema: EnumSchema = s.enumOf(Reason1); diff --git a/plugins/authlete/sdk/typescript/src/models/reason2.ts b/plugins/authlete/sdk/typescript/src/models/reason2.ts new file mode 100644 index 00000000..bface3d2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/reason2.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The reason of the failure of the backchannel authentication request. This request parameter is + * not mandatory but optional. However, giving this parameter is recommended. If omitted, + * `SERVER_ERROR` is used as a reason. + */ +export const Reason2 = { + AccessDenied: "ACCESS_DENIED", + ExpiredLoginHintToken: "EXPIRED_LOGIN_HINT_TOKEN", + InvalidBindingMessage: "INVALID_BINDING_MESSAGE", + InvalidTarget: "INVALID_TARGET", + InvalidUserCode: "INVALID_USER_CODE", + MissingUserCode: "MISSING_USER_CODE", + ServerError: "SERVER_ERROR", + UnauthorizedClient: "UNAUTHORIZED_CLIENT", + UnknownUserId: "UNKNOWN_USER_ID", +} as const; +export type Reason2 = (typeof Reason2)[keyof typeof Reason2] | (string & {}); + +export const reason2Schema: EnumSchema = s.enumOf(Reason2); diff --git a/plugins/authlete/sdk/typescript/src/models/response-mode.ts b/plugins/authlete/sdk/typescript/src/models/response-mode.ts new file mode 100644 index 00000000..6e314c3f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/response-mode.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const ResponseMode = { + Query: "QUERY", + Fragment: "FRAGMENT", + FormPost: "FORM_POST", + Jwt: "JWT", + QueryJwt: "QUERY_JWT", + FragmentJwt: "FRAGMENT_JWT", + FormPostJwt: "FORM_POST_JWT", +} as const; +export type ResponseMode = (typeof ResponseMode)[keyof typeof ResponseMode] | (string & {}); + +export const responseModeSchema: EnumSchema = s.enumOf(ResponseMode); diff --git a/plugins/authlete/sdk/typescript/src/models/response-type.ts b/plugins/authlete/sdk/typescript/src/models/response-type.ts new file mode 100644 index 00000000..a685f010 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/response-type.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const ResponseType = { + None: "NONE", + Code: "CODE", + Token: "TOKEN", + IdToken: "ID_TOKEN", + CodeToken: "CODE_TOKEN", + CodeIdToken: "CODE_ID_TOKEN", + IdTokenToken: "ID_TOKEN_TOKEN", + CodeIdTokenToken: "CODE_ID_TOKEN_TOKEN", +} as const; +export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType] | (string & {}); + +export const responseTypeSchema: EnumSchema = s.enumOf(ResponseType); diff --git a/plugins/authlete/sdk/typescript/src/models/result-error.ts b/plugins/authlete/sdk/typescript/src/models/result-error.ts new file mode 100644 index 00000000..4a99961b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/result-error.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ResultError = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; +}; + +export const resultErrorSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/result.ts b/plugins/authlete/sdk/typescript/src/models/result.ts new file mode 100644 index 00000000..5b02b57d --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/result.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Result = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; +}; + +export const resultSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/result1.ts b/plugins/authlete/sdk/typescript/src/models/result1.ts new file mode 100644 index 00000000..deb08f44 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/result1.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The result of the end-user authentication and authorization. One of the following. Details are + * described in the description. + */ +export const Result1 = { + TransactionFailed: "TRANSACTION_FAILED", + AccessDenied: "ACCESS_DENIED", + Authorized: "AUTHORIZED", +} as const; +export type Result1 = (typeof Result1)[keyof typeof Result1] | (string & {}); + +export const result1Schema: EnumSchema = s.enumOf(Result1); diff --git a/plugins/authlete/sdk/typescript/src/models/revocation-request.ts b/plugins/authlete/sdk/typescript/src/models/revocation-request.ts new file mode 100644 index 00000000..20345af9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/revocation-request.ts @@ -0,0 +1,63 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type RevocationRequest = { + /** + * OAuth 2.0 token revocation request parameters which are the request parameters that the OAuth + * 2.0 token revocation endpoint ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)) of + * the authorization server implementation received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the revocation request from the client + * application. + * + * If the revocation endpoint of the authorization server implementation supports Basic + * Authentication as a means of client authentication, and the request from the client application + * contains its client ID in `Authorization` header, the value should be extracted and set to this + * parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the revocation request from the + * client application. + * + * If the revocation endpoint of the authorization server implementation supports basic + * authentication as a means of client authentication, and the request from the client application + * contained its client secret in `Authorization` header, the value should be extracted and set to + * this parameter. + */ + clientSecret?: string; + /** + * The client certificate used in the TLS connection between the client application and the + * revocation endpoint. + */ + clientCertificate?: string; + /** The certificate path presented by the client during client authentication. */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification + * of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; +}; + +export const revocationRequestSchema: Schema = s.object({ + parameters: s.string(), + clientId: s.optional(s.string()), + clientSecret: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + clientCertificatePath: s.optional(s.array(s.string())), + oauthClientAttestation: s.optional(s.string()), + oauthClientAttestationPop: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/revocation-response.ts b/plugins/authlete/sdk/typescript/src/models/revocation-response.ts new file mode 100644 index 00000000..d1fdfed8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/revocation-response.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action9Schema, type Action9 } from "./action9.js"; + +export type RevocationResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action9; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; +}; + +export const revocationResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action9Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/scope.ts b/plugins/authlete/sdk/typescript/src/models/scope.ts new file mode 100644 index 00000000..2832cef7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/scope.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { taggedValueSchema, type TaggedValue } from "./tagged-value.js"; + +export type Scope = { + /** The name of the scope. */ + name?: string; + /** + * `true` to mark the scope as default. Scopes marked as default are regarded as requested when an + * authorization request from a client application does not contain scope request parameter. + */ + defaultEntry?: boolean; + /** The description about the scope. */ + description?: string; + /** The descriptions about this scope in multiple languages. */ + descriptions?: TaggedValue[]; + /** The attributes of the scope. */ + attributes?: Pair[] | null; +}; + +export const scopeSchema: Schema = s.object({ + name: s.optional(s.string()), + defaultEntry: s.optional(s.boolean()), + description: s.optional(s.string()), + descriptions: s.optional(s.array(s.lazy(() => taggedValueSchema))), + attributes: s.optionalNullable(s.array(s.lazy(() => pairSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/service-create-idp-request.ts b/plugins/authlete/sdk/typescript/src/models/service-create-idp-request.ts new file mode 100644 index 00000000..7ae8aa14 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-create-idp-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { serviceSchema, type Service } from "./service.js"; + +export type ServiceCreateIdpRequest = { + /** + * The numeric ID of the API server to create the service on. Official SDKs inject this + * automatically for known Authlete clusters, so it can be omitted there; all other callers must + * provide it. + */ + apiServerId?: number; + /** The numeric ID of the organization the service belongs to. */ + organizationId: number; + service?: Service; +}; + +export const serviceCreateIdpRequestSchema: Schema = + s.object({ + apiServerId: s.optional(s.int()), + organizationId: s.int(), + service: s.optional(s.lazy(() => serviceSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/service-get-list-response.ts b/plugins/authlete/sdk/typescript/src/models/service-get-list-response.ts new file mode 100644 index 00000000..223009b4 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-get-list-response.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { serviceSchema, type Service } from "./service.js"; + +export type ServiceGetListResponse = { + /** + * Start index (inclusive) of the result set. The default value is 0. Must not be a negative + * number. + */ + start?: number; + /** + * Start index (inclusive) of the result set. The default value is 0. Must not be a negative + * number. + */ + end?: number; + /** + * Total number of services owned by the service owner. This doesn't mean the number of services + * contained in the response. + */ + totalCount?: number; + /** An array of services. */ + services?: Service[]; +}; + +export const serviceGetListResponseSchema: Schema = s.object({ + start: s.optional(s.int()), + end: s.optional(s.int()), + totalCount: s.optional(s.int()), + services: s.optional(s.array(s.lazy(() => serviceSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/service-jwks-get-response.ts b/plugins/authlete/sdk/typescript/src/models/service-jwks-get-response.ts new file mode 100644 index 00000000..ac750f73 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-jwks-get-response.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ServiceJwksGetResponse = { + /** An array of [JWK](https://datatracker.ietf.org/doc/html/rfc7517)s. */ + keys?: Record[]; +}; + +export const serviceJwksGetResponseSchema: Schema = s.object({ + keys: s.optional(s.array(s.record(s.string(), s.unknown()))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/service-profile.ts b/plugins/authlete/sdk/typescript/src/models/service-profile.ts new file mode 100644 index 00000000..2e3c3da0 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-profile.ts @@ -0,0 +1,10 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const ServiceProfile = { + Fapi: "FAPI", + OpenBanking: "OPEN_BANKING", +} as const; +export type ServiceProfile = (typeof ServiceProfile)[keyof typeof ServiceProfile] | (string & {}); + +export const serviceProfileSchema: EnumSchema = s.enumOf(ServiceProfile); diff --git a/plugins/authlete/sdk/typescript/src/models/service-remove-idp-request.ts b/plugins/authlete/sdk/typescript/src/models/service-remove-idp-request.ts new file mode 100644 index 00000000..fcf1c29c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-remove-idp-request.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ServiceRemoveIdpRequest = { + /** + * The numeric ID of the API server the service belongs to. Official SDKs inject this + * automatically for known Authlete clusters, so it can be omitted there; all other callers must + * provide it. + */ + apiServerId?: number; + /** The numeric ID of the organization the service belongs to. */ + organizationId: number; + /** The numeric ID of the service to delete. */ + serviceId: number; +}; + +export const serviceRemoveIdpRequestSchema: Schema = + s.object({ + apiServerId: s.optional(s.int()), + organizationId: s.int(), + serviceId: s.int(), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/service-token-cache.ts b/plugins/authlete/sdk/typescript/src/models/service-token-cache.ts new file mode 100644 index 00000000..999299e6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service-token-cache.ts @@ -0,0 +1,30 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +/** Cache statistics for service tokens. */ +export type ServiceTokenCache = { + /** The number of cache hits. */ + hitCount?: number; + /** The number of cache misses. */ + missCount?: number; + /** The number of successful loads. */ + loadSuccessCount?: number; + /** The number of failed loads. */ + loadFailureCount?: number; + /** The total load time in nanoseconds. */ + totalLoadTime?: number; + /** The number of evictions. */ + evictionCount?: number; + /** The weight of evicted entries. */ + evictionWeight?: number; +}; + +export const serviceTokenCacheSchema: Schema = s.object({ + hitCount: s.optional(s.int()), + missCount: s.optional(s.int()), + loadSuccessCount: s.optional(s.int()), + loadFailureCount: s.optional(s.int()), + totalLoadTime: s.optional(s.int()), + evictionCount: s.optional(s.int()), + evictionWeight: s.optional(s.int()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/service.ts b/plugins/authlete/sdk/typescript/src/models/service.ts new file mode 100644 index 00000000..7b859b53 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/service.ts @@ -0,0 +1,1350 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { attachmentTypeSchema, type AttachmentType } from "./attachment-type.js"; +import { claimTypeSchema, type ClaimType } from "./claim-type.js"; +import { clientAuthMethodSchema, type ClientAuthMethod } from "./client-auth-method.js"; +import { clientRegistrationTypeSchema, type ClientRegistrationType } from "./client-registration-type.js"; +import { + credentialIssuerMetadataSchema, + type CredentialIssuerMetadata, +} from "./credential-issuer-metadata.js"; +import { deliveryModeSchema, type DeliveryMode } from "./delivery-mode.js"; +import { displaySchema, type Display } from "./display.js"; +import { fapiModeSchema, type FapiMode } from "./fapi-mode.js"; +import { grantTypeSchema, type GrantType } from "./grant-type.js"; +import { hskSchema, type Hsk } from "./hsk.js"; +import { jwsAlgSchema, type JwsAlg } from "./jws-alg.js"; +import { namedUriSchema, type NamedUri } from "./named-uri.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { promptSchema, type Prompt } from "./prompt.js"; +import { responseTypeSchema, type ResponseType } from "./response-type.js"; +import { scopeSchema, type Scope } from "./scope.js"; +import { serviceProfileSchema, type ServiceProfile } from "./service-profile.js"; +import { snsCredentialsSchema, type SnsCredentials } from "./sns-credentials.js"; +import { snsSchema, type Sns } from "./sns.js"; +import { trustAnchorSchema, type TrustAnchor } from "./trust-anchor.js"; +import { userCodeCharsetSchema, type UserCodeCharset } from "./user-code-charset.js"; +import { + verifiedClaimsValidationSchemaSchema, + type VerifiedClaimsValidationSchema, +} from "./verified-claims-validation-schema.js"; + +export type Service = { + /** The sequential number of the service. The value of this property is assigned by Authlete. */ + number?: number; + /** The name of this service. */ + serviceName?: string | null; + /** + * The issuer identifier of the service. + * + * A URL that starts with https:// and has no query or fragment component. + * + * The value of this property is used as `iss` claim in an [ID + * token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and `issuer` property in + * the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + issuer?: string | null; + /** The description about the service. */ + description?: string | null; + /** + * The service ID used in Authlete API calls. The value of this property is assigned by Authlete. + */ + apiKey?: number; + /** + * The API secret of this service. This value is assigned by Authlete and is used for service + * authentication in API calls. + */ + apiSecret?: string | null; + /** + * The endpoint for batch token notifications. This endpoint is called when multiple tokens are + * issued or revoked in a batch operation. + */ + tokenBatchNotificationEndpoint?: string | null; + /** + * The flag indicating whether the audience of client assertion JWTs must match the issuer + * identifier of this service. + */ + clientAssertionAudRestrictedToIssuer?: boolean; + /** The number of the organization that owns this service. This value is assigned by Authlete. */ + serviceOwnerNumber?: number; + /** The maximum number of client applications that a developer can have. */ + clientsPerDeveloper?: number; + /** + * The endpoint for developer authentication callbacks. This is used when developers log into the + * developer portal. + */ + developerAuthenticationCallbackEndpoint?: string | null; + /** The API key for basic authentication at the developer authentication callback endpoint. */ + developerAuthenticationCallbackApiKey?: string | null; + /** The API secret for basic authentication at the developer authentication callback endpoint. */ + developerAuthenticationCallbackApiSecret?: string | null; + /** Social login services (SNS) that this service supports for end-user authentication. */ + supportedSnses?: Sns[] | null; + /** The credentials for social login services (SNS) that are used for end-user authentication. */ + snsCredentials?: SnsCredentials[] | null; + /** Social login services (SNS) that this service supports for developer authentication. */ + supportedDeveloperSnses?: Sns[] | null; + /** The credentials for social login services (SNS) that are used for developer authentication. */ + developerSnsCredentials?: SnsCredentials[] | null; + /** Deprecated. Always `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The `metadata` of the service. The content of the returned array depends on contexts. The + * predefined service metadata is listed in the following table. + * + * | Key | Description | + * | --- | --- | + * | `clientCount` | The number of client applications which belong to this service. | + */ + metadata?: Pair[] | null; + /** + * The time at which this service was created. The value is represented as milliseconds since the + * UNIX epoch (`1970-01-01`). + */ + createdAt?: number; + /** + * The time at which this service was last modified. The value is represented as milliseconds + * since the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** + * A Web API endpoint for user authentication which is to be prepared on the service side. + * + * The endpoint must be implemented if you do not implement the UI at the authorization endpoint + * but use the one provided by Authlete. + * + * The user authentication at the authorization endpoint provided by Authlete is performed by + * making a `POST` request to this endpoint. + */ + authenticationCallbackEndpoint?: string | null; + /** + * API key for basic authentication at the authentication callback endpoint. + * + * If the value is not empty, Authlete generates Authorization header for Basic authentication + * when making a request to the authentication callback endpoint. + */ + authenticationCallbackApiKey?: string | null; + /** API secret for `basic` authentication at the authentication callback endpoint. */ + authenticationCallbackApiSecret?: string | null; + /** + * Values of acrs (authentication context class references) that the service supports. + * + * The value of this property is used as `acr_values_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedAcrs?: string[] | null; + /** + * Values of `grant_type` request parameter that the service supports. + * + * The value of this property is used as `grant_types_supported property` in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedGrantTypes?: GrantType[] | null; + /** + * Values of `response_type` request parameter that the service supports. Valid values are listed + * in Response Type. + * + * The value of this property is used as `response_types_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedResponseTypes?: ResponseType[] | null; + /** + * The supported data types that can be used as values of the type field in + * `authorization_details`. + * + * This property corresponds to the `authorization_details_types_supported` metadata. See "OAuth + * 2.0 Rich Authorization Requests" (RAR) for details. + */ + supportedAuthorizationDetailsTypes?: string[] | null; + /** The profiles that this service supports. */ + supportedServiceProfiles?: ServiceProfile[] | null; + /** + * The flag to indicate whether the `error_description` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_description` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_description` response parameter in error + * responses. + */ + errorDescriptionOmitted?: boolean; + /** + * The flag to indicate whether the `error_uri` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_uri` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_uri` response parameter in error responses. + */ + errorUriOmitted?: boolean; + /** + * The authorization endpoint of the service. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/authorization`. + * + * The value of this property is used as `authorization_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + authorizationEndpoint?: string | null; + /** + * Deprecated. The direct authorization endpoint was removed in Authlete 3.0.15, so this flag has + * no effect and is always returned as `false`. + * + * @deprecated + */ + directAuthorizationEndpointEnabled?: boolean; + /** + * UI locales that the service supports. + * + * Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). For + * example, `en-US` and `ja-JP`. + * + * The value of this property is used as `ui_locales_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedUiLocales?: string[] | null; + /** + * Values of `display` request parameter that service supports. + * + * The value of this property is used as `display_values_supported` property in the Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedDisplays?: Display[] | null; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** + * The duration of authorization response JWTs in seconds. + * + * [Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 + * (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines new values for the + * `response_mode` request parameter. They are `query.jwt`, `fragment.jwt`, `form_post.jwt` and + * `jwt`. If one of them is specified as the response mode, response parameters from the + * authorization endpoint will be packed into a JWT. This property is used to compute the value of + * the `exp` claim of the JWT. + */ + authorizationResponseDuration?: number; + /** The duration of authorization codes in seconds. */ + authorizationCodeDuration?: number; + /** + * The [token endpoint](https://tools.ietf.org/html/rfc6749#section-3.2) of the service. + * + * A URL that starts with `https://` and has not fragment component. For example, + * `https://example.com/auth/token`. + * + * The value of this property is used as `token_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tokenEndpoint?: string | null; + /** + * Deprecated. The direct token endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + * + * @deprecated + */ + directTokenEndpointEnabled?: boolean; + /** + * Client authentication methods supported by the token endpoint of the service. + * + * The value of this property is used as `token_endpoint_auth_methods_supports` property in the + * [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedTokenAuthMethods?: ClientAuthMethod[] | null; + /** + * The flag to indicate token requests from public clients without the `client_id` request + * parameter are allowed when the client can be guessed from `authorization_code` or + * `refresh_token`. + * + * This flag should not be set unless you have special reasons. + */ + missingClientIdAllowed?: boolean; + /** + * The [revocation endpoint](https://tools.ietf.org/html/rfc7009) of the service. + * + * A URL that starts with `https://`. For example, `https://example.com/auth/revocation`. + */ + revocationEndpoint?: string | null; + /** + * Deprecated. The direct revocation endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + * + * @deprecated + */ + directRevocationEndpointEnabled?: boolean; + /** Client authentication methods supported at the revocation endpoint. */ + supportedRevocationAuthMethods?: ClientAuthMethod[] | null; + /** The URI of the introspection endpoint. */ + introspectionEndpoint?: string | null; + /** + * Deprecated. The direct introspection endpoint was removed in Authlete 3.0.15, so this flag has + * no effect and is always returned as `false`. + * + * @deprecated + */ + directIntrospectionEndpointEnabled?: boolean; + /** Client authentication methods supported at the introspection endpoint. */ + supportedIntrospectionAuthMethods?: ClientAuthMethod[] | null; + /** + * The URI of the pushed authorization request endpoint. + * + * This property corresponds to the `pushed_authorization_request_endpoint` metadata defined in + * "[5. Authorization Server + * Metadata](https://tools.ietf.org/html/draft-lodderstedt-oauth-par#section-5)" of OAuth 2.0 + * Pushed Authorization Requests. + */ + pushedAuthReqEndpoint?: string | null; + /** The duration of pushed authorization requests in seconds. */ + pushedAuthReqDuration?: number; + /** + * The flag to indicate whether this service requires that clients use the pushed authorization + * request endpoint. + * + * This property corresponds to the `require_pushed_authorization_requests` server metadata + * defined in [OAuth 2.0 Pushed Authorization + * Requests](https://tools.ietf.org/html/draft-lodderstedt-oauth-par). + */ + parRequired?: boolean; + /** + * The flag to indicate whether this service requires that authorization requests always utilize a + * request object by using either request or `request_uri` request parameter. + * + * If this flag is set to `true` and the value of `traditionalRequestObjectProcessingApplied` is + * `false`, the value of `require_signed_request_object` server metadata of this service is + * reported as `true` in the discovery document. The metadata is defined in JAR (JWT Secured + * Authorization Request). That `require_signed_request_object` is `true` means that authorization + * requests which don't conform to the JAR specification are rejected. + */ + requestObjectRequired?: boolean; + /** + * The flag to indicate whether a request object is processed based on rules defined in [OpenID + * Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) or JAR (JWT Secured + * Authorization Request). + */ + traditionalRequestObjectProcessingApplied?: boolean; + /** + * The flag to indicate whether this service validates certificate chains during PKI-based client + * mutual TLS authentication. + */ + mutualTlsValidatePkiCertChain?: boolean; + /** + * The list of root certificates trusted by this service for PKI-based client mutual TLS + * authentication. + */ + trustedRootCertificates?: string[] | null; + /** The MTLS endpoint aliases. */ + mtlsEndpointAliases?: NamedUri[] | null; + /** + * The access token type. + * + * This value is used as the value of `token_type` property in access token responses. If this + * service complies with [RFC 6750](https://tools.ietf.org/html/rfc6750), the value of this + * property should be `Bearer`. + * + * See [RFC 6749 (OAuth 2.0), 7.1. Access Token + * Types](https://tools.ietf.org/html/rfc6749#section-7.1) for details. + */ + accessTokenType?: string | null; + /** + * The flag to indicate whether this service supports issuing TLS client certificate bound access + * tokens. + */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The duration of access tokens in seconds. This value is used as the value of `expires_in` + * property in access token responses. `expires_in` is defined [RFC 6749, 5.1. Successful + * Response](https://tools.ietf.org/html/rfc6749#section-5.1). + */ + accessTokenDuration?: number; + /** + * The flag to indicate whether the number of access tokens per subject (and per client) is at + * most one or can be more. + * + * If `true`, an attempt to issue a new access token invalidates existing access tokens that are + * associated with the same subject and the same client. + * + * Note that, however, attempts by [Client Credentials + * Flow](https://tools.ietf.org/html/rfc6749#section-4.4) do not invalidate existing access tokens + * because access tokens issued by Client Credentials Flow are not associated with any end-user's + * subject. Also note that an attempt by [Refresh Token + * Flow](https://tools.ietf.org/html/rfc6749#section-6) invalidates the coupled access token only + * and this invalidation is always performed regardless of whether the value of this setting item + * is `true` or `false`. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header of + * JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the algorithm + * used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + accessTokenSignAlg?: JwsAlg | null; + /** + * The key ID to identify a JWK used for signing access tokens. + * + * A JWK Set can be registered as a property of a service. A JWK Set can contain 0 or more JWKs. + * Authlete Server has to pick up one JWK for signing from the JWK Set when it generates a + * JWT-based access token. Authlete Server searches the registered JWK Set for a JWK which + * satisfies conditions for access token signature. If the number of JWK candidates which satisfy + * the conditions is 1, there is no problem. On the other hand, if there exist multiple + * candidates, a Key ID is needed to be specified so that Authlete Server can pick up one JWK from + * among the JWK candidates. + */ + accessTokenSignatureKeyId?: string | null; + /** + * The duration of refresh tokens in seconds. The related specifications have no requirements on + * refresh token duration, but Authlete sets expiration for refresh tokens. + */ + refreshTokenDuration?: number; + /** + * The flag to indicate whether the remaining duration of the used refresh token is taken over to + * the newly issued refresh token. + */ + refreshTokenDurationKept?: boolean; + /** + * The flag which indicates whether duration of refresh tokens are reset when they are used even + * if the `refreshTokenKept` property of this service set to is `true` (= even if "Refresh Token + * Continuous Use" is "Kept"). + * + * This flag has no effect when the `refreshTokenKept` property is set to `false`. In other words, + * if this service issues a new refresh token on every refresh token request, the refresh token + * will have fresh duration (unless `refreshTokenDurationKept` is set to `true`) and this + * `refreshTokenDurationReset` property is not referenced. + */ + refreshTokenDurationReset?: boolean; + /** + * The flag to indicate whether a refresh token remains unchanged or gets renewed after its use. + * + * If `true`, a refresh token used to get a new access token remains valid after its use. + * Otherwise, if `false`, a refresh token is invalidated after its use and a new refresh token is + * issued. + * + * See [RFC 6749 6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), as + * to how to get a new access token using a refresh token. + */ + refreshTokenKept?: boolean; + /** Scopes supported by the service. */ + supportedScopes?: Scope[] | null; + /** The flag to indicate whether requests that request no scope are rejected or not. */ + scopeRequired?: boolean; + /** + * 'The duration of [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)s in + * seconds. This value is used to calculate the value of `exp` claim in an ID token.' + */ + idTokenDuration?: number; + /** + * The allowable clock skew between the server and clients in seconds. + * + * The clock skew is taken into consideration when time-related claims in a JWT (e.g. `exp`, + * `iat`, `nbf`) are verified. + */ + allowableClockSkew?: number; + /** + * Claim types supported by the service. Valid values are listed in Claim Type. Note that Authlete + * currently doesn't provide any API to help implementations for `AGGREGATED` and `DISTRIBUTED`. + * + * The value of this property is used as `claim_types_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimTypes?: ClaimType[] | null; + /** + * Claim locales that the service supports. Each element is a language tag defined in [RFC + * 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`. See [OpenID + * Connect Core 1.0, 5.2. Languages and + * Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for + * details. + * + * The value of this property is used as `claims_locales_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimLocales?: string[] | null; + /** + * Claim names that the service supports. The standard claim names listed in [OpenID Connect Core + * 1.0, 5.1. Standard Claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) + * should be supported. The following is the list of standard claims. + */ + supportedClaims?: string[] | null; + /** + * The flag indicating whether claims specified by shortcut scopes (e.g. `profile`) are included + * in the issued ID token only when no access token is issued. + */ + claimShortcutRestrictive?: boolean; + /** + * The URL of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. For + * example, `http://example.com/auth/jwks`. + * + * Client applications accesses this URL (1) to get the public key of the service to validate the + * signature of an ID token issued by the service and (2) to get the public key of the service to + * encrypt an request object of the client application. See [OpenID Connect Core 1.0, 10. + * Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for + * details. + * + * The value of this property is used as `jwks_uri` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + jwksUri?: string | null; + /** + * Deprecated. The direct JWKS endpoint was removed in Authlete 3.0.15, so this flag has no effect + * and is always returned as `false`. + * + * @deprecated + */ + directJwksEndpointEnabled?: boolean; + /** + * The content of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. + * + * If this property is not `null` in a `/service/create` request or a `/service/update` request, + * Authlete hosts the content in the database. This property must not be `null` and must contain + * pairs of public/private keys if the service wants to support asymmetric signatures for ID + * tokens and asymmetric encryption for request objects. See [OpenID Connect Core 1.0, 10. + * Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for + * details. + */ + jwks?: string | null; + /** The key ID to identify a JWK used for ID token signature using an asymmetric key. */ + idTokenSignatureKeyId?: string | null; + /** The key ID to identify a JWK used for user info signature using an asymmetric key. */ + userInfoSignatureKeyId?: string | null; + /** + * The key ID to identify a JWK used for signing authorization responses using an asymmetric key. + */ + authorizationSignatureKeyId?: string | null; + /** + * The [user info endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) of the + * service. A URL that starts with `https://`. For example, `https://example.com/auth/userinfo`. + * + * The value of this property is used as `userinfo_endpoint` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + userInfoEndpoint?: string | null; + /** + * Deprecated. The direct userinfo endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + * + * @deprecated + */ + directUserInfoEndpointEnabled?: boolean; + /** + * The boolean flag which indicates whether the [OAuth 2.0 Dynamic Client Registration + * Protocol](https://tools.ietf.org/html/rfc7591) is supported. + */ + dynamicRegistrationSupported?: boolean; + /** + * The [registration + * endpoint](http://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration) of + * the service. A URL that starts with `https://`. For example, + * `https://example.com/auth/registration`. + * + * The value of this property is used as `registration_endpoint` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + registrationEndpoint?: string | null; + /** + * The URI of the registration management endpoint. If dynamic client registration is supported, + * and this is set, this URI will be used as the basis of the client's management endpoint by + * appending `/clientid}/` to it as a path element. If this is unset, the value of + * `registrationEndpoint` will be used as the URI base instead. + */ + registrationManagementEndpoint?: string | null; + /** + * The URL of the "Policy" of the service. + * + * The value of this property is used as `op_policy_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + policyUri?: string | null; + /** + * The URL of the "Terms Of Service" of the service. + * + * The value of this property is used as `op_tos_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tosUri?: string | null; + /** + * The URL of a page where documents for developers can be found. + * + * The value of this property is used as `service_documentation` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + serviceDocumentation?: string | null; + /** + * The URI of backchannel authentication endpoint, which is defined in the specification of [CIBA + * (Client Initiated Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + backchannelAuthenticationEndpoint?: string | null; + /** + * The supported backchannel token delivery modes. This property corresponds to the + * `backchannel_token_delivery_modes_supported` metadata. + * + * Backchannel token delivery modes are defined in the specification of [CIBA (Client Initiated + * Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + supportedBackchannelTokenDeliveryModes?: DeliveryMode[] | null; + /** + * The duration of backchannel authentication request IDs issued from the backchannel + * authentication endpoint in seconds. This is used as the value of the `expires_in` property in + * responses from the backchannel authentication endpoint. + */ + backchannelAuthReqIdDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications in + * seconds. This is used as the value of the `interval` property in responses from the backchannel + * authentication endpoint. + */ + backchannelPollingInterval?: number; + /** + * The boolean flag which indicates whether the `user_code` request parameter is supported at the + * backchannel authentication endpoint. This property corresponds to the + * `backchannel_user_code_parameter_supported` metadata. + */ + backchannelUserCodeParameterSupported?: boolean; + /** + * The flag to indicate whether the `binding_message` request parameter is always required + * whenever a backchannel authentication request is judged as a request for Financial-grade API. + */ + backchannelBindingMessageRequiredInFapi?: boolean; + /** + * The URI of the device authorization endpoint. + * + * Device authorization endpoint is defined in the specification of OAuth 2.0 Device Authorization + * Grant. + */ + deviceAuthorizationEndpoint?: string | null; + /** + * The verification URI for the device flow. This URI is used as the value of the + * `verification_uri` parameter in responses from the device authorization endpoint. + */ + deviceVerificationUri?: string | null; + /** + * The verification URI for the device flow with a placeholder for a user code. This URI is used + * to build the value of the `verification_uri_complete` parameter in responses from the device + * authorization endpoint. + */ + deviceVerificationUriComplete?: string | null; + /** + * The duration of device verification codes and end-user verification codes issued from the + * device authorization endpoint in seconds. This is used as the value of the `expires_in` + * property in responses from the device authorization endpoint. + */ + deviceFlowCodeDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications in + * seconds in device flow. This is used as the value of the `interval` property in responses from + * the device authorization endpoint. + */ + deviceFlowPollingInterval?: number; + userCodeCharset?: UserCodeCharset | null; + /** The length of end-user verification codes (`user_code`) for Device Flow. */ + userCodeLength?: number; + /** + * Trust frameworks supported by this service. This corresponds to the + * `trust_frameworks_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedTrustFrameworks?: string[] | null; + /** + * Evidence supported by this service. This corresponds to the `evidence_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedEvidence?: string[] | null; + /** + * Identity documents supported by this service. This corresponds to the `id_documents_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedIdentityDocuments?: string[] | null; + /** + * Verification methods supported by this service. This corresponds to the + * `id_documents_verification_methods_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerificationMethods?: string[] | null; + /** + * Verified claims supported by this service. This corresponds to the + * `claims_in_verified_claims_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerifiedClaims?: string[] | null; + /** The verified claims validation schema set. */ + verifiedClaimsValidationSchemaSet?: VerifiedClaimsValidationSchema; + /** The attributes of this service. */ + attributes?: Pair[] | null; + /** + * The flag indicating whether the nbf claim in the request object is optional even when the + * authorization request is regarded as a FAPI-Part2 request. + */ + nbfOptional?: boolean; + /** The flag indicating whether generation of the iss response parameter is suppressed. */ + issSuppressed?: boolean; + /** custom client metadata supported by this service. */ + supportedCustomClientMetadata?: string[] | null; + /** + * The flag indicating whether the expiration date of an access token never exceeds that of the + * corresponding refresh token. + */ + tokenExpirationLinked?: boolean; + /** + * The flag indicating whether encryption of request object is required when the request object is + * passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata of the client that has sent the request object. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE `enc` of encrypted request object must match the + * `request_object_encryption_enc` client metadata of the client that has sent the request object. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * The flag indicating whether HSM (Hardware Security Module) support is enabled for this service. + * + * When this flag is `false`, keys managed in HSMs are not used even if they exist. In addition, + * `/api/hsk/*` APIs reject all requests. + * + * Even if this flag is `true`, HSM-related features do not work if the configuration of the + * Authlete server you are using does not support HSM. + */ + hsmEnabled?: boolean; + /** + * The information about keys managed on HSMs (Hardware Security Modules). + * + * This `hsks` property is output only, meaning that `hsks` in requests to `/api/service/create` + * API and `/api/service/update` API do not have any effect. The contents of this property is + * controlled only by `/api/hsk/*` APIs. + */ + hsks?: Hsk[] | null; + /** The URL of the grant management endpoint. */ + grantManagementEndpoint?: string | null; + /** + * The flag indicating whether every authorization request (and any request serving as an + * authorization request such as CIBA backchannel authentication request and device authorization + * request) must include the `grant_management_action` request parameter. + */ + grantManagementActionRequired?: boolean; + /** + * The flag indicating whether Authlete's `/api/client/registration` API uses `UNAUTHORIZED` as a + * value of the `action` response parameter when appropriate. + */ + unauthorizedOnClientConfigSupported?: boolean; + /** + * The flag indicating whether the `scope` request parameter in dynamic client registration and + * update requests (RFC 7591 and RFC 7592) is used as scopes that the client can request. + * + * Limiting the range of scopes that a client can request is achieved by listing scopes in the + * `client.extension.requestableScopes` property and setting the + * `client.extension.requestableScopesEnabled` property to `true`. This feature is called + * "requestable scopes". + * + * This property affects behaviors of `/api/client/registration` and other family APIs. + */ + dcrScopeUsedAsRequestable?: boolean; + /** + * The endpoint for clients ending the sessions. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/endSession`. + * + * The value of this property is used as `end_session_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + endSessionEndpoint?: string | null; + /** + * The URL of the challenge endpoint, which is defined in OAuth 2.0 Attestation-Based Client + * Authentication. + */ + challengeEndpoint?: string | null; + /** + * The flag indicating whether the port number component of redirection URIs can be variable when + * the host component indicates loopback. + */ + loopbackRedirectionUriVariable?: boolean; + /** + * The flag indicating whether Authlete checks whether the `aud` claim of request objects matches + * the issuer identifier of this service. + */ + requestObjectAudienceChecked?: boolean; + /** + * The flag indicating whether Authlete generates access tokens for external attachments and + * embeds them in ID tokens and userinfo responses. + */ + accessTokenForExternalAttachmentEmbedded?: boolean; + /** + * Identifiers of entities that can issue entity statements for this service. This property + * corresponds to the `authority_hints` property that appears in a self-signed entity statement + * that is defined in OpenID Connect Federation 1.0. + */ + authorityHints?: string[] | null; + /** flag indicating whether this service supports OpenID Connect Federation 1 */ + federationEnabled?: boolean; + /** + * JWK Set document containing keys that are used to sign (1) self-signed entity statement of this + * service and (2) the response from `signed_jwks_uri`. + */ + federationJwks?: string | null; + /** A key ID to identify a JWK used to sign the entity configuration and the signed JWK Set. */ + federationSignatureKeyId?: string | null; + /** The duration of the entity configuration in seconds. */ + federationConfigurationDuration?: number; + /** + * The URI of the federation registration endpoint. This property corresponds to the + * `federation_registration_endpoint` server metadata that is defined in OpenID Connect Federation + * 1.0. + */ + federationRegistrationEndpoint?: string | null; + /** + * The human-readable name representing the organization that operates this service. This property + * corresponds to the `organization_name` server metadata that is defined in OpenID Connect + * Federation 1.0. + */ + organizationName?: string | null; + /** + * The transformed claims predefined by this service in JSON format. This property corresponds to + * the `transformed_claims_predefined` server metadata. + */ + predefinedTransformedClaims?: string | null; + /** + * flag indicating whether refresh token requests with the same refresh token can be made multiple + * times in quick succession and they can obtain the same renewed refresh token within the short + * period. + */ + refreshTokenIdempotent?: boolean; + /** + * The URI of the endpoint that returns this service's JWK Set document in the JWT format. This + * property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect + * Federation 1.0. + */ + signedJwksUri?: string | null; + /** + * Supported attachment types. This property corresponds to the {@code + * attachments_supported} server metadata which was added by the third implementer's draft of + * OpenID Connect for Identity Assurance 1.0. + */ + supportedAttachments?: AttachmentType[] | null; + /** + * Supported algorithms used to compute digest values of external attachments. This property + * corresponds to the `digest_algorithms_supported` server metadata which was added by the third + * implementer's draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDigestAlgorithms?: string[] | null; + /** + * Document types supported by this service. This property corresponds to the + * `documents_supported` server metadata. + */ + supportedDocuments?: string[] | null; + /** + * validation and verification processes supported by this service. This property corresponds to + * the `documents_methods_supported` server metadata. + * + * The third implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) renamed the + * `id_documents_verification_methods_supported` server metadata to `documents_methods_supported`. + */ + supportedDocumentsMethods?: string[] | null; + /** + * Document validation methods supported by this service. This property corresponds to the + * `documents_validation_methods_supported` server metadata which was added by the third + * implementer's draft of + */ + supportedDocumentsValidationMethods?: string[] | null; + /** + * Document verification methods supported by this service. This property corresponds to the + * `documents_verification_methods_supported` server metadata which was added by the third + * implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedDocumentsVerificationMethods?: string[] | null; + /** + * Electronic record types supported by this service. This property corresponds to the + * `electronic_records_supported` server metadata which was added by the third implementer's draft + * of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedElectronicRecords?: string[] | null; + supportedClientRegistrationTypes?: ClientRegistrationType[] | null; + /** + * The flag indicating whether to prohibit unidentifiable clients from making token exchange + * requests. + */ + tokenExchangeByIdentifiableClientsOnly?: boolean; + /** The flag indicating whether to prohibit public clients from making token exchange requests. */ + tokenExchangeByConfidentialClientsOnly?: boolean; + /** + * The flag indicating whether to prohibit clients that have no explicit permission from making + * token exchange requests. + */ + tokenExchangeByPermittedClientsOnly?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use encrypted JWTs as input + * tokens. + */ + tokenExchangeEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use unsigned JWTs as input + * tokens. + */ + tokenExchangeUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to prohibit unidentifiable clients from using the grant type + * "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantByIdentifiableClientsOnly?: boolean; + /** + * The flag indicating whether to reject token requests that use an encrypted JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token requests that use an unsigned JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to block DCR (Dynamic Client Registration) requests whose + * "software_id" has already been used previously. + */ + dcrDuplicateSoftwareIdBlocked?: boolean; + /** + * The trust anchors that are referenced when this service resolves trust chains of relying + * parties. + * + * If this property is empty, client registration fails regardless of whether its type is + * `automatic` or `explicit`. It means that OpenID Connect Federation 1.0 does not work. + */ + trustAnchors?: TrustAnchor[] | null; + /** + * The flag indicating whether the openid scope should be dropped from scopes list assigned to + * access token issued when a refresh token grant is used. + */ + openidDroppedOnRefreshWithoutOfflineAccess?: boolean; + /** + * The flag indicating whether to refrain from issuing an ID token when the `openid` scope has + * been removed from the scopes to be granted by the `scopes` request parameter of the + * `/auth/authorization/issue` API. + * + * Flows whose `response_type` contains `id_token`, and flows that requested Native SSO, are not + * affected, because both require an ID token. + */ + idTokenDroppedOnIssueWithoutOpenid?: boolean; + /** + * Supported document check methods. This property corresponds to the + * `documents_check_methods_supported` server metadata which was added by the fourth implementer's + * draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDocumentsCheckMethods?: string[] | null; + /** The flag indicating whether this service signs responses from the resource server. */ + rsResponseSigned?: boolean; + /** The duration of `c_nonce`. */ + cnonceDuration?: number; + /** + * Whether to require DPoP proof JWTs to include the `nonce` claim whenever they are presented. + */ + dpopNonceRequired?: boolean; + /** + * Get the flag indicating whether the feature of Verifiable Credentials for this service is + * enabled or not. + */ + verifiableCredentialsEnabled?: boolean; + /** The URL at which the JWK Set document of the credential issuer is exposed. */ + credentialJwksUri?: string | null; + /** The default duration of credential offers in seconds. */ + credentialOfferDuration?: number; + /** The duration of nonce values for DPoP proof JWTs in seconds. */ + dpopNonceDuration?: number; + /** + * The flag indicating whether token requests using the pre-authorized code grant flow by + * unidentifiable clients are allowed. + */ + preAuthorizedGrantAnonymousAccessSupported?: boolean; + /** + * The duration of transaction ID in seconds that may be issued as a result of a credential + * request or a batch credential request. + */ + credentialTransactionDuration?: number; + /** The key ID of the key for signing introspection responses. */ + introspectionSignatureKeyId?: string | null; + /** The key ID of the key for signing introspection responses. */ + resourceSignatureKeyId?: string | null; + /** The default length of user PINs. */ + userPinLength?: number; + /** The supported `prompt` values. */ + supportedPromptValues?: Prompt[] | null; + /** + * The flag indicating whether to enable the feature of ID token reissuance in the refresh token + * flow. + */ + idTokenReissuable?: boolean; + /** The JWK Set document containing private keys that are used to sign verifiable credentials. */ + credentialJwks?: string | null; + /** + * FAPI modes for this service. + * + * When the value of this property is not `null`, Authlete always processes requests to this + * service based on the specified FAPI modes if the FAPI feature is enabled in Authlete and the + * FAPI profile is supported by this service. + * + * For instance, when this property is set to an array containing `FAPI1_ADVANCED` only, Authlete + * always processes requests to this service based on "Financial-grade API Security Profile 1.0 - + * Part 2: Advanced" if the FAPI feature is enabled in Authlete and the FAPI profile is supported + * by this service. + */ + fapiModes?: FapiMode[] | null; + /** The default duration of verifiable credentials in seconds. */ + credentialDuration?: number; + credentialIssuerMetadata?: CredentialIssuerMetadata | null; + /** The type of the `aud` claim in ID tokens. */ + idTokenAudType?: string | null; + /** + * Flag that enables the [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (β€œNative SSO”). + * When this property is **not** `true`, Native SSO specific parameters are ignored or treated as + * errors. For example: + * + * * The `device_sso` scope has no special meaning (Authlete does not embed the `sid` claim in ID + * tokens). + * * The `urn:openid:params:token-type:device-secret` token type is treated as unknown and results + * in an error. + * + * When set to `true`, the server metadata advertises `"native_sso_supported": true`. See [OpenID + * Connect Discovery + * 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) and [RFC 8414 + * Β§2](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for background. Native SSO is + * available in Authlete 3.0 and later. + */ + nativeSsoSupported?: boolean; + /** + * Version of the [OpenID for Verifiable Credential + * Issuance](https://www.authlete.com/developers/oid4vci/) (OID4VCI) specification to support. + * + * Accepted values are: + * + * * `null` or `"1.0-ID1"` β†’ Implementer’s Draft 1. + * * `"1.0"` or `"1.0-Final"` β†’ Final 1.0 specification. + * + * Choose the value that matches the OID4VCI behaviour your service should expose. See the OID4VCI + * documentation for details. + */ + oid4VciVersion?: string | null; + /** + * Flag that controls whether the CIMD metadata policy is applied to client metadata obtained + * through the Client ID Metadata Document (CIMD) mechanism. + */ + cimdMetadataPolicyEnabled?: boolean; + /** + * Indicates whether the Client ID Metadata Document (CIMD) mechanism is supported. When `true`, + * the service will attempt to retrieve client metadata via CIMD where applicable. + */ + clientIdMetadataDocumentSupported?: boolean; + /** + * Enables the allowlist for CIMD. When `true`, only CIMD endpoints that are on the allowlist are + * used. + */ + cimdAllowlistEnabled?: boolean; + /** + * The allowlist of CIMD endpoints (hosts/URIs) that may be used when retrieving client metadata + * via Client ID Metadata Documents. + */ + cimdAllowlist?: string[] | null; + /** If `true`, CIMD retrieval is always attempted for clients, regardless of other conditions. */ + cimdAlwaysRetrieved?: boolean; + /** Allows CIMD retrieval over plain HTTP. When `false`, only HTTPS CIMD endpoints are allowed. */ + cimdHttpPermitted?: boolean; + /** + * Allows the use of query parameters when retrieving CIMD metadata. When `false`, query + * parameters are disallowed for CIMD requests. + */ + cimdQueryPermitted?: boolean; + /** + * The metadata policy applied to client metadata obtained through the CIMD mechanism. The value + * must follow the metadata policy grammar defined in [OpenID Federation 1.0 Β§6.1 Metadata + * Policy](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy). + */ + cimdMetadataPolicy?: string | null; + /** When `true`, client ID aliases starting with `https://` or `http://` are prohibited. */ + httpAliasProhibited?: boolean; + /** + * The time window of attestation challenges in seconds. This is used for OAuth 2.0 + * Attestation-Based Client Authentication. + */ + attestationChallengeTimeWindow?: number; + /** + * The flag indicating whether the attester roots for Client Attestation JWT x5c chain validation + * are enabled. + */ + clientAttesterRootsEnabled?: boolean; + /** + * The flag indicating whether only client authentication validated via attester roots is + * accepted. + */ + clientAttesterRootsOnly?: boolean; + /** + * The flag indicating whether the attester roots for Key Attestation JWT x5c chain validation are + * enabled. + */ + keyAttesterRootsEnabled?: boolean; + /** The flag indicating whether only key attestation validated via attester roots is accepted. */ + keyAttesterRootsOnly?: boolean; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Client + * Attestation JWTs. + */ + clientAttesterRoots?: string[] | null; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Key + * Attestation JWTs. + */ + keyAttesterRoots?: string[] | null; + /** The flag indicating whether this service supports OpenID Connect Back-Channel Logout 1.0. */ + backchannelLogoutSupported?: boolean; + /** + * The flag indicating whether this service includes a `sid` (session ID) claim in ID tokens, + * supporting per-session backchannel logout. + */ + backchannelLogoutSessionSupported?: boolean; + /** + * The HAIP version to comply with. Valid values for this property are as follows: `"1.0"`. If + * this property is set, requests to this service undergo additional validation based on the + * specified version of the + * [HAIP](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) + * specification. + */ + haipVersion?: string | null; +}; + +export const serviceSchema: Schema = s.object({ + number: s.optional(s.int()), + serviceName: s.optionalNullable(s.string()), + issuer: s.optionalNullable(s.string()), + description: s.optionalNullable(s.string()), + apiKey: s.optional(s.int()), + apiSecret: s.optionalNullable(s.string()), + tokenBatchNotificationEndpoint: s.optionalNullable(s.string()), + clientAssertionAudRestrictedToIssuer: s.optional(s.boolean()), + serviceOwnerNumber: s.optional(s.int()), + clientsPerDeveloper: s.optional(s.int()), + developerAuthenticationCallbackEndpoint: s.optionalNullable(s.string()), + developerAuthenticationCallbackApiKey: s.optionalNullable(s.string()), + developerAuthenticationCallbackApiSecret: s.optionalNullable(s.string()), + supportedSnses: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + snsCredentials: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + supportedDeveloperSnses: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + developerSnsCredentials: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + clientIdAliasEnabled: s.optional(s.boolean()), + metadata: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + createdAt: s.optional(s.int()), + modifiedAt: s.optional(s.int()), + authenticationCallbackEndpoint: s.optionalNullable(s.string()), + authenticationCallbackApiKey: s.optionalNullable(s.string()), + authenticationCallbackApiSecret: s.optionalNullable(s.string()), + supportedAcrs: s.optionalNullable(s.array(s.string())), + supportedGrantTypes: s.optionalNullable(s.array(s.lazy(() => grantTypeSchema))), + supportedResponseTypes: s.optionalNullable(s.array(s.lazy(() => responseTypeSchema))), + supportedAuthorizationDetailsTypes: s.optionalNullable(s.array(s.string())), + supportedServiceProfiles: s.optionalNullable(s.array(s.lazy(() => serviceProfileSchema))), + errorDescriptionOmitted: s.optional(s.boolean()), + errorUriOmitted: s.optional(s.boolean()), + authorizationEndpoint: s.optionalNullable(s.string()), + directAuthorizationEndpointEnabled: s.optional(s.boolean()), + supportedUiLocales: s.optionalNullable(s.array(s.string())), + supportedDisplays: s.optionalNullable(s.array(s.lazy(() => displaySchema))), + pkceRequired: s.optional(s.boolean()), + pkceS256Required: s.optional(s.boolean()), + authorizationResponseDuration: s.optional(s.int()), + authorizationCodeDuration: s.optional(s.int()), + tokenEndpoint: s.optionalNullable(s.string()), + directTokenEndpointEnabled: s.optional(s.boolean()), + supportedTokenAuthMethods: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + missingClientIdAllowed: s.optional(s.boolean()), + revocationEndpoint: s.optionalNullable(s.string()), + directRevocationEndpointEnabled: s.optional(s.boolean()), + supportedRevocationAuthMethods: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + introspectionEndpoint: s.optionalNullable(s.string()), + directIntrospectionEndpointEnabled: s.optional(s.boolean()), + supportedIntrospectionAuthMethods: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + pushedAuthReqEndpoint: s.optionalNullable(s.string()), + pushedAuthReqDuration: s.optional(s.int()), + parRequired: s.optional(s.boolean()), + requestObjectRequired: s.optional(s.boolean()), + traditionalRequestObjectProcessingApplied: s.optional(s.boolean()), + mutualTlsValidatePkiCertChain: s.optional(s.boolean()), + trustedRootCertificates: s.optionalNullable(s.array(s.string())), + mtlsEndpointAliases: s.optionalNullable(s.array(s.lazy(() => namedUriSchema))), + accessTokenType: s.optionalNullable(s.string()), + tlsClientCertificateBoundAccessTokens: s.optional(s.boolean()), + accessTokenDuration: s.optional(s.int()), + singleAccessTokenPerSubject: s.optional(s.boolean()), + accessTokenSignAlg: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + accessTokenSignatureKeyId: s.optionalNullable(s.string()), + refreshTokenDuration: s.optional(s.int()), + refreshTokenDurationKept: s.optional(s.boolean()), + refreshTokenDurationReset: s.optional(s.boolean()), + refreshTokenKept: s.optional(s.boolean()), + supportedScopes: s.optionalNullable(s.array(s.lazy(() => scopeSchema))), + scopeRequired: s.optional(s.boolean()), + idTokenDuration: s.optional(s.int()), + allowableClockSkew: s.optional(s.int()), + supportedClaimTypes: s.optionalNullable(s.array(s.lazy(() => claimTypeSchema))), + supportedClaimLocales: s.optionalNullable(s.array(s.string())), + supportedClaims: s.optionalNullable(s.array(s.string())), + claimShortcutRestrictive: s.optional(s.boolean()), + jwksUri: s.optionalNullable(s.string()), + directJwksEndpointEnabled: s.optional(s.boolean()), + jwks: s.optionalNullable(s.string()), + idTokenSignatureKeyId: s.optionalNullable(s.string()), + userInfoSignatureKeyId: s.optionalNullable(s.string()), + authorizationSignatureKeyId: s.optionalNullable(s.string()), + userInfoEndpoint: s.optionalNullable(s.string()), + directUserInfoEndpointEnabled: s.optional(s.boolean()), + dynamicRegistrationSupported: s.optional(s.boolean()), + registrationEndpoint: s.optionalNullable(s.string()), + registrationManagementEndpoint: s.optionalNullable(s.string()), + policyUri: s.optionalNullable(s.string()), + tosUri: s.optionalNullable(s.string()), + serviceDocumentation: s.optionalNullable(s.string()), + backchannelAuthenticationEndpoint: s.optionalNullable(s.string()), + supportedBackchannelTokenDeliveryModes: s.optionalNullable(s.array(s.lazy(() => deliveryModeSchema))), + backchannelAuthReqIdDuration: s.optional(s.int()), + backchannelPollingInterval: s.optional(s.int()), + backchannelUserCodeParameterSupported: s.optional(s.boolean()), + backchannelBindingMessageRequiredInFapi: s.optional(s.boolean()), + deviceAuthorizationEndpoint: s.optionalNullable(s.string()), + deviceVerificationUri: s.optionalNullable(s.string()), + deviceVerificationUriComplete: s.optionalNullable(s.string()), + deviceFlowCodeDuration: s.optional(s.int()), + deviceFlowPollingInterval: s.optional(s.int()), + userCodeCharset: s.optionalNullable(s.lazy(() => userCodeCharsetSchema)), + userCodeLength: s.optional(s.int()), + supportedTrustFrameworks: s.optionalNullable(s.array(s.string())), + supportedEvidence: s.optionalNullable(s.array(s.string())), + supportedIdentityDocuments: s.optionalNullable(s.array(s.string())), + supportedVerificationMethods: s.optionalNullable(s.array(s.string())), + supportedVerifiedClaims: s.optionalNullable(s.array(s.string())), + verifiedClaimsValidationSchemaSet: s.optional(s.lazy(() => verifiedClaimsValidationSchemaSchema)), + attributes: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + nbfOptional: s.optional(s.boolean()), + issSuppressed: s.optional(s.boolean()), + supportedCustomClientMetadata: s.optionalNullable(s.array(s.string())), + tokenExpirationLinked: s.optional(s.boolean()), + frontChannelRequestObjectEncryptionRequired: s.optional(s.boolean()), + requestObjectEncryptionAlgMatchRequired: s.optional(s.boolean()), + requestObjectEncryptionEncMatchRequired: s.optional(s.boolean()), + hsmEnabled: s.optional(s.boolean()), + hsks: s.optionalNullable(s.array(s.lazy(() => hskSchema))), + grantManagementEndpoint: s.optionalNullable(s.string()), + grantManagementActionRequired: s.optional(s.boolean()), + unauthorizedOnClientConfigSupported: s.optional(s.boolean()), + dcrScopeUsedAsRequestable: s.optional(s.boolean()), + endSessionEndpoint: s.optionalNullable(s.string()), + challengeEndpoint: s.optionalNullable(s.string()), + loopbackRedirectionUriVariable: s.optional(s.boolean()), + requestObjectAudienceChecked: s.optional(s.boolean()), + accessTokenForExternalAttachmentEmbedded: s.optional(s.boolean()), + authorityHints: s.optionalNullable(s.array(s.string())), + federationEnabled: s.optional(s.boolean()), + federationJwks: s.optionalNullable(s.string()), + federationSignatureKeyId: s.optionalNullable(s.string()), + federationConfigurationDuration: s.optional(s.int()), + federationRegistrationEndpoint: s.optionalNullable(s.string()), + organizationName: s.optionalNullable(s.string()), + predefinedTransformedClaims: s.optionalNullable(s.string()), + refreshTokenIdempotent: s.optional(s.boolean()), + signedJwksUri: s.optionalNullable(s.string()), + supportedAttachments: s.optionalNullable(s.array(s.lazy(() => attachmentTypeSchema))), + supportedDigestAlgorithms: s.optionalNullable(s.array(s.string())), + supportedDocuments: s.optionalNullable(s.array(s.string())), + supportedDocumentsMethods: s.optionalNullable(s.array(s.string())), + supportedDocumentsValidationMethods: s.optionalNullable(s.array(s.string())), + supportedDocumentsVerificationMethods: s.optionalNullable(s.array(s.string())), + supportedElectronicRecords: s.optionalNullable(s.array(s.string())), + supportedClientRegistrationTypes: s.optionalNullable(s.array(s.lazy(() => clientRegistrationTypeSchema))), + tokenExchangeByIdentifiableClientsOnly: s.optional(s.boolean()), + tokenExchangeByConfidentialClientsOnly: s.optional(s.boolean()), + tokenExchangeByPermittedClientsOnly: s.optional(s.boolean()), + tokenExchangeEncryptedJwtRejected: s.optional(s.boolean()), + tokenExchangeUnsignedJwtRejected: s.optional(s.boolean()), + jwtGrantByIdentifiableClientsOnly: s.optional(s.boolean()), + jwtGrantEncryptedJwtRejected: s.optional(s.boolean()), + jwtGrantUnsignedJwtRejected: s.optional(s.boolean()), + dcrDuplicateSoftwareIdBlocked: s.optional(s.boolean()), + trustAnchors: s.optionalNullable(s.array(s.lazy(() => trustAnchorSchema))), + openidDroppedOnRefreshWithoutOfflineAccess: s.optional(s.boolean()), + idTokenDroppedOnIssueWithoutOpenid: s.optional(s.boolean()), + supportedDocumentsCheckMethods: s.optionalNullable(s.array(s.string())), + rsResponseSigned: s.optional(s.boolean()), + cnonceDuration: s.optional(s.int()), + dpopNonceRequired: s.optional(s.boolean()), + verifiableCredentialsEnabled: s.optional(s.boolean()), + credentialJwksUri: s.optionalNullable(s.string()), + credentialOfferDuration: s.optional(s.int()), + dpopNonceDuration: s.optional(s.int()), + preAuthorizedGrantAnonymousAccessSupported: s.optional(s.boolean()), + credentialTransactionDuration: s.optional(s.int()), + introspectionSignatureKeyId: s.optionalNullable(s.string()), + resourceSignatureKeyId: s.optionalNullable(s.string()), + userPinLength: s.optional(s.int()), + supportedPromptValues: s.optionalNullable(s.array(s.lazy(() => promptSchema))), + idTokenReissuable: s.optional(s.boolean()), + credentialJwks: s.optionalNullable(s.string()), + fapiModes: s.optionalNullable(s.array(s.lazy(() => fapiModeSchema))), + credentialDuration: s.optional(s.int()), + credentialIssuerMetadata: s.optionalNullable(s.lazy(() => credentialIssuerMetadataSchema)), + idTokenAudType: s.optionalNullable(s.string()), + nativeSsoSupported: s.optional(s.boolean()), + oid4VciVersion: s.optionalNullable(s.string()), + cimdMetadataPolicyEnabled: s.optional(s.boolean()), + clientIdMetadataDocumentSupported: s.optional(s.boolean()), + cimdAllowlistEnabled: s.optional(s.boolean()), + cimdAllowlist: s.optionalNullable(s.array(s.string())), + cimdAlwaysRetrieved: s.optional(s.boolean()), + cimdHttpPermitted: s.optional(s.boolean()), + cimdQueryPermitted: s.optional(s.boolean()), + cimdMetadataPolicy: s.optionalNullable(s.string()), + httpAliasProhibited: s.optional(s.boolean()), + attestationChallengeTimeWindow: s.optional(s.int()), + clientAttesterRootsEnabled: s.optional(s.boolean()), + clientAttesterRootsOnly: s.optional(s.boolean()), + keyAttesterRootsEnabled: s.optional(s.boolean()), + keyAttesterRootsOnly: s.optional(s.boolean()), + clientAttesterRoots: s.optionalNullable(s.array(s.string())), + keyAttesterRoots: s.optionalNullable(s.array(s.string())), + backchannelLogoutSupported: s.optional(s.boolean()), + backchannelLogoutSessionSupported: s.optional(s.boolean()), + haipVersion: s.optionalNullable(s.string()), + _keysMap: { + oid4VciVersion: "oid4vciVersion", + }, +}); diff --git a/plugins/authlete/sdk/typescript/src/models/sns-credentials.ts b/plugins/authlete/sdk/typescript/src/models/sns-credentials.ts new file mode 100644 index 00000000..7dc6a897 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/sns-credentials.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type SnsCredentials = { + /** SNS. */ + sns?: string; + /** API key. */ + apiKey?: string; + /** API secret. */ + apiSecret?: string; +}; + +export const snsCredentialsSchema: Schema = s.object({ + sns: s.optional(s.string()), + apiKey: s.optional(s.string()), + apiSecret: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/sns.ts b/plugins/authlete/sdk/typescript/src/models/sns.ts new file mode 100644 index 00000000..49712f80 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/sns.ts @@ -0,0 +1,9 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const Sns = { + Facebook: "FACEBOOK", +} as const; +export type Sns = (typeof Sns)[keyof typeof Sns] | (string & {}); + +export const snsSchema: EnumSchema = s.enumOf(Sns); diff --git a/plugins/authlete/sdk/typescript/src/models/standard-introspection-request.ts b/plugins/authlete/sdk/typescript/src/models/standard-introspection-request.ts new file mode 100644 index 00000000..3e3d885b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/standard-introspection-request.ts @@ -0,0 +1,114 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type StandardIntrospectionRequest = { + /** + * Request parameters which comply with the introspection request defined in "[2.1. Introspection + * Request](https://datatracker.ietf.org/doc/html/rfc7662#section-2.1)" in RFC 7662. + * + * The implementation of the introspection endpoint of your authorization server will receive an + * HTTP POST [[RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231)] request with parameters + * in the `application/x-www-form-urlencoded` format. It is the entity body of the request that + * Authlete's `/api/auth/introspection/standard` API expects as the value of `parameters`. + */ + parameters: string; + /** + * Flag indicating whether to include hidden properties in the output. + * + * Authlete has a mechanism whereby to associate arbitrary key-value pairs with an access token. + * Each key-value pair has a hidden attribute. By default, key-value pairs whose hidden attribute + * is set to `true` are not embedded in the standard introspection output. + * + * If the `withHiddenProperties` request parameter is given and its value is `true`, + * `/api/auth/introspection/standard API includes all the associated key-value pairs into the + * output regardless of the value of the hidden attribute. + */ + withHiddenProperties?: boolean; + /** + * The URI of the resource server making the introspection request. + * + * If the `rsUri` request parameter is given and the token has audience values, Authlete checks if + * the value of the `rsUri` request parameter is contained in the audience values. If not + * contained, Authlete generates an introspection response with the `active` property set to + * `false`. + * + * The `rsUri` request parameter is required when the resource server requests a JWT introspection + * response, i.e., when the value of the `httpAcceptHeader` request parameter is set to + * `"application/token-introspection+jwt"`. + */ + rsUri?: string; + /** + * The value of the `HTTP Accept` header in the introspection request. + * + * If the value of the `httpAcceptHeader` request parameter is + * `"application/token-introspection+jwt"`, Authlete generates a JWT introspection response. See + * "[4. Requesting a JWT Response](https://www.rfc-editor.org/rfc/rfc9701.html#section-4)" of + * "[RFC 9701: JWT Response for OAuth Token + * Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)" for more details. + */ + httpAcceptHeader?: string; + /** + * The JWS `alg` algorithm for signing the introspection response. This parameter corresponds to + * `introspection_signed_response_alg` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * The default value is `RS256`. + */ + introspectionSignAlg?: string; + /** + * The JWE `alg` algorithm for encrypting the introspection response. This parameter corresponds + * to `introspection_encrypted_response_alg` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * If the `introspectionEncryptionAlg` request parameter is specified, Authlete generates a JWT + * introspection response encrypted with the algorithm by this property and the algorithm + * specified by the `introspectionEncryptionEnc` request parameter. + */ + introspectionEncryptionAlg?: string; + /** + * The JWE `enc` algorithm for encrypting the introspection response. This parameter corresponds + * to `introspection_encrypted_response_enc` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * The default value is `A128CBC_HS256`. + */ + introspectionEncryptionEnc?: string; + /** + * The shared key for signing the introspection response with a symmetric algorithm. + * + * The `sharedKeyForSign` request parameter is required when the introspection response is + * requested to be signed with a symmetric algorithm. + */ + sharedKeyForSign?: string; + /** + * The shared key for encrypting the introspection response with a symmetric algorithm. + * + * The `sharedKeyForEncryption` request parameter is required when the introspection response is + * requested to be encrypted with a symmetric algorithm. + */ + sharedKeyForEncryption?: string; + /** + * The public key for signing the introspection response with an asymmetric algorithm. + * + * The `publicKeyForEncryption` request parameter is required when the introspection response is + * requested to be encrypted with an asymmetric algorithm. + */ + publicKeyForEncryption?: string; +}; + +export const standardIntrospectionRequestSchema: Schema = + s.object({ + parameters: s.string(), + withHiddenProperties: s.optional(s.boolean()), + rsUri: s.optional(s.string()), + httpAcceptHeader: s.optional(s.string()), + introspectionSignAlg: s.optional(s.string()), + introspectionEncryptionAlg: s.optional(s.string()), + introspectionEncryptionEnc: s.optional(s.string()), + sharedKeyForSign: s.optional(s.string()), + sharedKeyForEncryption: s.optional(s.string()), + publicKeyForEncryption: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/standard-introspection-response.ts b/plugins/authlete/sdk/typescript/src/models/standard-introspection-response.ts new file mode 100644 index 00000000..a78ce367 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/standard-introspection-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action8Schema, type Action8 } from "./action8.js"; + +export type StandardIntrospectionResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action8; + /** + * The content that the authorization server implementation is to return to the client + * application. + */ + responseContent?: string; +}; + +export const standardIntrospectionResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action8Schema)), + responseContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/status.ts b/plugins/authlete/sdk/typescript/src/models/status.ts new file mode 100644 index 00000000..68afa5e6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/status.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** Whether the action completed successfully or failed. */ +export const Status = { + Completed: "completed", + Failed: "failed", +} as const; +export type Status = (typeof Status)[keyof typeof Status] | (string & {}); + +export const statusSchema: EnumSchema = s.enumOf(Status); diff --git a/plugins/authlete/sdk/typescript/src/models/subject-type.ts b/plugins/authlete/sdk/typescript/src/models/subject-type.ts new file mode 100644 index 00000000..d2748146 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/subject-type.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The subject type that the client application requests. Details about the subject type are + * described in [OpenID Connect Core 1.0, 8. Subjct Identifier + * Types](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes). + * + * This property corresponds to `subject_type` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ +export const SubjectType = { + Public: "PUBLIC", + Pairwise: "PAIRWISE", +} as const; +export type SubjectType = (typeof SubjectType)[keyof typeof SubjectType] | (string & {}); + +export const subjectTypeSchema: EnumSchema = s.enumOf(SubjectType); diff --git a/plugins/authlete/sdk/typescript/src/models/tagged-value.ts b/plugins/authlete/sdk/typescript/src/models/tagged-value.ts new file mode 100644 index 00000000..c1e5c583 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/tagged-value.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type TaggedValue = { + /** The language tag part. */ + tag?: string; + /** The value part. */ + value?: string; +}; + +export const taggedValueSchema: Schema = s.object({ + tag: s.optional(s.string()), + value: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-create-request.ts b/plugins/authlete/sdk/typescript/src/models/token-create-request.ts new file mode 100644 index 00000000..df50562a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-create-request.ts @@ -0,0 +1,150 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { grantTypeSchema, type GrantType } from "./grant-type.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenCreateRequest = { + /** The grant type of the access token when the access token was created. */ + grantType: GrantType; + /** + * The ID of the client application which will be associated with a newly created access token. + */ + clientId?: number; + /** + * The subject (= unique identifier) of the user who will be associated with a newly created + * access token. This parameter is required unless the grant type is `CLIENT_CREDENTIALS`. The + * value must consist of only ASCII characters and its length must not exceed 100. + */ + subject?: string; + /** + * The scopes which will be associated with a newly created access token. Scopes that are not + * supported by the service cannot be specified and requesting them will cause an error. + */ + scopes?: string[]; + /** + * The duration of a newly created access token in seconds. If the value is 0, the duration is + * determined according to the settings of the service. + */ + accessTokenDuration?: number; + /** + * The duration of a newly created refresh token in seconds. If the value is 0, the duration is + * determined according to the settings of the service. + * + * A refresh token is not created (1) if the service does not support `REFRESH_TOKEN`, or (2) if + * the specified grant type is either `IMPLICIT`or `CLIENT_CREDENTIALS`. + */ + refreshTokenDuration?: number; + /** + * Extra properties to associate with a newly created access token. Note that properties parameter + * is accepted only when the HTTP method of the request is POST and Content-Type of the request is + * `application/json`, so don't use `GET` method or `application/x-www-form-urlencoded` if you + * want to specify properties. + */ + properties?: Property[]; + /** + * A boolean request parameter which indicates whether to emulate that the client ID alias is used + * instead of the original numeric client ID when a new access token is created. + */ + clientIdAliasUsed?: boolean; + /** The value of the new access token. */ + accessToken?: string; + /** The value of the new refresh token. */ + refreshToken?: string; + /** + * Get whether the access token expires or not. By default, all access tokens expire after a + * period of time determined by their service. + * + * If this request parameter is `true`, then the access token will not automatically expire and + * must be revoked or deleted manually at the service. If this request parameter is true, the + * `accessTokenDuration` request parameter is ignored. + */ + accessTokenPersistent?: boolean; + /** + * The thumbprint of the MTLS certificate bound to this token. If this property is set, a + * certificate with the corresponding value MUST be presented with the access token when it is + * used by a client. The value of this property must be a SHA256 certificate thumbprint, base64url + * encoded. + */ + certificateThumbprint?: string; + /** + * The thumbprint of the public key used for DPoP presentation of this token. If this property is + * set, a DPoP proof signed with the corresponding private key MUST be presented with the access + * token when it is used by a client. Additionally, the token's `token_type` will be set to + * 'DPoP'. + */ + dpopKeyThumbprint?: string; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** + * The value of the resources to associate with the token. This property represents the value of + * one or more `resource` request parameters which is defined in "RFC8707 Resource Indicators for + * OAuth 2.0". + */ + resources?: string[]; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The Authentication Context Class Reference of the user authentication that the authorization + * server performed during the course of issuing the access token. + */ + acr?: string; + /** + * The time when the user authentication was performed during the course of issuing the access + * token. + */ + authTime?: number; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** The client Identifier associated with the newly issued access token. */ + clientIdentifier?: string; + /** + * The session ID, which is the ID of the user's authentication session, associated with a newly + * created access token. + */ + sessionId?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism rather than from the Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const tokenCreateRequestSchema: Schema = s.object({ + grantType: grantTypeSchema, + clientId: s.optional(s.int()), + subject: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + clientIdAliasUsed: s.optional(s.boolean()), + accessToken: s.optional(s.string()), + refreshToken: s.optional(s.string()), + accessTokenPersistent: s.optional(s.boolean()), + certificateThumbprint: s.optional(s.string()), + dpopKeyThumbprint: s.optional(s.string()), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + resources: s.optional(s.array(s.string())), + forExternalAttachment: s.optional(s.boolean()), + jwtAtClaims: s.optional(s.string()), + acr: s.optional(s.string()), + authTime: s.optional(s.int()), + clientEntityIdUsed: s.optional(s.boolean()), + clientIdentifier: s.optional(s.string()), + sessionId: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-create-response.ts b/plugins/authlete/sdk/typescript/src/models/token-create-response.ts new file mode 100644 index 00000000..0e5faf00 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-create-response.ts @@ -0,0 +1,88 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action23Schema, type Action23 } from "./action23.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenCreateResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action23; + /** The newly issued access token. */ + accessToken?: string; + /** The ID of the client application associated with the access token. */ + clientId?: number; + /** The time at which the access token expires. */ + expiresAt?: number; + /** The duration of the newly issued access token in seconds. */ + expiresIn?: number; + /** The grant type for the newly issued access token. */ + grantType?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** The newly issued refresh token. */ + refreshToken?: string; + /** Scopes which are associated with the access token. */ + scopes?: string[]; + /** + * The subject (= unique identifier) of the user associated with the newly issued access token. + */ + subject?: string; + /** The token type of the access token. */ + tokenType?: string; + /** + * If the authorization server is configured to issue JWT-based access tokens (= if + * `Service.accessTokenSignAlg` is set to a `non-null` value), a JWT-based access token is issued + * along with the original random-string one. + */ + jwtAccessToken?: string; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** Set the unique token identifier. */ + tokenId?: string; + /** The scopes associated with the refresh token. May be null. */ + refreshTokenScopes?: string[]; + /** The client Identifier that will be associated with a newly created access token. */ + clientIdentifier?: string; + /** + * The session ID associated with a newly created access token. + * + * NOTE: A refresh token must be associated with a session ID, which is the ID of the user's + * authentication session, in order to be used to obtain a [Native + * SSO](https://openid.net/specs/openid-connect-native-sso-1_0.html)-compliant ID token in the + * refresh token flow. + */ + sessionId?: string; +}; + +export const tokenCreateResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action23Schema)), + accessToken: s.optional(s.string()), + clientId: s.optional(s.int()), + expiresAt: s.optional(s.int()), + expiresIn: s.optional(s.int()), + grantType: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + refreshToken: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + subject: s.optional(s.string()), + tokenType: s.optional(s.string()), + jwtAccessToken: s.optional(s.string()), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + forExternalAttachment: s.optional(s.boolean()), + tokenId: s.optional(s.string()), + refreshTokenScopes: s.optional(s.array(s.string())), + clientIdentifier: s.optional(s.string()), + sessionId: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-fail-request.ts b/plugins/authlete/sdk/typescript/src/models/token-fail-request.ts new file mode 100644 index 00000000..a6e05783 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-fail-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { reason1Schema, type Reason1 } from "./reason1.js"; + +export type TokenFailRequest = { + /** The ticket issued from Authlete `/auth/token` API. */ + ticket: string; + /** The reason of the failure of the token request. */ + reason: Reason1; +}; + +export const tokenFailRequestSchema: Schema = s.object({ + ticket: s.string(), + reason: reason1Schema, +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-fail-response.ts b/plugins/authlete/sdk/typescript/src/models/token-fail-response.ts new file mode 100644 index 00000000..0beec0bb --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-fail-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action5Schema, type Action5 } from "./action5.js"; + +export type TokenFailResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action5; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. See description + * for details. + */ + responseContent?: string; +}; + +export const tokenFailResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action5Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-get-list-response.ts b/plugins/authlete/sdk/typescript/src/models/token-get-list-response.ts new file mode 100644 index 00000000..4e6b7b51 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-get-list-response.ts @@ -0,0 +1,27 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { accessTokenSchema, type AccessToken } from "./access-token.js"; +import { clientLimitedSchema, type ClientLimited } from "./client-limited.js"; + +export type TokenGetListResponse = { + /** Start index of search results (inclusive). */ + start?: number; + /** End index of search results (exclusive). */ + end?: number; + /** Unique ID of a client developer. */ + totalCount?: number; + client?: ClientLimited; + /** Unique user ID of an end-user. */ + subject?: string; + /** An array of access tokens. */ + accessTokens?: AccessToken[]; +}; + +export const tokenGetListResponseSchema: Schema = s.object({ + start: s.optional(s.int()), + end: s.optional(s.int()), + totalCount: s.optional(s.int()), + client: s.optional(s.lazy(() => clientLimitedSchema)), + subject: s.optional(s.string()), + accessTokens: s.optional(s.array(s.lazy(() => accessTokenSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-info.ts b/plugins/authlete/sdk/typescript/src/models/token-info.ts new file mode 100644 index 00000000..dd3b8901 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-info.ts @@ -0,0 +1,67 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + authorizationDetailsElementSchema, + type AuthorizationDetailsElement, +} from "./authorization-details-element.js"; +import { propertySchema, type Property } from "./property.js"; +import { scopeSchema, type Scope } from "./scope.js"; + +export type TokenInfo = { + /** The client id. */ + clientId?: number; + /** The alias of the client. */ + clientIdAlias?: string; + /** Flag specifying if the alias was used to identify the client */ + clientIdAliasUsed?: boolean; + /** the resource owner unique id */ + subject?: string; + /** The scopes granted on the token */ + scopes?: Scope[]; + /** time which the token expires. */ + expiresAt?: number; + /** Extra properties associated with the token */ + properties?: Property[]; + /** The array of the resources of the token. */ + resources?: string[]; + authorizationDetails?: AuthorizationDetailsElement; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const tokenInfoSchema: Schema = s.object({ + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + subject: s.optional(s.string()), + scopes: s.optional(s.array(s.lazy(() => scopeSchema))), + expiresAt: s.optional(s.int()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + resources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authorizationDetailsElementSchema)), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/token-issue-request.ts new file mode 100644 index 00000000..2bb57f11 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-issue-request.ts @@ -0,0 +1,48 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenIssueRequest = { + /** The ticket issued from Authlete `/auth/token` API. */ + ticket: string; + /** The subject (= unique identifier) of the authenticated user. */ + subject: string; + /** + * Extra properties to associate with a newly created access token. Note that properties parameter + * is accepted only when `Content-Type` of the request is `application/json`, so don't use + * `application/x-www-form-urlencoded` if you want to specify properties. + */ + properties?: Property[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the access + * token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the refresh + * token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; +}; + +export const tokenIssueRequestSchema: Schema = s.object({ + ticket: s.string(), + subject: s.string(), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + jwtAtClaims: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/token-issue-response.ts new file mode 100644 index 00000000..cc218b13 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-issue-response.ts @@ -0,0 +1,146 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action6Schema, type Action6 } from "./action6.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action6; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format is JSON. + */ + responseContent?: string; + /** + * The newly issued access token. This parameter is a non-null value only when the value of + * `action` parameter is `OK`. + */ + accessToken?: string; + /** + * The datetime at which the newly issued access token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + accessTokenExpiresAt?: number; + /** The duration of the newly issued access token in seconds. */ + accessTokenDuration?: number; + /** + * The refresh token. This parameter is a non-null value only when `action` is `OK` and the + * service supports the refresh token flow. If `refreshTokenKept` is set to `false`, a new refresh + * token is issued and the old refresh token used in the refresh token flow is invalidated. On the + * contrary, if `refreshTokenKept` is set to `true`, the refresh token itself is not refreshed. + */ + refreshToken?: string; + /** + * The datetime at which the newly issued refresh token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + refreshTokenExpiresAt?: number; + /** The duration of the newly issued refresh token in seconds. */ + refreshTokenDuration?: number; + /** The client ID. */ + clientId?: number; + /** The client ID alias. If the client did not have an alias, this parameter is `null`. */ + clientIdAlias?: string; + /** + * The flag which indicates whether the client ID alias was used when the token request was made. + * `true` if the client ID alias was used when the token request was made. + */ + clientIdAliasUsed?: boolean; + /** + * The subject (= resource owner's ID) of the access token. Even if an access token has been + * issued by calling `/api/auth/token` API, this parameter is `null` if the flow of the token + * request was [Client Credentials + * Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) + * (`grant_type=client_credentials`) because it means the access token is not associated with any + * specific end-user. + */ + subject?: string; + /** The scopes covered by the access token. */ + scopes?: string[]; + /** + * The extra properties associated with the access token. This parameter is `null` when no extra + * property is associated with the issued access token. + */ + properties?: Property[]; + /** + * The newly issued access token in JWT format. If the authorization server is configured to issue + * JWT-based access tokens (= if the service's `accessTokenSignAlg` value is a non-null value), a + * JWT-based access token is issued along with the original random-string one. + */ + jwtAccessToken?: string; + /** + * The target resources of the access token being issued. See "Resource Indicators for OAuth 2.0" + * for details. + */ + accessTokenResources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** The scopes associated with the refresh token. May be null. */ + refreshTokenScopes?: string[]; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const tokenIssueResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action6Schema)), + responseContent: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + accessTokenDuration: s.optional(s.int()), + refreshToken: s.optional(s.string()), + refreshTokenExpiresAt: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + subject: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + jwtAccessToken: s.optional(s.string()), + accessTokenResources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + refreshTokenScopes: s.optional(s.array(s.string())), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-request.ts b/plugins/authlete/sdk/typescript/src/models/token-request.ts new file mode 100644 index 00000000..6ef1a093 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-request.ts @@ -0,0 +1,150 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cimdOptionsSchema, type CimdOptions } from "./cimd-options.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenRequest = { + /** + * OAuth 2.0 token request parameters which are the request parameters that the OAuth 2.0 token + * endpoint of the authorization server implementation received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the token request from the client + * application. + * + * If the token endpoint of the authorization server implementation supports basic authentication + * as a means of client authentication, and the request from the client application contained its + * client ID in `Authorization` header, the value should be extracted and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the token request from the client + * application. + * + * If the token endpoint of the authorization server implementation supports basic authentication + * as a means of client authentication, and the request from the client application contained its + * client secret in `Authorization` header, the value should be extracted and set to this + * parameter. + */ + clientSecret?: string; + /** The client certificate from the MTLS of the token request from the client application. */ + clientCertificate?: string; + /** + * The certificate path presented by the client during client authentication. These certificates + * are strings in PEM format. + */ + clientCertificatePath?: string[]; + /** + * Extra properties to associate with an access token. See [Extra + * Properties](https://www.authlete.com/developers/definitive_guide/extra_properties/) for + * details. + */ + properties?: Property[]; + /** + * `DPoP` header presented by the client during the request to the token endpoint. + * + * The header contains a signed JWT which includes the public key that is paired with the private + * key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the token request. This field is used to validate the `DPoP` header. + * + * In normal cases, the value is `POST`. When this parameter is omitted, `POST` is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htm?: string; + /** + * URL of the token endpoint. This field is used to validate the `DPoP` header. + * + * If this parameter is omitted, the `tokenEndpoint` property of the Service is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API call. + */ + accessToken?: string; + /** + * Additional claims that are added to the payload part of the JWT access token. + * + * Effective only in the client_credentials grant flow (and the Native SSO branch of Token + * Exchange). For other grant flows, set additional JWT claims via the corresponding API: + * /auth/authorization/issue (authorization_code), /auth/backchannel/authentication/complete + * (CIBA), /auth/device/complete (device_code). The parameter is silently ignored when sent with + * other grants. + */ + jwtAtClaims?: string; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the specification + * of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the access + * token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the refresh + * token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even if + * the service's `dpopNonceRequired` property is `false`, calling the `/auth/token` API with this + * `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the DPoP + * proof JWT includes the expected `nonce` value. + */ + dpopNonceRequired?: boolean; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; +}; + +export const tokenRequestSchema: Schema = s.object({ + parameters: s.string(), + clientId: s.optional(s.string()), + clientSecret: s.optional(s.string()), + clientCertificate: s.optional(s.string()), + clientCertificatePath: s.optional(s.array(s.string())), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + dpop: s.optional(s.string()), + htm: s.optional(s.string()), + htu: s.optional(s.string()), + accessToken: s.optional(s.string()), + jwtAtClaims: s.optional(s.string()), + oauthClientAttestation: s.optional(s.string()), + oauthClientAttestationPop: s.optional(s.string()), + accessTokenDuration: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + dpopNonceRequired: s.optional(s.boolean()), + cimdOptions: s.optional(s.lazy(() => cimdOptionsSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-response.ts b/plugins/authlete/sdk/typescript/src/models/token-response.ts new file mode 100644 index 00000000..4a70eafc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-response.ts @@ -0,0 +1,293 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action4Schema, type Action4 } from "./action4.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { propertySchema, type Property } from "./property.js"; +import { tokenInfoSchema, type TokenInfo } from "./token-info.js"; +import { tokenTypeSchema, type TokenType } from "./token-type.js"; + +export type TokenResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action4; + /** + * The content that the authorization server implementation is to return to the client + * application. Its format varies depending on the value of `action` parameter. + */ + responseContent?: string; + /** + * The value of `username` request parameter in the token request. The client application must + * specify username when it uses [Resource Owner Password + * Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3). In other words, when the + * value of `grant_type` request parameter is `password`, `username` request parameter must come + * along. + * + * This parameter has a value only if the value of `grant_type` request parameter is `password` + * and the token request is valid. + */ + username?: string; + /** + * The value of `password` request parameter in the token request. The client application must + * specify password when it uses [Resource Owner Password + * Grant](https://datatracker.ietf.org/doc/html/rfc6749#section-4.3). In other words, when the + * value of `grant_type` request parameter is `password`, `password` request parameter must come + * along. + * + * This parameter has a value only if the value of `grant_type` request parameter is `password` + * and the token request is valid. + */ + password?: string; + /** + * The ticket which is necessary to call Authlete's `/auth/token/fail` API or `/auth/token/issue` + * API. + * + * This parameter has a value only if the value of `grant_type` request parameter is `password` + * and the token request is valid. + */ + ticket?: string; + /** The newly issued access token. */ + accessToken?: string; + /** + * The datetime at which the newly issued access token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + accessTokenExpiresAt?: number; + /** The duration of the newly issued access token in seconds. */ + accessTokenDuration?: number; + /** The newly issued refresh token. */ + refreshToken?: string; + /** + * The datetime at which the newly issued refresh token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + refreshTokenExpiresAt?: number; + /** The duration of the newly issued refresh token in seconds. */ + refreshTokenDuration?: number; + /** + * The newly issued ID token. Note that an ID token is issued from a token endpoint only when the + * `response_type` request parameter of the authorization request to an authorization endpoint has + * contained `code` and the `scope` request parameter has contained `openid`. + */ + idToken?: string; + /** The grant type of the token request. */ + grantType?: string; + /** The client ID. */ + clientId?: number; + /** + * The client ID alias when the token request was made. If the client did not have an alias, this + * parameter is `null`. Also, if the token request was invalid and it failed to identify a client, + * this parameter is `null`. + */ + clientIdAlias?: string; + /** + * The flag which indicates whether the client ID alias was used when the token request was made. + * `true` if the client ID alias was used when the token request was made. + */ + clientIdAliasUsed?: boolean; + /** + * The subject (= resource owner's ID) of the access token. Even if an access token has been + * issued by the call of `/api/auth/token` API, this parameter is `null` if the flow of the token + * request was [Client Credentials + * Flow](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) + * (`grant_type=client_credentials`) because it means the access token is not associated with any + * specific end-user. + */ + subject?: string; + /** The scopes covered by the access token. */ + scopes?: string[]; + /** + * The extra properties associated with the access token. This parameter is `null` when no extra + * property is associated with the issued access token. + */ + properties?: Property[]; + /** + * The newly issued access token in JWT format. If the authorization server is configured to issue + * JWT-based access tokens (= if the service's `accessTokenSignAlg` value is a non-null value), a + * JWT-based access token is issued along with the original random-string one. + */ + jwtAccessToken?: string; + /** + * The resources specified by the `resource` request parameters in the token request. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * The target resources of the access token being issued. See "Resource Indicators for OAuth 2.0" + * for details. + */ + accessTokenResources?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** Additional claims to be embedded in an ID token. */ + additionalClaims?: string; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** The client authentication method that was performed at the token endpoint. */ + clientAuthMethod?: string; + /** + * the value of the `grant_id` request parameter of the device authorization request. + * + * The `grant_id` request parameter is defined in [Grant Management for OAuth + * 2.0](https://openid.net/specs/fapi-grant-management.html) , which is supported by Authlete 2.3 + * and newer versions. + */ + grantId?: string; + /** The audiences on the token exchange request */ + audiences?: string[]; + /** + * The token type identifier used in OAuth 2.0 Token Exchange (RFC 8693). The API returns short + * codes (enum constant names) in response fields. + */ + requestedTokenType?: TokenType; + subjectToken?: string; + /** + * The token type identifier used in OAuth 2.0 Token Exchange (RFC 8693). The API returns short + * codes (enum constant names) in response fields. + */ + subjectTokenType?: TokenType; + subjectTokenInfo?: TokenInfo; + actorToken?: string; + /** + * The token type identifier used in OAuth 2.0 Token Exchange (RFC 8693). The API returns short + * codes (enum constant names) in response fields. + */ + actorTokenType?: TokenType; + actorTokenInfo?: TokenInfo; + /** + * For RFC 7523 JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization + * Grants + */ + assertion?: string; + /** + * Indicate whether the previous refresh token that had been kept in the database for a short time + * was used + */ + previousRefreshTokenUsed?: boolean; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** Duration of the `c_nonce` in seconds. */ + cnonceDuration?: number; + /** + * Get the expected nonce value for DPoP proof JWT, which should be used as the value of the + * `DPoP-Nonce` HTTP header. + */ + dpopNonce?: string; + /** Get the `c_nonce`. */ + cnonce?: string; + /** + * Get the time at which the `c_nonce` expires in milliseconds since the Unix epoch (1970-01-01). + */ + cnonceExpiresAt?: number; + /** + * Get the names of the claims that the authorization request (which resulted in generation of the + * access token) requested to be embedded in ID tokens. + */ + requestedIdTokenClaims?: string[]; + /** Scopes associated with the refresh token. */ + refreshTokenScopes?: string[]; + /** + * The session ID, which is the ID of the user's authentication session, associated with a newly + * created access token. + */ + sessionId?: string; + /** + * If the response from the `/auth/token` API contains the `deviceSecret` parameter, its value + * should be used as the value of this `deviceSecret` request parameter to the `/nativesso` API. + * The authorization server may choose to issue a new device secret; in that case, it is free to + * generate a new device secret and specify the new value. + */ + deviceSecret?: string; + /** + * The authorization server should compute the hash value of the device secret based on its own + * logic and specify the computed hash as the value of this `deviceSecretHash` request parameter + * to the `/nativesso` API. + */ + deviceSecretHash?: string; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const tokenResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action4Schema)), + responseContent: s.optional(s.string()), + username: s.optional(s.string()), + password: s.optional(s.string()), + ticket: s.optional(s.string()), + accessToken: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + accessTokenDuration: s.optional(s.int()), + refreshToken: s.optional(s.string()), + refreshTokenExpiresAt: s.optional(s.int()), + refreshTokenDuration: s.optional(s.int()), + idToken: s.optional(s.string()), + grantType: s.optional(s.string()), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + subject: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + jwtAccessToken: s.optional(s.string()), + resources: s.optional(s.array(s.string())), + accessTokenResources: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + additionalClaims: s.optional(s.string()), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAuthMethod: s.optional(s.string()), + grantId: s.optional(s.string()), + audiences: s.optional(s.array(s.string())), + requestedTokenType: s.optional(s.lazy(() => tokenTypeSchema)), + subjectToken: s.optional(s.string()), + subjectTokenType: s.optional(s.lazy(() => tokenTypeSchema)), + subjectTokenInfo: s.optional(s.lazy(() => tokenInfoSchema)), + actorToken: s.optional(s.string()), + actorTokenType: s.optional(s.lazy(() => tokenTypeSchema)), + actorTokenInfo: s.optional(s.lazy(() => tokenInfoSchema)), + assertion: s.optional(s.string()), + previousRefreshTokenUsed: s.optional(s.boolean()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + cnonceDuration: s.optional(s.int()), + dpopNonce: s.optional(s.string()), + cnonce: s.optional(s.string()), + cnonceExpiresAt: s.optional(s.int()), + requestedIdTokenClaims: s.optional(s.array(s.string())), + refreshTokenScopes: s.optional(s.array(s.string())), + sessionId: s.optional(s.string()), + deviceSecret: s.optional(s.string()), + deviceSecretHash: s.optional(s.string()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-revoke-request.ts b/plugins/authlete/sdk/typescript/src/models/token-revoke-request.ts new file mode 100644 index 00000000..3320cdee --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-revoke-request.ts @@ -0,0 +1,45 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type TokenRevokeRequest = { + /** + * The identifier of an access token to revoke + * + * The hash of an access token is recognized as an identifier as well as the access token itself. + */ + accessTokenIdentifier?: string; + /** + * The identifier of a refresh token to revoke. + * + * The hash of a refresh token is recognized as an identifier as well as the refresh token itself. + */ + refreshTokenIdentifier?: string; + /** + * The client ID of the access token to be revoked. + * + * Both the numeric client ID and the alias are recognized as an identifier of a client. + * + * Bulk revocation with `clientIdentifier` only or `clientIdentifier` + `subject` deletes at most + * **20 tokens per request** (the default of `token.revoke.count.max` in + * `ServerConfiguration.java`). If the target has more than 20 tokens, the response `count` will + * be 20 and the remainder is left untouched. To fully wipe them, call the endpoint repeatedly + * until `count` returns 0. + */ + clientIdentifier?: string; + /** + * The subject of a resource owner. + * + * Bulk revocation with `clientIdentifier` + `subject` or `subject` only deletes at most **20 + * tokens per request** (the default of `token.revoke.count.max` in `ServerConfiguration.java`). + * If the target has more than 20 tokens, the response `count` will be 20 and the remainder is + * left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0. + */ + subject?: string; +}; + +export const tokenRevokeRequestSchema: Schema = s.object({ + accessTokenIdentifier: s.optional(s.string()), + refreshTokenIdentifier: s.optional(s.string()), + clientIdentifier: s.optional(s.string()), + subject: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-revoke-response.ts b/plugins/authlete/sdk/typescript/src/models/token-revoke-response.ts new file mode 100644 index 00000000..ddd38dfd --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-revoke-response.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type TokenRevokeResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** + * The number of tokens revoked. + * + * If the target has more than 20 tokens, the response `count` will be 20 and the remainder is + * left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0. + */ + count?: number; +}; + +export const tokenRevokeResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + count: s.optional(s.int()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-type.ts b/plugins/authlete/sdk/typescript/src/models/token-type.ts new file mode 100644 index 00000000..ec2710f6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-type.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** + * The token type identifier used in OAuth 2.0 Token Exchange (RFC 8693). The API returns short + * codes (enum constant names) in response fields. + */ +export const TokenType = { + Jwt: "JWT", + AccessToken: "ACCESS_TOKEN", + RefreshToken: "REFRESH_TOKEN", + IdToken: "ID_TOKEN", + Saml1: "SAML1", + Saml2: "SAML2", + DeviceSecret: "DEVICE_SECRET", + DeviceCode: "DEVICE_CODE", + TokenExchange: "TOKEN_EXCHANGE", + JwtBearer: "JWT_BEARER", +} as const; +export type TokenType = (typeof TokenType)[keyof typeof TokenType] | (string & {}); + +export const tokenTypeSchema: EnumSchema = s.enumOf(TokenType); diff --git a/plugins/authlete/sdk/typescript/src/models/token-update-request.ts b/plugins/authlete/sdk/typescript/src/models/token-update-request.ts new file mode 100644 index 00000000..f6389ea3 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-update-request.ts @@ -0,0 +1,114 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenUpdateRequest = { + /** An access token. */ + accessToken?: string; + /** + * A new date at which the access token will expire in milliseconds since the Unix epoch + * (1970-01-01). If the `accessTokenExpiresAt` request parameter is not included in a request or + * its value is 0 (or negative), the expiration date of the access token is not changed. + */ + accessTokenExpiresAt?: number; + /** + * A new set of scopes assigned to the access token. Scopes that are not supported by the service + * and those that the client application associated with the access token is not allowed to + * request are ignored on the server side. If the `scopes` request parameter is not included in a + * request or its value is `null`, the scopes of the access token are not changed. Note that + * `properties` parameter is accepted only when `Content-Type` of the request is + * `application/json`, so don't use `application/x-www-form-urlencoded` if you want to specify + * `properties`. + */ + scopes?: string[]; + /** + * A new set of properties assigned to the access token. If the `properties` request parameter is + * not included in a request or its value is null, the properties of the access token are not + * changed. + */ + properties?: Property[]; + /** + * A boolean request parameter which indicates whether the API attempts to update the expiration + * date of the access token when the scopes linked to the access token are changed by this + * request. + */ + accessTokenExpiresAtUpdatedOnScopeUpdate?: boolean; + /** + * The hash of the access token value. Used when the hash of the token is known (perhaps from + * lookup) but the value of the token itself is not. The value of the `accessToken` parameter + * takes precedence. + */ + accessTokenHash?: string; + /** + * A boolean request parameter which indicates whether to update the value of the access token in + * the data store. If this parameter is set to `true` then a new access token value is generated + * by the server and returned in the response. + */ + accessTokenValueUpdated?: boolean; + /** + * The flag which indicates whether the access token expires or not. By default, all access tokens + * expire after a period of time determined by their service. If this request parameter is `true` + * then the access token will not automatically expire and must be revoked or deleted manually at + * the service. + * + * If this request parameter is `true`, the `accessTokenExpiresAt` request parameter is ignored. + * If this request parameter is `false`, the `accessTokenExpiresAt` request parameter is processed + * normally. + */ + accessTokenPersistent?: boolean; + /** + * The thumbprint of the MTLS certificate bound to this token. If this property is set, a + * certificate with the corresponding value MUST be presented with the access token when it is + * used by a client. The value of this property must be a SHA256 certificate thumbprint, base64url + * encoded. + */ + certificateThumbprint?: string; + /** + * The thumbprint of the public key used for DPoP presentation of this token. If this property is + * set, a DPoP proof signed with the corresponding private key MUST be presented with the access + * token when it is used by a client. Additionally, the token's `token_type` will be set to + * 'DPoP'. + */ + dpopKeyThumbprint?: string; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** + * A new date at which the access token will expire in milliseconds since the Unix epoch + * (1970-01-01). If the `refreshTokenExpiresAt` request parameter is not included in a request or + * its value is 0 (or negative), the expiration date of the refresh token is not changed. + */ + refreshTokenExpiresAt?: number; + /** + * A boolean request parameter which indicates whether the API attempts to update the expiration + * date of the refresh token when the scopes linked to the refresh token are changed by this + * request. + */ + refreshTokenExpiresAtUpdatedOnScopeUpdate?: boolean; + /** The token identifier. */ + tokenId?: string; +}; + +export const tokenUpdateRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + scopes: s.optional(s.array(s.string())), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + accessTokenExpiresAtUpdatedOnScopeUpdate: s.optional(s.boolean()), + accessTokenHash: s.optional(s.string()), + accessTokenValueUpdated: s.optional(s.boolean()), + accessTokenPersistent: s.optional(s.boolean()), + certificateThumbprint: s.optional(s.string()), + dpopKeyThumbprint: s.optional(s.string()), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + forExternalAttachment: s.optional(s.boolean()), + refreshTokenExpiresAt: s.optional(s.int()), + refreshTokenExpiresAtUpdatedOnScopeUpdate: s.optional(s.boolean()), + tokenId: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/token-update-response.ts b/plugins/authlete/sdk/typescript/src/models/token-update-response.ts new file mode 100644 index 00000000..48ba91ef --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/token-update-response.ts @@ -0,0 +1,54 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action24Schema, type Action24 } from "./action24.js"; +import { authzDetailsSchema, type AuthzDetails } from "./authz-details.js"; +import { propertySchema, type Property } from "./property.js"; + +export type TokenUpdateResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action24; + /** The access token which has been specified by the request. */ + accessToken?: string; + /** The date at which the access token will expire. */ + accessTokenExpiresAt?: number; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** The scopes associated with the access token. */ + scopes?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** The token type associated with the access token. */ + tokenType?: string; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** Set the unique token identifier. */ + tokenId?: string; + /** + * The datetime at which the newly issued refresh token will expire. The value is represented in + * milliseconds since the Unix epoch (1970-01-01). + */ + refreshTokenExpiresAt?: number; +}; + +export const tokenUpdateResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action24Schema)), + accessToken: s.optional(s.string()), + accessTokenExpiresAt: s.optional(s.int()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + scopes: s.optional(s.array(s.string())), + authorizationDetails: s.optional(s.lazy(() => authzDetailsSchema)), + tokenType: s.optional(s.string()), + forExternalAttachment: s.optional(s.boolean()), + tokenId: s.optional(s.string()), + refreshTokenExpiresAt: s.optional(s.int()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/trust-anchor.ts b/plugins/authlete/sdk/typescript/src/models/trust-anchor.ts new file mode 100644 index 00000000..6f660194 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/trust-anchor.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type TrustAnchor = { + /** the entity ID of the trust anchor */ + entityId?: string; + /** the JWK Set document containing public keys of the trust anchor */ + jwks?: string; +}; + +export const trustAnchorSchema: Schema = s.object({ + entityId: s.optional(s.string()), + jwks: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/user-code-charset.ts b/plugins/authlete/sdk/typescript/src/models/user-code-charset.ts new file mode 100644 index 00000000..856ddde6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/user-code-charset.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The character set for end-user verification codes (`user_code`) for Device Flow. */ +export const UserCodeCharset = { + Base20: "BASE20", + Numeric: "NUMERIC", +} as const; +export type UserCodeCharset = (typeof UserCodeCharset)[keyof typeof UserCodeCharset] | (string & {}); + +export const userCodeCharsetSchema: EnumSchema = s.enumOf(UserCodeCharset); diff --git a/plugins/authlete/sdk/typescript/src/models/userinfo-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/userinfo-issue-request.ts new file mode 100644 index 00000000..b2c9082e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/userinfo-issue-request.ts @@ -0,0 +1,44 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { pairSchema, type Pair } from "./pair.js"; + +export type UserinfoIssueRequest = { + /** + * The access token that has been passed to the userinfo endpoint by the client application. In + * other words, the access token which was contained in the userinfo request. + */ + token: string; + /** + * Claims in JSON format. As for the format, see [OpenID Connect Core 1.0, 5.1. Standard + * Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). + */ + claims?: string; + /** + * The value of the `sub` claim. If the value of this request parameter is not empty, it is used + * as the value of the `sub` claim. Otherwise, the value of the subject associated with the access + * token is used. + */ + sub?: string; + /** Claim key-value pairs that are used to compute transformed claims. */ + claimsForTx?: string; + /** The Signature header value from the request. */ + requestSignature?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this must + * include the Signature and Signature-Input headers, as well as any additional headers covered by + * the signature. + */ + headers?: Pair[]; + /** Values of verified claims requested indirectly by "transformed claims". */ + verifiedClaimsForTx?: string[]; +}; + +export const userinfoIssueRequestSchema: Schema = s.object({ + token: s.string(), + claims: s.optional(s.string()), + sub: s.optional(s.string()), + claimsForTx: s.optional(s.string()), + requestSignature: s.optional(s.string()), + headers: s.optional(s.array(s.lazy(() => pairSchema))), + verifiedClaimsForTx: s.optional(s.array(s.string())), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/userinfo-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/userinfo-issue-response.ts new file mode 100644 index 00000000..141c73de --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/userinfo-issue-response.ts @@ -0,0 +1,33 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action11Schema, type Action11 } from "./action11.js"; + +export type UserinfoIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action11; + /** + * The content that the authorization server implementation can use as the value of + * `WWW-Authenticate` header on errors. + */ + responseContent?: string; + /** The signature header of the response message. */ + signature?: string; + /** The signature-input header of the response message */ + signatureInput?: string; + /** The content-digest header of the response message */ + contentDigest?: string; +}; + +export const userinfoIssueResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action11Schema)), + responseContent: s.optional(s.string()), + signature: s.optional(s.string()), + signatureInput: s.optional(s.string()), + contentDigest: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/userinfo-request.ts b/plugins/authlete/sdk/typescript/src/models/userinfo-request.ts new file mode 100644 index 00000000..03f2723a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/userinfo-request.ts @@ -0,0 +1,78 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { pairSchema, type Pair } from "./pair.js"; + +export type UserinfoRequest = { + /** An access token. */ + token: string; + /** + * Client certificate used in the TLS connection established between the client application and + * the userinfo endpoint. + * + * The value of this request parameter is referred to when the access token given to the userinfo + * endpoint was bound to a client certificate when it was issued. See [OAuth 2.0 Mutual TLS Client + * Authentication and Certificate-Bound Access Tokens] (https://datatracker.ietf.org/doc/rfc8705/) + * for details about the specification of certificate-bound access tokens. + */ + clientCertificate?: string; + /** + * `DPoP` header presented by the client during the request to the user info endpoint. + * + * The header contains a signed JWT which includes the public key that is paired with the private + * key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the user info request. This field is used to validate the DPoP header. In normal + * cases, the value is either `GET` or `POST`. + */ + htm?: string; + /** + * URL of the user info endpoint. This field is used to validate the DPoP header. + * + * If this parameter is omitted, the `userInfoEndpoint` property of the service is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** The full URL of the userinfo endpoint. */ + uri?: string; + /** The HTTP message body of the request, if present. */ + message?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this must + * include the Signature and Signature-Input headers, as well as any additional headers covered by + * the signature. + */ + headers?: Pair[]; + /** The target URI of the userinfo request, including the query part, if any. */ + targetUri?: string; + /** + * The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` value. + * + * If this request parameter is set to `true` or if the service's `dpopNonceRequired` property is + * set to `true`, the `/auth/userinfo` API checks if the DPoP proof JWT includes the expected + * `nonce` value. In this case, the response from the `/auth/userinfo` API will include the + * `dpopNonce` response parameter, which should be used as the value of the DPoP-Nonce HTTP + * header. + */ + dpopNonceRequired?: boolean; + /** The flag indicating whether the userinfo request contains a request body. */ + requestBodyContained?: boolean; +}; + +export const userinfoRequestSchema: Schema = s.object({ + token: s.string(), + clientCertificate: s.optional(s.string()), + dpop: s.optional(s.string()), + htm: s.optional(s.string()), + htu: s.optional(s.string()), + uri: s.optional(s.string()), + message: s.optional(s.string()), + headers: s.optional(s.array(s.lazy(() => pairSchema))), + targetUri: s.optional(s.string()), + dpopNonceRequired: s.optional(s.boolean()), + requestBodyContained: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/userinfo-response.ts b/plugins/authlete/sdk/typescript/src/models/userinfo-response.ts new file mode 100644 index 00000000..2cc3e146 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/userinfo-response.ts @@ -0,0 +1,166 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action7Schema, type Action7 } from "./action7.js"; +import { pairSchema, type Pair } from "./pair.js"; +import { propertySchema, type Property } from "./property.js"; + +export type UserinfoResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the authorization server implementation should take. */ + action?: Action7; + /** The list of claims that the client application requests to be embedded in the ID token. */ + claims?: string[]; + /** The ID of the client application which is associated with the access token. */ + clientId?: number; + /** The client ID alias when the authorization request for the access token was made. */ + clientIdAlias?: string; + /** + * The flag which indicates whether the client ID alias was used when the authorization request + * for the access token was made. + */ + clientIdAliasUsed?: boolean; + /** + * The content that the authorization server implementation can use as the value of + * `WWW-Authenticate` header on errors. + */ + responseContent?: string; + /** The scopes covered by the access token. */ + scopes?: string[]; + /** The subject (= resource owner's ID). */ + subject?: string; + /** The access token that came along with the userinfo request. */ + token?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** + * The value of the `userinfo` property in the `claims` request parameter or in the `claims` + * property in an authorization request object. + */ + userInfoClaims?: string; + /** The attributes of this service that the client application belongs to. */ + serviceAttributes?: Pair[]; + /** The attributes of the client. */ + clientAttributes?: Pair[]; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** + * Get names of claims that are requested indirectly by *"transformed claims"*. A client + * application can request *"transformed claims"* by adding names of transformed claims in the + * `claims` request parameter. The following is an example of the `claims` request parameter that + * requests a predefined transformed claim named `18_or_over` and a transformed claim named + * `nationality_usa` to be embedded in the response from the userinfo endpoint. + * ```json + * { + * "transformed_claims": { + * "nationality_usa": { + * "claim": "nationalities", + * "fn": [ + * [ "eq", "USA" ], + * "any" + * ] + * } + * }, + * "userinfo": { + * "::18_or_over": null, + * ":nationality_usa": null + * } + * } + * ``` + * The example above assumes that a transformed claim named `18_or_over` is predefined by the + * authorization server like below. + * ```json + * { + * "18_or_over": { + * "claim": "birthdate", + * "fn": [ + * "years_ago", + * [ "gte", 18 ] + * ] + * } + * } + * ``` + * In the example, the `nationalities` claim is requested indirectly by the `nationality_usa` + * transformed claim. Likewise, the `birthdate` claim is requested indirectly by the `18_or_over` + * transformed claim. When the `claims` request parameter of an authorization request is like the + * example above, this `requestedClaimsForTx` property will hold the following value. + * ```json + * [ "birthdate", "nationalities" ] + * ``` + * It is expected that the authorization server implementation prepares values of the listed + * claims and passes them as the value of the `claimsForTx` request parameter when it calls the + * `/api/auth/userinfo/issue` API. The following is an example of the value of the `claimsForTx` + * request parameter. + * ```json + * { + * "birthdate": "1970-01-23", + * "nationalities": [ "DEU", "USA" ] + * } + * ``` + */ + requestedClaimsForTx?: string[]; + /** Names of verified claims that will be referenced when transformed claims are computed. */ + requestedVerifiedClaimsForTx?: string[][]; + /** + * the value of the `transformed_claims` property in the `claims` request parameter of an + * authorization request or in the `claims` property in a request object. + */ + transformedClaims?: string; + /** The entity ID of the client. */ + clientEntityId?: string; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** + * The expected nonce value for DPoP proof JWT, which should be used as the value of the + * `DPoP-Nonce` HTTP header. + */ + dpopNonce?: string; + /** + * The location of the client's metadata document that was used to resolve client metadata. + * + * This property is set when client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism. + */ + metadataDocumentLocation?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the CIMD mechanism rather than from the + * Authlete database. + */ + metadataDocumentUsed?: boolean; +}; + +export const userinfoResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action7Schema)), + claims: s.optional(s.array(s.string())), + clientId: s.optional(s.int()), + clientIdAlias: s.optional(s.string()), + clientIdAliasUsed: s.optional(s.boolean()), + responseContent: s.optional(s.string()), + scopes: s.optional(s.array(s.string())), + subject: s.optional(s.string()), + token: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + userInfoClaims: s.optional(s.string()), + serviceAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + clientAttributes: s.optional(s.array(s.lazy(() => pairSchema))), + consentedClaims: s.optional(s.array(s.string())), + requestedClaimsForTx: s.optional(s.array(s.string())), + requestedVerifiedClaimsForTx: s.optional(s.array(s.array(s.string()))), + transformedClaims: s.optional(s.string()), + clientEntityId: s.optional(s.string()), + clientEntityIdUsed: s.optional(s.boolean()), + dpopNonce: s.optional(s.string()), + metadataDocumentLocation: s.optional(s.string()), + metadataDocumentUsed: s.optional(s.boolean()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-request.ts new file mode 100644 index 00000000..b6ef2802 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { credentialIssuanceOrderSchema, type CredentialIssuanceOrder } from "./credential-issuance-order.js"; + +export type VciBatchIssueRequest = { + /** The access token that came along with the credential request. */ + accessToken?: string; + /** The instructions for issuance of credentials and/or transaction IDs. */ + orders?: CredentialIssuanceOrder[]; +}; + +export const vciBatchIssueRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + orders: s.optional(s.array(s.lazy(() => credentialIssuanceOrderSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-response.ts new file mode 100644 index 00000000..9ed29bc2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-batch-issue-response.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action39Schema, type Action39 } from "./action39.js"; + +export type VciBatchIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the implementation of the batch credential endpoint should take. */ + action?: Action39; + /** + * The content of the response that the implementation of the batch credential endpoint should + * return. + */ + responseContent?: string; +}; + +export const vciBatchIssueResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action39Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-request.ts new file mode 100644 index 00000000..a455f632 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-request.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type VciBatchParseRequest = { + /** The access token that came along with the credential request. */ + accessToken?: string; + /** The message body of the batch credential request. */ + requestContent?: string; +}; + +export const vciBatchParseRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + requestContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-response.ts new file mode 100644 index 00000000..d0c75ff7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-batch-parse-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action38Schema, type Action38 } from "./action38.js"; +import { credentialRequestInfoSchema, type CredentialRequestInfo } from "./credential-request-info.js"; + +export type VciBatchParseResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the batch credential endpoint should take. */ + action?: Action38; + /** The content of the response to the request sender. */ + responseContent?: string; + /** Information about the credential requests in the batch credential request. */ + info?: CredentialRequestInfo[]; +}; + +export const vciBatchParseResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action38Schema)), + responseContent: s.optional(s.string()), + info: s.optional(s.array(s.lazy(() => credentialRequestInfoSchema))), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-request.ts new file mode 100644 index 00000000..18f7bb59 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-request.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { credentialIssuanceOrderSchema, type CredentialIssuanceOrder } from "./credential-issuance-order.js"; + +export type VciDeferredIssueRequest = { + order?: CredentialIssuanceOrder; +}; + +export const vciDeferredIssueRequestSchema: Schema = + s.object({ + order: s.optional(s.lazy(() => credentialIssuanceOrderSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-response.ts new file mode 100644 index 00000000..ce8a6389 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-deferred-issue-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action41Schema, type Action41 } from "./action41.js"; + +export type VciDeferredIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the implementation of the deferred credential endpoint should take. */ + action?: Action41; + /** + * The content of the response that the implementation of the deferred credential endpoint should + * return. + */ + responseContent?: string; +}; + +export const vciDeferredIssueResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action41Schema)), + responseContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-request.ts new file mode 100644 index 00000000..cb4b1ea9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-request.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type VciDeferredParseRequest = { + /** The access token that came along with the deferred credential request. */ + accessToken?: string; + /** The message body of the deferred credential request. */ + requestContent?: string; +}; + +export const vciDeferredParseRequestSchema: Schema = + s.object({ + accessToken: s.optional(s.string()), + requestContent: s.optional(s.string()), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-response.ts new file mode 100644 index 00000000..ebf3caa3 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-deferred-parse-response.ts @@ -0,0 +1,25 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action40Schema, type Action40 } from "./action40.js"; +import { credentialRequestInfoSchema, type CredentialRequestInfo } from "./credential-request-info.js"; + +export type VciDeferredParseResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the deferred credential endpoint should take. */ + action?: Action40; + /** The content of the response to the request sender. */ + responseContent?: string; + info?: CredentialRequestInfo; +}; + +export const vciDeferredParseResponseSchema: Schema = + s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action40Schema)), + responseContent: s.optional(s.string()), + info: s.optional(s.lazy(() => credentialRequestInfoSchema)), + }); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-jwks-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-jwks-response.ts new file mode 100644 index 00000000..c346e59c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-jwks-response.ts @@ -0,0 +1,27 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action33Schema, type Action33 } from "./action33.js"; + +export type VciJwksResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** + * The next action that the implementation of the JWK Set document endpoint of the credential + * issuer should take after getting a response from Authlete's `/vci/jwks` API. + */ + action?: Action33; + /** + * Get the content that the implementation of the credential issuer metadata endpoint should use + * when it constructs a response. + */ + responseContent?: string; +}; + +export const vciJwksResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action33Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-jwtissuer-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-jwtissuer-response.ts new file mode 100644 index 00000000..e312fa75 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-jwtissuer-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action32Schema, type Action32 } from "./action32.js"; + +export type VciJwtissuerResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** + * The next action that the implementation of the JWT issuer metadata endpoint + * (`/.well-known/jwt-issuer`) should take after getting a response from Authlete's + * `/vci/jwtissuer` API. + */ + action?: Action32; + /** + * Get the content that the implementation of the credential issuer metadata endpoint should use + * when it constructs a response. + */ + responseContent?: string; +}; + +export const vciJwtissuerResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action32Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-metadata-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-metadata-request.ts new file mode 100644 index 00000000..7ee226cb --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-metadata-request.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type VciMetadataRequest = { + /** The flag indicating whether the metadata is written in the pretty format or not. */ + pretty: boolean; +}; + +export const vciMetadataRequestSchema: Schema = s.object({ + pretty: s.boolean(), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-metadata-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-metadata-response.ts new file mode 100644 index 00000000..5325eaa8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-metadata-response.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action31Schema, type Action31 } from "./action31.js"; + +export type VciMetadataResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** + * The next action that the implementation of the credential issuer metadata endpoint + * (`/.well-known/openid-credential-issuer`) should take after getting a response from Authlete's + * `/vci/metadata` API. + */ + action?: Action31; + /** + * Get the content that the implementation of the credential issuer metadata endpoint should use + * when it constructs a response. + */ + responseContent?: string; +}; + +export const vciMetadataResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action31Schema)), + responseContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-offer-create-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-offer-create-request.ts new file mode 100644 index 00000000..3b9f03fa --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-offer-create-request.ts @@ -0,0 +1,65 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { propertySchema, type Property } from "./property.js"; + +export type VciOfferCreateRequest = { + /** + * The flag indicating whether the `authorization_code` object is included in the `grants` object. + */ + authorizationCodeGrantIncluded?: boolean; + /** + * The flag indicating whether the `issuer_state` property is included in the `authorization_code` + * object in the `grants` object. + */ + issuerStateIncluded?: boolean; + /** + * The flag to include the `urn:ietf:params:oauth:grant-type:pre-authorized_code` object in the + * `grants` object. + */ + preAuthorizedCodeGrantIncluded?: boolean; + /** The subject associated with the credential offer. */ + subject?: string; + /** The duration of the credential offer. */ + duration?: number; + /** The general-purpose arbitrary string. */ + context?: string; + /** Extra properties to associate with the credential offer. */ + properties?: Property[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The time at which the user authentication was performed during the course of issuing the + * credential offer. + */ + authTime?: number; + /** + * The Authentication Context Class Reference of the user authentication performed during the + * course of issuing the credential offer. + */ + acr?: string; + /** The value of the `credential_configuration_ids` array. */ + credentialConfigurationIds?: string[]; + /** The transaction code that should be associated with the credential offer. */ + txCode?: string; + /** The input mode of the transaction code. */ + txCodeInputMode?: string; + /** The description of the transaction code. */ + txCodeDescription?: string; +}; + +export const vciOfferCreateRequestSchema: Schema = s.object({ + authorizationCodeGrantIncluded: s.optional(s.boolean()), + issuerStateIncluded: s.optional(s.boolean()), + preAuthorizedCodeGrantIncluded: s.optional(s.boolean()), + subject: s.optional(s.string()), + duration: s.optional(s.int()), + context: s.optional(s.string()), + properties: s.optional(s.array(s.lazy(() => propertySchema))), + jwtAtClaims: s.optional(s.string()), + authTime: s.optional(s.int()), + acr: s.optional(s.string()), + credentialConfigurationIds: s.optional(s.array(s.string())), + txCode: s.optional(s.string()), + txCodeInputMode: s.optional(s.string()), + txCodeDescription: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-offer-create-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-offer-create-response.ts new file mode 100644 index 00000000..368cec1e --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-offer-create-response.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action34Schema, type Action34 } from "./action34.js"; +import { credentialOfferInfoSchema, type CredentialOfferInfo } from "./credential-offer-info.js"; + +export type VciOfferCreateResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The result of the `/vci/offer/create` API call. */ + action?: Action34; + info?: CredentialOfferInfo; +}; + +export const vciOfferCreateResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action34Schema)), + info: s.optional(s.lazy(() => credentialOfferInfoSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-offer-info-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-offer-info-request.ts new file mode 100644 index 00000000..482f4f97 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-offer-info-request.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type VciOfferInfoRequest = { + /** The identifier of the credential offer. */ + identifier?: string; +}; + +export const vciOfferInfoRequestSchema: Schema = s.object({ + identifier: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-offer-info-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-offer-info-response.ts new file mode 100644 index 00000000..1f3e51ae --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-offer-info-response.ts @@ -0,0 +1,21 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action35Schema, type Action35 } from "./action35.js"; +import { credentialOfferInfoSchema, type CredentialOfferInfo } from "./credential-offer-info.js"; + +export type VciOfferInfoResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The result of the `/vci/offer/info` API call. */ + action?: Action35; + info?: CredentialOfferInfo; +}; + +export const vciOfferInfoResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action35Schema)), + info: s.optional(s.lazy(() => credentialOfferInfoSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-single-issue-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-single-issue-request.ts new file mode 100644 index 00000000..44f21452 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-single-issue-request.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { credentialIssuanceOrderSchema, type CredentialIssuanceOrder } from "./credential-issuance-order.js"; + +export type VciSingleIssueRequest = { + /** The access token that came along with the credential request. */ + accessToken?: string; + order?: CredentialIssuanceOrder; + /** + * Set to `true` to signal that the credential issuer is declining to issue the requested + * credential. When set, the response will contain `"error":"credential_request_denied"` and the + * action in the response will be `BAD_REQUEST`. + */ + denied?: boolean; + /** The minimum polling interval in seconds for deferred credential issuance. */ + interval?: number; +}; + +export const vciSingleIssueRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + order: s.optional(s.lazy(() => credentialIssuanceOrderSchema)), + denied: s.optional(s.boolean()), + interval: s.optional(s.int()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-single-issue-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-single-issue-response.ts new file mode 100644 index 00000000..b04d542b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-single-issue-response.ts @@ -0,0 +1,26 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action37Schema, type Action37 } from "./action37.js"; + +export type VciSingleIssueResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the implementation of the credential endpoint should take. */ + action?: Action37; + /** + * The content of the response that the implementation of the credential endpoint should return. + */ + responseContent?: string; + /** The issued transaction ID. */ + transactionId?: string; +}; + +export const vciSingleIssueResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action37Schema)), + responseContent: s.optional(s.string()), + transactionId: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-single-parse-request.ts b/plugins/authlete/sdk/typescript/src/models/vci-single-parse-request.ts new file mode 100644 index 00000000..87b58ec1 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-single-parse-request.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type VciSingleParseRequest = { + /** The access token that came along with the credential request. */ + accessToken?: string; + /** The message body of the credential request. */ + requestContent?: string; +}; + +export const vciSingleParseRequestSchema: Schema = s.object({ + accessToken: s.optional(s.string()), + requestContent: s.optional(s.string()), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/vci-single-parse-response.ts b/plugins/authlete/sdk/typescript/src/models/vci-single-parse-response.ts new file mode 100644 index 00000000..a11d0d1b --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/vci-single-parse-response.ts @@ -0,0 +1,24 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { action36Schema, type Action36 } from "./action36.js"; +import { credentialRequestInfoSchema, type CredentialRequestInfo } from "./credential-request-info.js"; + +export type VciSingleParseResponse = { + /** The code which represents the result of the API call. */ + resultCode?: string; + /** A short message which explains the result of the API call. */ + resultMessage?: string; + /** The next action that the credential endpoint should take. */ + action?: Action36; + /** The content of the response to the request sender. */ + responseContent?: string; + info?: CredentialRequestInfo; +}; + +export const vciSingleParseResponseSchema: Schema = s.object({ + resultCode: s.optional(s.string()), + resultMessage: s.optional(s.string()), + action: s.optional(s.lazy(() => action36Schema)), + responseContent: s.optional(s.string()), + info: s.optional(s.lazy(() => credentialRequestInfoSchema)), +}); diff --git a/plugins/authlete/sdk/typescript/src/models/verified-claims-validation-schema.ts b/plugins/authlete/sdk/typescript/src/models/verified-claims-validation-schema.ts new file mode 100644 index 00000000..ae5615ba --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/models/verified-claims-validation-schema.ts @@ -0,0 +1,14 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +/** The verified claims validation schema set. */ +export const VerifiedClaimsValidationSchema = { + Standard: "standard", + StandardIdDocument: "standard+id_document", +} as const; +export type VerifiedClaimsValidationSchema = + | (typeof VerifiedClaimsValidationSchema)[keyof typeof VerifiedClaimsValidationSchema] + | (string & {}); + +export const verifiedClaimsValidationSchemaSchema: EnumSchema = + s.enumOf(VerifiedClaimsValidationSchema); diff --git a/plugins/authlete/sdk/typescript/src/resources/audit-logs.ts b/plugins/authlete/sdk/typescript/src/resources/audit-logs.ts new file mode 100644 index 00000000..3852f2e9 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/audit-logs.ts @@ -0,0 +1,179 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import * as s from "../core/validation/index.js"; +import { + auditEntriesGetResponseSchema, + type AuditEntriesGetResponse, +} from "../models/audit-entries-get-response.js"; +import { idpErrorSchema, type IdpError } from "../models/idp-error.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for retrieving audit logs, hosted on the Authlete IdP server. + */ +export class AuditLogs { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Get Audit Logs + * + * @remarks + * Retrieve audit logs as a cursor-paginated list. Results can be filtered by time range, event + * type, and organization. + * + * @returns A page of audit log entries matching the query. + * + * @throws {@link AuditLogs.AuditEntriesGetIdpApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + auditEntriesGetIdpApi( + request: AuditLogs.AuditEntriesGetIdpApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/audit/entries"), + auth: this.#auth.bearer, + pathParams: [], + query: [ + { name: "after", value: request.after, schema: s.optional(s.dateTime()) }, + { name: "before", value: request.before, schema: s.optional(s.dateTime()) }, + { name: "types", value: request.types, schema: s.optional(s.array(s.string())) }, + { name: "organizationId", value: request.organizationId, schema: s.optional(s.int()) }, + { name: "limit", value: request.limit, schema: s.defaulted(s.int(), 100) }, + { name: "cursor", value: request.cursor, schema: s.optional(s.string()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: auditEntriesGetResponseSchema }, + errorFactory: AuditLogs.AuditEntriesGetIdpApiError, + }, + options, + ); + } + + /** + * Get Audit Log Event Types + * + * @remarks + * Returns the list of available audit log event types for this environment. + * + * Accessible with a user access token or an organization token that has the `view_audit_log` + * permission (e.g. a token created with the `admin` or `audit_reader` preset). + * + * This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the + * regional API clusters. + * + * @returns List of available audit event type strings. + * + * @throws {@link AuditLogs.AuditTypesGetIdpApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + auditTypesGetIdpApi(options?: RequestOptions): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/audit/types"), + auth: this.#auth.bearer, + pathParams: [], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: s.array(s.string()) }, + errorFactory: AuditLogs.AuditTypesGetIdpApiError, + }, + options, + ); + } +} + +export namespace AuditLogs { + export type AuditEntriesGetIdpApiRequest = { + /** Return logs after this timestamp (ISO 8601). */ + after?: Date; + /** Return logs before this timestamp (ISO 8601). */ + before?: Date; + /** + * Filter by event type (e.g. `service.create`, `security.login`, `client.create`). Can be + * specified multiple times. The set of available event types is environment-specific and + * includes server-side types; retrieve the full list from the `/api/audit/types` endpoint. + */ + types?: string[]; + /** + * The organization to retrieve audit logs for. Not required when authenticating with an + * organization token β€” the token's own organization is used, and a differing value is rejected. + * Required for user access tokens, unless the user is an Authlete administrator (administrators + * may omit it to query across all organizations). + */ + organizationId?: number; + /** + * The maximum number of entries to return per page. Values outside the allowed range are + * clamped; the effective value is echoed back in the response. + * + * @default 100 + */ + limit?: number; + /** + * An opaque cursor returned as `nextCursor` by a previous response. When provided, it takes + * precedence over the `before` parameter. + */ + cursor?: string; + }; + + export class AuditEntriesGetIdpApiError extends ApiError { + declare readonly payload: + | Declared<"idpError", IdpError> + | Declared<"idpError2", IdpError> + | Declared<"idpError3", IdpError> + | Declared<"idpError4", IdpError> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "idpError", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 401, kind: "idpError2", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 403, kind: "idpError3", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 500, kind: "idpError4", decode: { kind: "json", schema: idpErrorSchema } }, + ]; + } + + export class AuditTypesGetIdpApiError extends ApiError { + declare readonly payload: + | Declared<"idpError", IdpError> + | Declared<"idpError2", IdpError> + | Declared<"idpError3", IdpError> + | Declared<"idpError4", IdpError> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "idpError", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 401, kind: "idpError2", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 403, kind: "idpError3", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 500, kind: "idpError4", decode: { kind: "json", schema: idpErrorSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/authorization-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/authorization-endpoint.ts new file mode 100644 index 00000000..09c683dc --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/authorization-endpoint.ts @@ -0,0 +1,538 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + authorizationFailResponseSchema, + type AuthorizationFailResponse, +} from "../models/authorization-fail-response.js"; +import { + authorizationIssueResponseSchema, + type AuthorizationIssueResponse, +} from "../models/authorization-issue-response.js"; +import { authorizationResponseSchema, type AuthorizationResponse } from "../models/authorization-response.js"; +import { + authorizationTicketInfoResponseSchema, + type AuthorizationTicketInfoResponse, +} from "../models/authorization-ticket-info-response.js"; +import { + authorizationTicketInfoSchema, + type AuthorizationTicketInfo, +} from "../models/authorization-ticket-info.js"; +import { + authorizationTicketUpdateResponseSchema, + type AuthorizationTicketUpdateResponse, +} from "../models/authorization-ticket-update-response.js"; +import { authzDetailsSchema, type AuthzDetails } from "../models/authz-details.js"; +import { cimdOptionsSchema, type CimdOptions } from "../models/cimd-options.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { reasonSchema, type Reason } from "../models/reason.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Authorization Endpoint. + */ +export class AuthorizationEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Authorization Request + * + * @remarks + * This API parses request parameters of an authorization request and returns necessary data for + * the authorization server implementation to process the authorization request further. + * + * @returns Authorization request processed successfully + * + * @throws {@link AuthorizationEndpoint.AuthAuthorizationApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authAuthorizationApi( + request: AuthorizationEndpoint.AuthAuthorizationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/authorization"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "context", value: request.context, schema: s.optional(s.string()) }, + { + name: "cimdOptions", + value: request.cimdOptions, + schema: s.optional(s.lazy(() => cimdOptionsSchema)), + }, + ], + }, + }, + { + success: { kind: "json", schema: authorizationResponseSchema }, + errorFactory: AuthorizationEndpoint.AuthAuthorizationApiError, + }, + options, + ); + } + + /** + * Fail Authorization Request + * + * @remarks + * This API generates a content of an error authorization response that the authorization server + * implementation returns to the client application. + * + * @throws {@link AuthorizationEndpoint.AuthAuthorizationFailApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authAuthorizationFailApi( + request: AuthorizationEndpoint.AuthAuthorizationFailApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/authorization/fail"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "reason", value: request.reason, schema: reasonSchema }, + { name: "description", value: request.description, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: authorizationFailResponseSchema }, + errorFactory: AuthorizationEndpoint.AuthAuthorizationFailApiError, + }, + options, + ); + } + + /** + * Issue Authorization Response + * + * @remarks + * This API parses request parameters of an authorization request and returns necessary data for + * the authorization server implementation to process the authorization request further. + * + * @throws {@link AuthorizationEndpoint.AuthAuthorizationIssueApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authAuthorizationIssueApi( + request: AuthorizationEndpoint.AuthAuthorizationIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/authorization/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + { name: "authTime", value: request.authTime, schema: s.optional(s.int()) }, + { name: "acr", value: request.acr, schema: s.optional(s.string()) }, + { name: "claims", value: request.claims, schema: s.optional(s.string()) }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { name: "sub", value: request.sub, schema: s.optional(s.string()) }, + { name: "idtHeaderParams", value: request.idtHeaderParams, schema: s.optional(s.string()) }, + { name: "claimsForTx", value: request.claimsForTx, schema: s.optional(s.string()) }, + { + name: "consentedClaims", + value: request.consentedClaims, + schema: s.optional(s.array(s.string())), + }, + { + name: "authorizationDetails", + value: request.authorizationDetails, + schema: s.optional(s.lazy(() => authzDetailsSchema)), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { name: "sessionId", value: request.sessionId, schema: s.optional(s.string()) }, + { name: "idTokenAudType", value: request.idTokenAudType, schema: s.optional(s.string()) }, + { + name: "verifiedClaimsForTx", + value: request.verifiedClaimsForTx, + schema: s.optional(s.array(s.string())), + }, + ], + }, + }, + { + success: { kind: "json", schema: authorizationIssueResponseSchema }, + errorFactory: AuthorizationEndpoint.AuthAuthorizationIssueApiError, + }, + options, + ); + } + + /** + * Get Ticket Information + * + * @throws {@link AuthorizationEndpoint.GetAuthorizationTicketInfoError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + getAuthorizationTicketInfo( + request: AuthorizationEndpoint.GetAuthorizationTicketInfoRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/authorization/ticket/info"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "ticket", value: request.ticket, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: authorizationTicketInfoResponseSchema }, + errorFactory: AuthorizationEndpoint.GetAuthorizationTicketInfoError, + }, + options, + ); + } + + /** + * Update Ticket Information + * + * @returns Authorization ticket updated successfully + * + * @throws {@link AuthorizationEndpoint.UpdateAuthorizationTicketError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + updateAuthorizationTicket( + request: AuthorizationEndpoint.UpdateAuthorizationTicketRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/authorization/ticket/update"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "info", value: request.info, schema: authorizationTicketInfoSchema }, + ], + }, + }, + { + success: { kind: "json", schema: authorizationTicketUpdateResponseSchema }, + errorFactory: AuthorizationEndpoint.UpdateAuthorizationTicketError, + }, + options, + ); + } +} + +export namespace AuthorizationEndpoint { + export type AuthAuthorizationApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * OAuth 2.0 authorization request parameters which are the request parameters that the OAuth + * 2.0 authorization endpoint of the authorization server implementation received from the + * client application. + * + * The value of parameters is either (1) the entire query string when the HTTP method of the + * request from the client application is `GET` or (2) the entire entity body (which is + * formatted in `application/x-www-form-urlencoded`) when the HTTP method of the request from + * the client application is `POST`. + */ + parameters: string; + /** + * The arbitrary text to be attached to the ticket that will be issued from the + * `/auth/authorization` API. + * + * The text can be retrieved later by the `/auth/authorization/ticket/info` API and can be + * updated by the `/auth/authorization/ticket/update` API. + * + * The text will be compressed and encrypted when it is saved in the Authlete database. + */ + context?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; + }; + + export class AuthAuthorizationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthAuthorizationFailApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued from Authlete `/auth/authorization` API. */ + ticket: string; + /** + * The reason of the failure of the authorization request. For more details, see + * [NO_INTERACTION] in the description of `/auth/authorization` API. + */ + reason: Reason; + /** The custom description about the authorization failure. */ + description?: string; + }; + + export class AuthAuthorizationFailApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthAuthorizationIssueApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued from Authlete `/auth/authorization` API. */ + ticket: string; + /** + * The subject (= a user account managed by the service) who has granted authorization to the + * client application. + */ + subject: string; + /** + * The time when the authentication of the end-user occurred. Its value is the number of seconds + * from `1970-01-01`. + */ + authTime?: number; + /** The Authentication Context Class Reference performed for the end-user authentication. */ + acr?: string; + /** + * The claims of the end-user (= pieces of information about the end-user) in JSON format. See + * [OpenID Connect Core 1.0, 5.1. Standard + * Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) for details + * about the format. + */ + claims?: string; + /** Extra properties to associate with an access token and/or an authorization code. */ + properties?: Property[]; + /** + * Scopes to associate with an access token and/or an authorization code. If a non-empty string + * array is given, it replaces the scopes specified by the original authorization request. + */ + scopes?: string[]; + /** + * The value of the `sub` claim to embed in an ID token. If this request parameter is `null` or + * empty, the value of the `subject` request parameter is used as the value of the `sub` claim. + */ + sub?: string; + /** + * JSON that represents additional JWS header parameters for ID tokens that may be issued based + * on the authorization request. + */ + idtHeaderParams?: string; + /** Claim key-value pairs that are used to compute transformed claims. */ + claimsForTx?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API + * call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * access token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The session ID of the user's authentication session. The specified value will be embedded in + * the ID token as the value of the `sid` claim. This parameter needs to be provided only if you + * want to support the [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (a.k.a. + * "Native SSO"). To enable support for the Native SSO specification, the `nativeSsoSupported` + * property of your service must be set to `true`. + */ + sessionId?: string; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; + /** Values of verified claims requested indirectly by "transformed claims". */ + verifiedClaimsForTx?: string[]; + }; + + export class AuthAuthorizationIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type GetAuthorizationTicketInfoRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket that has been issued from the `/auth/authorization` API. */ + ticket: string; + }; + + export class GetAuthorizationTicketInfoError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type UpdateAuthorizationTicketRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket. */ + ticket: string; + info: AuthorizationTicketInfo; + }; + + export class UpdateAuthorizationTicketError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/back-channel-logout.ts b/plugins/authlete/sdk/typescript/src/resources/back-channel-logout.ts new file mode 100644 index 00000000..9f918cb2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/back-channel-logout.ts @@ -0,0 +1,93 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + backchannelLogoutTokenRequestSchema, + type BackchannelLogoutTokenRequest, +} from "../models/backchannel-logout-token-request.js"; +import { + backchannelLogoutTokenResponseSchema, + type BackchannelLogoutTokenResponse, +} from "../models/backchannel-logout-token-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +export class BackChannelLogout { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Backchannel Logout Token Issuing + * + * @remarks + * The `/backchannel/logout/token` API issues a logout token for a client application in the + * context of [OpenID Connect Back-Channel Logout + * 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html). + * + * @throws {@link BackChannelLogout.BackchannelLogoutTokenApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + backchannelLogoutTokenApi( + request: BackChannelLogout.BackchannelLogoutTokenApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/backchannel/logout/token"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: backchannelLogoutTokenRequestSchema }, + }, + { + success: { kind: "json", schema: backchannelLogoutTokenResponseSchema }, + errorFactory: BackChannelLogout.BackchannelLogoutTokenApiError, + }, + options, + ); + } +} + +export namespace BackChannelLogout { + export type BackchannelLogoutTokenApiRequest = { + /** A service ID. */ + serviceId: string; + body: BackchannelLogoutTokenRequest; + }; + + export class BackchannelLogoutTokenApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/ciba.ts b/plugins/authlete/sdk/typescript/src/resources/ciba.ts new file mode 100644 index 00000000..2824470c --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/ciba.ts @@ -0,0 +1,531 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + backchannelAuthenticationCompleteResponseSchema, + type BackchannelAuthenticationCompleteResponse, +} from "../models/backchannel-authentication-complete-response.js"; +import { + backchannelAuthenticationFailResponseSchema, + type BackchannelAuthenticationFailResponse, +} from "../models/backchannel-authentication-fail-response.js"; +import { + backchannelAuthenticationIssueResponseSchema, + type BackchannelAuthenticationIssueResponse, +} from "../models/backchannel-authentication-issue-response.js"; +import { + backchannelAuthenticationResponseSchema, + type BackchannelAuthenticationResponse, +} from "../models/backchannel-authentication-response.js"; +import { cimdOptionsSchema, type CimdOptions } from "../models/cimd-options.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { reason2Schema, type Reason2 } from "../models/reason2.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { result1Schema, type Result1 } from "../models/result1.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing Client-Initiated Backchannel Authentication (CIBA). + */ +export class Ciba { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Backchannel Authentication Request + * + * @remarks + * This API parses request parameters of a [backchannel authentication + * request](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_request) + * and returns necessary data for the authorization server implementation to process the + * backchannel authentication request further. + * + * @throws {@link Ciba.BackchannelAuthenticationApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + backchannelAuthenticationApi( + request: Ciba.BackchannelAuthenticationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/backchannel/authentication"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.optional(s.string()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { + name: "clientCertificatePath", + value: request.clientCertificatePath, + schema: s.optional(s.array(s.string())), + }, + { + name: "oauthClientAttestation", + value: request.oauthClientAttestation, + schema: s.optional(s.string()), + }, + { + name: "oauthClientAttestationPop", + value: request.oauthClientAttestationPop, + schema: s.optional(s.string()), + }, + { + name: "cimdOptions", + value: request.cimdOptions, + schema: s.optional(s.lazy(() => cimdOptionsSchema)), + }, + ], + }, + }, + { + success: { kind: "json", schema: backchannelAuthenticationResponseSchema }, + errorFactory: Ciba.BackchannelAuthenticationApiError, + }, + options, + ); + } + + /** + * Complete Backchannel Authentication + * + * @remarks + * This API returns information about what action the authorization server should take after it + * receives the result of end-user's decision about whether the end-user has approved or rejected + * a client application's request on the authentication device. + * + * @returns Backchannel authentication completed successfully + * + * @throws {@link Ciba.BackchannelAuthenticationCompleteApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + backchannelAuthenticationCompleteApi( + request: Ciba.BackchannelAuthenticationCompleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/backchannel/authentication/complete"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "result", value: request.result, schema: result1Schema }, + { name: "subject", value: request.subject, schema: s.string() }, + { name: "sub", value: request.sub, schema: s.optional(s.string()) }, + { name: "authTime", value: request.authTime, schema: s.optional(s.int()) }, + { name: "acr", value: request.acr, schema: s.optional(s.string()) }, + { name: "claims", value: request.claims, schema: s.optional(s.string()) }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { name: "idtHeaderParams", value: request.idtHeaderParams, schema: s.optional(s.string()) }, + { name: "errorDescription", value: request.errorDescription, schema: s.optional(s.string()) }, + { name: "errorUri", value: request.errorUri, schema: s.optional(s.string()) }, + { + name: "consentedClaims", + value: request.consentedClaims, + schema: s.optional(s.array(s.string())), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { name: "idTokenAudType", value: request.idTokenAudType, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: backchannelAuthenticationCompleteResponseSchema }, + errorFactory: Ciba.BackchannelAuthenticationCompleteApiError, + }, + options, + ); + } + + /** + * Fail Backchannel Authentication Request + * + * @remarks + * The API prepares JSON that contains an error. The JSON should be used as the response body of + * the response which is returned to the client from the [backchannel authentication + * endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint). + * + * @throws {@link Ciba.BackchannelAuthenticationFailApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + backchannelAuthenticationFailApi( + request: Ciba.BackchannelAuthenticationFailApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/backchannel/authentication/fail"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "reason", value: request.reason, schema: reason2Schema }, + { name: "errorDescription", value: request.errorDescription, schema: s.optional(s.string()) }, + { name: "errorUri", value: request.errorUri, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: backchannelAuthenticationFailResponseSchema }, + errorFactory: Ciba.BackchannelAuthenticationFailApiError, + }, + options, + ); + } + + /** + * Issue Backchannel Authentication Response + * + * @remarks + * This API prepares JSON that contains an `auth_req_id`. The JSON should be used as the response + * body of the response which is returned to the client from the [backchannel authentication + * endpoint](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_backchannel_endpoint) + * + * @returns Backchannel authentication issued successfully + * + * @throws {@link Ciba.BackchannelAuthenticationIssueApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + backchannelAuthenticationIssueApi( + request: Ciba.BackchannelAuthenticationIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/backchannel/authentication/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "ticket", value: request.ticket, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: backchannelAuthenticationIssueResponseSchema }, + errorFactory: Ciba.BackchannelAuthenticationIssueApiError, + }, + options, + ); + } +} + +export namespace Ciba { + export type BackchannelAuthenticationApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * Parameters of a backchannel authentication request which are the request parameters that the + * backchannel authentication endpoint of the OpenID provider implementation received from the + * client application. + * + * The value of `parameters` is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from Authorization header of the backchannel authentication request + * from the client application. + * + * If the backchannel authentication endpoint of the OpenID provider implementation supports + * Basic Authentication as a means of client authentication, and the request from the client + * application contained its client ID in Authorization header, the value should be extracted + * and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from Authorization header of the backchannel authentication + * request from the client application. + * + * If the backchannel authentication endpoint of the OpenID provider implementation supports + * Basic Authentication as a means of client authentication, and the request from the client + * application contained its client secret in Authorization header, the value should be + * extracted and set to this parameter. + */ + clientSecret?: string; + /** + * The client certification used in the TLS connection between the client application and the + * backchannel authentication endpoint of the OpenID provider. + */ + clientCertificate?: string; + /** + * The client certificate path presented by the client during client authentication. Each + * element is a string in PEM format. + */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; + }; + + export class BackchannelAuthenticationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type BackchannelAuthenticationCompleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued by Authlete's `/backchannel/authentication` API. */ + ticket: string; + /** + * The result of the end-user authentication and authorization. One of the following. Details + * are described in the description. + */ + result: Result1; + /** The subject (= unique identifier) of the end-user. */ + subject: string; + /** The value of the sub claim that should be used in the ID token. */ + sub?: string; + /** + * The time at which the end-user was authenticated. Its value is the number of seconds from + * `1970-01-01`. + */ + authTime?: number; + /** + * The reference of the authentication context class which the end-user authentication + * satisfied. + */ + acr?: string; + /** Additional claims which will be embedded in the ID token. */ + claims?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** + * Scopes to replace the scopes specified in the original backchannel authentication request + * with. When nothing is specified for this parameter, replacement is not performed. + */ + scopes?: string[]; + /** JSON that represents additional JWS header parameters for ID tokens. */ + idtHeaderParams?: string; + /** + * The description of the error. If this optional request parameter is given, its value is used + * as the value of the `error_description` property, but it is used only when the result is not + * `AUTHORIZED`. To comply with the specification strictly, the description must not include + * characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. This corresponds to the + * `error_uri` property in the response to the client. + */ + errorUri?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API + * call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * access token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * refresh token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; + }; + + export class BackchannelAuthenticationCompleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type BackchannelAuthenticationFailApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The ticket which should be deleted on a call of Authlete's `/backchannel/authentication/fail` + * API. This request parameter is not mandatory but optional. If this request parameter is given + * and the ticket belongs to the service, the specified ticket is deleted from the database. + * Giving this parameter is recommended to clean up the storage area for the service. + */ + ticket: string; + /** + * The reason of the failure of the backchannel authentication request. This request parameter + * is not mandatory but optional. However, giving this parameter is recommended. If omitted, + * `SERVER_ERROR` is used as a reason. + */ + reason: Reason2; + /** + * The description of the error. This corresponds to the `error_description` property in the + * response to the client. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. If this optional request parameter + * is given, its value is used as the value of the `error_uri` property. + */ + errorUri?: string; + }; + + export class BackchannelAuthenticationFailApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type BackchannelAuthenticationIssueApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued from Authlete's `/backchannel/authentication` API. */ + ticket: string; + }; + + export class BackchannelAuthenticationIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/client-management.ts b/plugins/authlete/sdk/typescript/src/resources/client-management.ts new file mode 100644 index 00000000..8ad32f8a --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/client-management.ts @@ -0,0 +1,3030 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { applicationTypeSchema, type ApplicationType } from "../models/application-type.js"; +import { clientAuthMethodSchema, type ClientAuthMethod } from "../models/client-auth-method.js"; +import { + clientAuthorizationDeleteResponseSchema, + type ClientAuthorizationDeleteResponse, +} from "../models/client-authorization-delete-response.js"; +import { + clientAuthorizationGetListResponseSchema, + type ClientAuthorizationGetListResponse, +} from "../models/client-authorization-get-list-response.js"; +import { + clientCursorListResponseSchema, + type ClientCursorListResponse, +} from "../models/client-cursor-list-response.js"; +import { + clientExtensionRequestableScopesGetResponseSchema, + type ClientExtensionRequestableScopesGetResponse, +} from "../models/client-extension-requestable-scopes-get-response.js"; +import { + clientExtensionRequestableScopesUpdateRequestSchema, + type ClientExtensionRequestableScopesUpdateRequest, +} from "../models/client-extension-requestable-scopes-update-request.js"; +import { + clientExtensionRequestableScopesUpdateResponseSchema, + type ClientExtensionRequestableScopesUpdateResponse, +} from "../models/client-extension-requestable-scopes-update-response.js"; +import { clientExtensionSchema, type ClientExtension } from "../models/client-extension.js"; +import { + clientFlagUpdateResponseSchema, + type ClientFlagUpdateResponse, +} from "../models/client-flag-update-response.js"; +import { + clientGetListResponseSchema, + type ClientGetListResponse, +} from "../models/client-get-list-response.js"; +import { + clientRegistrationTypeSchema, + type ClientRegistrationType, +} from "../models/client-registration-type.js"; +import { clientSearchResponseSchema, type ClientSearchResponse } from "../models/client-search-response.js"; +import { + clientSecretRefreshResponseSchema, + type ClientSecretRefreshResponse, +} from "../models/client-secret-refresh-response.js"; +import { clientSourceSchema, type ClientSource } from "../models/client-source.js"; +import { clientTypeSchema, type ClientType } from "../models/client-type.js"; +import { clientSchema, type Client } from "../models/client.js"; +import { fapiModeSchema, type FapiMode } from "../models/fapi-mode.js"; +import { grantTypeSchema, type GrantType } from "../models/grant-type.js"; +import { jweAlgSchema, type JweAlg } from "../models/jwe-alg.js"; +import { jweEncSchema, type JweEnc } from "../models/jwe-enc.js"; +import { jwsAlgSchema, type JwsAlg } from "../models/jws-alg.js"; +import { pairSchema, type Pair } from "../models/pair.js"; +import { responseModeSchema, type ResponseMode } from "../models/response-mode.js"; +import { responseTypeSchema, type ResponseType } from "../models/response-type.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { subjectTypeSchema, type SubjectType } from "../models/subject-type.js"; +import { taggedValueSchema, type TaggedValue } from "../models/tagged-value.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for managing OAuth clients, including creation, update, and deletion of clients. + */ +export class ClientManagement { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Delete Client Tokens + * + * @remarks + * Delete all existing access tokens issued to a client application by an end-user. + * + * The subject parameter is required and must be provided as a query parameter. + * + * @throws {@link ClientManagement.ClientAuthorizationDeleteApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationDeleteApi( + request: ClientManagement.ClientAuthorizationDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/delete/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [{ name: "subject", value: request.subject, schema: s.string() }], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationDeleteApiError, + }, + options, + ); + } + + /** + * Delete Client Tokens + * + * @remarks + * Delete all existing access tokens issued to a client application by an end-user. + * + * The subject parameter is required. + * + * @throws {@link ClientManagement.ClientAuthorizationDeleteApiPostError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationDeleteApiPost( + request: ClientManagement.ClientAuthorizationDeleteApiPostRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/delete/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "subject", value: request.subject, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationDeleteApiPostError, + }, + options, + ); + } + + /** + * Delete Client Tokens (by Subject) + * + * @remarks + * Delete all existing access tokens issued to a client application by an end-user. In this + * variant, the subject is provided in the path. + * + * @throws {@link ClientManagement.ClientAuthorizationDeleteBySubjectApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationDeleteBySubjectApi( + request: ClientManagement.ClientAuthorizationDeleteBySubjectApiRequest, + options?: RequestOptions, + ): ApiPromise< + ClientAuthorizationDeleteResponse, + ClientManagement.ClientAuthorizationDeleteBySubjectApiError + > { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/authorization/delete/{clientId}/{subject}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationDeleteBySubjectApiError, + }, + options, + ); + } + + /** + * Get Authorized Applications + * + * @remarks + * Get a list of client applications that an end-user has authorized. + * + * The subject parameter is required and can be provided as a query parameter. + * + * @throws {@link ClientManagement.ClientAuthorizationGetListApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationGetListApi( + request: ClientManagement.ClientAuthorizationGetListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/get/list"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [ + { name: "subject", value: request.subject, schema: s.string() }, + { name: "developer", value: request.developer, schema: s.optional(s.string()) }, + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationGetListResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationGetListApiError, + }, + options, + ); + } + + /** + * Get Authorized Applications + * + * @remarks + * Get a list of client applications that an end-user has authorized. + * + * The subject parameter is required. + * + * @throws {@link ClientManagement.ClientAuthorizationGetListApiPostError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationGetListApiPost( + request: ClientManagement.ClientAuthorizationGetListApiPostRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/get/list"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "subject", value: request.subject, schema: s.string() }, + { name: "developer", value: request.developer, schema: s.optional(s.string()) }, + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + }, + }, + { + success: { kind: "json", schema: clientAuthorizationGetListResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationGetListApiPostError, + }, + options, + ); + } + + /** + * Get Authorized Applications (by Subject) + * + * @remarks + * Get a list of client applications that an end-user has authorized. In this variant, the subject + * is provided in the path. + * + * @throws {@link ClientManagement.ClientAuthorizationGetListBySubjectApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationGetListBySubjectApi( + request: ClientManagement.ClientAuthorizationGetListBySubjectApiRequest, + options?: RequestOptions, + ): ApiPromise< + ClientAuthorizationGetListResponse, + ClientManagement.ClientAuthorizationGetListBySubjectApiError + > { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/get/list/{subject}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + ], + query: [ + { name: "developer", value: request.developer, schema: s.optional(s.string()) }, + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationGetListResponseSchema }, + errorFactory: ClientManagement.ClientAuthorizationGetListBySubjectApiError, + }, + options, + ); + } + + /** + * Update Client Tokens + * + * @remarks + * Update attributes of all existing access tokens given to a client application. + * + * @throws {@link ClientManagement.ClientAuthorizationUpdateApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientAuthorizationUpdateApi( + request: ClientManagement.ClientAuthorizationUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/authorization/update/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "subject", value: request.subject, schema: s.string() }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + ], + }, + }, + { + success: { kind: "json", schema: resultSchema }, + errorFactory: ClientManagement.ClientAuthorizationUpdateApiError, + }, + options, + ); + } + + /** + * Create Client + * + * @remarks + * Create a new client. + * + * @returns Client created successfully (legacy compatibility) + * + * @throws {@link ClientManagement.ClientCreateApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientCreateApi( + request: ClientManagement.ClientCreateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/create"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: s.optional(s.lazy(() => clientSchema)) }, + }, + { + success: { kind: "json", schema: clientSchema }, + errorFactory: ClientManagement.ClientCreateApiError, + }, + options, + ); + } + + /** + * List Clients (Cursor Pagination) + * + * @remarks + * Retrieve the clients in the requested service via cursor based iteration. + * + * @throws {@link ClientManagement.ClientCursorListApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientCursorListApi( + request: ClientManagement.ClientCursorListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.int() }], + query: [ + { name: "cursor", value: request.cursor, schema: s.optional(s.string()) }, + { name: "limit", value: request.limit, schema: s.optional(s.int()) }, + { name: "includeTotalCount", value: request.includeTotalCount, schema: s.optional(s.boolean()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientCursorListResponseSchema }, + errorFactory: ClientManagement.ClientCursorListApiError, + }, + options, + ); + } + + /** + * Delete Client ⚑ + * + * @remarks + * Delete a client. + * + * @returns The client was successfully deleted. + * + * @throws {@link ClientManagement.ClientDeleteApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientDeleteApi( + request: ClientManagement.ClientDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/client/delete/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "empty" }, + errorFactory: ClientManagement.ClientDeleteApiError, + }, + options, + ); + } + + /** + * Delete Requestable Scopes + * + * @remarks + * Delete requestable scopes of a client + * + * @returns Requestable scopes was successfully deleted. + * + * @throws {@link ClientManagement.ClientExtensionRequestablesScopesDeleteApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientExtensionRequestablesScopesDeleteApi( + request: ClientManagement.ClientExtensionRequestablesScopesDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/extension/requestable_scopes/delete/{clientId}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "empty" }, + errorFactory: ClientManagement.ClientExtensionRequestablesScopesDeleteApiError, + }, + options, + ); + } + + /** + * Get Requestable Scopes + * + * @remarks + * Get the requestable scopes per client + * + * @throws {@link ClientManagement.ClientExtensionRequestablesScopesGetApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientExtensionRequestablesScopesGetApi( + request: ClientManagement.ClientExtensionRequestablesScopesGetApiRequest, + options?: RequestOptions, + ): ApiPromise< + ClientExtensionRequestableScopesGetResponse, + ClientManagement.ClientExtensionRequestablesScopesGetApiError + > { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/extension/requestable_scopes/get/{clientId}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientExtensionRequestableScopesGetResponseSchema }, + errorFactory: ClientManagement.ClientExtensionRequestablesScopesGetApiError, + }, + options, + ); + } + + /** + * Update Requestable Scopes + * + * @remarks + * Update requestable scopes of a client + * + * @returns Requestable scopes updated successfully + * + * @throws {@link ClientManagement.ClientExtensionRequestablesScopesUpdateApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientExtensionRequestablesScopesUpdateApi( + request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise< + ClientExtensionRequestableScopesUpdateResponse, + ClientManagement.ClientExtensionRequestablesScopesUpdateApiError + > { + return this.#rawClient.execute( + { + method: "PUT", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/extension/requestable_scopes/update/{clientId}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "json", + value: request.body, + schema: clientExtensionRequestableScopesUpdateRequestSchema, + }, + }, + { + success: { kind: "json", schema: clientExtensionRequestableScopesUpdateResponseSchema }, + errorFactory: ClientManagement.ClientExtensionRequestablesScopesUpdateApiError, + }, + options, + ); + } + + /** + * Update Requestable Scopes + * + * @remarks + * Update requestable scopes of a client + * + * @returns Requestable scopes updated successfully + * + * @throws {@link ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError} when the + * API answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientExtensionRequestablesScopesUpdateApiPost( + request: ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostRequest, + options?: RequestOptions, + ): ApiPromise< + ClientExtensionRequestableScopesUpdateResponse, + ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError + > { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/extension/requestable_scopes/update/{clientId}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "json", + value: request.body, + schema: clientExtensionRequestableScopesUpdateRequestSchema, + }, + }, + { + success: { kind: "json", schema: clientExtensionRequestableScopesUpdateResponseSchema }, + errorFactory: ClientManagement.ClientExtensionRequestablesScopesUpdateApiPostError, + }, + options, + ); + } + + /** + * Update Client Lock + * + * @remarks + * Lock and unlock a client + * + * @throws {@link ClientManagement.ClientFlagUpdateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientFlagUpdateApi( + request: ClientManagement.ClientFlagUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/lock_flag/update/{clientIdentifier}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "clientLocked", value: request.clientLocked, schema: s.boolean() }], + }, + }, + { + success: { kind: "json", schema: clientFlagUpdateResponseSchema }, + errorFactory: ClientManagement.ClientFlagUpdateApiError, + }, + options, + ); + } + + /** + * Get Client + * + * @remarks + * Get a client. + * + * @throws {@link ClientManagement.ClientGetApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGetApi( + request: ClientManagement.ClientGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/get/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientSchema }, + errorFactory: ClientManagement.ClientGetApiError, + }, + options, + ); + } + + /** + * List Clients + * + * @remarks + * Get a list of clients on a service. + * + * If the access token can view a full service (including an admin), all clients within the + * service are returned. Otherwise, only clients that the access token can view within the service + * are returned. + * - ViewClient: [] + * + * @throws {@link ClientManagement.ClientGetListApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGetListApi( + request: ClientManagement.ClientGetListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/get/list"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [ + { name: "developer", value: request.developer, schema: s.optional(s.string()) }, + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientGetListResponseSchema }, + errorFactory: ClientManagement.ClientGetListApiError, + }, + options, + ); + } + + /** + * Delete Granted Scopes + * + * @remarks + * Delete the set of scopes that an end-user has granted to a client application. + * + * Even if records about granted scopes are deleted by calling this API, existing access tokens + * are not deleted and scopes of existing access tokens are not changed. The subject parameter is + * required and must be provided as a query parameter. + * + * @throws {@link ClientManagement.ClientGrantedScopesDeleteApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGrantedScopesDeleteApi( + request: ClientManagement.ClientGrantedScopesDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/client/granted_scopes/delete/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [{ name: "subject", value: request.subject, schema: s.string() }], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: resultSchema }, + errorFactory: ClientManagement.ClientGrantedScopesDeleteApiError, + }, + options, + ); + } + + /** + * Delete Granted Scopes (by Subject) + * + * @remarks + * Delete the set of scopes that an end-user has granted to a client application. In this variant, + * the subject is provided in the path. + * + * @throws {@link ClientManagement.ClientGrantedScopesDeleteBySubjectApiError} when the API + * answers with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGrantedScopesDeleteBySubjectApi( + request: ClientManagement.ClientGrantedScopesDeleteBySubjectApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default( + "/api/{serviceId}/client/granted_scopes/delete/{clientId}/{subject}", + ), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: resultSchema }, + errorFactory: ClientManagement.ClientGrantedScopesDeleteBySubjectApiError, + }, + options, + ); + } + + /** + * Get Granted Scopes + * + * @remarks + * Get the set of scopes that a user has granted to a client application. + * + * @returns Successfully retrieved granted scopes + * + * @throws {@link ClientManagement.ClientGrantedScopesGetApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGrantedScopesGetApi( + request: ClientManagement.ClientGrantedScopesGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/granted_scopes/get/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [{ name: "subject", value: request.subject, schema: s.string() }], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientGrantedScopesGetApiError, + }, + options, + ); + } + + /** + * Get Granted Scopes + * + * @remarks + * Get the set of scopes that a user has granted to a client application. + * + * The subject parameter is required. + * + * @throws {@link ClientManagement.ClientGrantedScopesGetApiPostError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGrantedScopesGetApiPost( + request: ClientManagement.ClientGrantedScopesGetApiPostRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/granted_scopes/get/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "subject", value: request.subject, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientGrantedScopesGetApiPostError, + }, + options, + ); + } + + /** + * Get Granted Scopes (by Subject) + * + * @remarks + * Get the set of scopes that a user has granted to a client application. In this variant, the + * subject is provided in the path. + * + * @returns Successfully retrieved granted scopes + * + * @throws {@link ClientManagement.ClientGrantedScopesGetBySubjectApiError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientGrantedScopesGetBySubjectApi( + request: ClientManagement.ClientGrantedScopesGetBySubjectApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/granted_scopes/get/{clientId}/{subject}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + ], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientAuthorizationDeleteResponseSchema }, + errorFactory: ClientManagement.ClientGrantedScopesGetBySubjectApiError, + }, + options, + ); + } + + /** + * Search Clients + * + * @remarks + * Search the clients in the requested service that match the condition provided in the `q` query + * parameter. Results are returned via cursor based iteration. To iterate through all clients + * without a search condition, use the `/api/{serviceId}/client` API instead. + * + * @throws {@link ClientManagement.ClientSearchApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientSearchApi( + request: ClientManagement.ClientSearchApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/search"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.int() }], + query: [ + { name: "q", value: request.q, schema: s.optional(s.string()) }, + { name: "cursor", value: request.cursor, schema: s.optional(s.string()) }, + { name: "limit", value: request.limit, schema: s.optional(s.int()) }, + { name: "includeTotalCount", value: request.includeTotalCount, schema: s.optional(s.boolean()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientSearchResponseSchema }, + errorFactory: ClientManagement.ClientSearchApiError, + }, + options, + ); + } + + /** + * Rotate Client Secret + * + * @remarks + * Refresh the client secret of a client. A new value of the client secret will be generated by + * the Authlete server. + * + * If you want to specify a new value, use `/api/client/secret/update` API. + * + * @throws {@link ClientManagement.ClientSecretRefreshApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientSecretRefreshApi( + request: ClientManagement.ClientSecretRefreshApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/client/secret/refresh/{clientIdentifier}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.string() }, + ], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: clientSecretRefreshResponseSchema }, + errorFactory: ClientManagement.ClientSecretRefreshApiError, + }, + options, + ); + } + + /** + * Update Client Secret + * + * @remarks + * Update the client secret of a client. + * + * If you want to have the Authlete server generate a new value of the client secret, use + * `/api/client/secret/refresh` API. + * + * @throws {@link ClientManagement.ClientSecretUpdateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientSecretUpdateApi( + request: ClientManagement.ClientSecretUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/secret/update/{clientIdentifier}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "clientSecret", value: request.clientSecret, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: clientSecretRefreshResponseSchema }, + errorFactory: ClientManagement.ClientSecretUpdateApiError, + }, + options, + ); + } + + /** + * Update Client + * + * @remarks + * Update a client. + * + * @throws {@link ClientManagement.ClientUpdateApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientUpdateApi( + request: ClientManagement.ClientUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/update/{clientId}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "clientId", value: request.clientIdTemplate, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "number", value: request.number, schema: s.optional(s.int()) }, + { name: "serviceNumber", value: request.serviceNumber, schema: s.optional(s.int()) }, + { name: "clientName", value: request.clientName, schema: s.optional(s.string()) }, + { + name: "clientNames", + value: request.clientNames, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "description", value: request.description, schema: s.optional(s.string()) }, + { + name: "descriptions", + value: request.descriptions, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "clientId", value: request.clientId, schema: s.optional(s.int()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientIdAlias", value: request.clientIdAlias, schema: s.optional(s.string()) }, + { + name: "clientIdAliasEnabled", + value: request.clientIdAliasEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "clientType", + value: request.clientType, + schema: s.optional(s.lazy(() => clientTypeSchema)), + }, + { + name: "applicationType", + value: request.applicationType, + schema: s.optional(s.lazy(() => applicationTypeSchema)), + }, + { name: "logoUri", value: request.logoUri, schema: s.optional(s.string()) }, + { + name: "logoUris", + value: request.logoUris, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "contacts", value: request.contacts, schema: s.optional(s.array(s.string())) }, + { + name: "tlsClientCertificateBoundAccessTokens", + value: request.tlsClientCertificateBoundAccessTokens, + schema: s.optional(s.boolean()), + }, + { + name: "dynamicallyRegistered", + value: request.dynamicallyRegistered, + schema: s.optional(s.boolean()), + }, + { name: "softwareId", value: request.softwareId, schema: s.optional(s.string()) }, + { name: "developer", value: request.developer, schema: s.optional(s.string()) }, + { name: "softwareVersion", value: request.softwareVersion, schema: s.optional(s.string()) }, + { + name: "registrationAccessTokenHash", + value: request.registrationAccessTokenHash, + schema: s.optional(s.string()), + }, + { name: "createdAt", value: request.createdAt, schema: s.optional(s.int()) }, + { name: "modifiedAt", value: request.modifiedAt, schema: s.optional(s.int()) }, + { + name: "grantTypes", + value: request.grantTypes, + schema: s.optional(s.array(s.lazy(() => grantTypeSchema))), + }, + { + name: "responseTypes", + value: request.responseTypes, + schema: s.optional(s.array(s.lazy(() => responseTypeSchema))), + }, + { name: "redirectUris", value: request.redirectUris, schema: s.optional(s.array(s.string())) }, + { + name: "authorizationSignAlg", + value: request.authorizationSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "authorizationEncryptionAlg", + value: request.authorizationEncryptionAlg, + schema: s.optionalNullable(s.lazy(() => jweAlgSchema)), + }, + { + name: "authorizationEncryptionEnc", + value: request.authorizationEncryptionEnc, + schema: s.optionalNullable(s.lazy(() => jweEncSchema)), + }, + { + name: "tokenAuthMethod", + value: request.tokenAuthMethod, + schema: s.optional(s.lazy(() => clientAuthMethodSchema)), + }, + { + name: "tokenAuthSignAlg", + value: request.tokenAuthSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "selfSignedCertificateKeyId", + value: request.selfSignedCertificateKeyId, + schema: s.optional(s.string()), + }, + { + name: "tlsClientAuthSubjectDn", + value: request.tlsClientAuthSubjectDn, + schema: s.optional(s.string()), + }, + { + name: "tlsClientAuthSanDns", + value: request.tlsClientAuthSanDns, + schema: s.optional(s.string()), + }, + { + name: "tlsClientAuthSanUri", + value: request.tlsClientAuthSanUri, + schema: s.optional(s.string()), + }, + { name: "tlsClientAuthSanIp", value: request.tlsClientAuthSanIp, schema: s.optional(s.string()) }, + { + name: "tlsClientAuthSanEmail", + value: request.tlsClientAuthSanEmail, + schema: s.optional(s.string()), + }, + { name: "parRequired", value: request.parRequired, schema: s.optional(s.boolean()) }, + { + name: "requestObjectRequired", + value: request.requestObjectRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestSignAlg", + value: request.requestSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "requestEncryptionAlg", + value: request.requestEncryptionAlg, + schema: s.optionalNullable(s.lazy(() => jweAlgSchema)), + }, + { + name: "requestEncryptionEnc", + value: request.requestEncryptionEnc, + schema: s.optionalNullable(s.lazy(() => jweEncSchema)), + }, + { name: "requestUris", value: request.requestUris, schema: s.optional(s.array(s.string())) }, + { name: "defaultMaxAge", value: request.defaultMaxAge, schema: s.optional(s.int()) }, + { name: "defaultAcrs", value: request.defaultAcrs, schema: s.optional(s.array(s.string())) }, + { + name: "idTokenSignAlg", + value: request.idTokenSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "idTokenEncryptionAlg", + value: request.idTokenEncryptionAlg, + schema: s.optionalNullable(s.lazy(() => jweAlgSchema)), + }, + { + name: "idTokenEncryptionEnc", + value: request.idTokenEncryptionEnc, + schema: s.optionalNullable(s.lazy(() => jweEncSchema)), + }, + { name: "authTimeRequired", value: request.authTimeRequired, schema: s.optional(s.boolean()) }, + { + name: "subjectType", + value: request.subjectType, + schema: s.optional(s.lazy(() => subjectTypeSchema)), + }, + { + name: "sectorIdentifierUri", + value: request.sectorIdentifierUri, + schema: s.optional(s.string()), + }, + { + name: "derivedSectorIdentifier", + value: request.derivedSectorIdentifier, + schema: s.optional(s.string()), + }, + { name: "jwksUri", value: request.jwksUri, schema: s.optional(s.string()) }, + { name: "jwks", value: request.jwks, schema: s.optional(s.string()) }, + { + name: "userInfoSignAlg", + value: request.userInfoSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "userInfoEncryptionAlg", + value: request.userInfoEncryptionAlg, + schema: s.optionalNullable(s.lazy(() => jweAlgSchema)), + }, + { + name: "userInfoEncryptionEnc", + value: request.userInfoEncryptionEnc, + schema: s.optionalNullable(s.lazy(() => jweEncSchema)), + }, + { name: "loginUri", value: request.loginUri, schema: s.optional(s.string()) }, + { name: "tosUri", value: request.tosUri, schema: s.optional(s.string()) }, + { + name: "tosUris", + value: request.tosUris, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "policyUri", value: request.policyUri, schema: s.optional(s.string()) }, + { + name: "policyUris", + value: request.policyUris, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "clientUri", value: request.clientUri, schema: s.optional(s.string()) }, + { + name: "clientUris", + value: request.clientUris, + schema: s.optional(s.array(s.lazy(() => taggedValueSchema))), + }, + { name: "bcDeliveryMode", value: request.bcDeliveryMode, schema: s.optional(s.string()) }, + { + name: "bcNotificationEndpoint", + value: request.bcNotificationEndpoint, + schema: s.optional(s.string()), + }, + { + name: "bcRequestSignAlg", + value: request.bcRequestSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "bcUserCodeRequired", + value: request.bcUserCodeRequired, + schema: s.optional(s.boolean()), + }, + { + name: "backchannelLogoutUri", + value: request.backchannelLogoutUri, + schema: s.optional(s.string()), + }, + { + name: "backchannelLogoutSessionRequired", + value: request.backchannelLogoutSessionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "attributes", + value: request.attributes, + schema: s.optional(s.array(s.lazy(() => pairSchema))), + }, + { + name: "extension", + value: request.extension, + schema: s.optional(s.lazy(() => clientExtensionSchema)), + }, + { + name: "authorizationDetailsTypes", + value: request.authorizationDetailsTypes, + schema: s.optional(s.array(s.string())), + }, + { name: "customMetadata", value: request.customMetadata, schema: s.optional(s.string()) }, + { + name: "frontChannelRequestObjectEncryptionRequired", + value: request.frontChannelRequestObjectEncryptionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionAlgMatchRequired", + value: request.requestObjectEncryptionAlgMatchRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionEncMatchRequired", + value: request.requestObjectEncryptionEncMatchRequired, + schema: s.optional(s.boolean()), + }, + { name: "digestAlgorithm", value: request.digestAlgorithm, schema: s.optional(s.string()) }, + { + name: "singleAccessTokenPerSubject", + value: request.singleAccessTokenPerSubject, + schema: s.optional(s.boolean()), + }, + { name: "pkceRequired", value: request.pkceRequired, schema: s.optional(s.boolean()) }, + { name: "pkceS256Required", value: request.pkceS256Required, schema: s.optional(s.boolean()) }, + { name: "dpopRequired", value: request.dpopRequired, schema: s.optional(s.boolean()) }, + { + name: "automaticallyRegistered", + value: request.automaticallyRegistered, + schema: s.optional(s.boolean()), + }, + { + name: "explicitlyRegistered", + value: request.explicitlyRegistered, + schema: s.optional(s.boolean()), + }, + { name: "rsRequestSigned", value: request.rsRequestSigned, schema: s.optional(s.boolean()) }, + { + name: "rsSignedRequestKeyId", + value: request.rsSignedRequestKeyId, + schema: s.optional(s.string()), + }, + { + name: "clientRegistrationTypes", + value: request.clientRegistrationTypes, + schema: s.optional(s.array(s.lazy(() => clientRegistrationTypeSchema))), + }, + { name: "organizationName", value: request.organizationName, schema: s.optional(s.string()) }, + { name: "signedJwksUri", value: request.signedJwksUri, schema: s.optional(s.string()) }, + { name: "entityId", value: request.entityId, schema: s.optional(s.string()) }, + { name: "trustAnchorId", value: request.trustAnchorId, schema: s.optional(s.string()) }, + { name: "trustChain", value: request.trustChain, schema: s.optional(s.array(s.string())) }, + { name: "trustChainExpiresAt", value: request.trustChainExpiresAt, schema: s.optional(s.int()) }, + { name: "trustChainUpdatedAt", value: request.trustChainUpdatedAt, schema: s.optional(s.int()) }, + { name: "locked", value: request.locked, schema: s.optional(s.boolean()) }, + { + name: "credentialOfferEndpoint", + value: request.credentialOfferEndpoint, + schema: s.optional(s.string()), + }, + { + name: "fapiModes", + value: request.fapiModes, + schema: s.optional(s.array(s.lazy(() => fapiModeSchema))), + }, + { + name: "responseModes", + value: request.responseModes, + schema: s.optional(s.array(s.lazy(() => responseModeSchema))), + }, + { + name: "credentialResponseEncryptionRequired", + value: request.credentialResponseEncryptionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "mtlsEndpointAliasesUsed", + value: request.mtlsEndpointAliasesUsed, + schema: s.optional(s.boolean()), + }, + { + name: "inScopeForTokenMigration", + value: request.inScopeForTokenMigration, + schema: s.optional(s.boolean()), + }, + { + name: "metadataDocumentLocation", + value: request.metadataDocumentLocation, + schema: s.optional(s.string()), + }, + { + name: "metadataDocumentExpiresAt", + value: request.metadataDocumentExpiresAt, + schema: s.optional(s.int()), + }, + { + name: "metadataDocumentUpdatedAt", + value: request.metadataDocumentUpdatedAt, + schema: s.optional(s.int()), + }, + { + name: "discoveredByMetadataDocument", + value: request.discoveredByMetadataDocument, + schema: s.optional(s.boolean()), + }, + { + name: "clientSource", + value: request.clientSource, + schema: s.optional(s.lazy(() => clientSourceSchema)), + }, + { name: "spiffeId", value: request.spiffeId, schema: s.optional(s.string()) }, + { + name: "spiffeBundleEndpoint", + value: request.spiffeBundleEndpoint, + schema: s.optional(s.string()), + }, + ], + }, + }, + { + success: { kind: "json", schema: clientSchema }, + errorFactory: ClientManagement.ClientUpdateApiError, + }, + options, + ); + } +} + +export namespace ClientManagement { + export type ClientAuthorizationDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientAuthorizationDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationDeleteApiPostRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientAuthorizationDeleteApiPostError extends ApiError { + declare readonly payload: Declared<"result", Result> | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 429, kind: "result", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationDeleteBySubjectApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientAuthorizationDeleteBySubjectApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationGetListApiRequest = { + /** A service ID. */ + serviceId: string; + /** Unique user ID of an end-user. */ + subject: string; + /** Unique ID of a client developer. */ + developer?: string; + /** Start index of search results (inclusive). The default value is 0. */ + start?: number; + /** End index of search results (exclusive). The default value is 5. */ + end?: number; + }; + + export class ClientAuthorizationGetListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationGetListApiPostRequest = { + /** A service ID. */ + serviceId: string; + /** Unique user ID of an end-user. */ + subject: string; + /** Unique ID of a client developer. */ + developer?: string; + /** Start index of search results (inclusive). */ + start?: number; + /** End index of search results (exclusive). */ + end?: number; + }; + + export class ClientAuthorizationGetListApiPostError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationGetListBySubjectApiRequest = { + /** A service ID. */ + serviceId: string; + /** Unique user ID of an end-user. */ + subject: string; + /** Unique ID of a client developer. */ + developer?: string; + /** Start index of search results (inclusive). The default value is 0. */ + start?: number; + /** End index of search results (exclusive). The default value is 5. */ + end?: number; + }; + + export class ClientAuthorizationGetListBySubjectApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientAuthorizationUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** + * The subject (= unique identifier) of the end-user who has granted authorization to the client + * application. + */ + subject: string; + /** + * An array of new scopes. Optional. If a non-null value is given, the new scopes are set to all + * existing access tokens. If an API call is made using `"Content-Type: + * application/x-www-form-urlencoded"`, scope names listed in this request parameter should be + * delimited by spaces (after form encoding, spaces are converted to `+`). + */ + scopes?: string[]; + }; + + export class ClientAuthorizationUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"error404", undefined> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 404, kind: "error404", decode: { kind: "empty" } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientCreateApiRequest = { + /** A service ID. */ + serviceId: string; + body?: Client; + }; + + export class ClientCreateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientCursorListApiRequest = { + /** The ID of the service to retrieve a list of clients from. */ + serviceId: number; + /** + * This should be set to the value of `nextCursor` from a previous call to this endpoint. Which + * will retrieve the next set of clients available and receive the next available set. If this + * is not provided the list operation will start from the beginning. + */ + cursor?: string; + /** + * The maximum amount of results to return from this request. This value will be clamped between + * 1 and the allowed maximum. The default will be used if no value is provided. + */ + limit?: number; + /** + * Indicates whether the `totalCount` property should be calculated and included in the + * response, otherwise it will be unset. For some large datasets this can be expensive and is + * not always required to calculate. + */ + includeTotalCount?: boolean; + }; + + export class ClientCursorListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** The client ID. */ + clientId: string; + }; + + export class ClientDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"error404", undefined> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 404, kind: "error404", decode: { kind: "empty" } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientExtensionRequestablesScopesDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + }; + + export class ClientExtensionRequestablesScopesDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientExtensionRequestablesScopesGetApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + }; + + export class ClientExtensionRequestablesScopesGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientExtensionRequestablesScopesUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + body: ClientExtensionRequestableScopesUpdateRequest; + }; + + export class ClientExtensionRequestablesScopesUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientExtensionRequestablesScopesUpdateApiPostRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + body: ClientExtensionRequestableScopesUpdateRequest; + }; + + export class ClientExtensionRequestablesScopesUpdateApiPostError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientFlagUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientIdentifier: string; + /** The flag value to be set */ + clientLocked: boolean; + }; + + export class ClientFlagUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGetApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + }; + + export class ClientGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Declared<"result6", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 404, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result6", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGetListApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The developer of client applications. The default value is null. If this parameter is not set + * to `null`, client application of the specified developer are returned. Otherwise, all client + * applications that belong to the service are returned. + */ + developer?: string; + /** + * Start index (inclusive) of the result set. The default value is 0. Must not be a negative + * number. + */ + start?: number; + /** + * End index (exclusive) of the result set. The default value is 5. Must not be a negative + * number. + */ + end?: number; + }; + + export class ClientGetListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGrantedScopesDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientGrantedScopesDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGrantedScopesDeleteBySubjectApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientGrantedScopesDeleteBySubjectApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGrantedScopesGetApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientGrantedScopesGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGrantedScopesGetApiPostRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientGrantedScopesGetApiPostError extends ApiError { + declare readonly payload: Declared<"result", Result> | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 429, kind: "result", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientGrantedScopesGetBySubjectApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientId: string; + /** Unique user ID of an end-user. */ + subject: string; + }; + + export class ClientGrantedScopesGetBySubjectApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientSearchApiRequest = { + /** The ID of the service to retrieve a list of clients from. */ + serviceId: number; + /** + * The search query. See the description of this API for the supported syntax and fields. + * + * This parameter is required unless `cursor` is provided. When `cursor` is provided, this + * parameter is ignored and the search query stored in the cursor is used instead. + */ + q?: string; + /** + * This should be set to the value of `nextCursor` from a previous call to this endpoint, which + * will retrieve the next set of clients matching the original search query. If this is not + * provided the search will start from the beginning. + */ + cursor?: string; + /** + * The maximum amount of results to return from this request. This value will be clamped between + * 1 and the allowed maximum. The default will be used if no value is provided. + */ + limit?: number; + /** + * Indicates whether the `totalCount` property should be calculated and included in the + * response, otherwise it will be unset. + * + * **Warning:** Calculating the total count requires an additional query and reduces the + * performance and throughput of this API. Only set this to `true` when the total count is + * actually required, rather than on every request. + */ + includeTotalCount?: boolean; + }; + + export class ClientSearchApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientSecretRefreshApiRequest = { + /** A service ID. */ + serviceId: string; + /** The client ID or the client ID alias of a client. */ + clientIdentifier: string; + }; + + export class ClientSecretRefreshApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientSecretUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** The client ID or the client ID alias of a client. */ + clientIdentifier: string; + /** + * The new value of the client secret. Valid characters for a client secret are `A-Z`, `a-z`, + * `0-9`, `-`, and `_`. The maximum length of a client secret is 86. + */ + clientSecret: string; + }; + + export class ClientSecretUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** A client ID. */ + clientIdTemplate: string; + /** The sequential number of the client. The value of this property is assigned by Authlete. */ + number?: number; + /** + * The sequential number of the service of the client application. The value of this property is + * assigned by Authlete. + */ + serviceNumber?: number; + /** + * The name of the client application. This property corresponds to `client_name` in [OpenID + * Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientName?: string; + /** + * Client names with language tags. If the client application has different names for different + * languages, this property can be used to register the names. + */ + clientNames?: TaggedValue[]; + /** The description about the client application. */ + description?: string; + /** + * Descriptions about the client application with language tags. If the client application has + * different descriptions for different languages, this property can be used to register the + * descriptions. + */ + descriptions?: TaggedValue[]; + /** + * The client identifier used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + clientId?: number; + /** + * The client secret. A random 512-bit value encoded by base64url (86 letters). The value of + * this property is assigned by Authlete. + */ + clientSecret?: string; + /** + * The value of the client's `client_id` property used in OAuth and OpenID Connect calls. By + * default, this is a string version of the `clientId` property. + */ + clientIdAlias?: string; + /** Deprecated. Always set to `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The client type, either `CONFIDENTIAL` or `PUBLIC`. See [RFC 6749, 2.1. Client + * Types](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1) for details. + */ + clientType?: ClientType; + /** + * The application type. The value of this property affects the validation steps for a redirect + * URI. See the description about `redirectUris` property for more details. + */ + applicationType?: ApplicationType; + /** + * The URL pointing to the logo image of the client application. + * + * This property corresponds to `logo_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + logoUri?: string; + /** + * Logo image URLs with language tags. If the client application has different logo images for + * different languages, this property can be used to register URLs of the images. + */ + logoUris?: TaggedValue[]; + /** + * An array of email addresses of people responsible for the client application. + * + * This property corresponds to contacts in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + contacts?: string[]; + /** The flag to indicate whether this client use TLS client certificate bound access tokens. */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The flag to indicate whether this client has been registered dynamically. For more details, + * see [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + dynamicallyRegistered?: boolean; + /** + * The unique identifier string assigned by the client developer or software publisher used by + * registration endpoints to identify the client software to be dynamically registered. + * + * This property corresponds to the `software_id metadata` defined in [2. Client + * Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + softwareId?: string; + /** The unique identifier of the developer who created this client application. */ + developer?: string; + /** + * The version identifier string for the client software identified by the software ID. + * + * This property corresponds to the software_version metadata defined in [2. Client + * Metadata](https://datatracker.ietf.org/doc/html/rfc7591#section-2) of [RFC + * 7591](https://datatracker.ietf.org/doc/html/rfc7591). + */ + softwareVersion?: string; + /** The hash of the registration access token for this client. */ + registrationAccessTokenHash?: string; + /** + * The time at which this client was created. The value is represented as milliseconds since the + * UNIX epoch (1970-01-01). + */ + createdAt?: number; + /** + * The time at which this client was last modified. The value is represented as milliseconds + * since the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** + * A string array of grant types which the client application declares that it will restrict + * itself to using. This property corresponds to `grant_types` in [OpenID Connect Dynamic Client + * Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + grantTypes?: GrantType[]; + /** + * A string array of response types which the client application declares that it will restrict + * itself to using. This property corresponds to `response_types` in [OpenID Connect Dynamic + * Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + responseTypes?: ResponseType[]; + /** + * Redirect URIs that the client application uses to receive a response from the authorization + * endpoint. Requirements for a redirect URI are as follows. + */ + redirectUris?: string[]; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + authorizationSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server + * for transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + authorizationEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. + * For instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by + * server when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by + * the client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + authorizationEncryptionEnc?: JweEnc | null; + /** + * The client authentication method that the client application declares that it uses at the + * token endpoint. This property corresponds to `token_endpoint_auth_method` in [OpenID Connect + * Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + tokenAuthMethod?: ClientAuthMethod; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + tokenAuthSignAlg?: JwsAlg | null; + /** The key ID of a JWK containing a self-signed certificate of this client. */ + selfSignedCertificateKeyId?: string; + /** + * The string representation of the expected subject distinguished name of the certificate this + * client will use in mutual TLS authentication. + * + * See `tls_client_auth_subject_dn` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic + * Client Registration" for details. + */ + tlsClientAuthSubjectDn?: string; + /** + * The string representation of the expected DNS subject alternative name of the certificate + * this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_dns` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanDns?: string; + /** + * The string representation of the expected URI subject alternative name of the certificate + * this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_uri` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanUri?: string; + /** + * The string representation of the expected IP address subject alternative name of the + * certificate this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_ip` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic Client + * Registration" for details. + */ + tlsClientAuthSanIp?: string; + /** + * The string representation of the expected email address subject alternative name of the + * certificate this client will use in mutual TLS authentication. + * + * See `tls_client_auth_san_email` in "Mutual TLS Profiles for OAuth Clients, 2.3. Dynamic + * Client Registration" for details. + */ + tlsClientAuthSanEmail?: string; + /** + * The flag to indicate whether this client is required to use the pushed authorization request + * endpoint. This property corresponds to the `require_pushed_authorization_requests` client + * metadata defined in "OAuth 2.0 Pushed Authorization Requests". + */ + parRequired?: boolean; + /** + * The flag to indicate whether authorization requests from this client are always required to + * utilize a request object by using either `request` or `request_uri` request parameter. + * + * If this flag is set to `true` and the service's `traditionalRequestObjectProcessingApplied` + * is set to `false`, authorization requests from this client are processed as if + * `require_signed_request_object` client metadata of this client is `true`. The metadata is + * defined in "JAR (JWT Secured Authorization Request)". + */ + requestObjectRequired?: boolean; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + requestSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server + * for transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + requestEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. + * For instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by + * server when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by + * the client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + requestEncryptionEnc?: JweEnc | null; + /** + * An array of URLs each of which points to a request object. + * + * Authlete requires that URLs used as values for `request_uri` request parameter be + * pre-registered. This property is used for the pre-registration. See [OpenID Connect Core 1.0, + * 6.2. Passing a Request Object by + * Reference](https://openid.net/specs/openid-connect-core-1_0.html#RequestUriParameter) for + * details. + */ + requestUris?: string[]; + /** + * The default maximum authentication age in seconds. This value is used when an authorization + * request from the client application does not have `max_age` request parameter. + * + * This property corresponds to `default_max_age` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + defaultMaxAge?: number; + /** + * The default ACRs (Authentication Context Class References). This value is used when an + * authorization request from the client application has neither `acr_values` request parameter + * nor `acr` claim in claims request parameter. + */ + defaultAcrs?: string[]; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + idTokenSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server + * for transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + idTokenEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. + * For instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by + * server when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by + * the client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + idTokenEncryptionEnc?: JweEnc | null; + /** + * The flag to indicate whether this client requires `auth_time` claim to be embedded in the ID + * token. + * + * This property corresponds to `require_auth_time` in [OpenID Connect Dynamic Client + * Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + authTimeRequired?: boolean; + /** + * The subject type that the client application requests. Details about the subject type are + * described in [OpenID Connect Core 1.0, 8. Subjct Identifier + * Types](https://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes). + * + * This property corresponds to `subject_type` in [OpenID Connect Dynamic Client Registration + * 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + subjectType?: SubjectType; + /** + * The value of the sector identifier URI. This represents the `sector_identifier_uri` client + * metadata which is defined in [OpenID Connect Dynamic Client Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata) + */ + sectorIdentifierUri?: string; + /** + * The sector identifier host component as derived from either the `sector_identifier_uri` or + * the registered redirect URI. If no `sector_identifier_uri` is registered and multiple + * redirect URIs are also registered, the value of this property is `null`. + */ + derivedSectorIdentifier?: string; + /** + * The URL pointing to the JWK Set of the client application. The content pointed to by the URL + * is JSON which complies with the format described in [JSON Web Key (JWK), 5. JWK Set + * Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The JWK Set must not + * include private keys of the client application. + */ + jwksUri?: string; + /** + * The content of the JWK Set of the client application. The format is described in [JSON Web + * Key (JWK), 5. JWK Set Format](https://datatracker.ietf.org/doc/html/rfc7517#section-5). The + * JWK Set must not include private keys of the client application. + */ + jwks?: string; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + userInfoSignAlg?: JwsAlg | null; + /** + * this is the 'alg' header value for encrypted JWT tokens. Depending upon the context, this + * refers to key transport scheme to be used by the client and by the server. For instance: + * - as `authorizationEncryptionAlg` value, it refers to the encoding algorithm used by server + * for transporting they keys on JARM objects + * - as `requestEncryptionAlg` value, it refers to the expected key transport encoding algorithm + * that server expect from client when encrypting a Request Object + * - as `idTokenEncryptionAlg` value, it refers to the algorithm used by the server to key + * transport of id_tokens + * + * **Please note that some of the algorithms are more secure than others, some are not supported + * very well cross platforms and some (like RSA1_5) is known to be weak**. + */ + userInfoEncryptionAlg?: JweAlg | null; + /** + * This is the encryption algorithm to be used when encrypting a JWT on client or server side. + * Depending upon the context, this refers to encryption done by the client or by the server. + * For instance: + * - as `authorizationEncryptionEnc` value, it refers to the encryption algorithm used by + * server when creating a JARM response + * - as `requestEncryptionEnc` value, it refers to the expected encryption algorithm used by + * the client when encrypting a Request Object + * - as `idTokenEncryptionEnc` value, it refers to the algorithm used by the server to encrypt + * id_tokens + */ + userInfoEncryptionEnc?: JweEnc | null; + /** + * The URL which a third party can use to initiate a login by the client application. + * + * This property corresponds to `initiate_login_uri` in [OpenID Connect Dynamic Client + * Registration 1.0, 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + loginUri?: string; + /** + * The URL pointing to the "Terms Of Service" page. + * + * This property corresponds to `tos_uri` in [OpenID Connect Dynamic Client Registration 1.0, 2. + * Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + tosUri?: string; + /** + * URLs of "Terms Of Service" pages with language tags. + * + * If the client application has different "Terms Of Service" pages for different languages, + * this property can be used to register the URLs. + */ + tosUris?: TaggedValue[]; + /** + * The URL pointing to the page which describes the policy as to how end-user's profile data is + * used. + * + * This property corresponds to `policy_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + policyUri?: string; + /** + * URLs of policy pages with language tags. If the client application has different policy pages + * for different languages, this property can be used to register the URLs. + */ + policyUris?: TaggedValue[]; + /** + * The URL pointing to the home page of the client application. + * + * This property corresponds to `client_uri` in [OpenID Connect Dynamic Client Registration 1.0, + * 2. Client + * Metadata](https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata). + */ + clientUri?: string; + /** + * Home page URLs with language tags. If the client application has different home pages for + * different languages, this property can be used to register the URLs. + */ + clientUris?: TaggedValue[]; + /** + * The backchannel token delivery mode. + * + * This property corresponds to the `backchannel_token_delivery_mode` metadata. The backchannel + * token delivery mode is defined in the specification of "CIBA (Client Initiated Backchannel + * Authentication)". + */ + bcDeliveryMode?: string; + /** + * The backchannel client notification endpoint. + * + * This property corresponds to the `backchannel_client_notification_endpoint` metadata. The + * backchannel token delivery mode is defined in the specification of "CIBA (Client Initiated + * Backchannel Authentication)". + */ + bcNotificationEndpoint?: string; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + bcRequestSignAlg?: JwsAlg | null; + /** + * The boolean flag to indicate whether a user code is required when this client makes a + * backchannel authentication request. + * + * This property corresponds to the `backchannel_user_code_parameter` metadata. + */ + bcUserCodeRequired?: boolean; + /** + * The backchannel logout URI for this client. Used by the service to deliver logout tokens when + * OpenID Connect Back-Channel Logout 1.0 is triggered. + */ + backchannelLogoutUri?: string; + /** + * The flag indicating whether the client requires that a `sid` (session ID) claim be included + * in the logout token sent to `backchannelLogoutUri`. + */ + backchannelLogoutSessionRequired?: boolean; + /** The attributes of this client. */ + attributes?: Pair[]; + extension?: ClientExtension; + /** + * The authorization details types that this client may use as values of the `type` field in + * `authorization_details`. + * + * This property corresponds to the `authorization_details_types` metadata. See [OAuth 2.0 Rich + * Authorization Requests (RAR)](https://datatracker.ietf.org/doc/draft-ietf-oauth-rar/) for + * details. + * + * Note that the property name was renamed from authorizationDataTypes to + * authorizationDetailsTypes to align with the change made by the 5th draft of the RAR + * specification. + */ + authorizationDetailsTypes?: string[]; + /** The custom client metadata in JSON format. */ + customMetadata?: string; + /** + * The flag indicating whether encryption of request object is required when the request object + * is passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE enc of encrypted request object must match the + * `request_object_encryption_enc` client metadata. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * The digest algorithm that this client requests the server to use when it computes digest + * values of [external + * attachments](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#name-external-attachments), + * which may be referenced from within ID tokens or userinfo responses (or any place that can + * have the `verified_claims` claim). Possible values are listed in the [Hash Algorithm + * Registry](https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg) + * of IANA (Internet Assigned Numbers Authority), but the server does not necessarily support + * all the values there. When this property is omitted, `sha-256` is used as the default + * algorithm. This property corresponds to the `digest_algorithm` client metadata which was + * defined by the third implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html). + */ + digestAlgorithm?: string; + /** + * If `Enabled` is selected, an attempt to issue a new access token invalidates existing access + * tokens that are associated with the same combination of subject and client. + * + * Note that, however, attempts by Client Credentials Flow do not invalidate existing access + * tokens because access tokens issued by Client Credentials Flow are not associated with any + * end-user's subject. + * + * Even if `Disabled` is selected here, single access token per subject is effective if + * `singleAccessTokenPerSubject` of the `Service` this client belongs to is Enabled. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** If the DPoP is required for this client */ + dpopRequired?: boolean; + /** + * The flag indicating whether this client was registered by the "automatic" client registration + * of OIDC Federation. + */ + automaticallyRegistered?: boolean; + /** + * The flag indicating whether this client was registered by the "explicit" client registration + * of OIDC Federation. + */ + explicitlyRegistered?: boolean; + /** The flag indicating whether this service signs responses from the resource server. */ + rsRequestSigned?: boolean; + /** + * The key ID of a JWK containing the public key used by this client to sign requests to the + * resource server. + */ + rsSignedRequestKeyId?: string; + /** The client registration types that the client has declared it may use. */ + clientRegistrationTypes?: ClientRegistrationType[]; + /** + * The human-readable name representing the organization that manages this client. This property + * corresponds to the organization_name client metadata that is defined in OpenID Connect + * Federation 1.0. + */ + organizationName?: string; + /** + * The URI of the endpoint that returns this client's JWK Set document in the JWT format. This + * property corresponds to the `signed_jwks_uri` client metadata defined in OpenID Connect + * Federation 1.0. + */ + signedJwksUri?: string; + /** the entity ID of this client. */ + entityId?: string; + /** + * The entity ID of the trust anchor of the trust chain that was used when this client was + * registered or updated by the mechanism defined in OpenID Connect Federation 1.0 + */ + trustAnchorId?: string; + /** + * The trust chain that was used when this client was registered or updated by the mechanism + * defined in OpenID Connect Federation 1.0 + */ + trustChain?: string[]; + /** + * the expiration time of the trust chain that was used when this client was registered or + * updated by the mechanism defined in OpenID Connect Federation 1.0. The value is represented + * as milliseconds elapsed since the Unix epoch (1970-01-01). + */ + trustChainExpiresAt?: number; + /** + * the time at which the trust chain was updated by the mechanism defined in OpenID Connect + * Federation 1.0 + */ + trustChainUpdatedAt?: number; + /** The flag which indicates whether this client is locked. */ + locked?: boolean; + /** + * The URL of the credential offer endpoint at which this client (wallet) receives a credential + * offer from the credential issuer. + */ + credentialOfferEndpoint?: string; + /** The FAPI modes for this client. */ + fapiModes?: FapiMode[]; + /** The response modes that this client may use. */ + responseModes?: ResponseMode[]; + /** True if credential responses to this client must be always encrypted. */ + credentialResponseEncryptionRequired?: boolean; + /** + * The flag indicating whether the client intends to prefer mutual TLS endpoints over non-MTLS + * endpoints. + * + * This property corresponds to the `use_mtls_endpoint_aliases` client metadata that is defined + * in [FAPI 2.0 Security Profile, 8.1.1. + * use_mtls_endpoint_aliases](https://openid.bitbucket.io/fapi/fapi-2_0-security-profile.html#section-8.1.1). + */ + mtlsEndpointAliasesUsed?: boolean; + /** The flag indicating whether this client is in scope for token migration operations. */ + inScopeForTokenMigration?: boolean; + /** Location of the Client ID Metadata Document that was used for this client. */ + metadataDocumentLocation?: string; + /** Expiration time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentExpiresAt?: number; + /** Last-updated time of the metadata document (UNIX time in milliseconds). */ + metadataDocumentUpdatedAt?: number; + /** Indicates whether this client was discovered via a Client ID Metadata Document. */ + discoveredByMetadataDocument?: boolean; + /** Source of this client record. */ + clientSource?: ClientSource; + /** + * The SPIFFE ID of the client. Used for SPIFFE-based client authentication (`SPIFFE_JWT`). + * Corresponds to the `spiffe_id` client metadata parameter. + */ + spiffeId?: string; + /** + * The endpoint URL of the SPIFFE bundle for this client. Used to fetch the SPIFFE trust bundle + * for validating JWT-SVIDs. Corresponds to the `spiffe_bundle_endpoint` client metadata + * parameter. + */ + spiffeBundleEndpoint?: string; + }; + + export class ClientUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"error404", undefined> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 404, kind: "error404", decode: { kind: "empty" } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/device-flow.ts b/plugins/authlete/sdk/typescript/src/resources/device-flow.ts new file mode 100644 index 00000000..af3cbdcd --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/device-flow.ts @@ -0,0 +1,425 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { cimdOptionsSchema, type CimdOptions } from "../models/cimd-options.js"; +import { + deviceAuthorizationResponseSchema, + type DeviceAuthorizationResponse, +} from "../models/device-authorization-response.js"; +import { + deviceCompleteResponseSchema, + type DeviceCompleteResponse, +} from "../models/device-complete-response.js"; +import { + deviceVerificationResponseSchema, + type DeviceVerificationResponse, +} from "../models/device-verification-response.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { result1Schema, type Result1 } from "../models/result1.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Device Flow + */ +export class DeviceFlow { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Device Authorization Request + * + * @remarks + * This API parses request parameters of a [device authorization + * request](https://datatracker.ietf.org/doc/html/rfc8628#section-3.1) and returns necessary data + * for the authorization server implementation to process the device authorization request + * further. + * + * @throws {@link DeviceFlow.DeviceAuthorizationApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + deviceAuthorizationApi( + request: DeviceFlow.DeviceAuthorizationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/device/authorization"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.optional(s.string()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { + name: "clientCertificatePath", + value: request.clientCertificatePath, + schema: s.optional(s.array(s.string())), + }, + { + name: "oauthClientAttestation", + value: request.oauthClientAttestation, + schema: s.optional(s.string()), + }, + { + name: "oauthClientAttestationPop", + value: request.oauthClientAttestationPop, + schema: s.optional(s.string()), + }, + { + name: "cimdOptions", + value: request.cimdOptions, + schema: s.optional(s.lazy(() => cimdOptionsSchema)), + }, + ], + }, + }, + { + success: { kind: "json", schema: deviceAuthorizationResponseSchema }, + errorFactory: DeviceFlow.DeviceAuthorizationApiError, + }, + options, + ); + } + + /** + * Complete Device Authorization + * + * @remarks + * This API returns information about what action the authorization server should take after it + * receives the result of end-user's decision about whether the end-user has approved or rejected + * a client application's request. + * + * @throws {@link DeviceFlow.DeviceCompleteApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + deviceCompleteApi( + request: DeviceFlow.DeviceCompleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/device/complete"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "userCode", value: request.userCode, schema: s.string() }, + { name: "result", value: request.result, schema: result1Schema }, + { name: "subject", value: request.subject, schema: s.string() }, + { name: "sub", value: request.sub, schema: s.optional(s.string()) }, + { name: "authTime", value: request.authTime, schema: s.optional(s.int()) }, + { name: "acr", value: request.acr, schema: s.optional(s.string()) }, + { name: "claims", value: request.claims, schema: s.optional(s.string()) }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { name: "errorDescription", value: request.errorDescription, schema: s.optional(s.string()) }, + { name: "errorUri", value: request.errorUri, schema: s.optional(s.string()) }, + { name: "idtHeaderParams", value: request.idtHeaderParams, schema: s.optional(s.string()) }, + { + name: "consentedClaims", + value: request.consentedClaims, + schema: s.optional(s.array(s.string())), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { name: "idTokenAudType", value: request.idTokenAudType, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: deviceCompleteResponseSchema }, + errorFactory: DeviceFlow.DeviceCompleteApiError, + }, + options, + ); + } + + /** + * Process Device Verification Request + * + * @remarks + * The API returns information associated with a user code. + * + * @returns Device verification completed successfully + * + * @throws {@link DeviceFlow.DeviceVerificationApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + deviceVerificationApi( + request: DeviceFlow.DeviceVerificationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/device/verification"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "userCode", value: request.userCode, schema: s.string() }], + }, + }, + { + success: { kind: "json", schema: deviceVerificationResponseSchema }, + errorFactory: DeviceFlow.DeviceVerificationApiError, + }, + options, + ); + } +} + +export namespace DeviceFlow { + export type DeviceAuthorizationApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * Parameters of a device authorization request which are the request parameters that the device + * authorization endpoint of the authorization server implementation received from the client + * application. + * + * The value of `parameters` is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from Authorization header of the device authorization request from + * the client application. + * + * If the device authorization endpoint of the authorization server implementation supports + * Basic `Authentication` as a means of client authentication, and the request from the client + * application contained its client ID in `Authorization` header, the value should be extracted + * and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the device authorization request + * from the client application. + * + * If the device authorization endpoint of the authorization server implementation supports + * Basic Authentication as a means of client authentication, and the request from the client + * application contained its client secret in `Authorization` header, the value should be + * extracted and set to this parameter. + */ + clientSecret?: string; + /** + * The client certificate used in the TLS connection between the client application and the + * device authorization endpoint of the authorization server. + */ + clientCertificate?: string; + /** + * The client certificate path presented by the client during client authentication. Each + * element is a string in PEM format. + */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; + }; + + export class DeviceAuthorizationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type DeviceCompleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** A user code. */ + userCode: string; + /** + * The result of the end-user authentication and authorization. One of the following. Details + * are described in the description. + */ + result: Result1; + /** The subject (= unique identifier) of the end-user. */ + subject: string; + /** The value of the sub claim that should be used in the ID token. */ + sub?: string; + /** + * The time at which the end-user was authenticated. Its value is the number of seconds from + * `1970-01-01`. + */ + authTime?: number; + /** + * The reference of the authentication context class which the end-user authentication + * satisfied. + */ + acr?: string; + /** Additional claims which will be embedded in the ID token. */ + claims?: string; + /** The extra properties associated with the access token. */ + properties?: Property[]; + /** + * Scopes to replace the scopes specified in the original device authorization request with. + * When nothing is specified for this parameter, replacement is not performed. + */ + scopes?: string[]; + /** + * The description of the error. If this optional request parameter is given, its value is used + * as the value of the `error_description` property, but it is used only when the result is not + * `AUTHORIZED`. To comply with the specification strictly, the description must not include + * characters outside the set `%x20-21 / %x23-5B / %x5D-7E`. + */ + errorDescription?: string; + /** + * The URI of a document which describes the error in detail. This corresponds to the + * `error_uri` property in the response to the client. + */ + errorUri?: string; + /** JSON that represents additional JWS header parameters for ID tokens. */ + idtHeaderParams?: string; + /** the claims that the user has consented for the client application to know. */ + consentedClaims?: string[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * access token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * refresh token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The type of the `aud` claim of the ID token being issued. Valid values are as follows. + * + * | Value | Description | + * | ----- | ----------- | + * | "array" | The type of the aud claim is always an array of strings. | + * | "string" | The type of the aud claim is always a single string. | + * | null | The type of the aud claim remains the same as before. | + * + * This request parameter takes precedence over the `idTokenAudType` property of the service. + */ + idTokenAudType?: string; + }; + + export class DeviceCompleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type DeviceVerificationApiRequest = { + /** A service ID. */ + serviceId: string; + /** A user code. */ + userCode: string; + }; + + export class DeviceVerificationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/dynamic-client-registration.ts b/plugins/authlete/sdk/typescript/src/resources/dynamic-client-registration.ts new file mode 100644 index 00000000..7f909c36 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/dynamic-client-registration.ts @@ -0,0 +1,296 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + apiClientRegistrationDeleteRequestSchema, + type ApiClientRegistrationDeleteRequest, +} from "../models/api-client-registration-delete-request.js"; +import { + apiClientRegistrationGetRequestSchema, + type ApiClientRegistrationGetRequest, +} from "../models/api-client-registration-get-request.js"; +import { + apiClientRegistrationRequestSchema, + type ApiClientRegistrationRequest, +} from "../models/api-client-registration-request.js"; +import { + apiClientRegistrationUpdateRequestSchema, + type ApiClientRegistrationUpdateRequest, +} from "../models/api-client-registration-update-request.js"; +import { + clientRegistrationResponseSchema, + type ClientRegistrationResponse, +} from "../models/client-registration-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Dynamic Client Registration. + */ +export class DynamicClientRegistration { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Register Client + * + * @remarks + * Register a client. This API is supposed to be used to implement a client registration endpoint + * that complies with [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) (OAuth 2.0 Dynamic + * Client Registration Protocol). + * + * @returns Client registration completed successfully + * + * @throws {@link DynamicClientRegistration.ClientRegistrationApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientRegistrationApi( + request: DynamicClientRegistration.ClientRegistrationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/registration"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: apiClientRegistrationRequestSchema }, + }, + { + success: { kind: "json", schema: clientRegistrationResponseSchema }, + errorFactory: DynamicClientRegistration.ClientRegistrationApiError, + }, + options, + ); + } + + /** + * Delete Client + * + * @remarks + * Delete a dynamically registered client. This API is supposed to be used to implement a client + * registration management endpoint that complies with [RFC + * 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration + * Management). + * + * @throws {@link DynamicClientRegistration.ClientRegistrationDeleteApiError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientRegistrationDeleteApi( + request: DynamicClientRegistration.ClientRegistrationDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/registration/delete"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: apiClientRegistrationDeleteRequestSchema }, + }, + { + success: { kind: "json", schema: clientRegistrationResponseSchema }, + errorFactory: DynamicClientRegistration.ClientRegistrationDeleteApiError, + }, + options, + ); + } + + /** + * Get Client + * + * @remarks + * Get a dynamically registered client. This API is supposed to be used to implement a client + * registration management endpoint that complies with [RFC + * 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration + * Management). + * + * @throws {@link DynamicClientRegistration.ClientRegistrationGetApiError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientRegistrationGetApi( + request: DynamicClientRegistration.ClientRegistrationGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/registration/get"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: apiClientRegistrationGetRequestSchema }, + }, + { + success: { kind: "json", schema: clientRegistrationResponseSchema }, + errorFactory: DynamicClientRegistration.ClientRegistrationGetApiError, + }, + options, + ); + } + + /** + * Update Client + * + * @remarks + * Update a dynamically registered client. This API is supposed to be used to implement a client + * registration management endpoint that complies with [RFC + * 7592](https://datatracker.ietf.org/doc/html/rfc7592) (OAuth 2.0 Dynamic Registration + * Management). + * + * @throws {@link DynamicClientRegistration.ClientRegistrationUpdateApiError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + clientRegistrationUpdateApi( + request: DynamicClientRegistration.ClientRegistrationUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/client/registration/update"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: apiClientRegistrationUpdateRequestSchema }, + }, + { + success: { kind: "json", schema: clientRegistrationResponseSchema }, + errorFactory: DynamicClientRegistration.ClientRegistrationUpdateApiError, + }, + options, + ); + } +} + +export namespace DynamicClientRegistration { + export type ClientRegistrationApiRequest = { + /** A service ID. */ + serviceId: string; + body: ApiClientRegistrationRequest; + }; + + export class ClientRegistrationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientRegistrationDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + body: ApiClientRegistrationDeleteRequest; + }; + + export class ClientRegistrationDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientRegistrationGetApiRequest = { + /** A service ID. */ + serviceId: string; + body: ApiClientRegistrationGetRequest; + }; + + export class ClientRegistrationGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ClientRegistrationUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + body: ApiClientRegistrationUpdateRequest; + }; + + export class ClientRegistrationUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/federation-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/federation-endpoint.ts new file mode 100644 index 00000000..e08d6aff --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/federation-endpoint.ts @@ -0,0 +1,188 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + apiFederationConfigurationRequestSchema, + type ApiFederationConfigurationRequest, +} from "../models/api-federation-configuration-request.js"; +import { + federationConfigurationResponseSchema, + type FederationConfigurationResponse, +} from "../models/federation-configuration-response.js"; +import { + federationRegistrationResponseSchema, + type FederationRegistrationResponse, +} from "../models/federation-registration-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OpenID Federation using Authlete. + */ +export class FederationEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Entity Configuration Request + * + * @remarks + * This API gathers the federation configuration about a service. The authorization server + * implementation should retrieve the value of the `action` response parameter from the API + * response and take the following steps according to the value. + * + * @returns Federation configuration retrieved successfully + * + * @throws {@link FederationEndpoint.FederationConfigurationApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + federationConfigurationApi( + request: FederationEndpoint.FederationConfigurationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/federation/configuration"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: apiFederationConfigurationRequestSchema }, + }, + { + success: { kind: "json", schema: federationConfigurationResponseSchema }, + errorFactory: FederationEndpoint.FederationConfigurationApiError, + }, + options, + ); + } + + /** + * Process Federation Registration Request + * + * @remarks + * The Authlete API is for implementations of the **federation registration endpoint** that + * accepts "explicit client registration". Its details are defined in [OpenID Connect Federation + * 1.0](https://openid.net/specs/openid-connect-federation-1_0.html). The endpoint accepts `POST` + * requests whose `Content-Type` is either of the following. + * 1. `application/entity-statement+jwt`- `application/trust-chain+json` When the `Content-Type` + * of a request is `application/entity-statement+jwt`, the content of the request is the entity + * configuration of a relying party that is to be registered. In this case, the implementation + * of the federation registration endpoint should call Authlete's `/federation/registration` + * API with the entity configuration set to the `entityConfiguration` request parameter. On the + * other hand, when the `Content-Type` of a request is `application/trust-chain+json`, the + * content of the request is a JSON array that contains entity statements in JWT format. The + * sequence of the entity statements composes the trust chain of a relying party that is to be + * registered. In this case, the implementation of the federation registration endpoint should + * call Authlete's `/federation/registration` API with the trust chain set to the `trustChain` + * request parameter. + * + * @throws {@link FederationEndpoint.FederationRegistrationApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + federationRegistrationApi( + request: FederationEndpoint.FederationRegistrationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/federation/registration"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { + name: "entityConfiguration", + value: request.entityConfiguration, + schema: s.optional(s.string()), + }, + { name: "trustChain", value: request.trustChain, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: federationRegistrationResponseSchema }, + errorFactory: FederationEndpoint.FederationRegistrationApiError, + }, + options, + ); + } +} + +export namespace FederationEndpoint { + export type FederationConfigurationApiRequest = { + /** A service ID. */ + serviceId: string; + body: ApiFederationConfigurationRequest; + }; + + export class FederationConfigurationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type FederationRegistrationApiRequest = { + /** A service ID. */ + serviceId: string; + /** The entity configuration of a relying party. */ + entityConfiguration?: string; + /** The trust chain of a relying party. */ + trustChain?: string; + }; + + export class FederationRegistrationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/grant-management-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/grant-management-endpoint.ts new file mode 100644 index 00000000..963765d8 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/grant-management-endpoint.ts @@ -0,0 +1,92 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { gmRequestSchema, type GmRequest } from "../models/gm-request.js"; +import { gmResponseSchema, type GmResponse } from "../models/gm-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoint for implementing OAuth 2.0 grants, including grant management actions like updating + * and revoking grants. + */ +export class GrantManagementEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Grant Management Request + * + * @remarks + * The API is for the implementation of the grant management endpoint which is defined in "[Grant + * Management for OAuth 2.0](https://openid.net/specs/fapi-grant-management.html)". + * + * @returns Grant management completed successfully + * + * @throws {@link GrantManagementEndpoint.GrantMApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + grantMApi( + request: GrantManagementEndpoint.GrantMApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/gm"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: gmRequestSchema }, + }, + { + success: { kind: "json", schema: gmResponseSchema }, + errorFactory: GrantManagementEndpoint.GrantMApiError, + }, + options, + ); + } +} + +export namespace GrantManagementEndpoint { + export type GrantMApiRequest = { + /** A service ID. */ + serviceId: string; + body: GmRequest; + }; + + export class GrantMApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/hardware-security-key.ts b/plugins/authlete/sdk/typescript/src/resources/hardware-security-key.ts new file mode 100644 index 00000000..a5bb3722 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/hardware-security-key.ts @@ -0,0 +1,299 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { hskCreateResponseSchema, type HskCreateResponse } from "../models/hsk-create-response.js"; +import { hskDeleteResponseSchema, type HskDeleteResponse } from "../models/hsk-delete-response.js"; +import { hskGetListResponseSchema, type HskGetListResponse } from "../models/hsk-get-list-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for managing hardware security keys (HSK). + */ +export class HardwareSecurityKey { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Create Security Key + * + * @returns HSK created successfully + * + * @throws {@link HardwareSecurityKey.HskCreateApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + hskCreateApi( + request: HardwareSecurityKey.HskCreateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/hsk/create"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "kty", value: request.kty, schema: s.optional(s.string()) }, + { name: "use", value: request.use, schema: s.optional(s.string()) }, + { name: "kid", value: request.kid, schema: s.optional(s.string()) }, + { name: "hsmName", value: request.hsmName, schema: s.optional(s.string()) }, + { name: "alg", value: request.alg, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: hskCreateResponseSchema }, + errorFactory: HardwareSecurityKey.HskCreateApiError, + }, + options, + ); + } + + /** + * Delete Security Key + * + * @returns HSK deleted successfully + * + * @throws {@link HardwareSecurityKey.HskDeleteApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + hskDeleteApi( + request: HardwareSecurityKey.HskDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/hsk/delete/{handle}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "handle", value: request.handle, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: hskDeleteResponseSchema }, + errorFactory: HardwareSecurityKey.HskDeleteApiError, + }, + options, + ); + } + + /** + * Get Security Key + * + * @throws {@link HardwareSecurityKey.HskGetApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + hskGetApi( + request: HardwareSecurityKey.HskGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/hsk/get/{handle}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "handle", value: request.handle, schema: s.string() }, + ], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: hskDeleteResponseSchema }, + errorFactory: HardwareSecurityKey.HskGetApiError, + }, + options, + ); + } + + /** + * List Security Keys + * + * @returns HSK list retrieved successfully + * + * @throws {@link HardwareSecurityKey.HskGetListApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + hskGetListApi( + request: HardwareSecurityKey.HskGetListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/hsk/get/list"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: hskGetListResponseSchema }, + errorFactory: HardwareSecurityKey.HskGetListApiError, + }, + options, + ); + } +} + +export namespace HardwareSecurityKey { + export type HskCreateApiRequest = { + /** A service ID. */ + serviceId: string; + /** The key type (EC or RSA) */ + kty?: string; + /** + * The key on the HSM. When the key use is "sig" (signature), the private key on the HSM is used + * to sign data and the corresponding public key is used to verify the signature. When the key + * use is "enc" (encryption), the private key on the HSM is used to decrypt encrypted data which + * have been encrypted with the corresponding public key + */ + use?: string; + /** Key ID for the key on the HSM. */ + kid?: string; + /** + * The name of the HSM. The identifier for the HSM that sits behind the Authlete server. For + * example, "google". + */ + hsmName?: string; + /** + * The algorithm of the key on the HSM. When the key use is `"sig"`, the algorithm represents a + * signing algorithm such as `"ES256"`. When the key use is `"enc"`, the algorithm represents an + * encryption algorithm such as `"RSA-OAEP-256"`. + * + * It is rare that HSMs support all the algorithms listed in [RFC 7518 JSON Web Algorithms + * (JWA)](https://www.rfc-editor.org/rfc/rfc7518.html). When the specified algorithm is not + * supported by the HSM, the request to the `/hsk/create` API fails. + */ + alg?: string; + }; + + export class HskCreateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type HskDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + handle: string; + }; + + export class HskDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type HskGetApiRequest = { + /** A service ID. */ + serviceId: string; + handle: string; + }; + + export class HskGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type HskGetListApiRequest = { + /** A service ID. */ + serviceId: string; + }; + + export class HskGetListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/introspection-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/introspection-endpoint.ts new file mode 100644 index 00000000..07f2aa02 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/introspection-endpoint.ts @@ -0,0 +1,426 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { introspectionResponseSchema, type IntrospectionResponse } from "../models/introspection-response.js"; +import { pairSchema, type Pair } from "../models/pair.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { + standardIntrospectionResponseSchema, + type StandardIntrospectionResponse, +} from "../models/standard-introspection-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Introspection Endpoint. + */ +export class IntrospectionEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Introspection Request + * + * @remarks + * This API gathers information about an access token. + * + * @throws {@link IntrospectionEndpoint.AuthIntrospectionApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authIntrospectionApi( + request: IntrospectionEndpoint.AuthIntrospectionApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/introspection"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "token", value: request.token, schema: s.string() }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { name: "subject", value: request.subject, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { name: "dpop", value: request.dpop, schema: s.optional(s.string()) }, + { name: "htm", value: request.htm, schema: s.optional(s.string()) }, + { name: "htu", value: request.htu, schema: s.optional(s.string()) }, + { name: "resources", value: request.resources, schema: s.optional(s.array(s.string())) }, + { name: "acrValues", value: request.acrValues, schema: s.optional(s.array(s.string())) }, + { name: "maxAge", value: request.maxAge, schema: s.optional(s.int()) }, + { + name: "requiredComponents", + value: request.requiredComponents, + schema: s.optional(s.array(s.string())), + }, + { name: "uri", value: request.uri, schema: s.optional(s.string()) }, + { name: "message", value: request.message, schema: s.optional(s.string()) }, + { + name: "headers", + value: request.headers, + schema: s.optional(s.array(s.lazy(() => pairSchema))), + }, + { name: "targetUri", value: request.targetUri, schema: s.optional(s.string()) }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + { + name: "requestBodyContained", + value: request.requestBodyContained, + schema: s.optional(s.boolean()), + }, + ], + }, + }, + { + success: { kind: "json", schema: introspectionResponseSchema }, + errorFactory: IntrospectionEndpoint.AuthIntrospectionApiError, + }, + options, + ); + } + + /** + * Process OAuth 2.0 Introspection Request + * + * @remarks + * This API exists to help your authorization server provide its own introspection API which + * complies with [RFC 7662](https://tools.ietf.org/html/rfc7662) (OAuth 2.0 Token Introspection). + * + * @returns Token introspection completed successfully + * + * @throws {@link IntrospectionEndpoint.AuthIntrospectionStandardApiError} when the API answers + * with an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authIntrospectionStandardApi( + request: IntrospectionEndpoint.AuthIntrospectionStandardApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/introspection/standard"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { + name: "withHiddenProperties", + value: request.withHiddenProperties, + schema: s.optional(s.boolean()), + }, + { name: "rsUri", value: request.rsUri, schema: s.optional(s.string()) }, + { name: "httpAcceptHeader", value: request.httpAcceptHeader, schema: s.optional(s.string()) }, + { + name: "introspectionSignAlg", + value: request.introspectionSignAlg, + schema: s.optional(s.string()), + }, + { + name: "introspectionEncryptionAlg", + value: request.introspectionEncryptionAlg, + schema: s.optional(s.string()), + }, + { + name: "introspectionEncryptionEnc", + value: request.introspectionEncryptionEnc, + schema: s.optional(s.string()), + }, + { name: "sharedKeyForSign", value: request.sharedKeyForSign, schema: s.optional(s.string()) }, + { + name: "sharedKeyForEncryption", + value: request.sharedKeyForEncryption, + schema: s.optional(s.string()), + }, + { + name: "publicKeyForEncryption", + value: request.publicKeyForEncryption, + schema: s.optional(s.string()), + }, + ], + }, + }, + { + success: { kind: "json", schema: standardIntrospectionResponseSchema }, + errorFactory: IntrospectionEndpoint.AuthIntrospectionStandardApiError, + }, + options, + ); + } +} + +export namespace IntrospectionEndpoint { + export type AuthIntrospectionApiRequest = { + /** A service ID. */ + serviceId: string; + /** An access token to introspect. */ + token: string; + /** + * A string array listing names of scopes which the caller (= a protected resource endpoint of + * the service) requires. When the content type of the request from the service is + * `application/x-www-form-urlencoded`, the format of `scopes` is a space-separated list of + * scope names. + * + * If this parameter is a non-empty array and if it contains a scope which is not covered by the + * access token,`action=FORBIDDEN` with `error=insufficient_scope` is returned from Authlete. + */ + scopes?: string[]; + /** + * A subject (= a user account managed by the service) whom the caller (= a protected resource + * endpoint of the service) requires. + * + * If this parameter is not `null` and if the value does not match the subject who is associated + * with the access token, `action=FORBIDDEN` with `error=invalid_request` is returned from + * Authlete. + */ + subject?: string; + /** + * Client certificate in PEM format, used to validate binding against access tokens using the + * TLS client certificate confirmation method. + */ + clientCertificate?: string; + /** + * `DPoP` header presented by the client during the request to the resource server. + * + * The header contains a signed JWT which includes the public key that is paired with the + * private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the request from the client to the protected resource endpoint. This field is + * used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htm?: string; + /** + * URL of the protected resource endpoint. This field is used to validate the `DPoP` header. + * + * See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** + * The resources specified by the `resource` request parameters in the token request. See + * "Resource Indicators for OAuth 2.0" for details. + */ + resources?: string[]; + /** + * Authentication Context Class Reference values one of which the user authentication performed + * during the course of issuing the access token must satisfy. + */ + acrValues?: string[]; + /** + * The maximum authentication age which is the maximum allowable elapsed time since the user + * authentication was performed during the course of issuing the access token. + */ + maxAge?: number; + /** + * HTTP Message Components required to be in the signature. If absent, defaults to [ "@method", + * "@target-uri", "authorization" ]. + */ + requiredComponents?: string[]; + /** The full URL of the userinfo endpoint. */ + uri?: string; + /** The HTTP message body of the request, if present. */ + message?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this + * must include the Signature and Signature-Input headers, as well as any additional headers + * covered by the signature. + */ + headers?: Pair[]; + /** The target URI of the resource request, including the query part, if any. */ + targetUri?: string; + /** + * The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` + * value. + * + * If this request parameter is set to `true` or if the service's `dpopNonceRequired` property + * is set to `true`, the `/auth/introspection` API checks if the DPoP proof JWT includes the + * expected `nonce` value. In this case, the response from the `/auth/introspection` API will + * include the `dpopNonce` response parameter, which should be used as the value of the + * DPoP-Nonce HTTP header. + */ + dpopNonceRequired?: boolean; + /** + * The flag indicating whether the resource request contains a request body. + * + * When the resource request must comply with the HTTP message signing requirements defined in + * the FAPI 2.0 Message Signing specification, the `"content-digest"` component identifier must + * be included in the signature base of the HTTP message signature (see [RFC 9421 HTTP Message + * Signatures](https://www.rfc-editor.org/rfc/rfc9421.html)) if the resource request contains a + * request body. + * + * When this `requestBodyContained` parameter is set to `true`, Authlete checks whether + * `"content-digest"` is included in the signature base, if the FAPI profile applies to the + * resource request. + */ + requestBodyContained?: boolean; + }; + + export class AuthIntrospectionApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthIntrospectionStandardApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * Request parameters which comply with the introspection request defined in "[2.1. + * Introspection Request](https://datatracker.ietf.org/doc/html/rfc7662#section-2.1)" in RFC + * 7662. + * + * The implementation of the introspection endpoint of your authorization server will receive an + * HTTP POST [[RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231)] request with parameters + * in the `application/x-www-form-urlencoded` format. It is the entity body of the request that + * Authlete's `/api/auth/introspection/standard` API expects as the value of `parameters`. + */ + parameters: string; + /** + * Flag indicating whether to include hidden properties in the output. + * + * Authlete has a mechanism whereby to associate arbitrary key-value pairs with an access token. + * Each key-value pair has a hidden attribute. By default, key-value pairs whose hidden + * attribute is set to `true` are not embedded in the standard introspection output. + * + * If the `withHiddenProperties` request parameter is given and its value is `true`, + * `/api/auth/introspection/standard API includes all the associated key-value pairs into the + * output regardless of the value of the hidden attribute. + */ + withHiddenProperties?: boolean; + /** + * The URI of the resource server making the introspection request. + * + * If the `rsUri` request parameter is given and the token has audience values, Authlete checks + * if the value of the `rsUri` request parameter is contained in the audience values. If not + * contained, Authlete generates an introspection response with the `active` property set to + * `false`. + * + * The `rsUri` request parameter is required when the resource server requests a JWT + * introspection response, i.e., when the value of the `httpAcceptHeader` request parameter is + * set to `"application/token-introspection+jwt"`. + */ + rsUri?: string; + /** + * The value of the `HTTP Accept` header in the introspection request. + * + * If the value of the `httpAcceptHeader` request parameter is + * `"application/token-introspection+jwt"`, Authlete generates a JWT introspection response. See + * "[4. Requesting a JWT Response](https://www.rfc-editor.org/rfc/rfc9701.html#section-4)" of + * "[RFC 9701: JWT Response for OAuth Token + * Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)" for more details. + */ + httpAcceptHeader?: string; + /** + * The JWS `alg` algorithm for signing the introspection response. This parameter corresponds to + * `introspection_signed_response_alg` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * The default value is `RS256`. + */ + introspectionSignAlg?: string; + /** + * The JWE `alg` algorithm for encrypting the introspection response. This parameter corresponds + * to `introspection_encrypted_response_alg` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * If the `introspectionEncryptionAlg` request parameter is specified, Authlete generates a JWT + * introspection response encrypted with the algorithm by this property and the algorithm + * specified by the `introspectionEncryptionEnc` request parameter. + */ + introspectionEncryptionAlg?: string; + /** + * The JWE `enc` algorithm for encrypting the introspection response. This parameter corresponds + * to `introspection_encrypted_response_enc` defined in "[6. Client + * Metadata](https://www.rfc-editor.org/rfc/rfc9701.html#section-6)" of "[RFC 9701: JWT Response + * for OAuth Token Introspection](https://www.rfc-editor.org/rfc/rfc9701.html)". + * + * The default value is `A128CBC_HS256`. + */ + introspectionEncryptionEnc?: string; + /** + * The shared key for signing the introspection response with a symmetric algorithm. + * + * The `sharedKeyForSign` request parameter is required when the introspection response is + * requested to be signed with a symmetric algorithm. + */ + sharedKeyForSign?: string; + /** + * The shared key for encrypting the introspection response with a symmetric algorithm. + * + * The `sharedKeyForEncryption` request parameter is required when the introspection response is + * requested to be encrypted with a symmetric algorithm. + */ + sharedKeyForEncryption?: string; + /** + * The public key for signing the introspection response with an asymmetric algorithm. + * + * The `publicKeyForEncryption` request parameter is required when the introspection response is + * requested to be encrypted with an asymmetric algorithm. + */ + publicKeyForEncryption?: string; + }; + + export class AuthIntrospectionStandardApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/jose-object.ts b/plugins/authlete/sdk/typescript/src/resources/jose-object.ts new file mode 100644 index 00000000..c795a5e2 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/jose-object.ts @@ -0,0 +1,115 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { joseVerifyResponseSchema, type JoseVerifyResponse } from "../models/jose-verify-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for JOSE objects. + */ +export class JoseObject { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Verify JOSE + * + * @remarks + * This API verifies a JOSE object. + * + * @throws {@link JoseObject.JoseVerifyApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + joseVerifyApi( + request: JoseObject.JoseVerifyApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/jose/verify"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "jose", value: request.jose, schema: s.string() }, + { + name: "mandatoryClaims", + value: request.mandatoryClaims, + schema: s.optional(s.array(s.string())), + }, + { name: "clockSkew", value: request.clockSkew, schema: s.optional(s.int()) }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.optional(s.string()) }, + { name: "signedByClient", value: request.signedByClient, schema: s.optional(s.boolean()) }, + ], + }, + }, + { + success: { kind: "json", schema: joseVerifyResponseSchema }, + errorFactory: JoseObject.JoseVerifyApiError, + }, + options, + ); + } +} + +export namespace JoseObject { + export type JoseVerifyApiRequest = { + /** A service ID. */ + serviceId: string; + /** A JOSE object. */ + jose: string; + /** Mandatory claims that are required to be included in the JOSE object. */ + mandatoryClaims?: string[]; + /** Allowable clock skew in seconds. */ + clockSkew?: number; + /** + * The identifier of the client application whose keys are required for verification of the JOSE + * object. + */ + clientIdentifier?: string; + /** + * The flag which indicates whether the signature of the JOSE object has been signed by a client + * application with the client's private key or a shared symmetric key. + */ + signedByClient?: boolean; + }; + + export class JoseVerifyApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/jwk-set-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/jwk-set-endpoint.ts new file mode 100644 index 00000000..3d486811 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/jwk-set-endpoint.ts @@ -0,0 +1,111 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import * as s from "../core/validation/index.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { + serviceJwksGetResponseSchema, + type ServiceJwksGetResponse, +} from "../models/service-jwks-get-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for to generate JSON Web Key Set (JWKS) for a service. + */ +export class JwkSetEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Get JWK Set + * + * @remarks + * This API gathers JWK Set information for a service so that its client applications can verify + * signatures by the service and encrypt their requests to the service. + * + * This API is supposed to be called from within the implementation of the jwk set endpoint of the + * service where the service that supports OpenID Connect must expose its JWK Set information so + * that client applications can verify signatures by the service and encrypt their requests to the + * service. The URI of the endpoint can be found as the value of `jwks_uri` in [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) if the + * service supports [OpenID Connect Discovery + * 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html). + * + * @throws {@link JwkSetEndpoint.ServiceJwksGetApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceJwksGetApi( + request: JwkSetEndpoint.ServiceJwksGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/service/jwks/get"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [ + { name: "includePrivateKeys", value: request.includePrivateKeys, schema: s.optional(s.boolean()) }, + { name: "pretty", value: request.pretty, schema: s.optional(s.boolean()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: serviceJwksGetResponseSchema }, + errorFactory: JwkSetEndpoint.ServiceJwksGetApiError, + }, + options, + ); + } +} + +export namespace JwkSetEndpoint { + export type ServiceJwksGetApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The boolean value that indicates whether the response should include the private keys + * associated with the service or not. If `true`, the private keys are included in the response. + * The default value is `false`. + */ + includePrivateKeys?: boolean; + /** + * This boolean value indicates whether the JSON in the response should be formatted or not. If + * `true`, the JSON in the response is pretty-formatted. The default value is `false`. + */ + pretty?: boolean; + }; + + export class ServiceJwksGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/lifecycle.ts b/plugins/authlete/sdk/typescript/src/resources/lifecycle.ts new file mode 100644 index 00000000..0b1691e6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/lifecycle.ts @@ -0,0 +1,70 @@ +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import { noneAuth } from "../core/auth/schemes.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import * as s from "../core/validation/index.js"; +import type { Servers } from "../servers.js"; + +export class Lifecycle { + readonly #rawClient: RawClient; + readonly #servers: Servers; + + constructor(rawClient: RawClient, servers: Servers) { + this.#rawClient = rawClient; + this.#servers = servers; + } + + /** + * Health Check + * + * @remarks + * Perform a health check of the server. + * + * @returns OK + * + * @throws {@link Lifecycle.HealthCheckError} when the API answers with an error status β€” narrow + * on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + healthCheck( + request: Lifecycle.HealthCheckRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/lifecycle/healthcheck"), + auth: noneAuth, + pathParams: [], + query: [{ name: "extended", value: request.extended, schema: s.optional(s.boolean()) }], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "text", schema: s.string() }, + errorFactory: Lifecycle.HealthCheckError, + }, + options, + ); + } +} + +export namespace Lifecycle { + export type HealthCheckRequest = { + /** If `true`, perform extended health checks (e.g. database connectivity). */ + extended?: boolean; + }; + + export class HealthCheckError extends ApiError { + declare readonly payload: Declared<"error500", string> | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 500, kind: "error500", decode: { kind: "text", schema: s.string() } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/native-sso.ts b/plugins/authlete/sdk/typescript/src/resources/native-sso.ts new file mode 100644 index 00000000..01949bf5 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/native-sso.ts @@ -0,0 +1,168 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + nativeSsoLogoutRequestSchema, + type NativeSsoLogoutRequest, +} from "../models/native-sso-logout-request.js"; +import { + nativeSsoLogoutResponseSchema, + type NativeSsoLogoutResponse, +} from "../models/native-sso-logout-response.js"; +import { nativeSsoRequestSchema, type NativeSsoRequest } from "../models/native-sso-request.js"; +import { nativeSsoResponseSchema, type NativeSsoResponse } from "../models/native-sso-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for Native SSO + */ +export class NativeSso { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Native SSO Processing + * + * @remarks + * This API should be called by the implementation of a token endpoint to generate the ID token + * and token response that comply with [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO) when Authlete’s + * `/auth/token` response indicates `action = NATIVE_SSO` (after you validate the session id and + * verify or generate the device secret as required by the flow). The token endpoint + * implementation should retrieve the value of `action` from the response and take the following + * steps according to the value. + * + * @returns Native SSO processing completed successfully + * + * @throws {@link NativeSso.NativeSsoApiError} when the API answers with an error status β€” narrow + * on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + nativeSsoApi( + request: NativeSso.NativeSsoApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/nativesso"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: nativeSsoRequestSchema }, + }, + { + success: { kind: "json", schema: nativeSsoResponseSchema }, + errorFactory: NativeSso.NativeSsoApiError, + }, + options, + ); + } + + /** + * Native SSO Logout Processing + * + * @remarks + * The `/nativesso/logout` API is supposed to be used to support the concept of "logout from all + * applications" in the context of [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) (Native SSO). This is + * accomplished by deleting access/refresh token records associated with the specified session ID. + * In Authlete's implementation, access/refresh token records can be associated with a session ID + * only through the mechanism introduced by Native SSO. + * + * @throws {@link NativeSso.NativeSsoLogoutApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + nativeSsoLogoutApi( + request: NativeSso.NativeSsoLogoutApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/nativesso/logout"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: nativeSsoLogoutRequestSchema }, + }, + { + success: { kind: "json", schema: nativeSsoLogoutResponseSchema }, + errorFactory: NativeSso.NativeSsoLogoutApiError, + }, + options, + ); + } +} + +export namespace NativeSso { + export type NativeSsoApiRequest = { + /** A service ID. */ + serviceId: string; + body: NativeSsoRequest; + }; + + export class NativeSsoApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type NativeSsoLogoutApiRequest = { + /** A service ID. */ + serviceId: string; + body: NativeSsoLogoutRequest; + }; + + export class NativeSsoLogoutApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/pushed-authorization-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/pushed-authorization-endpoint.ts new file mode 100644 index 00000000..cadbdec6 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/pushed-authorization-endpoint.ts @@ -0,0 +1,169 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { + pushedAuthorizationResponseSchema, + type PushedAuthorizationResponse, +} from "../models/pushed-authorization-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Pushed Authorization Requests (PAR). + */ +export class PushedAuthorizationEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Pushed Authorization Request + * + * @remarks + * This API creates a pushed request authorization. It authenticates the client and creates a + * authorization_uri to be returned by the authorization server. + * + * @throws {@link PushedAuthorizationEndpoint.PushedAuthReqApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + pushedAuthReqApi( + request: PushedAuthorizationEndpoint.PushedAuthReqApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/pushed_auth_req"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.optional(s.string()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { + name: "clientCertificatePath", + value: request.clientCertificatePath, + schema: s.optional(s.array(s.string())), + }, + { name: "dpop", value: request.dpop, schema: s.optional(s.string()) }, + { name: "htm", value: request.htm, schema: s.optional(s.string()) }, + { name: "htu", value: request.htu, schema: s.optional(s.string()) }, + { + name: "oauthClientAttestation", + value: request.oauthClientAttestation, + schema: s.optional(s.string()), + }, + { + name: "oauthClientAttestationPop", + value: request.oauthClientAttestationPop, + schema: s.optional(s.string()), + }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + ], + }, + }, + { + success: { kind: "json", schema: pushedAuthorizationResponseSchema }, + errorFactory: PushedAuthorizationEndpoint.PushedAuthReqApiError, + }, + options, + ); + } +} + +export namespace PushedAuthorizationEndpoint { + export type PushedAuthReqApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The pushed authorization request body received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the pushed request from the client + * application. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the pushed authorization request + * from the client application. + */ + clientSecret?: string; + /** + * The client certificate from the MTLS connection to pushed authorization endpoint from the + * client application. + */ + clientCertificate?: string; + /** + * The certificate path presented by the client during client authentication. These certificates + * are strings in PEM format. + */ + clientCertificatePath?: string[]; + /** DPoP Header */ + dpop?: string; + /** HTTP Method (for DPoP validation). */ + htm?: string; + /** HTTP URL base (for DPoP validation). */ + htu?: string; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even + * if the service's `dpopNonceRequired` property is `false`, calling the `/pushed_auth_req` API + * with this `dpopNonceRequired` parameter `true` will force the Authlete API to check whether + * the DPoP proof JWT includes the expected `nonce` value. + */ + dpopNonceRequired?: boolean; + }; + + export class PushedAuthReqApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/revocation-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/revocation-endpoint.ts new file mode 100644 index 00000000..9871af6f --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/revocation-endpoint.ts @@ -0,0 +1,159 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { revocationResponseSchema, type RevocationResponse } from "../models/revocation-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoint for implementing OAuth 2.0 Revocation Endpoint. + */ +export class RevocationEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Revocation Request + * + * @remarks + * This API revokes access tokens and refresh tokens. + * + * @returns Token revoked successfully + * + * @throws {@link RevocationEndpoint.AuthRevocationApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authRevocationApi( + request: RevocationEndpoint.AuthRevocationApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/revocation"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.optional(s.string()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { + name: "clientCertificatePath", + value: request.clientCertificatePath, + schema: s.optional(s.array(s.string())), + }, + { + name: "oauthClientAttestation", + value: request.oauthClientAttestation, + schema: s.optional(s.string()), + }, + { + name: "oauthClientAttestationPop", + value: request.oauthClientAttestationPop, + schema: s.optional(s.string()), + }, + ], + }, + }, + { + success: { kind: "json", schema: revocationResponseSchema }, + errorFactory: RevocationEndpoint.AuthRevocationApiError, + }, + options, + ); + } +} + +export namespace RevocationEndpoint { + export type AuthRevocationApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * OAuth 2.0 token revocation request parameters which are the request parameters that the OAuth + * 2.0 token revocation endpoint ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)) of + * the authorization server implementation received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the revocation request from the client + * application. + * + * If the revocation endpoint of the authorization server implementation supports Basic + * Authentication as a means of client authentication, and the request from the client + * application contains its client ID in `Authorization` header, the value should be extracted + * and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the revocation request from the + * client application. + * + * If the revocation endpoint of the authorization server implementation supports basic + * authentication as a means of client authentication, and the request from the client + * application contained its client secret in `Authorization` header, the value should be + * extracted and set to this parameter. + */ + clientSecret?: string; + /** + * The client certificate used in the TLS connection between the client application and the + * revocation endpoint. + */ + clientCertificate?: string; + /** The certificate path presented by the client during client authentication. */ + clientCertificatePath?: string[]; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + }; + + export class AuthRevocationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/service-management.ts b/plugins/authlete/sdk/typescript/src/resources/service-management.ts new file mode 100644 index 00000000..363f3042 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/service-management.ts @@ -0,0 +1,4502 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { attachmentTypeSchema, type AttachmentType } from "../models/attachment-type.js"; +import { claimTypeSchema, type ClaimType } from "../models/claim-type.js"; +import { clientAuthMethodSchema, type ClientAuthMethod } from "../models/client-auth-method.js"; +import { + clientRegistrationTypeSchema, + type ClientRegistrationType, +} from "../models/client-registration-type.js"; +import { + credentialIssuerMetadataSchema, + type CredentialIssuerMetadata, +} from "../models/credential-issuer-metadata.js"; +import { deliveryModeSchema, type DeliveryMode } from "../models/delivery-mode.js"; +import { displaySchema, type Display } from "../models/display.js"; +import { fapiModeSchema, type FapiMode } from "../models/fapi-mode.js"; +import { grantTypeSchema, type GrantType } from "../models/grant-type.js"; +import { hskSchema, type Hsk } from "../models/hsk.js"; +import { idpErrorSchema, type IdpError } from "../models/idp-error.js"; +import { jwsAlgSchema, type JwsAlg } from "../models/jws-alg.js"; +import { namedUriSchema, type NamedUri } from "../models/named-uri.js"; +import { pairSchema, type Pair } from "../models/pair.js"; +import { promptSchema, type Prompt } from "../models/prompt.js"; +import { responseTypeSchema, type ResponseType } from "../models/response-type.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { scopeSchema, type Scope } from "../models/scope.js"; +import { + serviceCreateIdpRequestSchema, + type ServiceCreateIdpRequest, +} from "../models/service-create-idp-request.js"; +import { + serviceGetListResponseSchema, + type ServiceGetListResponse, +} from "../models/service-get-list-response.js"; +import { serviceProfileSchema, type ServiceProfile } from "../models/service-profile.js"; +import { + serviceRemoveIdpRequestSchema, + type ServiceRemoveIdpRequest, +} from "../models/service-remove-idp-request.js"; +import { serviceSchema, type Service } from "../models/service.js"; +import { snsCredentialsSchema, type SnsCredentials } from "../models/sns-credentials.js"; +import { snsSchema, type Sns } from "../models/sns.js"; +import { trustAnchorSchema, type TrustAnchor } from "../models/trust-anchor.js"; +import { userCodeCharsetSchema, type UserCodeCharset } from "../models/user-code-charset.js"; +import { + verifiedClaimsValidationSchemaSchema, + type VerifiedClaimsValidationSchema, +} from "../models/verified-claims-validation-schema.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for managing services, including creation, update, and deletion of services. + */ +export class ServiceManagement { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Get Service Configuration + * + * @remarks + * This API gathers configuration information about a service. + * + * This API is supposed to be called from within the implementation of the configuration endpoint + * of the service where the service that supports OpenID Connect and [OpenID Connect Discovery + * 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) must expose its configuration + * information in a JSON format. Details about the format are described in "[3. OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata)" in + * OpenID Connect Discovery 1.0. + * + * @returns Service configuration retrieved successfully + * + * @throws {@link ServiceManagement.ServiceConfigurationApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceConfigurationApi( + request: ServiceManagement.ServiceConfigurationApiRequest, + options?: RequestOptions, + ): ApiPromise, ServiceManagement.ServiceConfigurationApiError> { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/service/configuration"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [ + { name: "pretty", value: request.pretty, schema: s.optional(s.boolean()) }, + { name: "patch", value: request.patch, schema: s.optional(s.string()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: s.record(s.string(), s.unknown()) }, + errorFactory: ServiceManagement.ServiceConfigurationApiError, + }, + options, + ); + } + + /** + * Create Service + * + * @remarks + * Create a new service. + * + * @returns Service created successfully (legacy compatibility) + * + * @throws {@link ServiceManagement.ServiceCreateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceCreateApi( + request: ServiceManagement.ServiceCreateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/service/create"), + auth: this.#auth.bearer, + pathParams: [], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "number", value: request.number, schema: s.optional(s.int()) }, + { name: "serviceName", value: request.serviceName, schema: s.optionalNullable(s.string()) }, + { name: "issuer", value: request.issuer, schema: s.optionalNullable(s.string()) }, + { name: "description", value: request.description, schema: s.optionalNullable(s.string()) }, + { name: "apiKey", value: request.apiKey, schema: s.optional(s.int()) }, + { name: "apiSecret", value: request.apiSecret, schema: s.optionalNullable(s.string()) }, + { + name: "tokenBatchNotificationEndpoint", + value: request.tokenBatchNotificationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "clientAssertionAudRestrictedToIssuer", + value: request.clientAssertionAudRestrictedToIssuer, + schema: s.optional(s.boolean()), + }, + { name: "serviceOwnerNumber", value: request.serviceOwnerNumber, schema: s.optional(s.int()) }, + { name: "clientsPerDeveloper", value: request.clientsPerDeveloper, schema: s.optional(s.int()) }, + { + name: "developerAuthenticationCallbackEndpoint", + value: request.developerAuthenticationCallbackEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "developerAuthenticationCallbackApiKey", + value: request.developerAuthenticationCallbackApiKey, + schema: s.optionalNullable(s.string()), + }, + { + name: "developerAuthenticationCallbackApiSecret", + value: request.developerAuthenticationCallbackApiSecret, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedSnses", + value: request.supportedSnses, + schema: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + }, + { + name: "snsCredentials", + value: request.snsCredentials, + schema: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + }, + { + name: "supportedDeveloperSnses", + value: request.supportedDeveloperSnses, + schema: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + }, + { + name: "developerSnsCredentials", + value: request.developerSnsCredentials, + schema: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + }, + { + name: "clientIdAliasEnabled", + value: request.clientIdAliasEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "metadata", + value: request.metadata, + schema: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + }, + { name: "createdAt", value: request.createdAt, schema: s.optional(s.int()) }, + { name: "modifiedAt", value: request.modifiedAt, schema: s.optional(s.int()) }, + { + name: "authenticationCallbackEndpoint", + value: request.authenticationCallbackEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "authenticationCallbackApiKey", + value: request.authenticationCallbackApiKey, + schema: s.optionalNullable(s.string()), + }, + { + name: "authenticationCallbackApiSecret", + value: request.authenticationCallbackApiSecret, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedAcrs", + value: request.supportedAcrs, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedGrantTypes", + value: request.supportedGrantTypes, + schema: s.optionalNullable(s.array(s.lazy(() => grantTypeSchema))), + }, + { + name: "supportedResponseTypes", + value: request.supportedResponseTypes, + schema: s.optionalNullable(s.array(s.lazy(() => responseTypeSchema))), + }, + { + name: "supportedAuthorizationDetailsTypes", + value: request.supportedAuthorizationDetailsTypes, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedServiceProfiles", + value: request.supportedServiceProfiles, + schema: s.optionalNullable(s.array(s.lazy(() => serviceProfileSchema))), + }, + { + name: "errorDescriptionOmitted", + value: request.errorDescriptionOmitted, + schema: s.optional(s.boolean()), + }, + { name: "errorUriOmitted", value: request.errorUriOmitted, schema: s.optional(s.boolean()) }, + { + name: "authorizationEndpoint", + value: request.authorizationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directAuthorizationEndpointEnabled", + value: request.directAuthorizationEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedUiLocales", + value: request.supportedUiLocales, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDisplays", + value: request.supportedDisplays, + schema: s.optionalNullable(s.array(s.lazy(() => displaySchema))), + }, + { name: "pkceRequired", value: request.pkceRequired, schema: s.optional(s.boolean()) }, + { name: "pkceS256Required", value: request.pkceS256Required, schema: s.optional(s.boolean()) }, + { + name: "authorizationResponseDuration", + value: request.authorizationResponseDuration, + schema: s.optional(s.int()), + }, + { + name: "authorizationCodeDuration", + value: request.authorizationCodeDuration, + schema: s.optional(s.int()), + }, + { name: "tokenEndpoint", value: request.tokenEndpoint, schema: s.optionalNullable(s.string()) }, + { + name: "directTokenEndpointEnabled", + value: request.directTokenEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedTokenAuthMethods", + value: request.supportedTokenAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "missingClientIdAllowed", + value: request.missingClientIdAllowed, + schema: s.optional(s.boolean()), + }, + { + name: "revocationEndpoint", + value: request.revocationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directRevocationEndpointEnabled", + value: request.directRevocationEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedRevocationAuthMethods", + value: request.supportedRevocationAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "introspectionEndpoint", + value: request.introspectionEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directIntrospectionEndpointEnabled", + value: request.directIntrospectionEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedIntrospectionAuthMethods", + value: request.supportedIntrospectionAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "pushedAuthReqEndpoint", + value: request.pushedAuthReqEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "pushedAuthReqDuration", + value: request.pushedAuthReqDuration, + schema: s.optional(s.int()), + }, + { name: "parRequired", value: request.parRequired, schema: s.optional(s.boolean()) }, + { + name: "requestObjectRequired", + value: request.requestObjectRequired, + schema: s.optional(s.boolean()), + }, + { + name: "traditionalRequestObjectProcessingApplied", + value: request.traditionalRequestObjectProcessingApplied, + schema: s.optional(s.boolean()), + }, + { + name: "mutualTlsValidatePkiCertChain", + value: request.mutualTlsValidatePkiCertChain, + schema: s.optional(s.boolean()), + }, + { + name: "trustedRootCertificates", + value: request.trustedRootCertificates, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "mtlsEndpointAliases", + value: request.mtlsEndpointAliases, + schema: s.optionalNullable(s.array(s.lazy(() => namedUriSchema))), + }, + { + name: "accessTokenType", + value: request.accessTokenType, + schema: s.optionalNullable(s.string()), + }, + { + name: "tlsClientCertificateBoundAccessTokens", + value: request.tlsClientCertificateBoundAccessTokens, + schema: s.optional(s.boolean()), + }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "singleAccessTokenPerSubject", + value: request.singleAccessTokenPerSubject, + schema: s.optional(s.boolean()), + }, + { + name: "accessTokenSignAlg", + value: request.accessTokenSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "accessTokenSignatureKeyId", + value: request.accessTokenSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { + name: "refreshTokenDurationKept", + value: request.refreshTokenDurationKept, + schema: s.optional(s.boolean()), + }, + { + name: "refreshTokenDurationReset", + value: request.refreshTokenDurationReset, + schema: s.optional(s.boolean()), + }, + { name: "refreshTokenKept", value: request.refreshTokenKept, schema: s.optional(s.boolean()) }, + { + name: "supportedScopes", + value: request.supportedScopes, + schema: s.optionalNullable(s.array(s.lazy(() => scopeSchema))), + }, + { name: "scopeRequired", value: request.scopeRequired, schema: s.optional(s.boolean()) }, + { name: "idTokenDuration", value: request.idTokenDuration, schema: s.optional(s.int()) }, + { name: "allowableClockSkew", value: request.allowableClockSkew, schema: s.optional(s.int()) }, + { + name: "supportedClaimTypes", + value: request.supportedClaimTypes, + schema: s.optionalNullable(s.array(s.lazy(() => claimTypeSchema))), + }, + { + name: "supportedClaimLocales", + value: request.supportedClaimLocales, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedClaims", + value: request.supportedClaims, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "claimShortcutRestrictive", + value: request.claimShortcutRestrictive, + schema: s.optional(s.boolean()), + }, + { name: "jwksUri", value: request.jwksUri, schema: s.optionalNullable(s.string()) }, + { + name: "directJwksEndpointEnabled", + value: request.directJwksEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { name: "jwks", value: request.jwks, schema: s.optionalNullable(s.string()) }, + { + name: "idTokenSignatureKeyId", + value: request.idTokenSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "userInfoSignatureKeyId", + value: request.userInfoSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "authorizationSignatureKeyId", + value: request.authorizationSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "userInfoEndpoint", + value: request.userInfoEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directUserInfoEndpointEnabled", + value: request.directUserInfoEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "dynamicRegistrationSupported", + value: request.dynamicRegistrationSupported, + schema: s.optional(s.boolean()), + }, + { + name: "registrationEndpoint", + value: request.registrationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "registrationManagementEndpoint", + value: request.registrationManagementEndpoint, + schema: s.optionalNullable(s.string()), + }, + { name: "policyUri", value: request.policyUri, schema: s.optionalNullable(s.string()) }, + { name: "tosUri", value: request.tosUri, schema: s.optionalNullable(s.string()) }, + { + name: "serviceDocumentation", + value: request.serviceDocumentation, + schema: s.optionalNullable(s.string()), + }, + { + name: "backchannelAuthenticationEndpoint", + value: request.backchannelAuthenticationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedBackchannelTokenDeliveryModes", + value: request.supportedBackchannelTokenDeliveryModes, + schema: s.optionalNullable(s.array(s.lazy(() => deliveryModeSchema))), + }, + { + name: "backchannelAuthReqIdDuration", + value: request.backchannelAuthReqIdDuration, + schema: s.optional(s.int()), + }, + { + name: "backchannelPollingInterval", + value: request.backchannelPollingInterval, + schema: s.optional(s.int()), + }, + { + name: "backchannelUserCodeParameterSupported", + value: request.backchannelUserCodeParameterSupported, + schema: s.optional(s.boolean()), + }, + { + name: "backchannelBindingMessageRequiredInFapi", + value: request.backchannelBindingMessageRequiredInFapi, + schema: s.optional(s.boolean()), + }, + { + name: "deviceAuthorizationEndpoint", + value: request.deviceAuthorizationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceVerificationUri", + value: request.deviceVerificationUri, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceVerificationUriComplete", + value: request.deviceVerificationUriComplete, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceFlowCodeDuration", + value: request.deviceFlowCodeDuration, + schema: s.optional(s.int()), + }, + { + name: "deviceFlowPollingInterval", + value: request.deviceFlowPollingInterval, + schema: s.optional(s.int()), + }, + { + name: "userCodeCharset", + value: request.userCodeCharset, + schema: s.optionalNullable(s.lazy(() => userCodeCharsetSchema)), + }, + { name: "userCodeLength", value: request.userCodeLength, schema: s.optional(s.int()) }, + { + name: "supportedTrustFrameworks", + value: request.supportedTrustFrameworks, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedEvidence", + value: request.supportedEvidence, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedIdentityDocuments", + value: request.supportedIdentityDocuments, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedVerificationMethods", + value: request.supportedVerificationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedVerifiedClaims", + value: request.supportedVerifiedClaims, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "verifiedClaimsValidationSchemaSet", + value: request.verifiedClaimsValidationSchemaSet, + schema: s.optional(s.lazy(() => verifiedClaimsValidationSchemaSchema)), + }, + { + name: "attributes", + value: request.attributes, + schema: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + }, + { name: "nbfOptional", value: request.nbfOptional, schema: s.optional(s.boolean()) }, + { name: "issSuppressed", value: request.issSuppressed, schema: s.optional(s.boolean()) }, + { + name: "supportedCustomClientMetadata", + value: request.supportedCustomClientMetadata, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "tokenExpirationLinked", + value: request.tokenExpirationLinked, + schema: s.optional(s.boolean()), + }, + { + name: "frontChannelRequestObjectEncryptionRequired", + value: request.frontChannelRequestObjectEncryptionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionAlgMatchRequired", + value: request.requestObjectEncryptionAlgMatchRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionEncMatchRequired", + value: request.requestObjectEncryptionEncMatchRequired, + schema: s.optional(s.boolean()), + }, + { name: "hsmEnabled", value: request.hsmEnabled, schema: s.optional(s.boolean()) }, + { + name: "hsks", + value: request.hsks, + schema: s.optionalNullable(s.array(s.lazy(() => hskSchema))), + }, + { + name: "grantManagementEndpoint", + value: request.grantManagementEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "grantManagementActionRequired", + value: request.grantManagementActionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "unauthorizedOnClientConfigSupported", + value: request.unauthorizedOnClientConfigSupported, + schema: s.optional(s.boolean()), + }, + { + name: "dcrScopeUsedAsRequestable", + value: request.dcrScopeUsedAsRequestable, + schema: s.optional(s.boolean()), + }, + { + name: "endSessionEndpoint", + value: request.endSessionEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "challengeEndpoint", + value: request.challengeEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "loopbackRedirectionUriVariable", + value: request.loopbackRedirectionUriVariable, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectAudienceChecked", + value: request.requestObjectAudienceChecked, + schema: s.optional(s.boolean()), + }, + { + name: "accessTokenForExternalAttachmentEmbedded", + value: request.accessTokenForExternalAttachmentEmbedded, + schema: s.optional(s.boolean()), + }, + { + name: "authorityHints", + value: request.authorityHints, + schema: s.optionalNullable(s.array(s.string())), + }, + { name: "federationEnabled", value: request.federationEnabled, schema: s.optional(s.boolean()) }, + { name: "federationJwks", value: request.federationJwks, schema: s.optionalNullable(s.string()) }, + { + name: "federationSignatureKeyId", + value: request.federationSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "federationConfigurationDuration", + value: request.federationConfigurationDuration, + schema: s.optional(s.int()), + }, + { + name: "federationRegistrationEndpoint", + value: request.federationRegistrationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "organizationName", + value: request.organizationName, + schema: s.optionalNullable(s.string()), + }, + { + name: "predefinedTransformedClaims", + value: request.predefinedTransformedClaims, + schema: s.optionalNullable(s.string()), + }, + { + name: "refreshTokenIdempotent", + value: request.refreshTokenIdempotent, + schema: s.optional(s.boolean()), + }, + { name: "signedJwksUri", value: request.signedJwksUri, schema: s.optionalNullable(s.string()) }, + { + name: "supportedAttachments", + value: request.supportedAttachments, + schema: s.optionalNullable(s.array(s.lazy(() => attachmentTypeSchema))), + }, + { + name: "supportedDigestAlgorithms", + value: request.supportedDigestAlgorithms, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocuments", + value: request.supportedDocuments, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsMethods", + value: request.supportedDocumentsMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsValidationMethods", + value: request.supportedDocumentsValidationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsVerificationMethods", + value: request.supportedDocumentsVerificationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedElectronicRecords", + value: request.supportedElectronicRecords, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedClientRegistrationTypes", + value: request.supportedClientRegistrationTypes, + schema: s.optionalNullable(s.array(s.lazy(() => clientRegistrationTypeSchema))), + }, + { + name: "tokenExchangeByIdentifiableClientsOnly", + value: request.tokenExchangeByIdentifiableClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeByConfidentialClientsOnly", + value: request.tokenExchangeByConfidentialClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeByPermittedClientsOnly", + value: request.tokenExchangeByPermittedClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeEncryptedJwtRejected", + value: request.tokenExchangeEncryptedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeUnsignedJwtRejected", + value: request.tokenExchangeUnsignedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantByIdentifiableClientsOnly", + value: request.jwtGrantByIdentifiableClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantEncryptedJwtRejected", + value: request.jwtGrantEncryptedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantUnsignedJwtRejected", + value: request.jwtGrantUnsignedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "dcrDuplicateSoftwareIdBlocked", + value: request.dcrDuplicateSoftwareIdBlocked, + schema: s.optional(s.boolean()), + }, + { + name: "trustAnchors", + value: request.trustAnchors, + schema: s.optionalNullable(s.array(s.lazy(() => trustAnchorSchema))), + }, + { + name: "openidDroppedOnRefreshWithoutOfflineAccess", + value: request.openidDroppedOnRefreshWithoutOfflineAccess, + schema: s.optional(s.boolean()), + }, + { + name: "idTokenDroppedOnIssueWithoutOpenid", + value: request.idTokenDroppedOnIssueWithoutOpenid, + schema: s.optional(s.boolean()), + }, + { + name: "supportedDocumentsCheckMethods", + value: request.supportedDocumentsCheckMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { name: "rsResponseSigned", value: request.rsResponseSigned, schema: s.optional(s.boolean()) }, + { name: "cnonceDuration", value: request.cnonceDuration, schema: s.optional(s.int()) }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + { + name: "verifiableCredentialsEnabled", + value: request.verifiableCredentialsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "credentialJwksUri", + value: request.credentialJwksUri, + schema: s.optionalNullable(s.string()), + }, + { + name: "credentialOfferDuration", + value: request.credentialOfferDuration, + schema: s.optional(s.int()), + }, + { name: "dpopNonceDuration", value: request.dpopNonceDuration, schema: s.optional(s.int()) }, + { + name: "preAuthorizedGrantAnonymousAccessSupported", + value: request.preAuthorizedGrantAnonymousAccessSupported, + schema: s.optional(s.boolean()), + }, + { + name: "credentialTransactionDuration", + value: request.credentialTransactionDuration, + schema: s.optional(s.int()), + }, + { + name: "introspectionSignatureKeyId", + value: request.introspectionSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "resourceSignatureKeyId", + value: request.resourceSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { name: "userPinLength", value: request.userPinLength, schema: s.optional(s.int()) }, + { + name: "supportedPromptValues", + value: request.supportedPromptValues, + schema: s.optionalNullable(s.array(s.lazy(() => promptSchema))), + }, + { name: "idTokenReissuable", value: request.idTokenReissuable, schema: s.optional(s.boolean()) }, + { name: "credentialJwks", value: request.credentialJwks, schema: s.optionalNullable(s.string()) }, + { + name: "fapiModes", + value: request.fapiModes, + schema: s.optionalNullable(s.array(s.lazy(() => fapiModeSchema))), + }, + { name: "credentialDuration", value: request.credentialDuration, schema: s.optional(s.int()) }, + { + name: "credentialIssuerMetadata", + value: request.credentialIssuerMetadata, + schema: s.optionalNullable(s.lazy(() => credentialIssuerMetadataSchema)), + }, + { name: "idTokenAudType", value: request.idTokenAudType, schema: s.optionalNullable(s.string()) }, + { + name: "nativeSsoSupported", + value: request.nativeSsoSupported, + schema: s.optional(s.boolean()), + }, + { name: "oid4vciVersion", value: request.oid4VciVersion, schema: s.optionalNullable(s.string()) }, + { + name: "cimdMetadataPolicyEnabled", + value: request.cimdMetadataPolicyEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "clientIdMetadataDocumentSupported", + value: request.clientIdMetadataDocumentSupported, + schema: s.optional(s.boolean()), + }, + { + name: "cimdAllowlistEnabled", + value: request.cimdAllowlistEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "cimdAllowlist", + value: request.cimdAllowlist, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "cimdAlwaysRetrieved", + value: request.cimdAlwaysRetrieved, + schema: s.optional(s.boolean()), + }, + { name: "cimdHttpPermitted", value: request.cimdHttpPermitted, schema: s.optional(s.boolean()) }, + { + name: "cimdQueryPermitted", + value: request.cimdQueryPermitted, + schema: s.optional(s.boolean()), + }, + { + name: "cimdMetadataPolicy", + value: request.cimdMetadataPolicy, + schema: s.optionalNullable(s.string()), + }, + { + name: "httpAliasProhibited", + value: request.httpAliasProhibited, + schema: s.optional(s.boolean()), + }, + { + name: "attestationChallengeTimeWindow", + value: request.attestationChallengeTimeWindow, + schema: s.optional(s.int()), + }, + { + name: "clientAttesterRootsEnabled", + value: request.clientAttesterRootsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "clientAttesterRootsOnly", + value: request.clientAttesterRootsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "keyAttesterRootsEnabled", + value: request.keyAttesterRootsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "keyAttesterRootsOnly", + value: request.keyAttesterRootsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "clientAttesterRoots", + value: request.clientAttesterRoots, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "keyAttesterRoots", + value: request.keyAttesterRoots, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "backchannelLogoutSupported", + value: request.backchannelLogoutSupported, + schema: s.optional(s.boolean()), + }, + { + name: "backchannelLogoutSessionSupported", + value: request.backchannelLogoutSessionSupported, + schema: s.optional(s.boolean()), + }, + { name: "haipVersion", value: request.haipVersion, schema: s.optionalNullable(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: serviceSchema }, + errorFactory: ServiceManagement.ServiceCreateApiError, + }, + options, + ); + } + + /** + * Create Service (IDP) + * + * @remarks + * Create a new service on the API server, belonging to the specified organization. A service can + * be created with an organization token or by a user with the CREATE_SERVICE role. + * + * This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the + * regional API clusters. + * + * @returns Service created successfully. + * + * @throws {@link ServiceManagement.ServiceCreateIdpApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceCreateIdpApi( + request: ServiceManagement.ServiceCreateIdpApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/service"), + auth: this.#auth.bearer, + pathParams: [], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: serviceCreateIdpRequestSchema }, + }, + { + success: { kind: "json", schema: serviceSchema }, + errorFactory: ServiceManagement.ServiceCreateIdpApiError, + }, + options, + ); + } + + /** + * Delete Service ⚑ + * + * @remarks + * Delete a service. + * + * @returns The service was successfully deleted. + * + * @throws {@link ServiceManagement.ServiceDeleteApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceDeleteApi( + request: ServiceManagement.ServiceDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/service/delete"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "empty" }, + errorFactory: ServiceManagement.ServiceDeleteApiError, + }, + options, + ); + } + + /** + * Get Service + * + * @remarks + * Get a service. + * + * If the access token can only view or modify clients underneath this service, but does not have + * access to view this service directly, a limited view of the service will be returned. + * + * @throws {@link ServiceManagement.ServiceGetApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceGetApi( + request: ServiceManagement.ServiceGetApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/service/get"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: serviceSchema }, + errorFactory: ServiceManagement.ServiceGetApiError, + }, + options, + ); + } + + /** + * List Services + * + * @remarks + * Get a list of services. + * + * If the access token can only view or modify clients underneath a service, but does not have + * access to view that service directly, a limited view of the service will be returned. + * Otherwise, all properties of the service are returned. + * + * If the access token is an administrative token, this returns a list of all services on the + * Authlete instance. Otherwise, all services that the access token can view, even in a limited + * fashion, are returned. + * + * @throws {@link ServiceManagement.ServiceGetListApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceGetListApi( + request: ServiceManagement.ServiceGetListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/service/get/list"), + auth: this.#auth.bearer, + pathParams: [], + query: [ + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: serviceGetListResponseSchema }, + errorFactory: ServiceManagement.ServiceGetListApiError, + }, + options, + ); + } + + /** + * Remove Service (IDP) ⚑ + * + * @remarks + * Delete a service from the API server. A service can be deleted with an organization token or by + * a user with the MODIFY_SERVICE role. + * + * This endpoint is hosted on the Authlete IdP server (`https://login.authlete.com`), not on the + * regional API clusters. + * + * @returns Service removed successfully. + * + * @throws {@link ServiceManagement.ServiceRemoveIdpApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceRemoveIdpApi( + request: ServiceManagement.ServiceRemoveIdpApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/service/remove"), + auth: this.#auth.bearer, + pathParams: [], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: serviceRemoveIdpRequestSchema }, + }, + { + success: { kind: "empty" }, + errorFactory: ServiceManagement.ServiceRemoveIdpApiError, + }, + options, + ); + } + + /** + * Update Service + * + * @remarks + * Update a service. + * + * @throws {@link ServiceManagement.ServiceUpdateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + serviceUpdateApi( + request: ServiceManagement.ServiceUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/service/update"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "number", value: request.number, schema: s.optional(s.int()) }, + { name: "serviceName", value: request.serviceName, schema: s.optionalNullable(s.string()) }, + { name: "issuer", value: request.issuer, schema: s.optionalNullable(s.string()) }, + { name: "description", value: request.description, schema: s.optionalNullable(s.string()) }, + { name: "apiKey", value: request.apiKey, schema: s.optional(s.int()) }, + { name: "apiSecret", value: request.apiSecret, schema: s.optionalNullable(s.string()) }, + { + name: "tokenBatchNotificationEndpoint", + value: request.tokenBatchNotificationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "clientAssertionAudRestrictedToIssuer", + value: request.clientAssertionAudRestrictedToIssuer, + schema: s.optional(s.boolean()), + }, + { name: "serviceOwnerNumber", value: request.serviceOwnerNumber, schema: s.optional(s.int()) }, + { name: "clientsPerDeveloper", value: request.clientsPerDeveloper, schema: s.optional(s.int()) }, + { + name: "developerAuthenticationCallbackEndpoint", + value: request.developerAuthenticationCallbackEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "developerAuthenticationCallbackApiKey", + value: request.developerAuthenticationCallbackApiKey, + schema: s.optionalNullable(s.string()), + }, + { + name: "developerAuthenticationCallbackApiSecret", + value: request.developerAuthenticationCallbackApiSecret, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedSnses", + value: request.supportedSnses, + schema: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + }, + { + name: "snsCredentials", + value: request.snsCredentials, + schema: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + }, + { + name: "supportedDeveloperSnses", + value: request.supportedDeveloperSnses, + schema: s.optionalNullable(s.array(s.lazy(() => snsSchema))), + }, + { + name: "developerSnsCredentials", + value: request.developerSnsCredentials, + schema: s.optionalNullable(s.array(s.lazy(() => snsCredentialsSchema))), + }, + { + name: "clientIdAliasEnabled", + value: request.clientIdAliasEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "metadata", + value: request.metadata, + schema: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + }, + { name: "createdAt", value: request.createdAt, schema: s.optional(s.int()) }, + { name: "modifiedAt", value: request.modifiedAt, schema: s.optional(s.int()) }, + { + name: "authenticationCallbackEndpoint", + value: request.authenticationCallbackEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "authenticationCallbackApiKey", + value: request.authenticationCallbackApiKey, + schema: s.optionalNullable(s.string()), + }, + { + name: "authenticationCallbackApiSecret", + value: request.authenticationCallbackApiSecret, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedAcrs", + value: request.supportedAcrs, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedGrantTypes", + value: request.supportedGrantTypes, + schema: s.optionalNullable(s.array(s.lazy(() => grantTypeSchema))), + }, + { + name: "supportedResponseTypes", + value: request.supportedResponseTypes, + schema: s.optionalNullable(s.array(s.lazy(() => responseTypeSchema))), + }, + { + name: "supportedAuthorizationDetailsTypes", + value: request.supportedAuthorizationDetailsTypes, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedServiceProfiles", + value: request.supportedServiceProfiles, + schema: s.optionalNullable(s.array(s.lazy(() => serviceProfileSchema))), + }, + { + name: "errorDescriptionOmitted", + value: request.errorDescriptionOmitted, + schema: s.optional(s.boolean()), + }, + { name: "errorUriOmitted", value: request.errorUriOmitted, schema: s.optional(s.boolean()) }, + { + name: "authorizationEndpoint", + value: request.authorizationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directAuthorizationEndpointEnabled", + value: request.directAuthorizationEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedUiLocales", + value: request.supportedUiLocales, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDisplays", + value: request.supportedDisplays, + schema: s.optionalNullable(s.array(s.lazy(() => displaySchema))), + }, + { name: "pkceRequired", value: request.pkceRequired, schema: s.optional(s.boolean()) }, + { name: "pkceS256Required", value: request.pkceS256Required, schema: s.optional(s.boolean()) }, + { + name: "authorizationResponseDuration", + value: request.authorizationResponseDuration, + schema: s.optional(s.int()), + }, + { + name: "authorizationCodeDuration", + value: request.authorizationCodeDuration, + schema: s.optional(s.int()), + }, + { name: "tokenEndpoint", value: request.tokenEndpoint, schema: s.optionalNullable(s.string()) }, + { + name: "directTokenEndpointEnabled", + value: request.directTokenEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedTokenAuthMethods", + value: request.supportedTokenAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "missingClientIdAllowed", + value: request.missingClientIdAllowed, + schema: s.optional(s.boolean()), + }, + { + name: "revocationEndpoint", + value: request.revocationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directRevocationEndpointEnabled", + value: request.directRevocationEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedRevocationAuthMethods", + value: request.supportedRevocationAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "introspectionEndpoint", + value: request.introspectionEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directIntrospectionEndpointEnabled", + value: request.directIntrospectionEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "supportedIntrospectionAuthMethods", + value: request.supportedIntrospectionAuthMethods, + schema: s.optionalNullable(s.array(s.lazy(() => clientAuthMethodSchema))), + }, + { + name: "pushedAuthReqEndpoint", + value: request.pushedAuthReqEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "pushedAuthReqDuration", + value: request.pushedAuthReqDuration, + schema: s.optional(s.int()), + }, + { name: "parRequired", value: request.parRequired, schema: s.optional(s.boolean()) }, + { + name: "requestObjectRequired", + value: request.requestObjectRequired, + schema: s.optional(s.boolean()), + }, + { + name: "traditionalRequestObjectProcessingApplied", + value: request.traditionalRequestObjectProcessingApplied, + schema: s.optional(s.boolean()), + }, + { + name: "mutualTlsValidatePkiCertChain", + value: request.mutualTlsValidatePkiCertChain, + schema: s.optional(s.boolean()), + }, + { + name: "trustedRootCertificates", + value: request.trustedRootCertificates, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "mtlsEndpointAliases", + value: request.mtlsEndpointAliases, + schema: s.optionalNullable(s.array(s.lazy(() => namedUriSchema))), + }, + { + name: "accessTokenType", + value: request.accessTokenType, + schema: s.optionalNullable(s.string()), + }, + { + name: "tlsClientCertificateBoundAccessTokens", + value: request.tlsClientCertificateBoundAccessTokens, + schema: s.optional(s.boolean()), + }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "singleAccessTokenPerSubject", + value: request.singleAccessTokenPerSubject, + schema: s.optional(s.boolean()), + }, + { + name: "accessTokenSignAlg", + value: request.accessTokenSignAlg, + schema: s.optionalNullable(s.lazy(() => jwsAlgSchema)), + }, + { + name: "accessTokenSignatureKeyId", + value: request.accessTokenSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { + name: "refreshTokenDurationKept", + value: request.refreshTokenDurationKept, + schema: s.optional(s.boolean()), + }, + { + name: "refreshTokenDurationReset", + value: request.refreshTokenDurationReset, + schema: s.optional(s.boolean()), + }, + { name: "refreshTokenKept", value: request.refreshTokenKept, schema: s.optional(s.boolean()) }, + { + name: "supportedScopes", + value: request.supportedScopes, + schema: s.optionalNullable(s.array(s.lazy(() => scopeSchema))), + }, + { name: "scopeRequired", value: request.scopeRequired, schema: s.optional(s.boolean()) }, + { name: "idTokenDuration", value: request.idTokenDuration, schema: s.optional(s.int()) }, + { name: "allowableClockSkew", value: request.allowableClockSkew, schema: s.optional(s.int()) }, + { + name: "supportedClaimTypes", + value: request.supportedClaimTypes, + schema: s.optionalNullable(s.array(s.lazy(() => claimTypeSchema))), + }, + { + name: "supportedClaimLocales", + value: request.supportedClaimLocales, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedClaims", + value: request.supportedClaims, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "claimShortcutRestrictive", + value: request.claimShortcutRestrictive, + schema: s.optional(s.boolean()), + }, + { name: "jwksUri", value: request.jwksUri, schema: s.optionalNullable(s.string()) }, + { + name: "directJwksEndpointEnabled", + value: request.directJwksEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { name: "jwks", value: request.jwks, schema: s.optionalNullable(s.string()) }, + { + name: "idTokenSignatureKeyId", + value: request.idTokenSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "userInfoSignatureKeyId", + value: request.userInfoSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "authorizationSignatureKeyId", + value: request.authorizationSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "userInfoEndpoint", + value: request.userInfoEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "directUserInfoEndpointEnabled", + value: request.directUserInfoEndpointEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "dynamicRegistrationSupported", + value: request.dynamicRegistrationSupported, + schema: s.optional(s.boolean()), + }, + { + name: "registrationEndpoint", + value: request.registrationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "registrationManagementEndpoint", + value: request.registrationManagementEndpoint, + schema: s.optionalNullable(s.string()), + }, + { name: "policyUri", value: request.policyUri, schema: s.optionalNullable(s.string()) }, + { name: "tosUri", value: request.tosUri, schema: s.optionalNullable(s.string()) }, + { + name: "serviceDocumentation", + value: request.serviceDocumentation, + schema: s.optionalNullable(s.string()), + }, + { + name: "backchannelAuthenticationEndpoint", + value: request.backchannelAuthenticationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "supportedBackchannelTokenDeliveryModes", + value: request.supportedBackchannelTokenDeliveryModes, + schema: s.optionalNullable(s.array(s.lazy(() => deliveryModeSchema))), + }, + { + name: "backchannelAuthReqIdDuration", + value: request.backchannelAuthReqIdDuration, + schema: s.optional(s.int()), + }, + { + name: "backchannelPollingInterval", + value: request.backchannelPollingInterval, + schema: s.optional(s.int()), + }, + { + name: "backchannelUserCodeParameterSupported", + value: request.backchannelUserCodeParameterSupported, + schema: s.optional(s.boolean()), + }, + { + name: "backchannelBindingMessageRequiredInFapi", + value: request.backchannelBindingMessageRequiredInFapi, + schema: s.optional(s.boolean()), + }, + { + name: "deviceAuthorizationEndpoint", + value: request.deviceAuthorizationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceVerificationUri", + value: request.deviceVerificationUri, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceVerificationUriComplete", + value: request.deviceVerificationUriComplete, + schema: s.optionalNullable(s.string()), + }, + { + name: "deviceFlowCodeDuration", + value: request.deviceFlowCodeDuration, + schema: s.optional(s.int()), + }, + { + name: "deviceFlowPollingInterval", + value: request.deviceFlowPollingInterval, + schema: s.optional(s.int()), + }, + { + name: "userCodeCharset", + value: request.userCodeCharset, + schema: s.optionalNullable(s.lazy(() => userCodeCharsetSchema)), + }, + { name: "userCodeLength", value: request.userCodeLength, schema: s.optional(s.int()) }, + { + name: "supportedTrustFrameworks", + value: request.supportedTrustFrameworks, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedEvidence", + value: request.supportedEvidence, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedIdentityDocuments", + value: request.supportedIdentityDocuments, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedVerificationMethods", + value: request.supportedVerificationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedVerifiedClaims", + value: request.supportedVerifiedClaims, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "verifiedClaimsValidationSchemaSet", + value: request.verifiedClaimsValidationSchemaSet, + schema: s.optional(s.lazy(() => verifiedClaimsValidationSchemaSchema)), + }, + { + name: "attributes", + value: request.attributes, + schema: s.optionalNullable(s.array(s.lazy(() => pairSchema))), + }, + { name: "nbfOptional", value: request.nbfOptional, schema: s.optional(s.boolean()) }, + { name: "issSuppressed", value: request.issSuppressed, schema: s.optional(s.boolean()) }, + { + name: "supportedCustomClientMetadata", + value: request.supportedCustomClientMetadata, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "tokenExpirationLinked", + value: request.tokenExpirationLinked, + schema: s.optional(s.boolean()), + }, + { + name: "frontChannelRequestObjectEncryptionRequired", + value: request.frontChannelRequestObjectEncryptionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionAlgMatchRequired", + value: request.requestObjectEncryptionAlgMatchRequired, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectEncryptionEncMatchRequired", + value: request.requestObjectEncryptionEncMatchRequired, + schema: s.optional(s.boolean()), + }, + { name: "hsmEnabled", value: request.hsmEnabled, schema: s.optional(s.boolean()) }, + { + name: "hsks", + value: request.hsks, + schema: s.optionalNullable(s.array(s.lazy(() => hskSchema))), + }, + { + name: "grantManagementEndpoint", + value: request.grantManagementEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "grantManagementActionRequired", + value: request.grantManagementActionRequired, + schema: s.optional(s.boolean()), + }, + { + name: "unauthorizedOnClientConfigSupported", + value: request.unauthorizedOnClientConfigSupported, + schema: s.optional(s.boolean()), + }, + { + name: "dcrScopeUsedAsRequestable", + value: request.dcrScopeUsedAsRequestable, + schema: s.optional(s.boolean()), + }, + { + name: "endSessionEndpoint", + value: request.endSessionEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "challengeEndpoint", + value: request.challengeEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "loopbackRedirectionUriVariable", + value: request.loopbackRedirectionUriVariable, + schema: s.optional(s.boolean()), + }, + { + name: "requestObjectAudienceChecked", + value: request.requestObjectAudienceChecked, + schema: s.optional(s.boolean()), + }, + { + name: "accessTokenForExternalAttachmentEmbedded", + value: request.accessTokenForExternalAttachmentEmbedded, + schema: s.optional(s.boolean()), + }, + { + name: "authorityHints", + value: request.authorityHints, + schema: s.optionalNullable(s.array(s.string())), + }, + { name: "federationEnabled", value: request.federationEnabled, schema: s.optional(s.boolean()) }, + { name: "federationJwks", value: request.federationJwks, schema: s.optionalNullable(s.string()) }, + { + name: "federationSignatureKeyId", + value: request.federationSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "federationConfigurationDuration", + value: request.federationConfigurationDuration, + schema: s.optional(s.int()), + }, + { + name: "federationRegistrationEndpoint", + value: request.federationRegistrationEndpoint, + schema: s.optionalNullable(s.string()), + }, + { + name: "organizationName", + value: request.organizationName, + schema: s.optionalNullable(s.string()), + }, + { + name: "predefinedTransformedClaims", + value: request.predefinedTransformedClaims, + schema: s.optionalNullable(s.string()), + }, + { + name: "refreshTokenIdempotent", + value: request.refreshTokenIdempotent, + schema: s.optional(s.boolean()), + }, + { name: "signedJwksUri", value: request.signedJwksUri, schema: s.optionalNullable(s.string()) }, + { + name: "supportedAttachments", + value: request.supportedAttachments, + schema: s.optionalNullable(s.array(s.lazy(() => attachmentTypeSchema))), + }, + { + name: "supportedDigestAlgorithms", + value: request.supportedDigestAlgorithms, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocuments", + value: request.supportedDocuments, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsMethods", + value: request.supportedDocumentsMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsValidationMethods", + value: request.supportedDocumentsValidationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedDocumentsVerificationMethods", + value: request.supportedDocumentsVerificationMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedElectronicRecords", + value: request.supportedElectronicRecords, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "supportedClientRegistrationTypes", + value: request.supportedClientRegistrationTypes, + schema: s.optionalNullable(s.array(s.lazy(() => clientRegistrationTypeSchema))), + }, + { + name: "tokenExchangeByIdentifiableClientsOnly", + value: request.tokenExchangeByIdentifiableClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeByConfidentialClientsOnly", + value: request.tokenExchangeByConfidentialClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeByPermittedClientsOnly", + value: request.tokenExchangeByPermittedClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeEncryptedJwtRejected", + value: request.tokenExchangeEncryptedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "tokenExchangeUnsignedJwtRejected", + value: request.tokenExchangeUnsignedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantByIdentifiableClientsOnly", + value: request.jwtGrantByIdentifiableClientsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantEncryptedJwtRejected", + value: request.jwtGrantEncryptedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "jwtGrantUnsignedJwtRejected", + value: request.jwtGrantUnsignedJwtRejected, + schema: s.optional(s.boolean()), + }, + { + name: "dcrDuplicateSoftwareIdBlocked", + value: request.dcrDuplicateSoftwareIdBlocked, + schema: s.optional(s.boolean()), + }, + { + name: "trustAnchors", + value: request.trustAnchors, + schema: s.optionalNullable(s.array(s.lazy(() => trustAnchorSchema))), + }, + { + name: "openidDroppedOnRefreshWithoutOfflineAccess", + value: request.openidDroppedOnRefreshWithoutOfflineAccess, + schema: s.optional(s.boolean()), + }, + { + name: "idTokenDroppedOnIssueWithoutOpenid", + value: request.idTokenDroppedOnIssueWithoutOpenid, + schema: s.optional(s.boolean()), + }, + { + name: "supportedDocumentsCheckMethods", + value: request.supportedDocumentsCheckMethods, + schema: s.optionalNullable(s.array(s.string())), + }, + { name: "rsResponseSigned", value: request.rsResponseSigned, schema: s.optional(s.boolean()) }, + { name: "cnonceDuration", value: request.cnonceDuration, schema: s.optional(s.int()) }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + { + name: "verifiableCredentialsEnabled", + value: request.verifiableCredentialsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "credentialJwksUri", + value: request.credentialJwksUri, + schema: s.optionalNullable(s.string()), + }, + { + name: "credentialOfferDuration", + value: request.credentialOfferDuration, + schema: s.optional(s.int()), + }, + { name: "dpopNonceDuration", value: request.dpopNonceDuration, schema: s.optional(s.int()) }, + { + name: "preAuthorizedGrantAnonymousAccessSupported", + value: request.preAuthorizedGrantAnonymousAccessSupported, + schema: s.optional(s.boolean()), + }, + { + name: "credentialTransactionDuration", + value: request.credentialTransactionDuration, + schema: s.optional(s.int()), + }, + { + name: "introspectionSignatureKeyId", + value: request.introspectionSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { + name: "resourceSignatureKeyId", + value: request.resourceSignatureKeyId, + schema: s.optionalNullable(s.string()), + }, + { name: "userPinLength", value: request.userPinLength, schema: s.optional(s.int()) }, + { + name: "supportedPromptValues", + value: request.supportedPromptValues, + schema: s.optionalNullable(s.array(s.lazy(() => promptSchema))), + }, + { name: "idTokenReissuable", value: request.idTokenReissuable, schema: s.optional(s.boolean()) }, + { name: "credentialJwks", value: request.credentialJwks, schema: s.optionalNullable(s.string()) }, + { + name: "fapiModes", + value: request.fapiModes, + schema: s.optionalNullable(s.array(s.lazy(() => fapiModeSchema))), + }, + { name: "credentialDuration", value: request.credentialDuration, schema: s.optional(s.int()) }, + { + name: "credentialIssuerMetadata", + value: request.credentialIssuerMetadata, + schema: s.optionalNullable(s.lazy(() => credentialIssuerMetadataSchema)), + }, + { name: "idTokenAudType", value: request.idTokenAudType, schema: s.optionalNullable(s.string()) }, + { + name: "nativeSsoSupported", + value: request.nativeSsoSupported, + schema: s.optional(s.boolean()), + }, + { name: "oid4vciVersion", value: request.oid4VciVersion, schema: s.optionalNullable(s.string()) }, + { + name: "cimdMetadataPolicyEnabled", + value: request.cimdMetadataPolicyEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "clientIdMetadataDocumentSupported", + value: request.clientIdMetadataDocumentSupported, + schema: s.optional(s.boolean()), + }, + { + name: "cimdAllowlistEnabled", + value: request.cimdAllowlistEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "cimdAllowlist", + value: request.cimdAllowlist, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "cimdAlwaysRetrieved", + value: request.cimdAlwaysRetrieved, + schema: s.optional(s.boolean()), + }, + { name: "cimdHttpPermitted", value: request.cimdHttpPermitted, schema: s.optional(s.boolean()) }, + { + name: "cimdQueryPermitted", + value: request.cimdQueryPermitted, + schema: s.optional(s.boolean()), + }, + { + name: "cimdMetadataPolicy", + value: request.cimdMetadataPolicy, + schema: s.optionalNullable(s.string()), + }, + { + name: "httpAliasProhibited", + value: request.httpAliasProhibited, + schema: s.optional(s.boolean()), + }, + { + name: "attestationChallengeTimeWindow", + value: request.attestationChallengeTimeWindow, + schema: s.optional(s.int()), + }, + { + name: "clientAttesterRootsEnabled", + value: request.clientAttesterRootsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "clientAttesterRootsOnly", + value: request.clientAttesterRootsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "keyAttesterRootsEnabled", + value: request.keyAttesterRootsEnabled, + schema: s.optional(s.boolean()), + }, + { + name: "keyAttesterRootsOnly", + value: request.keyAttesterRootsOnly, + schema: s.optional(s.boolean()), + }, + { + name: "clientAttesterRoots", + value: request.clientAttesterRoots, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "keyAttesterRoots", + value: request.keyAttesterRoots, + schema: s.optionalNullable(s.array(s.string())), + }, + { + name: "backchannelLogoutSupported", + value: request.backchannelLogoutSupported, + schema: s.optional(s.boolean()), + }, + { + name: "backchannelLogoutSessionSupported", + value: request.backchannelLogoutSessionSupported, + schema: s.optional(s.boolean()), + }, + { name: "haipVersion", value: request.haipVersion, schema: s.optionalNullable(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: serviceSchema }, + errorFactory: ServiceManagement.ServiceUpdateApiError, + }, + options, + ); + } +} + +export namespace ServiceManagement { + export type ServiceConfigurationApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * This boolean value indicates whether the JSON in the response should be formatted or not. If + * `true`, the JSON in the response is pretty-formatted. The default value is `false`. + */ + pretty?: boolean; + /** + * Get the JSON Patch [RFC 6902 JavaScript Object Notation (JSON) + * Patch](https://www.rfc-editor.org/rfc/rfc6902) to be applied. + */ + patch?: string; + }; + + export class ServiceConfigurationApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ServiceCreateApiRequest = { + /** The sequential number of the service. The value of this property is assigned by Authlete. */ + number?: number; + /** The name of this service. */ + serviceName?: string | null; + /** + * The issuer identifier of the service. + * + * A URL that starts with https:// and has no query or fragment component. + * + * The value of this property is used as `iss` claim in an [ID + * token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and `issuer` property + * in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + issuer?: string | null; + /** The description about the service. */ + description?: string | null; + /** + * The service ID used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + apiKey?: number; + /** + * The API secret of this service. This value is assigned by Authlete and is used for service + * authentication in API calls. + */ + apiSecret?: string | null; + /** + * The endpoint for batch token notifications. This endpoint is called when multiple tokens are + * issued or revoked in a batch operation. + */ + tokenBatchNotificationEndpoint?: string | null; + /** + * The flag indicating whether the audience of client assertion JWTs must match the issuer + * identifier of this service. + */ + clientAssertionAudRestrictedToIssuer?: boolean; + /** + * The number of the organization that owns this service. This value is assigned by Authlete. + */ + serviceOwnerNumber?: number; + /** The maximum number of client applications that a developer can have. */ + clientsPerDeveloper?: number; + /** + * The endpoint for developer authentication callbacks. This is used when developers log into + * the developer portal. + */ + developerAuthenticationCallbackEndpoint?: string | null; + /** The API key for basic authentication at the developer authentication callback endpoint. */ + developerAuthenticationCallbackApiKey?: string | null; + /** + * The API secret for basic authentication at the developer authentication callback endpoint. + */ + developerAuthenticationCallbackApiSecret?: string | null; + /** Social login services (SNS) that this service supports for end-user authentication. */ + supportedSnses?: Sns[] | null; + /** + * The credentials for social login services (SNS) that are used for end-user authentication. + */ + snsCredentials?: SnsCredentials[] | null; + /** Social login services (SNS) that this service supports for developer authentication. */ + supportedDeveloperSnses?: Sns[] | null; + /** + * The credentials for social login services (SNS) that are used for developer authentication. + */ + developerSnsCredentials?: SnsCredentials[] | null; + /** Deprecated. Always `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The `metadata` of the service. The content of the returned array depends on contexts. The + * predefined service metadata is listed in the following table. + * + * | Key | Description | + * | --- | --- | + * | `clientCount` | The number of client applications which belong to this service. | + */ + metadata?: Pair[] | null; + /** + * The time at which this service was created. The value is represented as milliseconds since + * the UNIX epoch (`1970-01-01`). + */ + createdAt?: number; + /** + * The time at which this service was last modified. The value is represented as milliseconds + * since the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** + * A Web API endpoint for user authentication which is to be prepared on the service side. + * + * The endpoint must be implemented if you do not implement the UI at the authorization endpoint + * but use the one provided by Authlete. + * + * The user authentication at the authorization endpoint provided by Authlete is performed by + * making a `POST` request to this endpoint. + */ + authenticationCallbackEndpoint?: string | null; + /** + * API key for basic authentication at the authentication callback endpoint. + * + * If the value is not empty, Authlete generates Authorization header for Basic authentication + * when making a request to the authentication callback endpoint. + */ + authenticationCallbackApiKey?: string | null; + /** API secret for `basic` authentication at the authentication callback endpoint. */ + authenticationCallbackApiSecret?: string | null; + /** + * Values of acrs (authentication context class references) that the service supports. + * + * The value of this property is used as `acr_values_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedAcrs?: string[] | null; + /** + * Values of `grant_type` request parameter that the service supports. + * + * The value of this property is used as `grant_types_supported property` in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedGrantTypes?: GrantType[] | null; + /** + * Values of `response_type` request parameter that the service supports. Valid values are + * listed in Response Type. + * + * The value of this property is used as `response_types_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedResponseTypes?: ResponseType[] | null; + /** + * The supported data types that can be used as values of the type field in + * `authorization_details`. + * + * This property corresponds to the `authorization_details_types_supported` metadata. See "OAuth + * 2.0 Rich Authorization Requests" (RAR) for details. + */ + supportedAuthorizationDetailsTypes?: string[] | null; + /** The profiles that this service supports. */ + supportedServiceProfiles?: ServiceProfile[] | null; + /** + * The flag to indicate whether the `error_description` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_description` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_description` response parameter in error + * responses. + */ + errorDescriptionOmitted?: boolean; + /** + * The flag to indicate whether the `error_uri` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_uri` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_uri` response parameter in error responses. + */ + errorUriOmitted?: boolean; + /** + * The authorization endpoint of the service. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/authorization`. + * + * The value of this property is used as `authorization_endpoint` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + authorizationEndpoint?: string | null; + /** + * Deprecated. The direct authorization endpoint was removed in Authlete 3.0.15, so this flag + * has no effect and is always returned as `false`. + */ + directAuthorizationEndpointEnabled?: boolean; + /** + * UI locales that the service supports. + * + * Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). + * For example, `en-US` and `ja-JP`. + * + * The value of this property is used as `ui_locales_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedUiLocales?: string[] | null; + /** + * Values of `display` request parameter that service supports. + * + * The value of this property is used as `display_values_supported` property in the Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedDisplays?: Display[] | null; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** + * The duration of authorization response JWTs in seconds. + * + * [Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 + * (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines new values for the + * `response_mode` request parameter. They are `query.jwt`, `fragment.jwt`, `form_post.jwt` and + * `jwt`. If one of them is specified as the response mode, response parameters from the + * authorization endpoint will be packed into a JWT. This property is used to compute the value + * of the `exp` claim of the JWT. + */ + authorizationResponseDuration?: number; + /** The duration of authorization codes in seconds. */ + authorizationCodeDuration?: number; + /** + * The [token endpoint](https://tools.ietf.org/html/rfc6749#section-3.2) of the service. + * + * A URL that starts with `https://` and has not fragment component. For example, + * `https://example.com/auth/token`. + * + * The value of this property is used as `token_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tokenEndpoint?: string | null; + /** + * Deprecated. The direct token endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directTokenEndpointEnabled?: boolean; + /** + * Client authentication methods supported by the token endpoint of the service. + * + * The value of this property is used as `token_endpoint_auth_methods_supports` property in the + * [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedTokenAuthMethods?: ClientAuthMethod[] | null; + /** + * The flag to indicate token requests from public clients without the `client_id` request + * parameter are allowed when the client can be guessed from `authorization_code` or + * `refresh_token`. + * + * This flag should not be set unless you have special reasons. + */ + missingClientIdAllowed?: boolean; + /** + * The [revocation endpoint](https://tools.ietf.org/html/rfc7009) of the service. + * + * A URL that starts with `https://`. For example, `https://example.com/auth/revocation`. + */ + revocationEndpoint?: string | null; + /** + * Deprecated. The direct revocation endpoint was removed in Authlete 3.0.15, so this flag has + * no effect and is always returned as `false`. + */ + directRevocationEndpointEnabled?: boolean; + /** Client authentication methods supported at the revocation endpoint. */ + supportedRevocationAuthMethods?: ClientAuthMethod[] | null; + /** The URI of the introspection endpoint. */ + introspectionEndpoint?: string | null; + /** + * Deprecated. The direct introspection endpoint was removed in Authlete 3.0.15, so this flag + * has no effect and is always returned as `false`. + */ + directIntrospectionEndpointEnabled?: boolean; + /** Client authentication methods supported at the introspection endpoint. */ + supportedIntrospectionAuthMethods?: ClientAuthMethod[] | null; + /** + * The URI of the pushed authorization request endpoint. + * + * This property corresponds to the `pushed_authorization_request_endpoint` metadata defined in + * "[5. Authorization Server + * Metadata](https://tools.ietf.org/html/draft-lodderstedt-oauth-par#section-5)" of OAuth 2.0 + * Pushed Authorization Requests. + */ + pushedAuthReqEndpoint?: string | null; + /** The duration of pushed authorization requests in seconds. */ + pushedAuthReqDuration?: number; + /** + * The flag to indicate whether this service requires that clients use the pushed authorization + * request endpoint. + * + * This property corresponds to the `require_pushed_authorization_requests` server metadata + * defined in [OAuth 2.0 Pushed Authorization + * Requests](https://tools.ietf.org/html/draft-lodderstedt-oauth-par). + */ + parRequired?: boolean; + /** + * The flag to indicate whether this service requires that authorization requests always utilize + * a request object by using either request or `request_uri` request parameter. + * + * If this flag is set to `true` and the value of `traditionalRequestObjectProcessingApplied` is + * `false`, the value of `require_signed_request_object` server metadata of this service is + * reported as `true` in the discovery document. The metadata is defined in JAR (JWT Secured + * Authorization Request). That `require_signed_request_object` is `true` means that + * authorization requests which don't conform to the JAR specification are rejected. + */ + requestObjectRequired?: boolean; + /** + * The flag to indicate whether a request object is processed based on rules defined in [OpenID + * Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) or JAR (JWT Secured + * Authorization Request). + */ + traditionalRequestObjectProcessingApplied?: boolean; + /** + * The flag to indicate whether this service validates certificate chains during PKI-based + * client mutual TLS authentication. + */ + mutualTlsValidatePkiCertChain?: boolean; + /** + * The list of root certificates trusted by this service for PKI-based client mutual TLS + * authentication. + */ + trustedRootCertificates?: string[] | null; + /** The MTLS endpoint aliases. */ + mtlsEndpointAliases?: NamedUri[] | null; + /** + * The access token type. + * + * This value is used as the value of `token_type` property in access token responses. If this + * service complies with [RFC 6750](https://tools.ietf.org/html/rfc6750), the value of this + * property should be `Bearer`. + * + * See [RFC 6749 (OAuth 2.0), 7.1. Access Token + * Types](https://tools.ietf.org/html/rfc6749#section-7.1) for details. + */ + accessTokenType?: string | null; + /** + * The flag to indicate whether this service supports issuing TLS client certificate bound + * access tokens. + */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The duration of access tokens in seconds. This value is used as the value of `expires_in` + * property in access token responses. `expires_in` is defined [RFC 6749, 5.1. Successful + * Response](https://tools.ietf.org/html/rfc6749#section-5.1). + */ + accessTokenDuration?: number; + /** + * The flag to indicate whether the number of access tokens per subject (and per client) is at + * most one or can be more. + * + * If `true`, an attempt to issue a new access token invalidates existing access tokens that are + * associated with the same subject and the same client. + * + * Note that, however, attempts by [Client Credentials + * Flow](https://tools.ietf.org/html/rfc6749#section-4.4) do not invalidate existing access + * tokens because access tokens issued by Client Credentials Flow are not associated with any + * end-user's subject. Also note that an attempt by [Refresh Token + * Flow](https://tools.ietf.org/html/rfc6749#section-6) invalidates the coupled access token + * only and this invalidation is always performed regardless of whether the value of this + * setting item is `true` or `false`. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + accessTokenSignAlg?: JwsAlg | null; + /** + * The key ID to identify a JWK used for signing access tokens. + * + * A JWK Set can be registered as a property of a service. A JWK Set can contain 0 or more JWKs. + * Authlete Server has to pick up one JWK for signing from the JWK Set when it generates a + * JWT-based access token. Authlete Server searches the registered JWK Set for a JWK which + * satisfies conditions for access token signature. If the number of JWK candidates which + * satisfy the conditions is 1, there is no problem. On the other hand, if there exist multiple + * candidates, a Key ID is needed to be specified so that Authlete Server can pick up one JWK + * from among the JWK candidates. + */ + accessTokenSignatureKeyId?: string | null; + /** + * The duration of refresh tokens in seconds. The related specifications have no requirements on + * refresh token duration, but Authlete sets expiration for refresh tokens. + */ + refreshTokenDuration?: number; + /** + * The flag to indicate whether the remaining duration of the used refresh token is taken over + * to the newly issued refresh token. + */ + refreshTokenDurationKept?: boolean; + /** + * The flag which indicates whether duration of refresh tokens are reset when they are used even + * if the `refreshTokenKept` property of this service set to is `true` (= even if "Refresh Token + * Continuous Use" is "Kept"). + * + * This flag has no effect when the `refreshTokenKept` property is set to `false`. In other + * words, if this service issues a new refresh token on every refresh token request, the refresh + * token will have fresh duration (unless `refreshTokenDurationKept` is set to `true`) and this + * `refreshTokenDurationReset` property is not referenced. + */ + refreshTokenDurationReset?: boolean; + /** + * The flag to indicate whether a refresh token remains unchanged or gets renewed after its use. + * + * If `true`, a refresh token used to get a new access token remains valid after its use. + * Otherwise, if `false`, a refresh token is invalidated after its use and a new refresh token + * is issued. + * + * See [RFC 6749 6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), + * as to how to get a new access token using a refresh token. + */ + refreshTokenKept?: boolean; + /** Scopes supported by the service. */ + supportedScopes?: Scope[] | null; + /** The flag to indicate whether requests that request no scope are rejected or not. */ + scopeRequired?: boolean; + /** + * 'The duration of [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)s + * in seconds. This value is used to calculate the value of `exp` claim in an ID token.' + */ + idTokenDuration?: number; + /** + * The allowable clock skew between the server and clients in seconds. + * + * The clock skew is taken into consideration when time-related claims in a JWT (e.g. `exp`, + * `iat`, `nbf`) are verified. + */ + allowableClockSkew?: number; + /** + * Claim types supported by the service. Valid values are listed in Claim Type. Note that + * Authlete currently doesn't provide any API to help implementations for `AGGREGATED` and + * `DISTRIBUTED`. + * + * The value of this property is used as `claim_types_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimTypes?: ClaimType[] | null; + /** + * Claim locales that the service supports. Each element is a language tag defined in [RFC + * 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`. See [OpenID + * Connect Core 1.0, 5.2. Languages and + * Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for + * details. + * + * The value of this property is used as `claims_locales_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimLocales?: string[] | null; + /** + * Claim names that the service supports. The standard claim names listed in [OpenID Connect + * Core 1.0, 5.1. Standard + * Claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) should be + * supported. The following is the list of standard claims. + */ + supportedClaims?: string[] | null; + /** + * The flag indicating whether claims specified by shortcut scopes (e.g. `profile`) are included + * in the issued ID token only when no access token is issued. + */ + claimShortcutRestrictive?: boolean; + /** + * The URL of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. + * For example, `http://example.com/auth/jwks`. + * + * Client applications accesses this URL (1) to get the public key of the service to validate + * the signature of an ID token issued by the service and (2) to get the public key of the + * service to encrypt an request object of the client application. See [OpenID Connect Core 1.0, + * 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) + * for details. + * + * The value of this property is used as `jwks_uri` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + jwksUri?: string | null; + /** + * Deprecated. The direct JWKS endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directJwksEndpointEnabled?: boolean; + /** + * The content of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) + * document. + * + * If this property is not `null` in a `/service/create` request or a `/service/update` request, + * Authlete hosts the content in the database. This property must not be `null` and must contain + * pairs of public/private keys if the service wants to support asymmetric signatures for ID + * tokens and asymmetric encryption for request objects. See [OpenID Connect Core 1.0, 10. + * Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for + * details. + */ + jwks?: string | null; + /** The key ID to identify a JWK used for ID token signature using an asymmetric key. */ + idTokenSignatureKeyId?: string | null; + /** The key ID to identify a JWK used for user info signature using an asymmetric key. */ + userInfoSignatureKeyId?: string | null; + /** + * The key ID to identify a JWK used for signing authorization responses using an asymmetric + * key. + */ + authorizationSignatureKeyId?: string | null; + /** + * The [user info endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) of + * the service. A URL that starts with `https://`. For example, + * `https://example.com/auth/userinfo`. + * + * The value of this property is used as `userinfo_endpoint` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + userInfoEndpoint?: string | null; + /** + * Deprecated. The direct userinfo endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directUserInfoEndpointEnabled?: boolean; + /** + * The boolean flag which indicates whether the [OAuth 2.0 Dynamic Client Registration + * Protocol](https://tools.ietf.org/html/rfc7591) is supported. + */ + dynamicRegistrationSupported?: boolean; + /** + * The [registration + * endpoint](http://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration) of + * the service. A URL that starts with `https://`. For example, + * `https://example.com/auth/registration`. + * + * The value of this property is used as `registration_endpoint` property in the [OpenID + * Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + registrationEndpoint?: string | null; + /** + * The URI of the registration management endpoint. If dynamic client registration is supported, + * and this is set, this URI will be used as the basis of the client's management endpoint by + * appending `/clientid}/` to it as a path element. If this is unset, the value of + * `registrationEndpoint` will be used as the URI base instead. + */ + registrationManagementEndpoint?: string | null; + /** + * The URL of the "Policy" of the service. + * + * The value of this property is used as `op_policy_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + policyUri?: string | null; + /** + * The URL of the "Terms Of Service" of the service. + * + * The value of this property is used as `op_tos_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tosUri?: string | null; + /** + * The URL of a page where documents for developers can be found. + * + * The value of this property is used as `service_documentation` property in the [OpenID + * Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + serviceDocumentation?: string | null; + /** + * The URI of backchannel authentication endpoint, which is defined in the specification of + * [CIBA (Client Initiated Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + backchannelAuthenticationEndpoint?: string | null; + /** + * The supported backchannel token delivery modes. This property corresponds to the + * `backchannel_token_delivery_modes_supported` metadata. + * + * Backchannel token delivery modes are defined in the specification of [CIBA (Client Initiated + * Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + supportedBackchannelTokenDeliveryModes?: DeliveryMode[] | null; + /** + * The duration of backchannel authentication request IDs issued from the backchannel + * authentication endpoint in seconds. This is used as the value of the `expires_in` property in + * responses from the backchannel authentication endpoint. + */ + backchannelAuthReqIdDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications + * in seconds. This is used as the value of the `interval` property in responses from the + * backchannel authentication endpoint. + */ + backchannelPollingInterval?: number; + /** + * The boolean flag which indicates whether the `user_code` request parameter is supported at + * the backchannel authentication endpoint. This property corresponds to the + * `backchannel_user_code_parameter_supported` metadata. + */ + backchannelUserCodeParameterSupported?: boolean; + /** + * The flag to indicate whether the `binding_message` request parameter is always required + * whenever a backchannel authentication request is judged as a request for Financial-grade API. + */ + backchannelBindingMessageRequiredInFapi?: boolean; + /** + * The URI of the device authorization endpoint. + * + * Device authorization endpoint is defined in the specification of OAuth 2.0 Device + * Authorization Grant. + */ + deviceAuthorizationEndpoint?: string | null; + /** + * The verification URI for the device flow. This URI is used as the value of the + * `verification_uri` parameter in responses from the device authorization endpoint. + */ + deviceVerificationUri?: string | null; + /** + * The verification URI for the device flow with a placeholder for a user code. This URI is used + * to build the value of the `verification_uri_complete` parameter in responses from the device + * authorization endpoint. + */ + deviceVerificationUriComplete?: string | null; + /** + * The duration of device verification codes and end-user verification codes issued from the + * device authorization endpoint in seconds. This is used as the value of the `expires_in` + * property in responses from the device authorization endpoint. + */ + deviceFlowCodeDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications + * in seconds in device flow. This is used as the value of the `interval` property in responses + * from the device authorization endpoint. + */ + deviceFlowPollingInterval?: number; + userCodeCharset?: UserCodeCharset | null; + /** The length of end-user verification codes (`user_code`) for Device Flow. */ + userCodeLength?: number; + /** + * Trust frameworks supported by this service. This corresponds to the + * `trust_frameworks_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedTrustFrameworks?: string[] | null; + /** + * Evidence supported by this service. This corresponds to the `evidence_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedEvidence?: string[] | null; + /** + * Identity documents supported by this service. This corresponds to the + * `id_documents_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedIdentityDocuments?: string[] | null; + /** + * Verification methods supported by this service. This corresponds to the + * `id_documents_verification_methods_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerificationMethods?: string[] | null; + /** + * Verified claims supported by this service. This corresponds to the + * `claims_in_verified_claims_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerifiedClaims?: string[] | null; + /** The verified claims validation schema set. */ + verifiedClaimsValidationSchemaSet?: VerifiedClaimsValidationSchema; + /** The attributes of this service. */ + attributes?: Pair[] | null; + /** + * The flag indicating whether the nbf claim in the request object is optional even when the + * authorization request is regarded as a FAPI-Part2 request. + */ + nbfOptional?: boolean; + /** The flag indicating whether generation of the iss response parameter is suppressed. */ + issSuppressed?: boolean; + /** custom client metadata supported by this service. */ + supportedCustomClientMetadata?: string[] | null; + /** + * The flag indicating whether the expiration date of an access token never exceeds that of the + * corresponding refresh token. + */ + tokenExpirationLinked?: boolean; + /** + * The flag indicating whether encryption of request object is required when the request object + * is passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata of the client that has sent the request + * object. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE `enc` of encrypted request object must match the + * `request_object_encryption_enc` client metadata of the client that has sent the request + * object. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * The flag indicating whether HSM (Hardware Security Module) support is enabled for this + * service. + * + * When this flag is `false`, keys managed in HSMs are not used even if they exist. In addition, + * `/api/hsk/*` APIs reject all requests. + * + * Even if this flag is `true`, HSM-related features do not work if the configuration of the + * Authlete server you are using does not support HSM. + */ + hsmEnabled?: boolean; + /** + * The information about keys managed on HSMs (Hardware Security Modules). + * + * This `hsks` property is output only, meaning that `hsks` in requests to `/api/service/create` + * API and `/api/service/update` API do not have any effect. The contents of this property is + * controlled only by `/api/hsk/*` APIs. + */ + hsks?: Hsk[] | null; + /** The URL of the grant management endpoint. */ + grantManagementEndpoint?: string | null; + /** + * The flag indicating whether every authorization request (and any request serving as an + * authorization request such as CIBA backchannel authentication request and device + * authorization request) must include the `grant_management_action` request parameter. + */ + grantManagementActionRequired?: boolean; + /** + * The flag indicating whether Authlete's `/api/client/registration` API uses `UNAUTHORIZED` as + * a value of the `action` response parameter when appropriate. + */ + unauthorizedOnClientConfigSupported?: boolean; + /** + * The flag indicating whether the `scope` request parameter in dynamic client registration and + * update requests (RFC 7591 and RFC 7592) is used as scopes that the client can request. + * + * Limiting the range of scopes that a client can request is achieved by listing scopes in the + * `client.extension.requestableScopes` property and setting the + * `client.extension.requestableScopesEnabled` property to `true`. This feature is called + * "requestable scopes". + * + * This property affects behaviors of `/api/client/registration` and other family APIs. + */ + dcrScopeUsedAsRequestable?: boolean; + /** + * The endpoint for clients ending the sessions. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/endSession`. + * + * The value of this property is used as `end_session_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + endSessionEndpoint?: string | null; + /** + * The URL of the challenge endpoint, which is defined in OAuth 2.0 Attestation-Based Client + * Authentication. + */ + challengeEndpoint?: string | null; + /** + * The flag indicating whether the port number component of redirection URIs can be variable + * when the host component indicates loopback. + */ + loopbackRedirectionUriVariable?: boolean; + /** + * The flag indicating whether Authlete checks whether the `aud` claim of request objects + * matches the issuer identifier of this service. + */ + requestObjectAudienceChecked?: boolean; + /** + * The flag indicating whether Authlete generates access tokens for external attachments and + * embeds them in ID tokens and userinfo responses. + */ + accessTokenForExternalAttachmentEmbedded?: boolean; + /** + * Identifiers of entities that can issue entity statements for this service. This property + * corresponds to the `authority_hints` property that appears in a self-signed entity statement + * that is defined in OpenID Connect Federation 1.0. + */ + authorityHints?: string[] | null; + /** flag indicating whether this service supports OpenID Connect Federation 1 */ + federationEnabled?: boolean; + /** + * JWK Set document containing keys that are used to sign (1) self-signed entity statement of + * this service and (2) the response from `signed_jwks_uri`. + */ + federationJwks?: string | null; + /** A key ID to identify a JWK used to sign the entity configuration and the signed JWK Set. */ + federationSignatureKeyId?: string | null; + /** The duration of the entity configuration in seconds. */ + federationConfigurationDuration?: number; + /** + * The URI of the federation registration endpoint. This property corresponds to the + * `federation_registration_endpoint` server metadata that is defined in OpenID Connect + * Federation 1.0. + */ + federationRegistrationEndpoint?: string | null; + /** + * The human-readable name representing the organization that operates this service. This + * property corresponds to the `organization_name` server metadata that is defined in OpenID + * Connect Federation 1.0. + */ + organizationName?: string | null; + /** + * The transformed claims predefined by this service in JSON format. This property corresponds + * to the `transformed_claims_predefined` server metadata. + */ + predefinedTransformedClaims?: string | null; + /** + * flag indicating whether refresh token requests with the same refresh token can be made + * multiple times in quick succession and they can obtain the same renewed refresh token within + * the short period. + */ + refreshTokenIdempotent?: boolean; + /** + * The URI of the endpoint that returns this service's JWK Set document in the JWT format. This + * property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect + * Federation 1.0. + */ + signedJwksUri?: string | null; + /** + * Supported attachment types. This property corresponds to the {@code + * attachments_supported} server metadata which was added by the third implementer's draft + * of OpenID Connect for Identity Assurance 1.0. + */ + supportedAttachments?: AttachmentType[] | null; + /** + * Supported algorithms used to compute digest values of external attachments. This property + * corresponds to the `digest_algorithms_supported` server metadata which was added by the third + * implementer's draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDigestAlgorithms?: string[] | null; + /** + * Document types supported by this service. This property corresponds to the + * `documents_supported` server metadata. + */ + supportedDocuments?: string[] | null; + /** + * validation and verification processes supported by this service. This property corresponds to + * the `documents_methods_supported` server metadata. + * + * The third implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) renamed the + * `id_documents_verification_methods_supported` server metadata to + * `documents_methods_supported`. + */ + supportedDocumentsMethods?: string[] | null; + /** + * Document validation methods supported by this service. This property corresponds to the + * `documents_validation_methods_supported` server metadata which was added by the third + * implementer's draft of + */ + supportedDocumentsValidationMethods?: string[] | null; + /** + * Document verification methods supported by this service. This property corresponds to the + * `documents_verification_methods_supported` server metadata which was added by the third + * implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedDocumentsVerificationMethods?: string[] | null; + /** + * Electronic record types supported by this service. This property corresponds to the + * `electronic_records_supported` server metadata which was added by the third implementer's + * draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedElectronicRecords?: string[] | null; + supportedClientRegistrationTypes?: ClientRegistrationType[] | null; + /** + * The flag indicating whether to prohibit unidentifiable clients from making token exchange + * requests. + */ + tokenExchangeByIdentifiableClientsOnly?: boolean; + /** + * The flag indicating whether to prohibit public clients from making token exchange requests. + */ + tokenExchangeByConfidentialClientsOnly?: boolean; + /** + * The flag indicating whether to prohibit clients that have no explicit permission from making + * token exchange requests. + */ + tokenExchangeByPermittedClientsOnly?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use encrypted JWTs as + * input tokens. + */ + tokenExchangeEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use unsigned JWTs as + * input tokens. + */ + tokenExchangeUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to prohibit unidentifiable clients from using the grant type + * "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantByIdentifiableClientsOnly?: boolean; + /** + * The flag indicating whether to reject token requests that use an encrypted JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token requests that use an unsigned JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to block DCR (Dynamic Client Registration) requests whose + * "software_id" has already been used previously. + */ + dcrDuplicateSoftwareIdBlocked?: boolean; + /** + * The trust anchors that are referenced when this service resolves trust chains of relying + * parties. + * + * If this property is empty, client registration fails regardless of whether its type is + * `automatic` or `explicit`. It means that OpenID Connect Federation 1.0 does not work. + */ + trustAnchors?: TrustAnchor[] | null; + /** + * The flag indicating whether the openid scope should be dropped from scopes list assigned to + * access token issued when a refresh token grant is used. + */ + openidDroppedOnRefreshWithoutOfflineAccess?: boolean; + /** + * The flag indicating whether to refrain from issuing an ID token when the `openid` scope has + * been removed from the scopes to be granted by the `scopes` request parameter of the + * `/auth/authorization/issue` API. + * + * Flows whose `response_type` contains `id_token`, and flows that requested Native SSO, are not + * affected, because both require an ID token. + */ + idTokenDroppedOnIssueWithoutOpenid?: boolean; + /** + * Supported document check methods. This property corresponds to the + * `documents_check_methods_supported` server metadata which was added by the fourth + * implementer's draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDocumentsCheckMethods?: string[] | null; + /** The flag indicating whether this service signs responses from the resource server. */ + rsResponseSigned?: boolean; + /** The duration of `c_nonce`. */ + cnonceDuration?: number; + /** + * Whether to require DPoP proof JWTs to include the `nonce` claim whenever they are presented. + */ + dpopNonceRequired?: boolean; + /** + * Get the flag indicating whether the feature of Verifiable Credentials for this service is + * enabled or not. + */ + verifiableCredentialsEnabled?: boolean; + /** The URL at which the JWK Set document of the credential issuer is exposed. */ + credentialJwksUri?: string | null; + /** The default duration of credential offers in seconds. */ + credentialOfferDuration?: number; + /** The duration of nonce values for DPoP proof JWTs in seconds. */ + dpopNonceDuration?: number; + /** + * The flag indicating whether token requests using the pre-authorized code grant flow by + * unidentifiable clients are allowed. + */ + preAuthorizedGrantAnonymousAccessSupported?: boolean; + /** + * The duration of transaction ID in seconds that may be issued as a result of a credential + * request or a batch credential request. + */ + credentialTransactionDuration?: number; + /** The key ID of the key for signing introspection responses. */ + introspectionSignatureKeyId?: string | null; + /** The key ID of the key for signing introspection responses. */ + resourceSignatureKeyId?: string | null; + /** The default length of user PINs. */ + userPinLength?: number; + /** The supported `prompt` values. */ + supportedPromptValues?: Prompt[] | null; + /** + * The flag indicating whether to enable the feature of ID token reissuance in the refresh token + * flow. + */ + idTokenReissuable?: boolean; + /** + * The JWK Set document containing private keys that are used to sign verifiable credentials. + */ + credentialJwks?: string | null; + /** + * FAPI modes for this service. + * + * When the value of this property is not `null`, Authlete always processes requests to this + * service based on the specified FAPI modes if the FAPI feature is enabled in Authlete and the + * FAPI profile is supported by this service. + * + * For instance, when this property is set to an array containing `FAPI1_ADVANCED` only, + * Authlete always processes requests to this service based on "Financial-grade API Security + * Profile 1.0 - Part 2: Advanced" if the FAPI feature is enabled in Authlete and the FAPI + * profile is supported by this service. + */ + fapiModes?: FapiMode[] | null; + /** The default duration of verifiable credentials in seconds. */ + credentialDuration?: number; + credentialIssuerMetadata?: CredentialIssuerMetadata | null; + /** The type of the `aud` claim in ID tokens. */ + idTokenAudType?: string | null; + /** + * Flag that enables the [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (β€œNative + * SSO”). When this property is **not** `true`, Native SSO specific parameters are ignored or + * treated as errors. For example: + * + * * The `device_sso` scope has no special meaning (Authlete does not embed the `sid` claim in + * ID tokens). + * * The `urn:openid:params:token-type:device-secret` token type is treated as unknown and + * results in an error. + * + * When set to `true`, the server metadata advertises `"native_sso_supported": true`. See + * [OpenID Connect Discovery + * 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) and [RFC + * 8414 Β§2](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for background. Native SSO is + * available in Authlete 3.0 and later. + */ + nativeSsoSupported?: boolean; + /** + * Version of the [OpenID for Verifiable Credential + * Issuance](https://www.authlete.com/developers/oid4vci/) (OID4VCI) specification to support. + * + * Accepted values are: + * + * * `null` or `"1.0-ID1"` β†’ Implementer’s Draft 1. + * * `"1.0"` or `"1.0-Final"` β†’ Final 1.0 specification. + * + * Choose the value that matches the OID4VCI behaviour your service should expose. See the + * OID4VCI documentation for details. + */ + oid4VciVersion?: string | null; + /** + * Flag that controls whether the CIMD metadata policy is applied to client metadata obtained + * through the Client ID Metadata Document (CIMD) mechanism. + */ + cimdMetadataPolicyEnabled?: boolean; + /** + * Indicates whether the Client ID Metadata Document (CIMD) mechanism is supported. When `true`, + * the service will attempt to retrieve client metadata via CIMD where applicable. + */ + clientIdMetadataDocumentSupported?: boolean; + /** + * Enables the allowlist for CIMD. When `true`, only CIMD endpoints that are on the allowlist + * are used. + */ + cimdAllowlistEnabled?: boolean; + /** + * The allowlist of CIMD endpoints (hosts/URIs) that may be used when retrieving client metadata + * via Client ID Metadata Documents. + */ + cimdAllowlist?: string[] | null; + /** + * If `true`, CIMD retrieval is always attempted for clients, regardless of other conditions. + */ + cimdAlwaysRetrieved?: boolean; + /** + * Allows CIMD retrieval over plain HTTP. When `false`, only HTTPS CIMD endpoints are allowed. + */ + cimdHttpPermitted?: boolean; + /** + * Allows the use of query parameters when retrieving CIMD metadata. When `false`, query + * parameters are disallowed for CIMD requests. + */ + cimdQueryPermitted?: boolean; + /** + * The metadata policy applied to client metadata obtained through the CIMD mechanism. The value + * must follow the metadata policy grammar defined in [OpenID Federation 1.0 Β§6.1 Metadata + * Policy](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy). + */ + cimdMetadataPolicy?: string | null; + /** When `true`, client ID aliases starting with `https://` or `http://` are prohibited. */ + httpAliasProhibited?: boolean; + /** + * The time window of attestation challenges in seconds. This is used for OAuth 2.0 + * Attestation-Based Client Authentication. + */ + attestationChallengeTimeWindow?: number; + /** + * The flag indicating whether the attester roots for Client Attestation JWT x5c chain + * validation are enabled. + */ + clientAttesterRootsEnabled?: boolean; + /** + * The flag indicating whether only client authentication validated via attester roots is + * accepted. + */ + clientAttesterRootsOnly?: boolean; + /** + * The flag indicating whether the attester roots for Key Attestation JWT x5c chain validation + * are enabled. + */ + keyAttesterRootsEnabled?: boolean; + /** + * The flag indicating whether only key attestation validated via attester roots is accepted. + */ + keyAttesterRootsOnly?: boolean; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Client + * Attestation JWTs. + */ + clientAttesterRoots?: string[] | null; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Key + * Attestation JWTs. + */ + keyAttesterRoots?: string[] | null; + /** The flag indicating whether this service supports OpenID Connect Back-Channel Logout 1.0. */ + backchannelLogoutSupported?: boolean; + /** + * The flag indicating whether this service includes a `sid` (session ID) claim in ID tokens, + * supporting per-session backchannel logout. + */ + backchannelLogoutSessionSupported?: boolean; + /** + * The HAIP version to comply with. Valid values for this property are as follows: `"1.0"`. If + * this property is set, requests to this service undergo additional validation based on the + * specified version of the + * [HAIP](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) + * specification. + */ + haipVersion?: string | null; + }; + + export class ServiceCreateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ServiceCreateIdpApiRequest = { + body: ServiceCreateIdpRequest; + }; + + export class ServiceCreateIdpApiError extends ApiError { + declare readonly payload: + | Declared<"idpError", IdpError> + | Declared<"idpError2", IdpError> + | Declared<"idpError3", IdpError> + | Declared<"idpError4", IdpError> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "idpError", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 401, kind: "idpError2", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 403, kind: "idpError3", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 500, kind: "idpError4", decode: { kind: "json", schema: idpErrorSchema } }, + ]; + } + + export type ServiceDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + }; + + export class ServiceDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ServiceGetApiRequest = { + /** A service ID. */ + serviceId: string; + }; + + export class ServiceGetApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ServiceGetListApiRequest = { + /** + * Start index (inclusive) of the result set. The default value is 0. Must not be a negative + * number. + */ + start?: number; + /** + * End index (exclusive) of the result set. The default value is 5. Must not be a negative + * number. + */ + end?: number; + }; + + export class ServiceGetListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type ServiceRemoveIdpApiRequest = { + body: ServiceRemoveIdpRequest; + }; + + export class ServiceRemoveIdpApiError extends ApiError { + declare readonly payload: + | Declared<"idpError", IdpError> + | Declared<"idpError2", IdpError> + | Declared<"idpError3", IdpError> + | Declared<"idpError4", IdpError> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "idpError", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 401, kind: "idpError2", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 403, kind: "idpError3", decode: { kind: "json", schema: idpErrorSchema } }, + { on: 500, kind: "idpError4", decode: { kind: "json", schema: idpErrorSchema } }, + ]; + } + + export type ServiceUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** The sequential number of the service. The value of this property is assigned by Authlete. */ + number?: number; + /** The name of this service. */ + serviceName?: string | null; + /** + * The issuer identifier of the service. + * + * A URL that starts with https:// and has no query or fragment component. + * + * The value of this property is used as `iss` claim in an [ID + * token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and `issuer` property + * in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + issuer?: string | null; + /** The description about the service. */ + description?: string | null; + /** + * The service ID used in Authlete API calls. The value of this property is assigned by + * Authlete. + */ + apiKey?: number; + /** + * The API secret of this service. This value is assigned by Authlete and is used for service + * authentication in API calls. + */ + apiSecret?: string | null; + /** + * The endpoint for batch token notifications. This endpoint is called when multiple tokens are + * issued or revoked in a batch operation. + */ + tokenBatchNotificationEndpoint?: string | null; + /** + * The flag indicating whether the audience of client assertion JWTs must match the issuer + * identifier of this service. + */ + clientAssertionAudRestrictedToIssuer?: boolean; + /** + * The number of the organization that owns this service. This value is assigned by Authlete. + */ + serviceOwnerNumber?: number; + /** The maximum number of client applications that a developer can have. */ + clientsPerDeveloper?: number; + /** + * The endpoint for developer authentication callbacks. This is used when developers log into + * the developer portal. + */ + developerAuthenticationCallbackEndpoint?: string | null; + /** The API key for basic authentication at the developer authentication callback endpoint. */ + developerAuthenticationCallbackApiKey?: string | null; + /** + * The API secret for basic authentication at the developer authentication callback endpoint. + */ + developerAuthenticationCallbackApiSecret?: string | null; + /** Social login services (SNS) that this service supports for end-user authentication. */ + supportedSnses?: Sns[] | null; + /** + * The credentials for social login services (SNS) that are used for end-user authentication. + */ + snsCredentials?: SnsCredentials[] | null; + /** Social login services (SNS) that this service supports for developer authentication. */ + supportedDeveloperSnses?: Sns[] | null; + /** + * The credentials for social login services (SNS) that are used for developer authentication. + */ + developerSnsCredentials?: SnsCredentials[] | null; + /** Deprecated. Always `true`. */ + clientIdAliasEnabled?: boolean; + /** + * The `metadata` of the service. The content of the returned array depends on contexts. The + * predefined service metadata is listed in the following table. + * + * | Key | Description | + * | --- | --- | + * | `clientCount` | The number of client applications which belong to this service. | + */ + metadata?: Pair[] | null; + /** + * The time at which this service was created. The value is represented as milliseconds since + * the UNIX epoch (`1970-01-01`). + */ + createdAt?: number; + /** + * The time at which this service was last modified. The value is represented as milliseconds + * since the UNIX epoch (1970-01-01). + */ + modifiedAt?: number; + /** + * A Web API endpoint for user authentication which is to be prepared on the service side. + * + * The endpoint must be implemented if you do not implement the UI at the authorization endpoint + * but use the one provided by Authlete. + * + * The user authentication at the authorization endpoint provided by Authlete is performed by + * making a `POST` request to this endpoint. + */ + authenticationCallbackEndpoint?: string | null; + /** + * API key for basic authentication at the authentication callback endpoint. + * + * If the value is not empty, Authlete generates Authorization header for Basic authentication + * when making a request to the authentication callback endpoint. + */ + authenticationCallbackApiKey?: string | null; + /** API secret for `basic` authentication at the authentication callback endpoint. */ + authenticationCallbackApiSecret?: string | null; + /** + * Values of acrs (authentication context class references) that the service supports. + * + * The value of this property is used as `acr_values_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedAcrs?: string[] | null; + /** + * Values of `grant_type` request parameter that the service supports. + * + * The value of this property is used as `grant_types_supported property` in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedGrantTypes?: GrantType[] | null; + /** + * Values of `response_type` request parameter that the service supports. Valid values are + * listed in Response Type. + * + * The value of this property is used as `response_types_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedResponseTypes?: ResponseType[] | null; + /** + * The supported data types that can be used as values of the type field in + * `authorization_details`. + * + * This property corresponds to the `authorization_details_types_supported` metadata. See "OAuth + * 2.0 Rich Authorization Requests" (RAR) for details. + */ + supportedAuthorizationDetailsTypes?: string[] | null; + /** The profiles that this service supports. */ + supportedServiceProfiles?: ServiceProfile[] | null; + /** + * The flag to indicate whether the `error_description` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_description` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_description` response parameter in error + * responses. + */ + errorDescriptionOmitted?: boolean; + /** + * The flag to indicate whether the `error_uri` response parameter is omitted. + * + * According to [RFC 6749](https://tools.ietf.org/html/rfc6749), an authorization server may + * include the `error_uri` response parameter in error responses. + * + * If `true`, Authlete does not embed the `error_uri` response parameter in error responses. + */ + errorUriOmitted?: boolean; + /** + * The authorization endpoint of the service. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/authorization`. + * + * The value of this property is used as `authorization_endpoint` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + authorizationEndpoint?: string | null; + /** + * Deprecated. The direct authorization endpoint was removed in Authlete 3.0.15, so this flag + * has no effect and is always returned as `false`. + */ + directAuthorizationEndpointEnabled?: boolean; + /** + * UI locales that the service supports. + * + * Each element is a language tag defined in [RFC 5646](https://tools.ietf.org/html/rfc5646). + * For example, `en-US` and `ja-JP`. + * + * The value of this property is used as `ui_locales_supported` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedUiLocales?: string[] | null; + /** + * Values of `display` request parameter that service supports. + * + * The value of this property is used as `display_values_supported` property in the Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedDisplays?: Display[] | null; + /** + * The flag to indicate whether the use of Proof Key for Code Exchange (PKCE) is always required + * for authorization requests by Authorization Code Flow. + * + * If `true`, `code_challenge` request parameter is always required for authorization requests + * using Authorization Code Flow. + * + * See [RFC 7636](https://tools.ietf.org/html/rfc7636) (Proof Key for Code Exchange by OAuth + * Public Clients) for details about `code_challenge` request parameter. + */ + pkceRequired?: boolean; + /** + * The flag to indicate whether `S256` is always required as the code challenge method whenever + * [PKCE (RFC 7636)](https://tools.ietf.org/html/rfc7636) is used. + * + * If this flag is set to `true`, `code_challenge_method=S256` must be included in the + * authorization request whenever it includes the `code_challenge` request parameter. Neither + * omission of the `code_challenge_method` request parameter nor use of plain + * (`code_challenge_method=plain`) is allowed. + */ + pkceS256Required?: boolean; + /** + * The duration of authorization response JWTs in seconds. + * + * [Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 + * (JARM)](https://openid.net/specs/openid-financial-api-jarm.html) defines new values for the + * `response_mode` request parameter. They are `query.jwt`, `fragment.jwt`, `form_post.jwt` and + * `jwt`. If one of them is specified as the response mode, response parameters from the + * authorization endpoint will be packed into a JWT. This property is used to compute the value + * of the `exp` claim of the JWT. + */ + authorizationResponseDuration?: number; + /** The duration of authorization codes in seconds. */ + authorizationCodeDuration?: number; + /** + * The [token endpoint](https://tools.ietf.org/html/rfc6749#section-3.2) of the service. + * + * A URL that starts with `https://` and has not fragment component. For example, + * `https://example.com/auth/token`. + * + * The value of this property is used as `token_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tokenEndpoint?: string | null; + /** + * Deprecated. The direct token endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directTokenEndpointEnabled?: boolean; + /** + * Client authentication methods supported by the token endpoint of the service. + * + * The value of this property is used as `token_endpoint_auth_methods_supports` property in the + * [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedTokenAuthMethods?: ClientAuthMethod[] | null; + /** + * The flag to indicate token requests from public clients without the `client_id` request + * parameter are allowed when the client can be guessed from `authorization_code` or + * `refresh_token`. + * + * This flag should not be set unless you have special reasons. + */ + missingClientIdAllowed?: boolean; + /** + * The [revocation endpoint](https://tools.ietf.org/html/rfc7009) of the service. + * + * A URL that starts with `https://`. For example, `https://example.com/auth/revocation`. + */ + revocationEndpoint?: string | null; + /** + * Deprecated. The direct revocation endpoint was removed in Authlete 3.0.15, so this flag has + * no effect and is always returned as `false`. + */ + directRevocationEndpointEnabled?: boolean; + /** Client authentication methods supported at the revocation endpoint. */ + supportedRevocationAuthMethods?: ClientAuthMethod[] | null; + /** The URI of the introspection endpoint. */ + introspectionEndpoint?: string | null; + /** + * Deprecated. The direct introspection endpoint was removed in Authlete 3.0.15, so this flag + * has no effect and is always returned as `false`. + */ + directIntrospectionEndpointEnabled?: boolean; + /** Client authentication methods supported at the introspection endpoint. */ + supportedIntrospectionAuthMethods?: ClientAuthMethod[] | null; + /** + * The URI of the pushed authorization request endpoint. + * + * This property corresponds to the `pushed_authorization_request_endpoint` metadata defined in + * "[5. Authorization Server + * Metadata](https://tools.ietf.org/html/draft-lodderstedt-oauth-par#section-5)" of OAuth 2.0 + * Pushed Authorization Requests. + */ + pushedAuthReqEndpoint?: string | null; + /** The duration of pushed authorization requests in seconds. */ + pushedAuthReqDuration?: number; + /** + * The flag to indicate whether this service requires that clients use the pushed authorization + * request endpoint. + * + * This property corresponds to the `require_pushed_authorization_requests` server metadata + * defined in [OAuth 2.0 Pushed Authorization + * Requests](https://tools.ietf.org/html/draft-lodderstedt-oauth-par). + */ + parRequired?: boolean; + /** + * The flag to indicate whether this service requires that authorization requests always utilize + * a request object by using either request or `request_uri` request parameter. + * + * If this flag is set to `true` and the value of `traditionalRequestObjectProcessingApplied` is + * `false`, the value of `require_signed_request_object` server metadata of this service is + * reported as `true` in the discovery document. The metadata is defined in JAR (JWT Secured + * Authorization Request). That `require_signed_request_object` is `true` means that + * authorization requests which don't conform to the JAR specification are rejected. + */ + requestObjectRequired?: boolean; + /** + * The flag to indicate whether a request object is processed based on rules defined in [OpenID + * Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) or JAR (JWT Secured + * Authorization Request). + */ + traditionalRequestObjectProcessingApplied?: boolean; + /** + * The flag to indicate whether this service validates certificate chains during PKI-based + * client mutual TLS authentication. + */ + mutualTlsValidatePkiCertChain?: boolean; + /** + * The list of root certificates trusted by this service for PKI-based client mutual TLS + * authentication. + */ + trustedRootCertificates?: string[] | null; + /** The MTLS endpoint aliases. */ + mtlsEndpointAliases?: NamedUri[] | null; + /** + * The access token type. + * + * This value is used as the value of `token_type` property in access token responses. If this + * service complies with [RFC 6750](https://tools.ietf.org/html/rfc6750), the value of this + * property should be `Bearer`. + * + * See [RFC 6749 (OAuth 2.0), 7.1. Access Token + * Types](https://tools.ietf.org/html/rfc6749#section-7.1) for details. + */ + accessTokenType?: string | null; + /** + * The flag to indicate whether this service supports issuing TLS client certificate bound + * access tokens. + */ + tlsClientCertificateBoundAccessTokens?: boolean; + /** + * The duration of access tokens in seconds. This value is used as the value of `expires_in` + * property in access token responses. `expires_in` is defined [RFC 6749, 5.1. Successful + * Response](https://tools.ietf.org/html/rfc6749#section-5.1). + */ + accessTokenDuration?: number; + /** + * The flag to indicate whether the number of access tokens per subject (and per client) is at + * most one or can be more. + * + * If `true`, an attempt to issue a new access token invalidates existing access tokens that are + * associated with the same subject and the same client. + * + * Note that, however, attempts by [Client Credentials + * Flow](https://tools.ietf.org/html/rfc6749#section-4.4) do not invalidate existing access + * tokens because access tokens issued by Client Credentials Flow are not associated with any + * end-user's subject. Also note that an attempt by [Refresh Token + * Flow](https://tools.ietf.org/html/rfc6749#section-6) invalidates the coupled access token + * only and this invalidation is always performed regardless of whether the value of this + * setting item is `true` or `false`. + */ + singleAccessTokenPerSubject?: boolean; + /** + * The signature algorithm for JWT. This value is represented on 'alg' attribute of the header + * of JWT. + * + * it's semantics depends upon where is this defined, for instance: + * - as service accessTokenSignAlg value, it defines that access token are JWT and the + * algorithm used to sign it. Check your [KB + * article](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/jwt-based-access-token). + * - as client authorizationSignAlg value, it represents the signature algorithm used when + * [creating a JARM + * response](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/enabling-jarm). + * - or as client requestSignAlg value, it specifies which is the expected signature used by + * [client on a Request + * Object](https://kb.authlete.com/en/s/oauth-and-openid-connect/a/request-objects). + */ + accessTokenSignAlg?: JwsAlg | null; + /** + * The key ID to identify a JWK used for signing access tokens. + * + * A JWK Set can be registered as a property of a service. A JWK Set can contain 0 or more JWKs. + * Authlete Server has to pick up one JWK for signing from the JWK Set when it generates a + * JWT-based access token. Authlete Server searches the registered JWK Set for a JWK which + * satisfies conditions for access token signature. If the number of JWK candidates which + * satisfy the conditions is 1, there is no problem. On the other hand, if there exist multiple + * candidates, a Key ID is needed to be specified so that Authlete Server can pick up one JWK + * from among the JWK candidates. + */ + accessTokenSignatureKeyId?: string | null; + /** + * The duration of refresh tokens in seconds. The related specifications have no requirements on + * refresh token duration, but Authlete sets expiration for refresh tokens. + */ + refreshTokenDuration?: number; + /** + * The flag to indicate whether the remaining duration of the used refresh token is taken over + * to the newly issued refresh token. + */ + refreshTokenDurationKept?: boolean; + /** + * The flag which indicates whether duration of refresh tokens are reset when they are used even + * if the `refreshTokenKept` property of this service set to is `true` (= even if "Refresh Token + * Continuous Use" is "Kept"). + * + * This flag has no effect when the `refreshTokenKept` property is set to `false`. In other + * words, if this service issues a new refresh token on every refresh token request, the refresh + * token will have fresh duration (unless `refreshTokenDurationKept` is set to `true`) and this + * `refreshTokenDurationReset` property is not referenced. + */ + refreshTokenDurationReset?: boolean; + /** + * The flag to indicate whether a refresh token remains unchanged or gets renewed after its use. + * + * If `true`, a refresh token used to get a new access token remains valid after its use. + * Otherwise, if `false`, a refresh token is invalidated after its use and a new refresh token + * is issued. + * + * See [RFC 6749 6. Refreshing an Access Token](https://tools.ietf.org/html/rfc6749#section-6), + * as to how to get a new access token using a refresh token. + */ + refreshTokenKept?: boolean; + /** Scopes supported by the service. */ + supportedScopes?: Scope[] | null; + /** The flag to indicate whether requests that request no scope are rejected or not. */ + scopeRequired?: boolean; + /** + * 'The duration of [ID token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)s + * in seconds. This value is used to calculate the value of `exp` claim in an ID token.' + */ + idTokenDuration?: number; + /** + * The allowable clock skew between the server and clients in seconds. + * + * The clock skew is taken into consideration when time-related claims in a JWT (e.g. `exp`, + * `iat`, `nbf`) are verified. + */ + allowableClockSkew?: number; + /** + * Claim types supported by the service. Valid values are listed in Claim Type. Note that + * Authlete currently doesn't provide any API to help implementations for `AGGREGATED` and + * `DISTRIBUTED`. + * + * The value of this property is used as `claim_types_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimTypes?: ClaimType[] | null; + /** + * Claim locales that the service supports. Each element is a language tag defined in [RFC + * 5646](https://tools.ietf.org/html/rfc5646). For example, `en-US` and `ja-JP`. See [OpenID + * Connect Core 1.0, 5.2. Languages and + * Scripts](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsLanguagesAndScripts) for + * details. + * + * The value of this property is used as `claims_locales_supported` property in the [OpenID + * Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + supportedClaimLocales?: string[] | null; + /** + * Claim names that the service supports. The standard claim names listed in [OpenID Connect + * Core 1.0, 5.1. Standard + * Claim](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) should be + * supported. The following is the list of standard claims. + */ + supportedClaims?: string[] | null; + /** + * The flag indicating whether claims specified by shortcut scopes (e.g. `profile`) are included + * in the issued ID token only when no access token is issued. + */ + claimShortcutRestrictive?: boolean; + /** + * The URL of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) document. + * For example, `http://example.com/auth/jwks`. + * + * Client applications accesses this URL (1) to get the public key of the service to validate + * the signature of an ID token issued by the service and (2) to get the public key of the + * service to encrypt an request object of the client application. See [OpenID Connect Core 1.0, + * 10. Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) + * for details. + * + * The value of this property is used as `jwks_uri` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + jwksUri?: string | null; + /** + * Deprecated. The direct JWKS endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directJwksEndpointEnabled?: boolean; + /** + * The content of the service's [JSON Web Key Set](https://tools.ietf.org/html/rfc7517) + * document. + * + * If this property is not `null` in a `/service/create` request or a `/service/update` request, + * Authlete hosts the content in the database. This property must not be `null` and must contain + * pairs of public/private keys if the service wants to support asymmetric signatures for ID + * tokens and asymmetric encryption for request objects. See [OpenID Connect Core 1.0, 10. + * Signatures and Encryption](https://openid.net/specs/openid-connect-core-1_0.html#SigEnc) for + * details. + */ + jwks?: string | null; + /** The key ID to identify a JWK used for ID token signature using an asymmetric key. */ + idTokenSignatureKeyId?: string | null; + /** The key ID to identify a JWK used for user info signature using an asymmetric key. */ + userInfoSignatureKeyId?: string | null; + /** + * The key ID to identify a JWK used for signing authorization responses using an asymmetric + * key. + */ + authorizationSignatureKeyId?: string | null; + /** + * The [user info endpoint](http://openid.net/specs/openid-connect-core-1_0.html#UserInfo) of + * the service. A URL that starts with `https://`. For example, + * `https://example.com/auth/userinfo`. + * + * The value of this property is used as `userinfo_endpoint` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + userInfoEndpoint?: string | null; + /** + * Deprecated. The direct userinfo endpoint was removed in Authlete 3.0.15, so this flag has no + * effect and is always returned as `false`. + */ + directUserInfoEndpointEnabled?: boolean; + /** + * The boolean flag which indicates whether the [OAuth 2.0 Dynamic Client Registration + * Protocol](https://tools.ietf.org/html/rfc7591) is supported. + */ + dynamicRegistrationSupported?: boolean; + /** + * The [registration + * endpoint](http://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration) of + * the service. A URL that starts with `https://`. For example, + * `https://example.com/auth/registration`. + * + * The value of this property is used as `registration_endpoint` property in the [OpenID + * Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + registrationEndpoint?: string | null; + /** + * The URI of the registration management endpoint. If dynamic client registration is supported, + * and this is set, this URI will be used as the basis of the client's management endpoint by + * appending `/clientid}/` to it as a path element. If this is unset, the value of + * `registrationEndpoint` will be used as the URI base instead. + */ + registrationManagementEndpoint?: string | null; + /** + * The URL of the "Policy" of the service. + * + * The value of this property is used as `op_policy_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + policyUri?: string | null; + /** + * The URL of the "Terms Of Service" of the service. + * + * The value of this property is used as `op_tos_uri` property in the [OpenID Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + tosUri?: string | null; + /** + * The URL of a page where documents for developers can be found. + * + * The value of this property is used as `service_documentation` property in the [OpenID + * Provider + * Metadata](http://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + serviceDocumentation?: string | null; + /** + * The URI of backchannel authentication endpoint, which is defined in the specification of + * [CIBA (Client Initiated Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + backchannelAuthenticationEndpoint?: string | null; + /** + * The supported backchannel token delivery modes. This property corresponds to the + * `backchannel_token_delivery_modes_supported` metadata. + * + * Backchannel token delivery modes are defined in the specification of [CIBA (Client Initiated + * Backchannel + * Authentication)](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). + */ + supportedBackchannelTokenDeliveryModes?: DeliveryMode[] | null; + /** + * The duration of backchannel authentication request IDs issued from the backchannel + * authentication endpoint in seconds. This is used as the value of the `expires_in` property in + * responses from the backchannel authentication endpoint. + */ + backchannelAuthReqIdDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications + * in seconds. This is used as the value of the `interval` property in responses from the + * backchannel authentication endpoint. + */ + backchannelPollingInterval?: number; + /** + * The boolean flag which indicates whether the `user_code` request parameter is supported at + * the backchannel authentication endpoint. This property corresponds to the + * `backchannel_user_code_parameter_supported` metadata. + */ + backchannelUserCodeParameterSupported?: boolean; + /** + * The flag to indicate whether the `binding_message` request parameter is always required + * whenever a backchannel authentication request is judged as a request for Financial-grade API. + */ + backchannelBindingMessageRequiredInFapi?: boolean; + /** + * The URI of the device authorization endpoint. + * + * Device authorization endpoint is defined in the specification of OAuth 2.0 Device + * Authorization Grant. + */ + deviceAuthorizationEndpoint?: string | null; + /** + * The verification URI for the device flow. This URI is used as the value of the + * `verification_uri` parameter in responses from the device authorization endpoint. + */ + deviceVerificationUri?: string | null; + /** + * The verification URI for the device flow with a placeholder for a user code. This URI is used + * to build the value of the `verification_uri_complete` parameter in responses from the device + * authorization endpoint. + */ + deviceVerificationUriComplete?: string | null; + /** + * The duration of device verification codes and end-user verification codes issued from the + * device authorization endpoint in seconds. This is used as the value of the `expires_in` + * property in responses from the device authorization endpoint. + */ + deviceFlowCodeDuration?: number; + /** + * The minimum interval between polling requests to the token endpoint from client applications + * in seconds in device flow. This is used as the value of the `interval` property in responses + * from the device authorization endpoint. + */ + deviceFlowPollingInterval?: number; + userCodeCharset?: UserCodeCharset | null; + /** The length of end-user verification codes (`user_code`) for Device Flow. */ + userCodeLength?: number; + /** + * Trust frameworks supported by this service. This corresponds to the + * `trust_frameworks_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedTrustFrameworks?: string[] | null; + /** + * Evidence supported by this service. This corresponds to the `evidence_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedEvidence?: string[] | null; + /** + * Identity documents supported by this service. This corresponds to the + * `id_documents_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedIdentityDocuments?: string[] | null; + /** + * Verification methods supported by this service. This corresponds to the + * `id_documents_verification_methods_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerificationMethods?: string[] | null; + /** + * Verified claims supported by this service. This corresponds to the + * `claims_in_verified_claims_supported` + * [metadata](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html#rfc.section.7). + */ + supportedVerifiedClaims?: string[] | null; + /** The verified claims validation schema set. */ + verifiedClaimsValidationSchemaSet?: VerifiedClaimsValidationSchema; + /** The attributes of this service. */ + attributes?: Pair[] | null; + /** + * The flag indicating whether the nbf claim in the request object is optional even when the + * authorization request is regarded as a FAPI-Part2 request. + */ + nbfOptional?: boolean; + /** The flag indicating whether generation of the iss response parameter is suppressed. */ + issSuppressed?: boolean; + /** custom client metadata supported by this service. */ + supportedCustomClientMetadata?: string[] | null; + /** + * The flag indicating whether the expiration date of an access token never exceeds that of the + * corresponding refresh token. + */ + tokenExpirationLinked?: boolean; + /** + * The flag indicating whether encryption of request object is required when the request object + * is passed through the front channel. + */ + frontChannelRequestObjectEncryptionRequired?: boolean; + /** + * The flag indicating whether the JWE alg of encrypted request object must match the + * `request_object_encryption_alg` client metadata of the client that has sent the request + * object. + */ + requestObjectEncryptionAlgMatchRequired?: boolean; + /** + * The flag indicating whether the JWE `enc` of encrypted request object must match the + * `request_object_encryption_enc` client metadata of the client that has sent the request + * object. + */ + requestObjectEncryptionEncMatchRequired?: boolean; + /** + * The flag indicating whether HSM (Hardware Security Module) support is enabled for this + * service. + * + * When this flag is `false`, keys managed in HSMs are not used even if they exist. In addition, + * `/api/hsk/*` APIs reject all requests. + * + * Even if this flag is `true`, HSM-related features do not work if the configuration of the + * Authlete server you are using does not support HSM. + */ + hsmEnabled?: boolean; + /** + * The information about keys managed on HSMs (Hardware Security Modules). + * + * This `hsks` property is output only, meaning that `hsks` in requests to `/api/service/create` + * API and `/api/service/update` API do not have any effect. The contents of this property is + * controlled only by `/api/hsk/*` APIs. + */ + hsks?: Hsk[] | null; + /** The URL of the grant management endpoint. */ + grantManagementEndpoint?: string | null; + /** + * The flag indicating whether every authorization request (and any request serving as an + * authorization request such as CIBA backchannel authentication request and device + * authorization request) must include the `grant_management_action` request parameter. + */ + grantManagementActionRequired?: boolean; + /** + * The flag indicating whether Authlete's `/api/client/registration` API uses `UNAUTHORIZED` as + * a value of the `action` response parameter when appropriate. + */ + unauthorizedOnClientConfigSupported?: boolean; + /** + * The flag indicating whether the `scope` request parameter in dynamic client registration and + * update requests (RFC 7591 and RFC 7592) is used as scopes that the client can request. + * + * Limiting the range of scopes that a client can request is achieved by listing scopes in the + * `client.extension.requestableScopes` property and setting the + * `client.extension.requestableScopesEnabled` property to `true`. This feature is called + * "requestable scopes". + * + * This property affects behaviors of `/api/client/registration` and other family APIs. + */ + dcrScopeUsedAsRequestable?: boolean; + /** + * The endpoint for clients ending the sessions. + * + * A URL that starts with `https://` and has no fragment component. For example, + * `https://example.com/auth/endSession`. + * + * The value of this property is used as `end_session_endpoint` property in the [OpenID Provider + * Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata). + */ + endSessionEndpoint?: string | null; + /** + * The URL of the challenge endpoint, which is defined in OAuth 2.0 Attestation-Based Client + * Authentication. + */ + challengeEndpoint?: string | null; + /** + * The flag indicating whether the port number component of redirection URIs can be variable + * when the host component indicates loopback. + */ + loopbackRedirectionUriVariable?: boolean; + /** + * The flag indicating whether Authlete checks whether the `aud` claim of request objects + * matches the issuer identifier of this service. + */ + requestObjectAudienceChecked?: boolean; + /** + * The flag indicating whether Authlete generates access tokens for external attachments and + * embeds them in ID tokens and userinfo responses. + */ + accessTokenForExternalAttachmentEmbedded?: boolean; + /** + * Identifiers of entities that can issue entity statements for this service. This property + * corresponds to the `authority_hints` property that appears in a self-signed entity statement + * that is defined in OpenID Connect Federation 1.0. + */ + authorityHints?: string[] | null; + /** flag indicating whether this service supports OpenID Connect Federation 1 */ + federationEnabled?: boolean; + /** + * JWK Set document containing keys that are used to sign (1) self-signed entity statement of + * this service and (2) the response from `signed_jwks_uri`. + */ + federationJwks?: string | null; + /** A key ID to identify a JWK used to sign the entity configuration and the signed JWK Set. */ + federationSignatureKeyId?: string | null; + /** The duration of the entity configuration in seconds. */ + federationConfigurationDuration?: number; + /** + * The URI of the federation registration endpoint. This property corresponds to the + * `federation_registration_endpoint` server metadata that is defined in OpenID Connect + * Federation 1.0. + */ + federationRegistrationEndpoint?: string | null; + /** + * The human-readable name representing the organization that operates this service. This + * property corresponds to the `organization_name` server metadata that is defined in OpenID + * Connect Federation 1.0. + */ + organizationName?: string | null; + /** + * The transformed claims predefined by this service in JSON format. This property corresponds + * to the `transformed_claims_predefined` server metadata. + */ + predefinedTransformedClaims?: string | null; + /** + * flag indicating whether refresh token requests with the same refresh token can be made + * multiple times in quick succession and they can obtain the same renewed refresh token within + * the short period. + */ + refreshTokenIdempotent?: boolean; + /** + * The URI of the endpoint that returns this service's JWK Set document in the JWT format. This + * property corresponds to the `signed_jwks_uri` server metadata defined in OpenID Connect + * Federation 1.0. + */ + signedJwksUri?: string | null; + /** + * Supported attachment types. This property corresponds to the {@code + * attachments_supported} server metadata which was added by the third implementer's draft + * of OpenID Connect for Identity Assurance 1.0. + */ + supportedAttachments?: AttachmentType[] | null; + /** + * Supported algorithms used to compute digest values of external attachments. This property + * corresponds to the `digest_algorithms_supported` server metadata which was added by the third + * implementer's draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDigestAlgorithms?: string[] | null; + /** + * Document types supported by this service. This property corresponds to the + * `documents_supported` server metadata. + */ + supportedDocuments?: string[] | null; + /** + * validation and verification processes supported by this service. This property corresponds to + * the `documents_methods_supported` server metadata. + * + * The third implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) renamed the + * `id_documents_verification_methods_supported` server metadata to + * `documents_methods_supported`. + */ + supportedDocumentsMethods?: string[] | null; + /** + * Document validation methods supported by this service. This property corresponds to the + * `documents_validation_methods_supported` server metadata which was added by the third + * implementer's draft of + */ + supportedDocumentsValidationMethods?: string[] | null; + /** + * Document verification methods supported by this service. This property corresponds to the + * `documents_verification_methods_supported` server metadata which was added by the third + * implementer's draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedDocumentsVerificationMethods?: string[] | null; + /** + * Electronic record types supported by this service. This property corresponds to the + * `electronic_records_supported` server metadata which was added by the third implementer's + * draft of [OpenID Connect for Identity Assurance + * 1.0](https://openid.net/specs/openid-connect-4-identity-assurance-1_0.html) + */ + supportedElectronicRecords?: string[] | null; + supportedClientRegistrationTypes?: ClientRegistrationType[] | null; + /** + * The flag indicating whether to prohibit unidentifiable clients from making token exchange + * requests. + */ + tokenExchangeByIdentifiableClientsOnly?: boolean; + /** + * The flag indicating whether to prohibit public clients from making token exchange requests. + */ + tokenExchangeByConfidentialClientsOnly?: boolean; + /** + * The flag indicating whether to prohibit clients that have no explicit permission from making + * token exchange requests. + */ + tokenExchangeByPermittedClientsOnly?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use encrypted JWTs as + * input tokens. + */ + tokenExchangeEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token exchange requests which use unsigned JWTs as + * input tokens. + */ + tokenExchangeUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to prohibit unidentifiable clients from using the grant type + * "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantByIdentifiableClientsOnly?: boolean; + /** + * The flag indicating whether to reject token requests that use an encrypted JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantEncryptedJwtRejected?: boolean; + /** + * The flag indicating whether to reject token requests that use an unsigned JWT as an + * authorization grant with the grant type "urn:ietf:params:oauth:grant-type:jwt-bearer". + */ + jwtGrantUnsignedJwtRejected?: boolean; + /** + * The flag indicating whether to block DCR (Dynamic Client Registration) requests whose + * "software_id" has already been used previously. + */ + dcrDuplicateSoftwareIdBlocked?: boolean; + /** + * The trust anchors that are referenced when this service resolves trust chains of relying + * parties. + * + * If this property is empty, client registration fails regardless of whether its type is + * `automatic` or `explicit`. It means that OpenID Connect Federation 1.0 does not work. + */ + trustAnchors?: TrustAnchor[] | null; + /** + * The flag indicating whether the openid scope should be dropped from scopes list assigned to + * access token issued when a refresh token grant is used. + */ + openidDroppedOnRefreshWithoutOfflineAccess?: boolean; + /** + * The flag indicating whether to refrain from issuing an ID token when the `openid` scope has + * been removed from the scopes to be granted by the `scopes` request parameter of the + * `/auth/authorization/issue` API. + * + * Flows whose `response_type` contains `id_token`, and flows that requested Native SSO, are not + * affected, because both require an ID token. + */ + idTokenDroppedOnIssueWithoutOpenid?: boolean; + /** + * Supported document check methods. This property corresponds to the + * `documents_check_methods_supported` server metadata which was added by the fourth + * implementer's draft of OpenID Connect for Identity Assurance 1.0. + */ + supportedDocumentsCheckMethods?: string[] | null; + /** The flag indicating whether this service signs responses from the resource server. */ + rsResponseSigned?: boolean; + /** The duration of `c_nonce`. */ + cnonceDuration?: number; + /** + * Whether to require DPoP proof JWTs to include the `nonce` claim whenever they are presented. + */ + dpopNonceRequired?: boolean; + /** + * Get the flag indicating whether the feature of Verifiable Credentials for this service is + * enabled or not. + */ + verifiableCredentialsEnabled?: boolean; + /** The URL at which the JWK Set document of the credential issuer is exposed. */ + credentialJwksUri?: string | null; + /** The default duration of credential offers in seconds. */ + credentialOfferDuration?: number; + /** The duration of nonce values for DPoP proof JWTs in seconds. */ + dpopNonceDuration?: number; + /** + * The flag indicating whether token requests using the pre-authorized code grant flow by + * unidentifiable clients are allowed. + */ + preAuthorizedGrantAnonymousAccessSupported?: boolean; + /** + * The duration of transaction ID in seconds that may be issued as a result of a credential + * request or a batch credential request. + */ + credentialTransactionDuration?: number; + /** The key ID of the key for signing introspection responses. */ + introspectionSignatureKeyId?: string | null; + /** The key ID of the key for signing introspection responses. */ + resourceSignatureKeyId?: string | null; + /** The default length of user PINs. */ + userPinLength?: number; + /** The supported `prompt` values. */ + supportedPromptValues?: Prompt[] | null; + /** + * The flag indicating whether to enable the feature of ID token reissuance in the refresh token + * flow. + */ + idTokenReissuable?: boolean; + /** + * The JWK Set document containing private keys that are used to sign verifiable credentials. + */ + credentialJwks?: string | null; + /** + * FAPI modes for this service. + * + * When the value of this property is not `null`, Authlete always processes requests to this + * service based on the specified FAPI modes if the FAPI feature is enabled in Authlete and the + * FAPI profile is supported by this service. + * + * For instance, when this property is set to an array containing `FAPI1_ADVANCED` only, + * Authlete always processes requests to this service based on "Financial-grade API Security + * Profile 1.0 - Part 2: Advanced" if the FAPI feature is enabled in Authlete and the FAPI + * profile is supported by this service. + */ + fapiModes?: FapiMode[] | null; + /** The default duration of verifiable credentials in seconds. */ + credentialDuration?: number; + credentialIssuerMetadata?: CredentialIssuerMetadata | null; + /** The type of the `aud` claim in ID tokens. */ + idTokenAudType?: string | null; + /** + * Flag that enables the [OpenID Connect Native SSO for Mobile Apps + * 1.0](https://openid.net/specs/openid-connect-native-sso-1_0.html) specification (β€œNative + * SSO”). When this property is **not** `true`, Native SSO specific parameters are ignored or + * treated as errors. For example: + * + * * The `device_sso` scope has no special meaning (Authlete does not embed the `sid` claim in + * ID tokens). + * * The `urn:openid:params:token-type:device-secret` token type is treated as unknown and + * results in an error. + * + * When set to `true`, the server metadata advertises `"native_sso_supported": true`. See + * [OpenID Connect Discovery + * 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata) and [RFC + * 8414 Β§2](https://www.rfc-editor.org/rfc/rfc8414.html#section-2) for background. Native SSO is + * available in Authlete 3.0 and later. + */ + nativeSsoSupported?: boolean; + /** + * Version of the [OpenID for Verifiable Credential + * Issuance](https://www.authlete.com/developers/oid4vci/) (OID4VCI) specification to support. + * + * Accepted values are: + * + * * `null` or `"1.0-ID1"` β†’ Implementer’s Draft 1. + * * `"1.0"` or `"1.0-Final"` β†’ Final 1.0 specification. + * + * Choose the value that matches the OID4VCI behaviour your service should expose. See the + * OID4VCI documentation for details. + */ + oid4VciVersion?: string | null; + /** + * Flag that controls whether the CIMD metadata policy is applied to client metadata obtained + * through the Client ID Metadata Document (CIMD) mechanism. + */ + cimdMetadataPolicyEnabled?: boolean; + /** + * Indicates whether the Client ID Metadata Document (CIMD) mechanism is supported. When `true`, + * the service will attempt to retrieve client metadata via CIMD where applicable. + */ + clientIdMetadataDocumentSupported?: boolean; + /** + * Enables the allowlist for CIMD. When `true`, only CIMD endpoints that are on the allowlist + * are used. + */ + cimdAllowlistEnabled?: boolean; + /** + * The allowlist of CIMD endpoints (hosts/URIs) that may be used when retrieving client metadata + * via Client ID Metadata Documents. + */ + cimdAllowlist?: string[] | null; + /** + * If `true`, CIMD retrieval is always attempted for clients, regardless of other conditions. + */ + cimdAlwaysRetrieved?: boolean; + /** + * Allows CIMD retrieval over plain HTTP. When `false`, only HTTPS CIMD endpoints are allowed. + */ + cimdHttpPermitted?: boolean; + /** + * Allows the use of query parameters when retrieving CIMD metadata. When `false`, query + * parameters are disallowed for CIMD requests. + */ + cimdQueryPermitted?: boolean; + /** + * The metadata policy applied to client metadata obtained through the CIMD mechanism. The value + * must follow the metadata policy grammar defined in [OpenID Federation 1.0 Β§6.1 Metadata + * Policy](https://openid.net/specs/openid-federation-1_0.html#name-metadata-policy). + */ + cimdMetadataPolicy?: string | null; + /** When `true`, client ID aliases starting with `https://` or `http://` are prohibited. */ + httpAliasProhibited?: boolean; + /** + * The time window of attestation challenges in seconds. This is used for OAuth 2.0 + * Attestation-Based Client Authentication. + */ + attestationChallengeTimeWindow?: number; + /** + * The flag indicating whether the attester roots for Client Attestation JWT x5c chain + * validation are enabled. + */ + clientAttesterRootsEnabled?: boolean; + /** + * The flag indicating whether only client authentication validated via attester roots is + * accepted. + */ + clientAttesterRootsOnly?: boolean; + /** + * The flag indicating whether the attester roots for Key Attestation JWT x5c chain validation + * are enabled. + */ + keyAttesterRootsEnabled?: boolean; + /** + * The flag indicating whether only key attestation validated via attester roots is accepted. + */ + keyAttesterRootsOnly?: boolean; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Client + * Attestation JWTs. + */ + clientAttesterRoots?: string[] | null; + /** + * The trusted root certificates (PEM-encoded X.509) for validating the x5c chain in Key + * Attestation JWTs. + */ + keyAttesterRoots?: string[] | null; + /** The flag indicating whether this service supports OpenID Connect Back-Channel Logout 1.0. */ + backchannelLogoutSupported?: boolean; + /** + * The flag indicating whether this service includes a `sid` (session ID) claim in ID tokens, + * supporting per-session backchannel logout. + */ + backchannelLogoutSessionSupported?: boolean; + /** + * The HAIP version to comply with. Valid values for this property are as follows: `"1.0"`. If + * this property is set, requests to this service undergo additional validation based on the + * specified version of the + * [HAIP](https://openid.net/specs/openid4vc-high-assurance-interoperability-profile-1_0.html) + * specification. + */ + haipVersion?: string | null; + }; + + export class ServiceUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/token-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/token-endpoint.ts new file mode 100644 index 00000000..a1498aa7 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/token-endpoint.ts @@ -0,0 +1,523 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { cimdOptionsSchema, type CimdOptions } from "../models/cimd-options.js"; +import { + idtokenReissueRequestSchema, + type IdtokenReissueRequest, +} from "../models/idtoken-reissue-request.js"; +import { + idtokenReissueResponseSchema, + type IdtokenReissueResponse, +} from "../models/idtoken-reissue-response.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { reason1Schema, type Reason1 } from "../models/reason1.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { tokenFailResponseSchema, type TokenFailResponse } from "../models/token-fail-response.js"; +import { tokenIssueResponseSchema, type TokenIssueResponse } from "../models/token-issue-response.js"; +import { tokenResponseSchema, type TokenResponse } from "../models/token-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OAuth 2.0 Token Endpoint. + */ +export class TokenEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process Token Request + * + * @remarks + * This API parses request parameters of an authorization request and returns necessary data for + * the authorization server implementation to process the authorization request further. + * + * @returns Token operation completed successfully + * + * @throws {@link TokenEndpoint.AuthTokenApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenApi( + request: TokenEndpoint.AuthTokenApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "parameters", value: request.parameters, schema: s.string() }, + { name: "clientId", value: request.clientId, schema: s.optional(s.string()) }, + { name: "clientSecret", value: request.clientSecret, schema: s.optional(s.string()) }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { + name: "clientCertificatePath", + value: request.clientCertificatePath, + schema: s.optional(s.array(s.string())), + }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "dpop", value: request.dpop, schema: s.optional(s.string()) }, + { name: "htm", value: request.htm, schema: s.optional(s.string()) }, + { name: "htu", value: request.htu, schema: s.optional(s.string()) }, + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { + name: "oauthClientAttestation", + value: request.oauthClientAttestation, + schema: s.optional(s.string()), + }, + { + name: "oauthClientAttestationPop", + value: request.oauthClientAttestationPop, + schema: s.optional(s.string()), + }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + { + name: "cimdOptions", + value: request.cimdOptions, + schema: s.optional(s.lazy(() => cimdOptionsSchema)), + }, + ], + }, + }, + { + success: { kind: "json", schema: tokenResponseSchema }, + errorFactory: TokenEndpoint.AuthTokenApiError, + }, + options, + ); + } + + /** + * Fail Token Request + * + * @remarks + * This API generates a content of an error token response that the authorization server + * implementation returns to the client application. + * + * @throws {@link TokenEndpoint.AuthTokenFailApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenFailApi( + request: TokenEndpoint.AuthTokenFailApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/fail"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "reason", value: request.reason, schema: reason1Schema }, + ], + }, + }, + { + success: { kind: "json", schema: tokenFailResponseSchema }, + errorFactory: TokenEndpoint.AuthTokenFailApiError, + }, + options, + ); + } + + /** + * Issue Token Response + * + * @remarks + * This API generates a content of a successful token response that the authorization server + * implementation returns to the client application. + * + * @returns Token issued successfully + * + * @throws {@link TokenEndpoint.AuthTokenIssueApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenIssueApi( + request: TokenEndpoint.AuthTokenIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "ticket", value: request.ticket, schema: s.string() }, + { name: "subject", value: request.subject, schema: s.string() }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + ], + }, + }, + { + success: { kind: "json", schema: tokenIssueResponseSchema }, + errorFactory: TokenEndpoint.AuthTokenIssueApiError, + }, + options, + ); + } + + /** + * Reissue ID Token + * + * @remarks + * The API is expected to be called only when the value of the `action` parameter in a response + * from the `/auth/token` API is + * [ID_TOKEN_REISSUABLE](https://authlete.github.io/authlete-java-common/com/authlete/common/dto/TokenResponse.Action.html#ID_TOKEN_REISSUABLE). + * The purpose of the `/idtoken/reissue` API is to generate a token response that includes a new + * ID token together with a new access token and a refresh token. + * + * @returns ID token reissued successfully + * + * @throws {@link TokenEndpoint.IdtokenReissueApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + idtokenReissueApi( + request: TokenEndpoint.IdtokenReissueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/idtoken/reissue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "json", + value: request.body, + schema: s.optional(s.lazy(() => idtokenReissueRequestSchema)), + }, + }, + { + success: { kind: "json", schema: idtokenReissueResponseSchema }, + errorFactory: TokenEndpoint.IdtokenReissueApiError, + }, + options, + ); + } +} + +export namespace TokenEndpoint { + export type AuthTokenApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * OAuth 2.0 token request parameters which are the request parameters that the OAuth 2.0 token + * endpoint of the authorization server implementation received from the client application. + * + * The value of parameters is the entire entity body (which is formatted in + * `application/x-www-form-urlencoded`) of the request from the client application. + */ + parameters: string; + /** + * The client ID extracted from `Authorization` header of the token request from the client + * application. + * + * If the token endpoint of the authorization server implementation supports basic + * authentication as a means of client authentication, and the request from the client + * application contained its client ID in `Authorization` header, the value should be extracted + * and set to this parameter. + */ + clientId?: string; + /** + * The client secret extracted from `Authorization` header of the token request from the client + * application. + * + * If the token endpoint of the authorization server implementation supports basic + * authentication as a means of client authentication, and the request from the client + * application contained its client secret in `Authorization` header, the value should be + * extracted and set to this parameter. + */ + clientSecret?: string; + /** The client certificate from the MTLS of the token request from the client application. */ + clientCertificate?: string; + /** + * The certificate path presented by the client during client authentication. These certificates + * are strings in PEM format. + */ + clientCertificatePath?: string[]; + /** + * Extra properties to associate with an access token. See [Extra + * Properties](https://www.authlete.com/developers/definitive_guide/extra_properties/) for + * details. + */ + properties?: Property[]; + /** + * `DPoP` header presented by the client during the request to the token endpoint. + * + * The header contains a signed JWT which includes the public key that is paired with the + * private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the token request. This field is used to validate the `DPoP` header. + * + * In normal cases, the value is `POST`. When this parameter is omitted, `POST` is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htm?: string; + /** + * URL of the token endpoint. This field is used to validate the `DPoP` header. + * + * If this parameter is omitted, the `tokenEndpoint` property of the Service is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API + * call. + */ + accessToken?: string; + /** + * Additional claims that are added to the payload part of the JWT access token. + * + * Effective only in the client_credentials grant flow (and the Native SSO branch of Token + * Exchange). For other grant flows, set additional JWT claims via the corresponding API: + * /auth/authorization/issue (authorization_code), /auth/backchannel/authentication/complete + * (CIBA), /auth/device/complete (device_code). The parameter is silently ignored when sent with + * other grants. + */ + jwtAtClaims?: string; + /** + * The value of the `OAuth-Client-Attestation` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestation?: string; + /** + * The value of the `OAuth-Client-Attestation-PoP` HTTP header, which is defined in the + * specification of [OAuth 2.0 Attestation-Based Client + * Authentication](https://datatracker.ietf.org/doc/draft-ietf-oauth-attestation-based-client-auth/). + */ + oauthClientAttestationPop?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * access token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * refresh token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + /** + * The flag indicating whether to require the DPoP proof JWT to include the `nonce` claim. Even + * if the service's `dpopNonceRequired` property is `false`, calling the `/auth/token` API with + * this `dpopNonceRequired` parameter `true` will force the Authlete API to check whether the + * DPoP proof JWT includes the expected `nonce` value. + */ + dpopNonceRequired?: boolean; + /** + * Options for [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD). + * + * These options allow per-request control over CIMD behavior, taking precedence over + * service-level configuration when provided. + */ + cimdOptions?: CimdOptions; + }; + + export class AuthTokenApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenFailApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued from Authlete `/auth/token` API. */ + ticket: string; + /** The reason of the failure of the token request. */ + reason: Reason1; + }; + + export class AuthTokenFailApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenIssueApiRequest = { + /** A service ID. */ + serviceId: string; + /** The ticket issued from Authlete `/auth/token` API. */ + ticket: string; + /** The subject (= unique identifier) of the authenticated user. */ + subject: string; + /** + * Extra properties to associate with a newly created access token. Note that properties + * parameter is accepted only when `Content-Type` of the request is `application/json`, so don't + * use `application/x-www-form-urlencoded` if you want to specify properties. + */ + properties?: Property[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The representation of an access token that may be issued as a result of the Authlete API + * call. + */ + accessToken?: string; + /** + * The duration (in seconds) of the access token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * access token in. In other cases, this request parameter is ignored. + */ + accessTokenDuration?: number; + /** + * The duration (in seconds) of the refresh token that may be issued as a result of the Authlete + * API call. + * + * When this request parameter holds a positive integer, it is used as the duration of the + * refresh token in. In other cases, this request parameter is ignored. + */ + refreshTokenDuration?: number; + }; + + export class AuthTokenIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type IdtokenReissueApiRequest = { + /** A service ID. */ + serviceId: string; + body?: IdtokenReissueRequest; + }; + + export class IdtokenReissueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/token-operations.ts b/plugins/authlete/sdk/typescript/src/resources/token-operations.ts new file mode 100644 index 00000000..38b08d21 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/token-operations.ts @@ -0,0 +1,730 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { authzDetailsSchema, type AuthzDetails } from "../models/authz-details.js"; +import { grantTypeSchema, type GrantType } from "../models/grant-type.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { tokenCreateResponseSchema, type TokenCreateResponse } from "../models/token-create-response.js"; +import { tokenGetListResponseSchema, type TokenGetListResponse } from "../models/token-get-list-response.js"; +import { tokenRevokeResponseSchema, type TokenRevokeResponse } from "../models/token-revoke-response.js"; +import { tokenUpdateResponseSchema, type TokenUpdateResponse } from "../models/token-update-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for various token related operations, including creating, revoking and deleting + * access_tokens with specified scopes. + */ +export class TokenOperations { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Create Access Token + * + * @remarks + * Create an access token. + * + * @returns Token created successfully + * + * @throws {@link TokenOperations.AuthTokenCreateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenCreateApi( + request: TokenOperations.AuthTokenCreateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/create"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "grantType", value: request.grantType, schema: grantTypeSchema }, + { name: "clientId", value: request.clientId, schema: s.optional(s.int()) }, + { name: "subject", value: request.subject, schema: s.optional(s.string()) }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { name: "accessTokenDuration", value: request.accessTokenDuration, schema: s.optional(s.int()) }, + { + name: "refreshTokenDuration", + value: request.refreshTokenDuration, + schema: s.optional(s.int()), + }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "clientIdAliasUsed", value: request.clientIdAliasUsed, schema: s.optional(s.boolean()) }, + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "refreshToken", value: request.refreshToken, schema: s.optional(s.string()) }, + { + name: "accessTokenPersistent", + value: request.accessTokenPersistent, + schema: s.optional(s.boolean()), + }, + { + name: "certificateThumbprint", + value: request.certificateThumbprint, + schema: s.optional(s.string()), + }, + { name: "dpopKeyThumbprint", value: request.dpopKeyThumbprint, schema: s.optional(s.string()) }, + { + name: "authorizationDetails", + value: request.authorizationDetails, + schema: s.optional(s.lazy(() => authzDetailsSchema)), + }, + { name: "resources", value: request.resources, schema: s.optional(s.array(s.string())) }, + { + name: "forExternalAttachment", + value: request.forExternalAttachment, + schema: s.optional(s.boolean()), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "acr", value: request.acr, schema: s.optional(s.string()) }, + { name: "authTime", value: request.authTime, schema: s.optional(s.int()) }, + { + name: "clientEntityIdUsed", + value: request.clientEntityIdUsed, + schema: s.optional(s.boolean()), + }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.optional(s.string()) }, + { name: "sessionId", value: request.sessionId, schema: s.optional(s.string()) }, + { + name: "metadataDocumentUsed", + value: request.metadataDocumentUsed, + schema: s.optional(s.boolean()), + }, + ], + }, + }, + { + success: { kind: "json", schema: tokenCreateResponseSchema }, + errorFactory: TokenOperations.AuthTokenCreateApiError, + }, + options, + ); + } + + /** + * Delete Access Token + * + * @remarks + * Delete an access token. + * + * @returns The access token was successfully deleted. + * + * @throws {@link TokenOperations.AuthTokenDeleteApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenDeleteApi( + request: TokenOperations.AuthTokenDeleteApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "DELETE", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/delete/{accessTokenIdentifier}"), + auth: this.#auth.bearer, + pathParams: [ + { name: "serviceId", value: request.serviceId, schema: s.string() }, + { name: "accessTokenIdentifier", value: request.accessTokenIdentifier, schema: s.string() }, + ], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "empty" }, + }, + { + success: { kind: "empty" }, + errorFactory: TokenOperations.AuthTokenDeleteApiError, + }, + options, + ); + } + + /** + * List Issued Tokens + * + * @remarks + * Get the list of access tokens that are associated with the service. + * + * @throws {@link TokenOperations.AuthTokenGetListApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenGetListApi( + request: TokenOperations.AuthTokenGetListApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/get/list"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [ + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.optional(s.string()) }, + { name: "subject", value: request.subject, schema: s.optional(s.string()) }, + { name: "start", value: request.start, schema: s.optional(s.int()) }, + { name: "end", value: request.end, schema: s.optional(s.int()) }, + ], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: tokenGetListResponseSchema }, + errorFactory: TokenOperations.AuthTokenGetListApiError, + }, + options, + ); + } + + /** + * Revoke Access Token + * + * @remarks + * Revoke an access token. + * + * @returns Token revoked successfully + * + * @throws {@link TokenOperations.AuthTokenRevokeApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenRevokeApi( + request: TokenOperations.AuthTokenRevokeApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/revoke"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { + name: "accessTokenIdentifier", + value: request.accessTokenIdentifier, + schema: s.optional(s.string()), + }, + { + name: "refreshTokenIdentifier", + value: request.refreshTokenIdentifier, + schema: s.optional(s.string()), + }, + { name: "clientIdentifier", value: request.clientIdentifier, schema: s.optional(s.string()) }, + { name: "subject", value: request.subject, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: tokenRevokeResponseSchema }, + errorFactory: TokenOperations.AuthTokenRevokeApiError, + }, + options, + ); + } + + /** + * Update Access Token + * + * @remarks + * Update an access token. + * + * @returns Token updated successfully + * + * @throws {@link TokenOperations.AuthTokenUpdateApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authTokenUpdateApi( + request: TokenOperations.AuthTokenUpdateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/token/update"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { + name: "accessTokenExpiresAt", + value: request.accessTokenExpiresAt, + schema: s.optional(s.int()), + }, + { name: "scopes", value: request.scopes, schema: s.optional(s.array(s.string())) }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { + name: "accessTokenExpiresAtUpdatedOnScopeUpdate", + value: request.accessTokenExpiresAtUpdatedOnScopeUpdate, + schema: s.optional(s.boolean()), + }, + { name: "accessTokenHash", value: request.accessTokenHash, schema: s.optional(s.string()) }, + { + name: "accessTokenValueUpdated", + value: request.accessTokenValueUpdated, + schema: s.optional(s.boolean()), + }, + { + name: "accessTokenPersistent", + value: request.accessTokenPersistent, + schema: s.optional(s.boolean()), + }, + { + name: "certificateThumbprint", + value: request.certificateThumbprint, + schema: s.optional(s.string()), + }, + { name: "dpopKeyThumbprint", value: request.dpopKeyThumbprint, schema: s.optional(s.string()) }, + { + name: "authorizationDetails", + value: request.authorizationDetails, + schema: s.optional(s.lazy(() => authzDetailsSchema)), + }, + { + name: "forExternalAttachment", + value: request.forExternalAttachment, + schema: s.optional(s.boolean()), + }, + { + name: "refreshTokenExpiresAt", + value: request.refreshTokenExpiresAt, + schema: s.optional(s.int()), + }, + { + name: "refreshTokenExpiresAtUpdatedOnScopeUpdate", + value: request.refreshTokenExpiresAtUpdatedOnScopeUpdate, + schema: s.optional(s.boolean()), + }, + { name: "tokenId", value: request.tokenId, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: tokenUpdateResponseSchema }, + errorFactory: TokenOperations.AuthTokenUpdateApiError, + }, + options, + ); + } +} + +export namespace TokenOperations { + export type AuthTokenCreateApiRequest = { + /** A service ID. */ + serviceId: string; + /** The grant type of the access token when the access token was created. */ + grantType: GrantType; + /** + * The ID of the client application which will be associated with a newly created access token. + */ + clientId?: number; + /** + * The subject (= unique identifier) of the user who will be associated with a newly created + * access token. This parameter is required unless the grant type is `CLIENT_CREDENTIALS`. The + * value must consist of only ASCII characters and its length must not exceed 100. + */ + subject?: string; + /** + * The scopes which will be associated with a newly created access token. Scopes that are not + * supported by the service cannot be specified and requesting them will cause an error. + */ + scopes?: string[]; + /** + * The duration of a newly created access token in seconds. If the value is 0, the duration is + * determined according to the settings of the service. + */ + accessTokenDuration?: number; + /** + * The duration of a newly created refresh token in seconds. If the value is 0, the duration is + * determined according to the settings of the service. + * + * A refresh token is not created (1) if the service does not support `REFRESH_TOKEN`, or (2) if + * the specified grant type is either `IMPLICIT`or `CLIENT_CREDENTIALS`. + */ + refreshTokenDuration?: number; + /** + * Extra properties to associate with a newly created access token. Note that properties + * parameter is accepted only when the HTTP method of the request is POST and Content-Type of + * the request is `application/json`, so don't use `GET` method or + * `application/x-www-form-urlencoded` if you want to specify properties. + */ + properties?: Property[]; + /** + * A boolean request parameter which indicates whether to emulate that the client ID alias is + * used instead of the original numeric client ID when a new access token is created. + */ + clientIdAliasUsed?: boolean; + /** The value of the new access token. */ + accessToken?: string; + /** The value of the new refresh token. */ + refreshToken?: string; + /** + * Get whether the access token expires or not. By default, all access tokens expire after a + * period of time determined by their service. + * + * If this request parameter is `true`, then the access token will not automatically expire and + * must be revoked or deleted manually at the service. If this request parameter is true, the + * `accessTokenDuration` request parameter is ignored. + */ + accessTokenPersistent?: boolean; + /** + * The thumbprint of the MTLS certificate bound to this token. If this property is set, a + * certificate with the corresponding value MUST be presented with the access token when it is + * used by a client. The value of this property must be a SHA256 certificate thumbprint, + * base64url encoded. + */ + certificateThumbprint?: string; + /** + * The thumbprint of the public key used for DPoP presentation of this token. If this property + * is set, a DPoP proof signed with the corresponding private key MUST be presented with the + * access token when it is used by a client. Additionally, the token's `token_type` will be set + * to 'DPoP'. + */ + dpopKeyThumbprint?: string; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** + * The value of the resources to associate with the token. This property represents the value of + * one or more `resource` request parameters which is defined in "RFC8707 Resource Indicators + * for OAuth 2.0". + */ + resources?: string[]; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The Authentication Context Class Reference of the user authentication that the authorization + * server performed during the course of issuing the access token. + */ + acr?: string; + /** + * The time when the user authentication was performed during the course of issuing the access + * token. + */ + authTime?: number; + /** + * Flag which indicates whether the entity ID of the client was used when the request for the + * access token was made. + */ + clientEntityIdUsed?: boolean; + /** The client Identifier associated with the newly issued access token. */ + clientIdentifier?: string; + /** + * The session ID, which is the ID of the user's authentication session, associated with a newly + * created access token. + */ + sessionId?: string; + /** + * Flag indicating whether a metadata document was used to resolve client metadata for this + * request. + * + * When `true`, the client metadata was retrieved via the [OAuth Client ID Metadata + * Document](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/) + * (CIMD) mechanism rather than from the Authlete database. + */ + metadataDocumentUsed?: boolean; + }; + + export class AuthTokenCreateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenDeleteApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The identifier of an existing access token. The identifier is the value of the access token + * or the value of the hash of the access token. + */ + accessTokenIdentifier: string; + }; + + export class AuthTokenDeleteApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenGetListApiRequest = { + /** A service ID. */ + serviceId: string; + /** Client Identifier (client ID or client ID alias). */ + clientIdentifier?: string; + /** Unique user ID. */ + subject?: string; + /** Start index of search results (inclusive). The default value is 0. */ + start?: number; + /** End index of search results (exclusive). The default value is 5. */ + end?: number; + }; + + export class AuthTokenGetListApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenRevokeApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The identifier of an access token to revoke + * + * The hash of an access token is recognized as an identifier as well as the access token + * itself. + */ + accessTokenIdentifier?: string; + /** + * The identifier of a refresh token to revoke. + * + * The hash of a refresh token is recognized as an identifier as well as the refresh token + * itself. + */ + refreshTokenIdentifier?: string; + /** + * The client ID of the access token to be revoked. + * + * Both the numeric client ID and the alias are recognized as an identifier of a client. + * + * Bulk revocation with `clientIdentifier` only or `clientIdentifier` + `subject` deletes at + * most **20 tokens per request** (the default of `token.revoke.count.max` in + * `ServerConfiguration.java`). If the target has more than 20 tokens, the response `count` will + * be 20 and the remainder is left untouched. To fully wipe them, call the endpoint repeatedly + * until `count` returns 0. + */ + clientIdentifier?: string; + /** + * The subject of a resource owner. + * + * Bulk revocation with `clientIdentifier` + `subject` or `subject` only deletes at most **20 + * tokens per request** (the default of `token.revoke.count.max` in `ServerConfiguration.java`). + * If the target has more than 20 tokens, the response `count` will be 20 and the remainder is + * left untouched. To fully wipe them, call the endpoint repeatedly until `count` returns 0. + */ + subject?: string; + }; + + export class AuthTokenRevokeApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Declared<"result6", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 404, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result6", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthTokenUpdateApiRequest = { + /** A service ID. */ + serviceId: string; + /** An access token. */ + accessToken?: string; + /** + * A new date at which the access token will expire in milliseconds since the Unix epoch + * (1970-01-01). If the `accessTokenExpiresAt` request parameter is not included in a request or + * its value is 0 (or negative), the expiration date of the access token is not changed. + */ + accessTokenExpiresAt?: number; + /** + * A new set of scopes assigned to the access token. Scopes that are not supported by the + * service and those that the client application associated with the access token is not allowed + * to request are ignored on the server side. If the `scopes` request parameter is not included + * in a request or its value is `null`, the scopes of the access token are not changed. Note + * that `properties` parameter is accepted only when `Content-Type` of the request is + * `application/json`, so don't use `application/x-www-form-urlencoded` if you want to specify + * `properties`. + */ + scopes?: string[]; + /** + * A new set of properties assigned to the access token. If the `properties` request parameter + * is not included in a request or its value is null, the properties of the access token are not + * changed. + */ + properties?: Property[]; + /** + * A boolean request parameter which indicates whether the API attempts to update the expiration + * date of the access token when the scopes linked to the access token are changed by this + * request. + */ + accessTokenExpiresAtUpdatedOnScopeUpdate?: boolean; + /** + * The hash of the access token value. Used when the hash of the token is known (perhaps from + * lookup) but the value of the token itself is not. The value of the `accessToken` parameter + * takes precedence. + */ + accessTokenHash?: string; + /** + * A boolean request parameter which indicates whether to update the value of the access token + * in the data store. If this parameter is set to `true` then a new access token value is + * generated by the server and returned in the response. + */ + accessTokenValueUpdated?: boolean; + /** + * The flag which indicates whether the access token expires or not. By default, all access + * tokens expire after a period of time determined by their service. If this request parameter + * is `true` then the access token will not automatically expire and must be revoked or deleted + * manually at the service. + * + * If this request parameter is `true`, the `accessTokenExpiresAt` request parameter is ignored. + * If this request parameter is `false`, the `accessTokenExpiresAt` request parameter is + * processed normally. + */ + accessTokenPersistent?: boolean; + /** + * The thumbprint of the MTLS certificate bound to this token. If this property is set, a + * certificate with the corresponding value MUST be presented with the access token when it is + * used by a client. The value of this property must be a SHA256 certificate thumbprint, + * base64url encoded. + */ + certificateThumbprint?: string; + /** + * The thumbprint of the public key used for DPoP presentation of this token. If this property + * is set, a DPoP proof signed with the corresponding private key MUST be presented with the + * access token when it is used by a client. Additionally, the token's `token_type` will be set + * to 'DPoP'. + */ + dpopKeyThumbprint?: string; + /** + * The authorization details. This represents the value of the `authorization_details` request + * parameter in the preceding device authorization request which is defined in "OAuth 2.0 Rich + * Authorization Requests". + */ + authorizationDetails?: AuthzDetails; + /** the flag which indicates whether the access token is for an external attachment. */ + forExternalAttachment?: boolean; + /** + * A new date at which the access token will expire in milliseconds since the Unix epoch + * (1970-01-01). If the `refreshTokenExpiresAt` request parameter is not included in a request + * or its value is 0 (or negative), the expiration date of the refresh token is not changed. + */ + refreshTokenExpiresAt?: number; + /** + * A boolean request parameter which indicates whether the API attempts to update the expiration + * date of the refresh token when the scopes linked to the refresh token are changed by this + * request. + */ + refreshTokenExpiresAtUpdatedOnScopeUpdate?: boolean; + /** The token identifier. */ + tokenId?: string; + }; + + export class AuthTokenUpdateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/user-info-endpoint.ts b/plugins/authlete/sdk/typescript/src/resources/user-info-endpoint.ts new file mode 100644 index 00000000..98f6c380 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/user-info-endpoint.ts @@ -0,0 +1,283 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { pairSchema, type Pair } from "../models/pair.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { + userinfoIssueResponseSchema, + type UserinfoIssueResponse, +} from "../models/userinfo-issue-response.js"; +import { userinfoResponseSchema, type UserinfoResponse } from "../models/userinfo-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing OpenID Connect UserInfo Endpoint. + */ +export class UserInfoEndpoint { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Process UserInfo Request + * + * @remarks + * This API gathers information about a user. + * + * @returns User info retrieved successfully + * + * @throws {@link UserInfoEndpoint.AuthUserinfoApiError} when the API answers with an error status + * β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authUserinfoApi( + request: UserInfoEndpoint.AuthUserinfoApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/userinfo"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "token", value: request.token, schema: s.string() }, + { name: "clientCertificate", value: request.clientCertificate, schema: s.optional(s.string()) }, + { name: "dpop", value: request.dpop, schema: s.optional(s.string()) }, + { name: "htm", value: request.htm, schema: s.optional(s.string()) }, + { name: "htu", value: request.htu, schema: s.optional(s.string()) }, + { name: "uri", value: request.uri, schema: s.optional(s.string()) }, + { name: "message", value: request.message, schema: s.optional(s.string()) }, + { + name: "headers", + value: request.headers, + schema: s.optional(s.array(s.lazy(() => pairSchema))), + }, + { name: "targetUri", value: request.targetUri, schema: s.optional(s.string()) }, + { name: "dpopNonceRequired", value: request.dpopNonceRequired, schema: s.optional(s.boolean()) }, + { + name: "requestBodyContained", + value: request.requestBodyContained, + schema: s.optional(s.boolean()), + }, + ], + }, + }, + { + success: { kind: "json", schema: userinfoResponseSchema }, + errorFactory: UserInfoEndpoint.AuthUserinfoApiError, + }, + options, + ); + } + + /** + * Issue UserInfo Response + * + * @remarks + * This API generates an ID token. + * + * @returns User info issued successfully + * + * @throws {@link UserInfoEndpoint.AuthUserinfoIssueApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + authUserinfoIssueApi( + request: UserInfoEndpoint.AuthUserinfoIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/auth/userinfo/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "token", value: request.token, schema: s.string() }, + { name: "claims", value: request.claims, schema: s.optional(s.string()) }, + { name: "sub", value: request.sub, schema: s.optional(s.string()) }, + { name: "claimsForTx", value: request.claimsForTx, schema: s.optional(s.string()) }, + { name: "requestSignature", value: request.requestSignature, schema: s.optional(s.string()) }, + { + name: "headers", + value: request.headers, + schema: s.optional(s.array(s.lazy(() => pairSchema))), + }, + { + name: "verifiedClaimsForTx", + value: request.verifiedClaimsForTx, + schema: s.optional(s.array(s.string())), + }, + ], + }, + }, + { + success: { kind: "json", schema: userinfoIssueResponseSchema }, + errorFactory: UserInfoEndpoint.AuthUserinfoIssueApiError, + }, + options, + ); + } +} + +export namespace UserInfoEndpoint { + export type AuthUserinfoApiRequest = { + /** A service ID. */ + serviceId: string; + /** An access token. */ + token: string; + /** + * Client certificate used in the TLS connection established between the client application and + * the userinfo endpoint. + * + * The value of this request parameter is referred to when the access token given to the + * userinfo endpoint was bound to a client certificate when it was issued. See [OAuth 2.0 Mutual + * TLS Client Authentication and Certificate-Bound Access Tokens] + * (https://datatracker.ietf.org/doc/rfc8705/) for details about the specification of + * certificate-bound access tokens. + */ + clientCertificate?: string; + /** + * `DPoP` header presented by the client during the request to the user info endpoint. + * + * The header contains a signed JWT which includes the public key that is paired with the + * private key used to sign the JWT. See [OAuth 2.0 Demonstration of Proof-of-Possession at the + * Application Layer (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for + * details. + */ + dpop?: string; + /** + * HTTP method of the user info request. This field is used to validate the DPoP header. In + * normal cases, the value is either `GET` or `POST`. + */ + htm?: string; + /** + * URL of the user info endpoint. This field is used to validate the DPoP header. + * + * If this parameter is omitted, the `userInfoEndpoint` property of the service is used as the + * default value. See [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer + * (DPoP)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop) for details. + */ + htu?: string; + /** The full URL of the userinfo endpoint. */ + uri?: string; + /** The HTTP message body of the request, if present. */ + message?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this + * must include the Signature and Signature-Input headers, as well as any additional headers + * covered by the signature. + */ + headers?: Pair[]; + /** The target URI of the userinfo request, including the query part, if any. */ + targetUri?: string; + /** + * The flag indicating whether to check if the DPoP proof JWT includes the expected `nonce` + * value. + * + * If this request parameter is set to `true` or if the service's `dpopNonceRequired` property + * is set to `true`, the `/auth/userinfo` API checks if the DPoP proof JWT includes the expected + * `nonce` value. In this case, the response from the `/auth/userinfo` API will include the + * `dpopNonce` response parameter, which should be used as the value of the DPoP-Nonce HTTP + * header. + */ + dpopNonceRequired?: boolean; + /** The flag indicating whether the userinfo request contains a request body. */ + requestBodyContained?: boolean; + }; + + export class AuthUserinfoApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type AuthUserinfoIssueApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The access token that has been passed to the userinfo endpoint by the client application. In + * other words, the access token which was contained in the userinfo request. + */ + token: string; + /** + * Claims in JSON format. As for the format, see [OpenID Connect Core 1.0, 5.1. Standard + * Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims). + */ + claims?: string; + /** + * The value of the `sub` claim. If the value of this request parameter is not empty, it is used + * as the value of the `sub` claim. Otherwise, the value of the subject associated with the + * access token is used. + */ + sub?: string; + /** Claim key-value pairs that are used to compute transformed claims. */ + claimsForTx?: string; + /** The Signature header value from the request. */ + requestSignature?: string; + /** + * HTTP headers to be included in processing the signature. If this is a signed request, this + * must include the Signature and Signature-Input headers, as well as any additional headers + * covered by the signature. + */ + headers?: Pair[]; + /** Values of verified claims requested indirectly by "transformed claims". */ + verifiedClaimsForTx?: string[]; + }; + + export class AuthUserinfoIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/utility-endpoints.ts b/plugins/authlete/sdk/typescript/src/resources/utility-endpoints.ts new file mode 100644 index 00000000..e04a6f64 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/utility-endpoints.ts @@ -0,0 +1,112 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import { noneAuth } from "../core/auth/schemes.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { infoResponseSchema, type InfoResponse } from "../models/info-response.js"; +import { resultSchema, type Result } from "../models/result.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for various utility operations. + */ +export class UtilityEndpoints { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Get Server Metadata + * + * @remarks + * get the server version and enabled features + * + * @returns API information retrieved successfully + * + * @throws {@link UtilityEndpoints.InfoApiError} when the API answers with an error status β€” + * narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + infoApi(options?: RequestOptions): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/info"), + auth: noneAuth, + pathParams: [], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "json", schema: infoResponseSchema }, + errorFactory: UtilityEndpoints.InfoApiError, + }, + options, + ); + } + + /** + * Echo + * + * @remarks + * Echo test endpoint. Will return all path parameters in the request + * + * @returns Successfully echoed the request parameters + * + * @throws {@link ApiError} when the API answers with an error status + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + miscEchoApi(options?: RequestOptions): ApiPromise { + return this.#rawClient.execute( + { + method: "GET", + urlTemplate: this.#servers.default("/api/misc/echo"), + auth: this.#auth.bearer, + pathParams: [], + query: [], + headers: [], + body: { kind: "empty" }, + }, + { + success: { kind: "empty" }, + errorFactory: ApiError, + }, + options, + ); + } +} + +export namespace UtilityEndpoints { + export class InfoApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/resources/verifiable-credential-issuer.ts b/plugins/authlete/sdk/typescript/src/resources/verifiable-credential-issuer.ts new file mode 100644 index 00000000..1b1c6d90 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/resources/verifiable-credential-issuer.ts @@ -0,0 +1,849 @@ +import type { AuthSchemes } from "../auth-schemes.js"; +import { ApiError, type Declared, type Undeclared } from "../core/api-error.js"; +import type { ApiPromise } from "../core/api-promise.js"; +import type { RequestOptions } from "../core/api-request.js"; +import type { RawClient } from "../core/raw-client.js"; +import type { ErrorDecoders } from "../core/response-decoder.js"; +import { uuid } from "../core/uuid.js"; +import * as s from "../core/validation/index.js"; +import { propertySchema, type Property } from "../models/property.js"; +import { resultSchema, type Result } from "../models/result.js"; +import { vciBatchIssueRequestSchema, type VciBatchIssueRequest } from "../models/vci-batch-issue-request.js"; +import { + vciBatchIssueResponseSchema, + type VciBatchIssueResponse, +} from "../models/vci-batch-issue-response.js"; +import { + vciBatchParseResponseSchema, + type VciBatchParseResponse, +} from "../models/vci-batch-parse-response.js"; +import { + vciDeferredIssueRequestSchema, + type VciDeferredIssueRequest, +} from "../models/vci-deferred-issue-request.js"; +import { + vciDeferredIssueResponseSchema, + type VciDeferredIssueResponse, +} from "../models/vci-deferred-issue-response.js"; +import { + vciDeferredParseResponseSchema, + type VciDeferredParseResponse, +} from "../models/vci-deferred-parse-response.js"; +import { vciJwksResponseSchema, type VciJwksResponse } from "../models/vci-jwks-response.js"; +import { vciJwtissuerResponseSchema, type VciJwtissuerResponse } from "../models/vci-jwtissuer-response.js"; +import { vciMetadataResponseSchema, type VciMetadataResponse } from "../models/vci-metadata-response.js"; +import { + vciOfferCreateResponseSchema, + type VciOfferCreateResponse, +} from "../models/vci-offer-create-response.js"; +import { vciOfferInfoResponseSchema, type VciOfferInfoResponse } from "../models/vci-offer-info-response.js"; +import { + vciSingleIssueRequestSchema, + type VciSingleIssueRequest, +} from "../models/vci-single-issue-request.js"; +import { + vciSingleIssueResponseSchema, + type VciSingleIssueResponse, +} from "../models/vci-single-issue-response.js"; +import { + vciSingleParseResponseSchema, + type VciSingleParseResponse, +} from "../models/vci-single-parse-response.js"; +import type { Servers } from "../servers.js"; + +/** + * API endpoints for implementing and running a Verifiable Credential Issuer (VCI). + */ +export class VerifiableCredentialIssuer { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + + constructor(rawClient: RawClient, servers: Servers, auth: AuthSchemes) { + this.#rawClient = rawClient; + this.#servers = servers; + this.#auth = auth; + } + + /** + * Issue Batch Credentials + * + * @remarks + * Issue multiple verifiable credentials in batch + * + * @returns Batch credential issuance completed successfully + * + * @throws {@link VerifiableCredentialIssuer.VciBatchIssueApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciBatchIssueApi( + request: VerifiableCredentialIssuer.VciBatchIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/batch/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: vciBatchIssueRequestSchema }, + }, + { + success: { kind: "json", schema: vciBatchIssueResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciBatchIssueApiError, + }, + options, + ); + } + + /** + * Parse Batch Credentials + * + * @remarks + * Parse multiple verifiable credentials in batch + * + * @returns Batch credential parsing completed successfully + * + * @throws {@link VerifiableCredentialIssuer.VciBatchParseApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciBatchParseApi( + request: VerifiableCredentialIssuer.VciBatchParseApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/batch/parse"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "requestContent", value: request.requestContent, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: vciBatchParseResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciBatchParseApiError, + }, + options, + ); + } + + /** + * Issue Deferred Credential + * + * @remarks + * Issue a deferred verifiable credential + * + * @throws {@link VerifiableCredentialIssuer.VciDeferredIssueApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciDeferredIssueApi( + request: VerifiableCredentialIssuer.VciDeferredIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/deferred/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: vciDeferredIssueRequestSchema }, + }, + { + success: { kind: "json", schema: vciDeferredIssueResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciDeferredIssueApiError, + }, + options, + ); + } + + /** + * Parse Deferred Credential + * + * @remarks + * Parse a deferred verifiable credential + * + * @throws {@link VerifiableCredentialIssuer.VciDeferredParseApiError} when the API answers with + * an error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciDeferredParseApi( + request: VerifiableCredentialIssuer.VciDeferredParseApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/deferred/parse"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "requestContent", value: request.requestContent, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: vciDeferredParseResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciDeferredParseApiError, + }, + options, + ); + } + + /** + * Get JSON Web Key Set + * + * @remarks + * Get JSON Web Key Set for VCI + * + * @throws {@link VerifiableCredentialIssuer.VciJwksApiError} when the API answers with an error + * status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciJwksApi( + request: VerifiableCredentialIssuer.VciJwksApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/jwks"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "pretty", value: request.pretty, schema: s.boolean() }], + }, + }, + { + success: { kind: "json", schema: vciJwksResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciJwksApiError, + }, + options, + ); + } + + /** + * Get JWT Issuer Information + * + * @remarks + * Get JWT issuer information for VCI + * + * @throws {@link VerifiableCredentialIssuer.VciJwtissuerApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciJwtissuerApi( + request: VerifiableCredentialIssuer.VciJwtissuerApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/jwtissuer"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "pretty", value: request.pretty, schema: s.boolean() }], + }, + }, + { + success: { kind: "json", schema: vciJwtissuerResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciJwtissuerApiError, + }, + options, + ); + } + + /** + * Get Verifiable Credential Issuer Metadata + * + * @remarks + * Get verifiable credential issuer metadata + * + * @returns VCI metadata retrieved successfully + * + * @throws {@link VerifiableCredentialIssuer.VciMetadataApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciMetadataApi( + request: VerifiableCredentialIssuer.VciMetadataApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/metadata"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "pretty", value: request.pretty, schema: s.boolean() }], + }, + }, + { + success: { kind: "json", schema: vciMetadataResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciMetadataApiError, + }, + options, + ); + } + + /** + * Create Credential Offer + * + * @remarks + * Create a verifiable credential offer + * + * @throws {@link VerifiableCredentialIssuer.VciOfferCreateApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciOfferCreateApi( + request: VerifiableCredentialIssuer.VciOfferCreateApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/offer/create"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { + name: "authorizationCodeGrantIncluded", + value: request.authorizationCodeGrantIncluded, + schema: s.optional(s.boolean()), + }, + { + name: "issuerStateIncluded", + value: request.issuerStateIncluded, + schema: s.optional(s.boolean()), + }, + { + name: "preAuthorizedCodeGrantIncluded", + value: request.preAuthorizedCodeGrantIncluded, + schema: s.optional(s.boolean()), + }, + { name: "subject", value: request.subject, schema: s.optional(s.string()) }, + { name: "duration", value: request.duration, schema: s.optional(s.int()) }, + { name: "context", value: request.context, schema: s.optional(s.string()) }, + { + name: "properties", + value: request.properties, + schema: s.optional(s.array(s.lazy(() => propertySchema))), + }, + { name: "jwtAtClaims", value: request.jwtAtClaims, schema: s.optional(s.string()) }, + { name: "authTime", value: request.authTime, schema: s.optional(s.int()) }, + { name: "acr", value: request.acr, schema: s.optional(s.string()) }, + { + name: "credentialConfigurationIds", + value: request.credentialConfigurationIds, + schema: s.optional(s.array(s.string())), + }, + { name: "txCode", value: request.txCode, schema: s.optional(s.string()) }, + { name: "txCodeInputMode", value: request.txCodeInputMode, schema: s.optional(s.string()) }, + { name: "txCodeDescription", value: request.txCodeDescription, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: vciOfferCreateResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciOfferCreateApiError, + }, + options, + ); + } + + /** + * Get Credential Offer Information + * + * @remarks + * Get information about a verifiable credential offer + * + * @returns Credential offer info retrieved successfully + * + * @throws {@link VerifiableCredentialIssuer.VciOfferInfoApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciOfferInfoApi( + request: VerifiableCredentialIssuer.VciOfferInfoApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/offer/info"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [{ name: "identifier", value: request.identifier, schema: s.optional(s.string()) }], + }, + }, + { + success: { kind: "json", schema: vciOfferInfoResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciOfferInfoApiError, + }, + options, + ); + } + + /** + * Issue Single Credential + * + * @remarks + * Issue a single verifiable credential + * + * @throws {@link VerifiableCredentialIssuer.VciSingleIssueApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciSingleIssueApi( + request: VerifiableCredentialIssuer.VciSingleIssueApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/single/issue"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { kind: "json", value: request.body, schema: vciSingleIssueRequestSchema }, + }, + { + success: { kind: "json", schema: vciSingleIssueResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciSingleIssueApiError, + }, + options, + ); + } + + /** + * Parse Single Credential + * + * @remarks + * Parse a single verifiable credential + * + * @throws {@link VerifiableCredentialIssuer.VciSingleParseApiError} when the API answers with an + * error status β€” narrow on `err.payload.kind` + * + * @throws {@link AuthleteApiError} when no usable response was produced: a connection failure, a + * timeout, a body that would not decode, a value that would not encode, or a credential that + * could not be obtained + */ + vciSingleParseApi( + request: VerifiableCredentialIssuer.VciSingleParseApiRequest, + options?: RequestOptions, + ): ApiPromise { + return this.#rawClient.execute( + { + method: "POST", + urlTemplate: this.#servers.default("/api/{serviceId}/vci/single/parse"), + auth: this.#auth.bearer, + pathParams: [{ name: "serviceId", value: request.serviceId, schema: s.string() }], + query: [], + headers: [{ name: "Idempotency-Key", value: uuid(), schema: s.string() }], + body: { + kind: "formUrlEncoded", + value: [ + { name: "accessToken", value: request.accessToken, schema: s.optional(s.string()) }, + { name: "requestContent", value: request.requestContent, schema: s.optional(s.string()) }, + ], + }, + }, + { + success: { kind: "json", schema: vciSingleParseResponseSchema }, + errorFactory: VerifiableCredentialIssuer.VciSingleParseApiError, + }, + options, + ); + } +} + +export namespace VerifiableCredentialIssuer { + export type VciBatchIssueApiRequest = { + /** A service ID. */ + serviceId: string; + body: VciBatchIssueRequest; + }; + + export class VciBatchIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciBatchParseApiRequest = { + /** A service ID. */ + serviceId: string; + /** The access token that came along with the credential request. */ + accessToken?: string; + /** The message body of the batch credential request. */ + requestContent?: string; + }; + + export class VciBatchParseApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciDeferredIssueApiRequest = { + /** A service ID. */ + serviceId: string; + body: VciDeferredIssueRequest; + }; + + export class VciDeferredIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciDeferredParseApiRequest = { + /** A service ID. */ + serviceId: string; + /** The access token that came along with the deferred credential request. */ + accessToken?: string; + /** The message body of the deferred credential request. */ + requestContent?: string; + }; + + export class VciDeferredParseApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciJwksApiRequest = { + /** A service ID. */ + serviceId: string; + /** The flag indicating whether the metadata is written in the pretty format or not. */ + pretty: boolean; + }; + + export class VciJwksApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciJwtissuerApiRequest = { + /** A service ID. */ + serviceId: string; + /** The flag indicating whether the metadata is written in the pretty format or not. */ + pretty: boolean; + }; + + export class VciJwtissuerApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciMetadataApiRequest = { + /** A service ID. */ + serviceId: string; + /** The flag indicating whether the metadata is written in the pretty format or not. */ + pretty: boolean; + }; + + export class VciMetadataApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciOfferCreateApiRequest = { + /** A service ID. */ + serviceId: string; + /** + * The flag indicating whether the `authorization_code` object is included in the `grants` + * object. + */ + authorizationCodeGrantIncluded?: boolean; + /** + * The flag indicating whether the `issuer_state` property is included in the + * `authorization_code` object in the `grants` object. + */ + issuerStateIncluded?: boolean; + /** + * The flag to include the `urn:ietf:params:oauth:grant-type:pre-authorized_code` object in the + * `grants` object. + */ + preAuthorizedCodeGrantIncluded?: boolean; + /** The subject associated with the credential offer. */ + subject?: string; + /** The duration of the credential offer. */ + duration?: number; + /** The general-purpose arbitrary string. */ + context?: string; + /** Extra properties to associate with the credential offer. */ + properties?: Property[]; + /** Additional claims that are added to the payload part of the JWT access token. */ + jwtAtClaims?: string; + /** + * The time at which the user authentication was performed during the course of issuing the + * credential offer. + */ + authTime?: number; + /** + * The Authentication Context Class Reference of the user authentication performed during the + * course of issuing the credential offer. + */ + acr?: string; + /** The value of the `credential_configuration_ids` array. */ + credentialConfigurationIds?: string[]; + /** The transaction code that should be associated with the credential offer. */ + txCode?: string; + /** The input mode of the transaction code. */ + txCodeInputMode?: string; + /** The description of the transaction code. */ + txCodeDescription?: string; + }; + + export class VciOfferCreateApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciOfferInfoApiRequest = { + /** A service ID. */ + serviceId: string; + /** The identifier of the credential offer. */ + identifier?: string; + }; + + export class VciOfferInfoApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciSingleIssueApiRequest = { + /** A service ID. */ + serviceId: string; + body: VciSingleIssueRequest; + }; + + export class VciSingleIssueApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } + + export type VciSingleParseApiRequest = { + /** A service ID. */ + serviceId: string; + /** The access token that came along with the credential request. */ + accessToken?: string; + /** The message body of the credential request. */ + requestContent?: string; + }; + + export class VciSingleParseApiError extends ApiError { + declare readonly payload: + | Declared<"result", Result> + | Declared<"result2", Result> + | Declared<"result3", Result> + | Declared<"result4", Result> + | Declared<"result5", Result> + | Undeclared; + + static readonly errors: ErrorDecoders = [ + { on: 400, kind: "result", decode: { kind: "json", schema: resultSchema } }, + { on: 401, kind: "result2", decode: { kind: "json", schema: resultSchema } }, + { on: 403, kind: "result3", decode: { kind: "json", schema: resultSchema } }, + { on: 429, kind: "result4", decode: { kind: "json", schema: resultSchema } }, + { on: 500, kind: "result5", decode: { kind: "json", schema: resultSchema } }, + ]; + } +} diff --git a/plugins/authlete/sdk/typescript/src/servers.ts b/plugins/authlete/sdk/typescript/src/servers.ts new file mode 100644 index 00000000..19825578 --- /dev/null +++ b/plugins/authlete/sdk/typescript/src/servers.ts @@ -0,0 +1,70 @@ +import type { ClientOptions } from "./client-options.js"; +import type { ServerBase, UrlTemplate } from "./core/api-request.js"; +import { ConfigurationError } from "./core/errors.js"; +import { resolveBaseUrl } from "./core/url.js"; +import * as s from "./core/validation/index.js"; + +export const ServerEnvironment = { + Production: "production", + Environment2: "environment2", + Environment3: "environment3", + Environment4: "environment4", + Environment5: "environment5", +} as const; +export type ServerEnvironment = (typeof ServerEnvironment)[keyof typeof ServerEnvironment]; + +export type Servers = { + default: (subPath: string) => UrlTemplate; +}; + +const productionSchemas = { + baseUrl: s.of(s.defaulted(s.string(), "https://us.authlete.com")), +}; + +const environment2Schemas = { + baseUrl: s.of(s.defaulted(s.string(), "https://jp.authlete.com")), +}; + +const environment3Schemas = { + baseUrl: s.of(s.defaulted(s.string(), "https://eu.authlete.com")), +}; + +const environment4Schemas = { + baseUrl: s.of(s.defaulted(s.string(), "https://br.authlete.com")), +}; + +const environment5Schemas = { + baseUrl: s.of(s.defaulted(s.string(), "https://login.authlete.com")), +}; + +export function buildServers(options: ClientOptions): Servers { + const base = { + default: resolveBaseUrl(defaultServer(options)), + }; + return { + default: (subPath) => ({ baseUrl: base.default, subPath }), + }; +} + +function defaultServer(options: ClientOptions): ServerBase { + const environment = options.serverEnvironment; + switch (environment) { + case ServerEnvironment.Production: + case undefined: + return { baseUrl: productionSchemas.baseUrl.decode(options.serverOptions?.baseUrl) }; + case ServerEnvironment.Environment2: + return { baseUrl: environment2Schemas.baseUrl.decode(options.serverOptions?.baseUrl) }; + case ServerEnvironment.Environment3: + return { baseUrl: environment3Schemas.baseUrl.decode(options.serverOptions?.baseUrl) }; + case ServerEnvironment.Environment4: + return { baseUrl: environment4Schemas.baseUrl.decode(options.serverOptions?.baseUrl) }; + case ServerEnvironment.Environment5: + return { baseUrl: environment5Schemas.baseUrl.decode(options.serverOptions?.baseUrl) }; + default: + unknownEnvironment(environment); + } +} + +function unknownEnvironment(environment: never): never { + throw new ConfigurationError(`Unknown server environment: ${String(environment)}`); +} diff --git a/plugins/authlete/sdk/typescript/tsconfig.dom.json b/plugins/authlete/sdk/typescript/tsconfig.dom.json new file mode 100644 index 00000000..8e1e3236 --- /dev/null +++ b/plugins/authlete/sdk/typescript/tsconfig.dom.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": [] + }, + "include": ["src"] +} diff --git a/plugins/authlete/sdk/typescript/tsconfig.json b/plugins/authlete/sdk/typescript/tsconfig.json new file mode 100644 index 00000000..53441182 --- /dev/null +++ b/plugins/authlete/sdk/typescript/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ES2022"], + "types": ["node"], + + "strict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + + "isolatedModules": true, + "verbatimModuleSyntax": true, + "forceConsistentCasingInFileNames": true, + + "declaration": true, + "sourceMap": true, + "stripInternal": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src"] +} diff --git a/plugins/authlete/sdk/typescript/tsconfig.tshy.json b/plugins/authlete/sdk/typescript/tsconfig.tshy.json new file mode 100644 index 00000000..bf91aa09 --- /dev/null +++ b/plugins/authlete/sdk/typescript/tsconfig.tshy.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "verbatimModuleSyntax": false, + "noEmit": false + } +} diff --git a/plugins/authlete/skills/typescript/typescript-authentication/SKILL.md b/plugins/authlete/skills/typescript/typescript-authentication/SKILL.md new file mode 100644 index 00000000..c676f732 --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-authentication/SKILL.md @@ -0,0 +1,390 @@ +--- +name: typescript-authentication +description: Load before setting credentials on an APIMatic-generated TypeScript SDK client, or when a call returns 401/403 β€” API keys, bearer and basic auth, the OAuth 2 client-credentials, password and authorization-code grants, token caching and refresh, and supplying your own token source. +--- + +# Authenticating an APIMatic TypeScript SDK client + +How you authenticate depends on the security scheme(s) the API declares. Each scheme is an +**optional credential member on `ClientOptions`**; set the one(s) your API uses, then construct +the client (see `typescript-client-initialization`). There is no separate credentials builder, no +per-request auth override, and no way to change a credential after construction. + +```ts +const client = new {Api}Client({ {scheme}: /* credentials for that scheme */ }); +``` + +> Throughout this skill, `{...}` is a placeholder for a name you take from your SDK (e.g. +> `{Api}Client`, `{package-name}`, `{scheme}`) β€” replace it with the concrete identifier from the +> source. + +To see which schemes a specific SDK accepts, read the **credential members on its `ClientOptions` +type** in `src/client-options.ts` β€” those are the source of truth. `src/core/auth/` ships *every* +scheme factory as shared runtime code regardless of what the API accepts, so rely on the options type +rather than that folder. Where a member's name does not tell you its kind, `src/auth-schemes.ts` shows +which core factory each member is wired to. + +The credential types below live under `src/core/auth/`, are exported from the package root and are +the **same across all APIMatic TypeScript SDKs**; only the **option member names** are per API (hence +the `{scheme}` placeholders). There are exactly **eight** scheme kinds, taking **three** credential +shapes: + +| Scheme kind | `ClientOptions.{scheme}` type | Sent as | +| --- | --- | --- | +| HTTP bearer | `TokenProvider` | `Authorization: Bearer {token}` | +| API key in header | `TokenProvider` | the API's header name | +| API key in query | `TokenProvider` | the API's query parameter | +| API key in cookie | `TokenProvider` | a `Cookie` pair under the API's name | +| HTTP basic | `BasicAuthCredentials` | `Authorization: Basic base64(user:pass)` | +| OAuth 2 client credentials | `OAuth2ClientCredentials` | `Authorization: Bearer {fetched token}` | +| OAuth 2 password | `OAuth2PasswordCredentials` | `Authorization: Bearer {fetched token}` | +| OAuth 2 authorization code | `OAuth2AuthorizationCodeCredentials` | `Authorization: Bearer {fetched token}` | + +## Basic auth + +```ts +const client = new {Api}Client({ + {scheme}: { + username: process.env.API_USERNAME!, + password: process.env.API_PASSWORD!, + }, +}); +``` + +Sends `Authorization: Basic base64(username:password)`. + +A username containing `:` throws `ConfigurationError` **from the constructor** (RFC 7617 Β§2), so a bad value +fails fast rather than on the first call. The password is not validated β€” an empty one is sent as an +empty one. + +## Bearer token + +Set the configured token member to your access-token string: + +```ts +const client = new {Api}Client({ {scheme}: process.env.ACCESS_TOKEN! }); +``` + +Sends `Authorization: Bearer ACCESS_TOKEN`. + +## API key (header, query, or cookie) + +The key is sent as a header, query parameter, or cookie β€” its placement and name are fixed by the +SDK, not by anything you pass. Set the configured key member to your key string: + +```ts +const client = new {Api}Client({ {scheme}: process.env.API_KEY! }); +``` + +All four of these kinds are the **same slot to you**: `TokenProvider`, which is a string, or something +that produces one. Which channel it travels on is the scheme's business. + +A query-placed key is percent-encoded exactly as an endpoint's own query parameter. A cookie-placed key +is **also** percent-encoded, and it is merged into any `Cookie` header already on the request rather +than replacing it β€” only a cookie of the same name is overwritten. + +### A `TokenProvider` may be a function + +```ts +type TokenProvider = string | ((signal: AbortSignal) => string | Promise); +``` + +Pass a **function** when the secret rotates, comes from a vault, or is not known at construction time. +It is called on **every request** that needs the scheme, and it may be async: + +```ts +const client = new {Api}Client({ + {scheme}: async () => await vault.read("api-key"), // re-read per request +}); +``` + +There is no caching around your callback β€” if the lookup is expensive, cache inside the callback. An +empty string counts as **absent** and the scheme sends nothing, so guard against a blank env var. + +> **The next five sections apply only when the SDK declares an OAuth 2 scheme.** The three grants, the +> token lifecycle, and the token-fetch failure mode describe machinery that exists only if +> `src/client-options.ts` carries an `OAuth2…Credentials` member. Where it carries none, skip ahead to +> *Combined / multiple schemes*; nothing below can occur in that SDK. + +## OAuth 2.0 β€” client credentials (machine-to-machine) + +```ts +const client = new {Api}Client({ + {scheme}: { + clientId: process.env.CLIENT_ID!, + clientSecret: process.env.CLIENT_SECRET!, // required for this grant + scope: "read write", // optional β€” omit if the API needs none + }, +}); +``` + +The SDK fetches and caches the token itself, acquiring a fresh one when it expires; on a `401` it +invalidates the cached token and re-acquires on the next call. + +`scope` is a **single space-delimited string**, not a list β€” you join it yourself, per RFC 6749 Β§3.3. +An empty string is treated as absent and no `scope` field is sent. It is typed plain `string` β€” **no +scope enum or literal union** β€” so a scope the provider does not offer type-checks and fails at the +provider. + +## OAuth 2.0 β€” authorization code (3-legged, with PKCE) + +This is the one grant that needs a human in the loop, so its credentials carry a **callback** the SDK +sends you to the authorization URL through. You return the authorization code: + +```ts +import { PkceMethod } from "{package-name}"; + +const client = new {Api}Client({ + {scheme}: { + clientId: process.env.CLIENT_ID!, + clientSecret: process.env.CLIENT_SECRET, // optional β€” see the note below + redirectUri: "https://app.example.com/callback", + scope: "read write", // optional + state: crypto.randomUUID(), // optional CSRF token + pkce: PkceMethod.S256, // default; RFC 7636 + promptForAuthorizationCode: async (authorizationUrl, signal) => { + // Send the user to authorizationUrl, then return the `code` your redirect endpoint received. + return await waitForRedirectCode(authorizationUrl, signal); + }, + }, +}); +``` + +The URL handed to your callback carries `response_type=code`, `client_id`, `redirect_uri`, `scope` +(when set), `state` (when set), and β€” unless PKCE is disabled β€” `code_challenge` plus +`code_challenge_method`. The matching `code_verifier` is sent on the token exchange automatically; you +never see or manage it. + +- **`promptForAuthorizationCode` is called lazily**, on the first request needing the scheme β€” the + same point at which the other grants fetch their token. For a server app, see *Supplying your own + token source* below. +- **PKCE is on by default.** Omitting `pkce` means `PkceMethod.S256`; `pkce: null` disables it. The + verifier and challenge are generated with `crypto.getRandomValues` / `crypto.subtle`, so the runtime + needs Web Crypto (Node 20+ and browsers have it). +- The `signal` handed to your callback is the call's own signal, or one that never aborts when the + call was given none β€” honour it, or a cancelled call leaves you waiting on a redirect forever. +- **Rejecting the callback is how you decline.** A user who closes the window, or a callback that + throws for any other reason, surfaces as `AuthError` with your error on `.cause`, and no token + request is sent. It is deliberately *not* a `ConnectionError`: a connection failure invites a + retry, a declined consent needs the user asked again. Two rejections pass through untouched + instead of being wrapped β€” one of the SDK's own error classes, and, when the call has been + cancelled underneath you, the signal's own `reason`. + +⚠ **No timeout covers the prompt, and it is not retried.** Only the call's `signal` ends it early, +and only if the prompt honours it. + +⚠ **The prompt must not make an SDK call that uses this same scheme.** Concurrent acquisitions share +one in-flight promise, so a call made from inside the prompt waits on the acquisition that is waiting +on the prompt β€” a deadlock that breaks only when a signal aborts. The same applies to a custom token source's +`getToken`. + +⚠ **`state` is passed through verbatim and the SDK does not validate it on the way back.** It is sent +on the authorization request and never generated or checked here β€” the SDK never sees the redirect, so +producing and verifying it are yours alone. Your redirect handler must compare it against what you +generated before returning the code, or the CSRF token is decorative. + +⚠ **`clientSecret` is optional in the type, and leaving it out throws only when PKCE is off.** + +| PKCE | `clientSecret` missing β‡’ | +| --- | --- | +| disabled (`pkce: null`) | `AuthError("A client secret is required when PKCE is disabled…")` β€” **before** the prompt runs | +| enabled | fine β€” this is the public-client case: the code exchange and every refresh send `client_id` as a form field and no `Authorization` header | + +An empty string counts as missing. The placement is not yours to choose: it is the `placement` +argument in `src/auth-schemes.ts` β€” `"header"` sends `Authorization: Basic +base64(clientId:clientSecret)` on the token request, `"body"` sends `client_id` / `client_secret` +as form fields. Without a secret, both placements send `client_id` alone, so a public client works +whichever one your spec chose. + +## OAuth 2.0 β€” resource owner password + +```ts +const client = new {Api}Client({ + {scheme}: { + clientId: process.env.CLIENT_ID!, + clientSecret: process.env.CLIENT_SECRET, // optional (public clients) + username: process.env.API_USERNAME!, + password: process.env.API_PASSWORD!, + scope: "read", // optional + }, +}); +``` + +## Token caching & refresh (all OAuth 2 grants) + +- Tokens are cached **in memory, per client instance**, and reused until **30 s** before expiry, or + for **half their lifetime** when that is longer. +- Acquisition is **lazy** β€” on the first request that requires the scheme, not at construction. A + client built with bad credentials constructs perfectly happily; the failure surfaces at the first + operation. One in-flight acquisition is **shared**: concurrent requests that arrive during a fetch + wait on that fetch rather than each starting their own. +- The fetch is a form-encoded `POST` to the token endpoint, sent with **no auth scheme of its own**, + and it runs on its own `retry.timeout` and the call's abort signal β€” so token-fetch time does not + come out of the operation's attempt. +- **A failure while obtaining the token ends the call.** The token request is itself a `POST` under the + client's retry policy, so with the default `httpMethodsToRetry` even a `503` from the token endpoint + is an immediate `AuthError`; name `POST` in the gate and the token request retries a retryable status + first. +- **The cache lives in the client object.** Build the client once (see + `typescript-client-initialization`) or every call re-runs the grant. +- **Only the authorization-code grant refreshes.** Client-credentials and resource-owner-password + re-run the whole grant when the token expires, and a `refresh_token` in a password-grant response is + **discarded** β€” do not infer refresh behaviour from what the provider returns. A refresh response + that omits `refresh_token` keeps the previous one, and a failed refresh does not throw: it falls back + to a full re-grant. +- On `401`, the cached token is invalidated and re-acquired on the **next** call β€” `401` is not in the + default `statusCodesToRetry`, so the failing request is **not** re-sent; handle it as an ordinary + `ApiError` (see `typescript-error-handling`). Add `401` to `statusCodesToRetry` to have the failing + call re-sent with a fresh token β€” every attempt resolves its credential again. A static key is + re-sent unchanged, so that only helps a scheme that fetches its credential. +- **Invalidation is a hint, not a barrier.** It clears the cache without cancelling a token fetch + already in flight, so that fetch can complete and re-populate it; "invalidate, then read" does not + guarantee a fresh token. + +⚠ **A `401` on the authorization-code grant is an interactive re-authorization, not a silent +refresh.** Invalidation clears the whole cached token, refresh token included, so "re-acquired" means +the *full* grant runs again and `promptForAuthorizationCode` fires. Expiry takes the refresh path; a +`401` does not. Plan for that in a non-interactive host. + +**A short-lived token is renewed halfway through its lifetime.** The 30-second buffer applies to a +token issued for a minute or more; below that, the token is reused for half its `expires_in` β€” a 20 s +token for 10 s. So no token is stale the moment it is cached, and a sub-minute token costs one token +round-trip per half-lifetime rather than one per call. + +⚠ **A token response without a positive `expires_in` never expires in the cache** β€” the first token +is kept for the life of the client and only a `401` replaces it, so a token revoked server-side is +sent until a request fails with it. For proactive rotation, supply your own token strategy (below). + +### Supplying your own token source + +The token strategy is a public extension point: alongside each OAuth 2 credentials member the options +type exposes a **`{scheme}Strategy`** member, typed `OAuth2TokenStrategy<{CredentialsType}>` (or +`OAuth2RefreshableTokenStrategy<…>` for the authorization-code grant), and the generated client falls +back to the built-in strategy only when you leave it unset. + +```ts +const client = new {Api}Client({ + {scheme}: { /* the same credentials object */ }, + {scheme}Strategy: { + async getToken(credentials, signal) { + const stored = await tokenStore.load(); + if (stored) return stored; // { accessToken, tokenType, expiresIn?, … } + const fresh = await mintTokenSomehow(credentials, signal); + await tokenStore.save(fresh); + return fresh; + }, + // Authorization-code grant only β€” return null to fall back to a full re-grant: + async tryRefreshToken(credentials, refreshToken, signal) { + const refreshed = await refreshSomehow(credentials, refreshToken, signal); + if (refreshed) await tokenStore.save(refreshed); + return refreshed ?? null; + }, + }, +}); +``` + +Reach for it when the token must come from somewhere other than the SDK's own call to the token +endpoint β€” a cache shared across processes, a secrets broker, a sidecar that already holds a valid +token, or a test double. The per-client in-memory cache above still wraps whatever you return. + +The token shape is `OAuthToken` (`accessToken`, `tokenType`, `expiresIn?`, `scope?`) plus +`refreshToken?` on `OAuthTokenRefreshable`. Note the field names are **camelCase** β€” the SDK maps them +to the wire's `access_token` / `expires_in` / `refresh_token` for you, so a strategy of your own +returns camelCase and a token you persisted as raw JSON needs converting on the way back in. Read the +`{scheme}Strategy` member's declared type in `src/client-options.ts` rather than inferring which of +the two interfaces it takes. + +⚠ **Never re-enter the interactive prompt from a request handler.** Acquire the authorization-code +token out of band, store it, and let `getToken` read the store β€” otherwise a cache miss under load +turns into a redirect attempt inside a request. + +## The failure mode that surprises everyone + +A token endpoint that answers `400 invalid_client` rejects the *operation* call with `AuthError` β€” its +`method`/`uri` name the operation, the token endpoint's `ApiError` is on `.cause`, nothing was sent, +and `.asApiResult()` does not convert it. Catch it around every authenticated call; the ladder is in +**typescript-error-handling**. + +## Combined / multiple schemes + +An operation can require more than one scheme, or any one of a set. The composition is applied for you +by `allAuth(...)` / `anyAuth(...)`; you configure it by setting the relevant credential members. + +- **AND (`allAuth`)** β€” every scheme resolves and their headers, query params and cookies are all + sent. If one has no credential it contributes nothing, and the request goes out **partially + authenticated** rather than failing. +- **OR (`anyAuth`)** β€” the **first** scheme in declaration order that has credentials is used and the + rest are ignored. So which credential is sent depends on declaration order in `src/auth-schemes.ts`, + not on which looks more specific. A `TokenProvider` **function** always counts as "has credentials", + even if it returns an empty string β€” so a function provider always wins the OR. + +Because composition is **per operation**, one client may authenticate different operations with +different schemes β€” so set every member the operations your integration actually calls need, not just +the ones the API's front page advertises. The per-operation requirement is the **Auth** bullet on the +operation's block in `map/operations/*.md` (and in `api-reference.md`), which spells the composition +out: `` `{scheme}` ``, `all of …`, or `any of … β€” the first one configured is sent`. + +## No auth + +An operation whose **Auth** bullet reads `none β€” public; no credential is sent` sends **no credential +even when every option member is filled**, and nothing you pass at construction adds one. + +## ⚠ Missing credentials must stop the app from starting + +Every section above covers supplying a credential. This one covers its **absence** β€” the case a +configuration-driven app hits in the real world, and the one most integrations get wrong. + +**Every credential member is optional and an unset one does not throw.** The operation that wanted it +simply sends no credential, and you get whatever the API answers β€” typically a `401`, surfacing as an +`ApiError`, not as an auth failure. The SDK will never tell you that you forgot to supply a +credential; only the provider will, one round-trip later and one layer away from the cause. A +"mysterious 401" is very often an unfilled option member β€” check that before suspecting the +credentials themselves. + +An API that serves anonymous traffic at all β€” public endpoints, or a reduced rate limit for keyless +callers β€” makes this worse by answering `200` and hiding the omission entirely. So verify the member +is actually set rather than waiting for a `401` to tell you. + +**A required credential that is not configured is a deployment fault, not a request fault.** There is +no `.env` support and no `fromEnvironment()` helper in the SDK, so **validate every required +credential in your startup path and refuse to boot** β€” not lazily on first use, which is a request. +The factory in **typescript-client-initialization** shows the shape. + +Three rules for the message it fails with: + +- **Name the missing config key**, so the operator knows what to set. `"API_KEY is not configured"` β€” + not `"authentication failed"`. +- **Never echo the value**, present or absent β€” no length, no prefix, no masked form. A + `"configured: sk_live_1234…"` line is a secret in a log. +- **Do not fall back to a default, a placeholder, or an unauthenticated client.** Booting degraded + hides the fault and pushes it to the first caller. + +Check every credential the scheme requires. Basic auth needs both halves β€” a username with an empty +password is misconfigured, not partially configured. An empty-string `TokenProvider` is treated as +absent, so a blank env var is the same fault as an unset one. + +## Notes + +- **Credentials are fixed at construction.** `ClientOptions` is `readonly` and the auth schemes are + built in the constructor, so to rotate a credential you use a `TokenProvider` **function**, replace + the strategy, or build a new client. There is no mutable accessor. +- **Rotating a long-lived client:** the token cache is per client instance, so swap atomically β€” build + the new client, then replace the shared reference. In-flight calls finish on the old one and there + is no window where calls have no credential. Do not mutate the old client. +- **Auth wins every header, query and cookie collision** β€” a scheme's `Authorization` header + overwrites anything else under that name, and only a `fetch` wrapper can strip or replace it (see + `typescript-configuration-resilience`). +- **Nothing hides secrets in a log you write.** Credentials are plain objects with no redacting + `toString`, so `console.log(options)` or a serialized `ClientOptions` prints them in full. The SDK's + own logging masks a credential in a header, the query or an OAuth form body, but not in a JSON request + body (**typescript-configuration-resilience**). +- On the OAuth 2 token request, header placement with a secret sends + `base64(clientId:clientSecret)` **without percent-encoding either half** first. A client id or + secret containing a character RFC 6749 Β§2.3.1 would have you encode is sent raw, so a provider that + follows the RFC strictly may reject it β€” use body placement or a custom strategy if your secret is + not in the unreserved set. +- `AuthError` (a credential could not be *obtained*) versus a 401/403 `ApiError` (refused by the API): + **typescript-error-handling**. +- A token endpoint may live on its own server group, so redirecting the operations' group at a mock + does not move it (**typescript-configuration-resilience**). +- Keep secrets out of source β€” read `process.env` or your secret manager at the call site. diff --git a/plugins/authlete/skills/typescript/typescript-calling-endpoints/SKILL.md b/plugins/authlete/skills/typescript/typescript-calling-endpoints/SKILL.md new file mode 100644 index 00000000..f85335ec --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-calling-endpoints/SKILL.md @@ -0,0 +1,342 @@ +--- +name: typescript-calling-endpoints +description: Load before writing the first call to an APIMatic-generated TypeScript SDK operation, or when an operation's request shape or return type is unclear β€” the one flat request object, required, optional, defaulted and SDK-fixed fields, request bodies and file uploads, binary responses, reading an `ApiPromise` or `.asApiResult()`, and per-call retry and cancellation. +--- + +# Calling endpoints on an APIMatic TypeScript SDK + +Operations are **methods returning `ApiPromise`** on the client. Most are **grouped under a resource +getter** and called `client.{resource}.{operation}(...)`; an operation with no resource group sits +**directly on the client**, called `client.{operation}(...)`, and an SDK may have no resource level at +all. Take the getter, the exact operation name and its signature from the SDK map, never from memory β€” +operation names follow no fixed verb/resource pattern. + +> Throughout this skill, `{...}` is a placeholder for a name you take from your SDK (e.g. `{Api}Client`, +> `{package-name}`, `{resource}`, `{Resource}`, `{Operation}`, `{Enum}`) β€” replace it with the concrete +> identifier from the source. + +## Where to read the operation's shape + +1. **`map/operations/{resource}.md`** (indexed from `sdk-map.md` at the package root) β€” the generated + contract sheet. Per operation: the exact **Signature**, the **Wire** verb and route, **Auth**, + **Request body**, **SDK-sent** fixed values, **Returns**, **Error** and its arms, then a **Fields** + table (`Field` Β· `Channel` Β· `Wire` Β· `Type` Β· `Req` Β· `Default`) and a **Type sources** table naming + the file under `src/models/` that declares each type. Start here. Operations that sit directly on + the client get their own page there too. +2. **`api-reference.md`** β€” per-operation prose: a usage snippet, per-parameter descriptions, and the + semantics that decide what you must pass (field interactions, ordering rules). +3. `src/client.ts` and `src/resources/{resource}.ts` β€” the source, when you need to confirm something + the docs do not carry. + +Both the docs and `src/` ship inside `node_modules/{package-name}/`, so read them there rather than the +compiled `dist/`. + +Resource classes are exported from the package root, but only for their merged namespaces and for +`instanceof`. Never construct one: its constructor takes engine internals that are not exported. + +## Method signature convention + +Every operation has the same shape, with no positional overload and no sync variant: + +```ts +{operation}(request: {Resource}.{Operation}Request, options?: RequestOptions): ApiPromise +``` + +- **There is exactly one request parameter**, and it is **not optional** β€” even when every field on it + is. An all-optional request still needs `{}` passed explicitly. +- **`options?: RequestOptions` is on every operation**, always last β€” see + [Cancellation and per-call retry](#cancellation-and-per-call-retry). +- **An operation whose every value is fixed takes no request parameter** and has no request type; + `options` is then its only parameter. Check the **Signature** bullet before passing `{}` β€” it will + not type-check against such an operation. + + ```ts + await client.{resource}.{operation}(); // no request + await client.{resource}.{operation}({ signal }); // options only + ``` +- Requiredness, defaults, whether there is a body, and the return type all vary per operation β€” read + each from its map entry. + +## The request object is flat and channel-blind + +There is **one** request parameter and it is **not nested by channel**. A field named `body` *is* the +whole request body; **every other field** is fanned out to the path, query string, headers or form body +by the SDK. + +```ts +// Correct β€” flat, whatever channel each field travels on: +await client.{resource}.{operation}({ + collectionId: "abc123", // travels in the path + page: 2, // travels in the query string + userAgent: "my-app/1.0", // travels as a header + body: { name: "My thing" }, // IS the request body +}); + +// Wrong β€” there is no channel nesting: +// await client.{resource}.{operation}({ path: { ... }, query: { ... } }); +``` + +Which channel a field travels on is the `Channel` column of the operation's **Fields** table. Because +the request is a named object, getting the channel wrong is impossible β€” but you cannot *read* it off +the field list either, so the table is the only place to learn what went on the wire. + +### Where a field lands β€” the rule + +| In the map | What you get | +|---|---| +| `Req` yes | `f: T` β€” a required member of the request object | +| `Req` no, no `Default` | `f?: T` β€” omit it and nothing is sent for it | +| a `Default` | `f?: T` β€” omit it and the SDK **still sends the default** | +| listed only under **SDK-sent** | **no field at all** β€” a fixed value (a `Content-Type`, an API version, a `grant_type`) spelled as it goes on the wire; no override short of wrapping `fetch` | + +- **Requiredness and channel are independent.** A required field may travel in the query, a header or + the form body just as easily as the path; never infer one from the route. +- **Field names are `camelCase` identifiers, not the wire names.** A header field `traceId` is + transmitted as `X-Trace-Id`; the `Wire` column gives the wire name where the two differ, and `β€”` + means they match. + +The request type is declared in the resource's merged namespace, so it is +`{Resource}.{Operation}Request` β€” importable from the package root if you want to name it: + +```ts +import { {Resource} } from "{package-name}"; + +const request: {Resource}.{Operation}Request = { /* ... */ }; +``` + +## Building request models + +**A JSON, text or binary operation has a `body` field; a form-urlencoded or multipart operation has +none** β€” its parts are **individual flat request fields** beside the path and query ones, and the SDK +assembles the form. The operation's **Request body** bullet says which. An **XML**-body operation has +no body field and sends no body (its **Request body** reads `none`); call that endpoint with `fetch` +directly. + +**A file field takes bytes, not a path.** Every binary `body` field and every multipart file field is +typed `FileInput`, which is a `Blob`, a `Uint8Array`, an `ArrayBuffer`, a `ReadableStream` of bytes, +any async iterable of them β€” which is what a Node `Readable` is β€” or a `{ data, fileName?, +contentType? }` wrapper around one. A filesystem path is **not** a file body: open it with +`createReadStream(path)` to stream it, or `await openAsBlob(path)` to send it with a +`Content-Length`. A field declared as an array of files takes `FileInput[]`, and the SDK sends one +part per element under the shared field name; an empty array sends no part at all. + +Nothing about a file is schema-checked, so a wrong value throws at the call rather than rejecting on +the wire. The media type the SDK declares is a fallback: a `contentType` on the wrapper, or a +non-empty `Blob.type`, overrides it. A file name is yours or absent β€” the SDK never invents one, and a +part with no file name is read as an ordinary text field by many servers, so name a file you upload. + +**A binary response is a handle, not a value.** Such an operation resolves to a `BinaryContent`: +`stream`, `contentType`, and a `fileName` parsed from `Content-Disposition` where the server sent one. +Read the stream exactly once β€” `new Response(file.stream)` to buffer it, a reader or `for await` to +stream it β€” or release it with `await file.stream.cancel()` if you decide not to. No timeout covers +the read, so an unread stream holds the connection until you read it, cancel it or abort the call's +`signal`. A server-chosen file +name is untrusted input: sanitise it before writing to disk. + +For the JSON case, the `body` field's type is a generated model written as a plain object literal +(**typescript-models**); take its name and the file that declares it from the **Type sources** table. + +**An optional-looking body is not permission to omit it.** A `body?: T` is optional only in the type: +a create passed `{}` type-checks and then fails at the provider. Where a call obviously needs a +payload, pass one. + +### Defaults are sent, not omitted + +An omitted field with a `Default` is filled **by the SDK** and sent, so the server sees the default, +and the response is the one that default asked for β€” a verbosity switch defaulting to the minimal form +(a create resolving to little more than an id and a status), or a field selector defaulting to one +section. Read the `Default` column **before** concluding the API dropped data β€” it was never +requested. + +### Every value is validated before anything is sent + +Each field is schema-encoded on the way out: a wrong type or a malformed format (a bad date string, +say) rejects with an `EncodeError` β€” its `cause` the `SchemaError` naming the field β€” and **nothing is +sent** (**typescript-error-handling**). + +## Enums + +Enum fields take a member of the generated `const` object β€” `{ status: {Enum}.Active }`. A raw string +also compiles (the type is open), but only the member catches a rename. See **typescript-models**. + +## Union types, collections, and dates + +Some fields are not plain scalars: `oneOf`/`anyOf`/polymorphic unions declared under +`src/models/unions/`, array and record collections, and `Date` fields whose wire format the schema pins +(ISO 8601, RFC 1123 or Unix seconds). If a request field or response member is one of these, see +**typescript-models** for how to build and read it. + +## Making the call and reading the response + +```ts +const value = await client.{resource}.{operation}({ /* ... */ }); +``` + +> **A non-2xx response *rejects*.** Before writing a real call, **load `typescript-error-handling`** for +> the `try`/`catch` shape and which error type to narrow on per operation β€” or use the non-throwing +> `.asApiResult()` form below. + +Operations return `ApiPromise`, a `Promise` subclass. Two ways to consume it: + +**Await it** β€” you get `T`, the decoded success body, and a non-2xx **rejects**. + +**Call `.asApiResult()`** β€” you get a discriminated result, and an HTTP error status does **not** reject: + +```ts +const outcome = await client.{resource}.{operation}({ /* ... */ }).asApiResult(); + +if (outcome.ok) { + console.log(outcome.status, outcome.headers.get("x-request-id"), outcome.value); +} else { + console.log(outcome.status, outcome.message, outcome.payload); // the error's members, not the error +} +``` + +```ts +type ApiResult = + | { ok: true; status: number; headers: Headers; value: T } + | { ok: false; status: number; headers: Headers; message: string; method: HttpMethod; uri: string; + payload: PayloadOf }; +``` + +- **Call `.asApiResult()` on the value the operation returned.** `ApiPromise` overrides + `Symbol.species`, so `.then()`, `.catch()` and `.finally()` hand back a plain `Promise` and the + method is gone β€” `op(...).then(x => x).asApiResult()` does not compile. +- **It converts only the HTTP error status.** Every other failure still rejects, so keep the + `try`/`catch`; on failure `outcome` carries the error's members, not the error object + (**typescript-error-handling**). +- **The request goes out when the operation is called**, not when you await it; awaiting and + `.asApiResult()` read the same outcome and never send twice, so a call you build and hold has already + been sent. +- **Nothing suppresses an unhandled rejection.** A call you never consume that fails is an + `unhandledRejection` β€” on Node, by default, the process ends. `.asApiResult()` marks the promise + handled as its first act, so call it on the returned value in the same turn. + +### Status and headers + +An awaited value gives you **only** the decoded body. `status` and `headers` are reachable in exactly +two places: on `ApiResult` above, and on a rejected `ResponseError`. The raw `fetch` `Response` is +deliberately unreachable. + +### The success type + +The operation's own **Returns** bullet names `T`, which can differ between sibling operations: + +- **A model type** β€” a JSON body decoded through its schema. The **Type sources** table names the file + under `src/models/` where you read its shape. +- **A primitive** β€” a `text/plain` body. +- **`undefined`** β€” no body; a non-empty one rejects with `DecodeError`. Resolving *is* the success + signal: do not test the value, re-read the resulting state with a separate call if you need it, and + use `.asApiResult()` to tell a `200` from a `204`. +- **`EventStream`** β€” a `text/event-stream` body. The call resolves at the response headers with + nothing read, and `for await` over the stream yields each event's decoded `data` as a `T`; the + stream holds its connection until the loop ends or you `close()` it. Load + **typescript-event-streams** before you write the loop. + +## Cancellation and per-call retry + +`RequestOptions` = `{ signal?: AbortSignal; retry?: RequestRetryOptions; logging?: RequestLoggingOptions }` +is the entire per-call surface. `retry` overrides `maxRetries`, `timeout` and `statusCodesToRetry` for +this call, field by field β€” a field you leave out keeps the client's value β€” and `logging` takes only a +`level`. Headers, base URL, auth and the other retry and logging fields are client-level only +(**typescript-client-initialization**, **typescript-configuration-resilience**). + +```ts +await client.{resource}.{operation}({ /* ... */ }, { retry: { maxRetries: 0 } }); // one attempt +await client.{resource}.{operation}({ /* ... */ }, { retry: { timeout: 5_000 } }); // 5 s per attempt + +const controller = new AbortController(); +setTimeout(() => controller.abort(new Error("too slow")), 5_000); +await client.{resource}.{operation}({ /* ... */ }, { signal: controller.signal }); +``` + +- `retry.timeout` bounds **one attempt**, in milliseconds, even when set per call β€” an idempotent call + gets up to four attempts plus backoff β€” so only your `signal` bounds the whole call + (**typescript-configuration-resilience** for deadlines and combining signals). An abort β€” an + already-aborted signal included β€” ends the call at once, cuts a backoff wait short and is never + retried. +- An abort rejects with the signal's own `reason`, unwrapped, and an exhausted timeout with + `TimeoutError`. Neither is an `ApiError`, and the abort is not an `{Api}Error` at all β€” see + **typescript-error-handling** for a ladder over the whole family. + +## Concurrency + +There is nothing to serialize β€” the client is stateless apart from its token cache, and that cache is +concurrency-safe (one in-flight token acquisition is shared). A handful of independent calls can go out +together: + +```ts +const [a, b] = await Promise.all([ + client.{resource}.{operationA}({ /* ... */ }), + client.{resource}.{operationB}({ /* ... */ }), +]); +``` + +**Bound it once the list is data rather than two literals.** `Promise.all` over an array opens as many +simultaneous requests as the array is long, against a provider that rate-limits. The SDK retries a +`429` on an idempotent call, honouring `Retry-After`, but every one of those calls then retries after +a similar wait β€” jitter spreads them by a quarter at most β€” keeping the pressure on, and a write +outside the retry gate simply fails. Cap the width: + +```ts +async function mapLimit(items: T[], limit: number, fn: (item: T) => Promise): Promise { + const results: R[] = []; + for (let i = 0; i < items.length; i += limit) { + results.push(...(await Promise.all(items.slice(i, i + limit).map(fn)))); + } + return results; +} + +const all = await mapLimit(ids, 10, (id) => client.{resource}.{operation}({ id })); +``` + +Note that `Promise.all` rejects on the first failure and leaves the rest of the batch running +unobserved; `Promise.allSettled` is usually what you want when partial success is acceptable. + +Nothing is paginated. The one `for await` form is over an `EventStream`, which only an operation +whose **Returns** bullet reads `EventStream` hands back (**typescript-event-streams**). If an +operation returns a page, advance its page or cursor field yourself in a loop β€” see +**typescript-configuration-resilience**. + +## Worked example β€” a list/GET call + +```ts +// Signature (illustrative β€” take the real one from map/operations/{resource}.md): +// {operation}(request: {Resource}.{Operation}Request, options?: RequestOptions): +// ApiPromise<{Model}, ApiError> +// +// Fields: collectionId path string yes +// status query {Enum} no +// page query number no default 1 +// perPage query number no default 20 + +import { {Enum} } from "{package-name}"; + +const outcome = await client.{resource}.{operation}( + { + collectionId: "abc123", + status: {Enum}.Active, + perPage: 100, + // page omitted β€” the SDK still fills and sends its default of 1 + }, + { signal: controller.signal }, +).asApiResult(); + +if (!outcome.ok) { + console.error(outcome.status, outcome.message, outcome.payload); +} else { + for (const item of outcome.value.{items}) { + console.log(item.id); + } +} +``` + +> This operation's success body is a model that nests its list under a member, so you read that member +> and then iterate. Another operation may return the array directly, or a primitive, or `undefined`. +> Check its **Returns** bullet. + +## Next + +- Build bodies, unions, collections, dates, enums β†’ **typescript-models** +- Errors and status codes β†’ **typescript-error-handling** +- Timeouts, the retry policy, logging, base URL, paging β†’ **typescript-configuration-resilience** diff --git a/plugins/authlete/skills/typescript/typescript-client-initialization/SKILL.md b/plugins/authlete/skills/typescript/typescript-client-initialization/SKILL.md new file mode 100644 index 00000000..9df28fbe --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-client-initialization/SKILL.md @@ -0,0 +1,298 @@ +--- +name: typescript-client-initialization +description: Load before writing the code that builds an APIMatic-generated TypeScript SDK client β€” `ClientOptions`, environments and base URLs, client lifetime and the token cache, wiring the client into app startup or dependency injection, ESM and CommonJS, and inbound webhooks. +--- + +# Initializing an APIMatic TypeScript SDK client + +This applies to **any** APIMatic-generated TypeScript SDK. Replace placeholders with the real names +from the SDK you are using: + +- `{Api}Client` β€” the single public client class (e.g. `FooClient`), declared in `src/client.ts`. +- `{package-name}` β€” the npm package name from `package.json`. This can differ from the client class + name, and it is the **only** import specifier: deep imports (`{package-name}/models/…`) do not + resolve, because the `exports` map exposes `.` and `./package.json` and nothing else. +- `{Environment}` β€” a member of the generated `ServerEnvironment` object (e.g. `Production`); + `{environment}` is that same environment's lower-camel key inside `serverOptions`. +- `{group}` β€” a named server group key inside `serverOptions` (`default` is the usual one). +- `{resource}` / `{operation}` β€” a resource getter on the client and an operation on it. +- `{scheme}` β€” a credential member on the options object, one per auth scheme the API declares. + +Everything under `src/core/` is **the same in every APIMatic TypeScript SDK**, so the engine behaviours +described in these skills hold without checking yours. What varies per SDK is +`src/client.ts`, `src/client-options.ts`, `src/servers.ts`, `src/auth-schemes.ts`, `src/resources/` +and `src/models/`. + +## Where to read the client's shape + +1. **`sdk-map.md` Β§ Getting a client** (at the package root) β€” the generated contract sheet. It prints + the construction sample, the complete `ClientOptions` table (`Field` Β· `Type` Β· `Default`, plus the + source file each field comes from), the per-request surface, and a **Not on this SDK** table naming + what is absent by design. Β§ **Servers & auth** carries the environments, the server groups and + which operations require which scheme; Β§ **Runtime & packaging** carries the dialects, the required + globals and the browser floors. Start here. +2. **`README.md`** β€” Quick Start prints the same construction sample, in both the ESM and the + CommonJS spelling. +3. `src/client.ts`, `src/client-options.ts` and `src/servers.ts` β€” the source, when you need to + confirm something the docs do not carry. + +Two facts about where those files live: + +- **The package ships its own `src/`, plus the generated map.** `package.json` `files` includes + `dist`, `src`, `README.md`, `api-reference.md`, `sdk-map.md` and `map`, so the readable TypeScript + source and the lookup index are both inside `node_modules/{package-name}/`. Read them there rather + than the compiled `dist/`. +- **`dist/` must exist before the package can be referenced.** The SDK builds with `tshy`; an SDK + consumed by path needs its `build` script run once. A `Cannot find module` on a + path-installed SDK is usually this, not a bad import. + +## Shape of the client + +APIMatic TypeScript SDKs expose **one public client class** with **one constructor**, taking a single +options object: + +```ts +constructor(options: ClientOptions = {}) +``` + +Nothing on it is required and neither is the argument, so `new {Api}Client()` compiles. The options +object is the whole configuration surface, and reading the environment into it is yours to do at the +call site. + +Operations hang off resource getters, `client.{resource}.{operation}(...)`, or sit directly on the +client when the SDK has no resource level β€” take them from the contract sheet, not the API's own +documentation (**typescript-calling-endpoints**). + +The options type always carries these six knobs (credential members vary per API β€” see +**typescript-authentication**): + +```ts +export type ClientOptions = { + readonly serverEnvironment?: ServerEnvironment; // selects the base URL + readonly serverOptions?: ServerOptions; // per-server, per-environment baseUrl override + readonly retry?: RetryOptions | undefined; // retry policy + per-attempt timeout (MILLISECONDS). + // Retries ON by default + readonly logging?: LoggingOptions | undefined; // request/response logging β€” OFF by default + readonly eventStream?: EventStreamOptions | undefined; // event-stream idle timeout (MILLISECONDS) β€” + // read only by an EventStream operation + readonly fetch?: FetchLike | undefined; // the ONE transport extension point β€” + // see typescript-configuration-resilience + // + one OPTIONAL credential member per auth scheme the API declares, and β€” for an OAuth2 scheme β€” + // a {scheme}Strategy member alongside it (see typescript-authentication) +}; +``` + +Those six names are fixed in every SDK; a credential named like one of them carries a suffix +instead. **Every member is `readonly`**, and the client captures your options once, at construction β€” +a different configuration means a new client. Each default is applied where the value is read: a +server variable takes the `@default` on its member in `src/client-options.ts`, `fetch` takes its in +`src/core/client-options.ts`, every `retry` field takes its in `src/core/retry.ts`, every +`logging` field its in `src/core/logging.ts`, and `eventStream.idleTimeout` its in +`src/core/event-stream.ts`. Read them there rather than trusting the numbers on this +page. The default `serverEnvironment` is the **first** `ServerEnvironment` member, which is not +necessarily the production one. + +**Retries are on by default**: an idempotent call (`GET`, `HEAD`, `PUT`, `OPTIONS`) that fails +retryably is re-sent up to 3 more times with backoff, so its failure surfaces seconds late, and +`retry.timeout` (milliseconds) bounds each attempt, not the call; `retry: { maxRetries: 0 }` turns +retrying off. **typescript-configuration-resilience** owns the policy. + +**`fetch` is the only transport extension point.** The SDK has no hooks, middleware or pagination, +and streams only a `text/event-stream` response, through an operation that resolves to an +`EventStream` (**typescript-event-streams**) β€” build anything else by wrapping `fetch` +(**typescript-configuration-resilience**). A replacement must +forward the `Request` it is handed, or its `signal`, or cancellation and timeouts go inert. **Logging is +built in and off by default**, and the `{APICLIENTTYPENAME}_LOG` environment variable (the client class +name in upper snake case, plus `_LOG`) can turn it on with no code change +(**typescript-configuration-resilience**). + +## Direct instantiation + +```ts +import { {Api}Client, ServerEnvironment } from "{package-name}"; + +const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, // pick the environment your API exposes + retry: { timeout: 10_000 }, // per attempt; the retry defaults stand + // ...set the credential member your API uses (see typescript-authentication) +}); +``` + +Spell `serverEnvironment` out even when it is the default, so the host a call reaches is visible +where the client is built rather than inherited silently. + +### What construction checks, and what it does not + +A clean construction does not mean a working client. **What throws from the constructor** is +`ConfigurationError` β€” no reachable `fetch` implementation, a basic-auth username containing `:`, or a +`serverEnvironment` the SDK does not know β€” and `SchemaError`, for a `serverOptions` override whose +value its schema refuses: every server group is resolved as the client is built. Everything else is +accepted and fails later, one layer from its cause: + +| Passed at construction | When it actually fails | +| --- | --- | +| a `retry` field out of range β€” a negative or fractional `timeout`, a `maxJitter` above `1`, a lower-cased `"get"` in `httpMethodsToRetry` | never β€” that field silently falls back to its default. `timeout: 0` is the exception: it is kept, and aborts every attempt | +| a `baseUrl` override | first call β€” as a connection error, or as a `401` against the wrong host | +| wrong credentials | first call β€” under OAuth2 as an `AuthError` from that operation, with the token endpoint's `ApiError` on `.cause` (**typescript-error-handling**) | +| a misspelled or extra credential field | never at run time. Credentials are plain objects checked only by TypeScript; nothing re-validates them at construction | +| **an omitted** credential | first call, as a `401`. An unset credential configures the client for **no auth** β€” the operation sends no credential rather than erroring. See **typescript-authentication** | + +That last row is the one integrations lose time to, which is why the factory below checks credentials +before constructing. + +### Client and fetch lifetime + +**Keep the SDK client long-lived.** `fetch` owns the connection pool, but the constructor resolves and +then owns the rest: the `fetch` implementation, every server group (mutating the options object +afterwards does nothing; construct a new client to change environment β€” +**typescript-configuration-resilience**, *What is captured when*), the memoized resource getters, and +the auth schemes β€” for an OAuth2 SDK the scheme *is* the access-token cache. A fresh client starts with +an empty token cache, so **every construction pays a token request on its first call**, and +throwaway clients cannot share one in-flight token acquisition the way a shared client does. + +Construct per request only where you genuinely need different credentials per request β€” and then +expect the token fetch. For rotating a secret without rebuilding, a credential member accepts a +**function** instead of a string; see **typescript-authentication**. + +**There is nothing to close** β€” no `close()`, `dispose()` or `Symbol.dispose`, and no shutdown hook to +register. There is no drain either: to stop in-flight requests at shutdown, abort each call's +`signal`. + +**The `fetch` implementation is resolved once, in the constructor** β€” yours, else `globalThis.fetch`; +with neither, the constructor throws `ConfigurationError`. Patching `globalThis.fetch` after a client +exists does not reach it, so pass a fake through `ClientOptions.fetch` and build the client after it +(**typescript-testing**). A replacement is called as `fetch(request)` β€” one `Request`, no `init` β€” with +`this` set to `undefined`, so bind a method before passing it; **typescript-configuration-resilience** +owns the rest of its contract. + +## Choosing the server / base URL + +Select an environment with `serverEnvironment: ServerEnvironment.{Environment}` β€” a **closed** const +object (no `| (string & {})` tail), one member per environment the API defines, even when there is only +one. There is no bare `baseUrl` option: an override is nested **per server group AND per environment** +β€” `serverOptions.{group}.{environment}.baseUrl` β€” with any template variables beside `baseUrl`, and it +merges key-wise over that environment's defaults: + +```ts +const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, + serverOptions: { + {group}: { {environment}: { baseUrl: "http://localhost:3000" } }, + }, +}); +``` + +The group keys, the environment members, and the URL each resolves to are in `DEFAULT_SERVER_OPTIONS` +in `src/servers.ts` and under **Servers & auth** in the contract sheet β€” **read them there**, because a +member's name does not reliably tell you its host. **typescript-configuration-resilience** owns server +and base-URL configuration, including an OAuth token endpoint on its own server group. + +## Wiring into an application + +The client is a plain object you construct and share, so the wiring is whatever your framework +already uses for singletons. + +The simplest form, and the right one for scripts and small services: + +```ts +// api-client.ts β€” one module-level instance, imported wherever it is needed +import { {Api}Client, ServerEnvironment } from "{package-name}"; + +export const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, + {scheme}: process.env.API_TOKEN!, +}); +``` + +⚠ **A module-level client reads `process.env` at import time**, which under ESM hoisting can precede +your config loader, `dotenv` or secret fetch. The credential is then `undefined`, which the SDK treats +as no auth: no startup error, just a `401` on the first call, reading like an expired token. Load +config before anything imports this module, or build the client in a factory called from your startup +path (below). + +### Building it in a factory + +The full shape. **The credential check is not optional to think about** β€” without it, a missing +environment variable is invisible until an unrelated `401`: + +```ts +import { {Api}Client, ServerEnvironment } from "{package-name}"; + +export function build{Api}Client(): {Api}Client { + const token = process.env.API_TOKEN; + if (!token) throw new Error("API_TOKEN is not set"); // fail at startup, not as a 401 later + + return new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, + retry: { timeout: 10_000 }, // per attempt; default 60_000 ms + {scheme}: token, + }); +} +``` + +Then register the result once, however your framework shares singletons, and inject it: + +```ts +// The registration call differs per framework; the rule β€” build once, share β€” does not. +const client = build{Api}Client(); + +export class MyService { + constructor(private readonly client: {Api}Client) {} + + doWork() { + return this.client.{resource}.{operation}({ /* ... */ }); + } +} +``` + +Taking the client as a constructor parameter rather than importing the module singleton directly is +what makes the service testable β€” a test passes a client built over a fake `fetch`. See +**typescript-testing**. + +### Where the client lives + +| Host | Where to build it | Why | +| --- | --- | --- | +| Long-running server (Express, Nest, Fastify, …) | once at startup, shared for the process lifetime | one token cache, one set of memoized resource getters | +| Serverless function | **module scope**, not inside the handler | module scope survives warm invocations, so the token cache does too. It does not survive a cold start, and nothing can make it | +| `cluster` workers | once per worker, after the fork | workers are separate processes: **N workers is N token caches and N first-call token fetches**, and no client can be shared between them | +| `worker_threads` | once per thread | a client is not structured-cloneable β€” it holds functions and private fields β€” so it cannot be posted across the boundary | +| CLI / script | one at the top, or one per run | the process is short-lived; the cache never pays back | +| Browser / edge runtime | wherever your bundle initialises | the package ships `dist/esm` and `dist/commonjs` only β€” no bundle, no UMD, no CDN artifact β€” so it goes through a bundler. Deno, Bun, Workers and Vercel Edge need only the globals the map lists, but none is a tested target | + +## ESM and CommonJS + +The package ships both dialects from a single entry, so `require` works with full types. In a +TypeScript CommonJS file use the `import … = require(…)` form β€” a destructuring `require` runs fine +but gives you `any`: + +```ts +import sdk = require("{package-name}"); + +const client = new sdk.{Api}Client({ serverEnvironment: sdk.ServerEnvironment.{Environment} }); +``` + +⚠ **A process that loads both dialects gets two copies of every class**, so `instanceof` is `false` +across them β€” pick one dialect per process, or narrow on `err.kind` (**typescript-error-handling**). + +Two consumer compiler settings change what you type at the construction site. Under +`exactOptionalPropertyTypes`, **omit or spread** an absent option rather than assigning `undefined` to +it. Under `verbatimModuleSyntax`, names that carry no runtime value β€” `ClientOptions` and every model +type β€” must be imported with `import type`; `{Api}Client` and `ServerEnvironment` are real values and +are imported normally. + +## Inbound webhooks β€” what the emitted package does and does not give you + +**There is no webhook surface at all** β€” no event parser, no signature verifier, and no member of +`ClientOptions` that turns one on. + +So verifying an inbound webhook is entirely yours to write, against the provider's published scheme: +capture the raw body before any JSON parsing, compute the signature yourself, and compare in constant +time. The SDK's model types stay useful for *deserializing* an event body once you have verified it β€” +but do not read "the SDK parses this" as "the SDK verified this". See **typescript-models**. + +## Next + +- Configure authentication β†’ **typescript-authentication** +- Make your first call β†’ **typescript-calling-endpoints** +- Tune timeouts, retries, logging, base URL β†’ **typescript-configuration-resilience** diff --git a/plugins/authlete/skills/typescript/typescript-configuration-resilience/SKILL.md b/plugins/authlete/skills/typescript/typescript-configuration-resilience/SKILL.md new file mode 100644 index 00000000..c8977033 --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-configuration-resilience/SKILL.md @@ -0,0 +1,842 @@ +--- +name: typescript-configuration-resilience +description: Load before constructing or tuning an APIMatic-generated TypeScript SDK client β€” picking a server environment or overriding a base URL, changing the on-by-default retries or `retry.timeout`, adding cancellation or a deadline, turning the built-in logging on or off, making a write safe to retry, paging through results, or wrapping `fetch` for header injection, proxies, TLS or connection pooling. +--- + +# Configuration & resilience for an APIMatic TypeScript SDK + +> Which server groups, environments, template variables, page/cursor fields and idempotency-key +> fields YOUR SDK has comes from `sdk-map.md` and `map/operations/{resource}.md` at the package root β€” +> **apply only the guidance that matches**. `{...}` marks a name to take from your SDK; `{Api}Client` +> is the one public client class, in `src/client.ts`. `{APICLIENTTYPENAME}_LOG` is the logging +> environment variable: the client class name in upper snake case, plus `_LOG`, so `AcmeClient` reads +> `ACME_CLIENT_LOG`. The getting-started skill's identity table names yours. + +Everything configurable is a field on `ClientOptions` (`src/client-options.ts`), passed to the one +constructor. There is no `Configuration` class, no builder, and no per-request override beyond +`{ signal, retry, logging }`. Server groups, environments and their URL templates live in `src/servers.ts`. + +## The whole configuration surface + +| Field | Type | Default | Purpose | +| --- | --- | --- | --- | +| `serverEnvironment` | `ServerEnvironment` | first declared | selects which environment's server options are read | +| `serverOptions` | `ServerOptions` | `{}` | per-group, per-environment `baseUrl` and template-variable overrides | +| `retry` | `RetryOptions` | retries **on** β€” see *Retries* | the retry policy, and `retry.timeout` (**ms**, `60_000`) bounding each attempt | +| `logging` | `LoggingOptions` | **off** β€” see *Logging* | the built-in request/response logger, its levels and its redaction | +| `eventStream` | `EventStreamOptions` | `idleTimeout` `60_000` (**ms**) | how long an event stream may stay silent; read only by an operation that resolves to an `EventStream` β€” see **typescript-event-streams** | +| `fetch` | `FetchLike` (`typeof fetch`) | global `fetch` | **the one transport extension point** | +| `{scheme}` / `{scheme}Strategy` | per scheme | unset | credentials β€” see **typescript-authentication** | + +Read the defaults off your own SDK rather than trusting the column: the credential members and the +default environment are per-SDK. + +## Not on this SDK + +| You might reach for | Reality | +| --- | --- | +| hooks, middleware, interceptors, `onRequest`/`onResponse` | none. `fetch` is the one transport extension point; `retry.onRetry` is the one callback, and it sees only retries | +| pagination, `for await`, auto-paging helpers | no operation is paginated. The one async-iterable is an `EventStream`, which an operation the map says resolves to `EventStream` returns | +| SSE request bodies, WebSockets | none. `text/event-stream` is a response media type only β€” an operation whose success is one resolves to an `EventStream` you iterate with `for await` (**typescript-event-streams**). Every other decoder reads the body to completion, bar a binary success, which hands its stream over unread | +| multipart responses, XML bodies | none. Body kinds are empty, JSON, form-urlencoded, text, multipart and binary | +| per-request `headers`, `baseUrl`, idempotency key | none. `RequestOptions` is `{ signal, retry, logging }`; `retry` takes only `maxRetries`, `timeout` and `statusCodesToRetry`, and `logging` only `level` | +| a circuit breaker, a rate limiter | none. The retry loop honours `Retry-After`, but nothing limits how many calls you start | +| the raw `fetch` `Response` | deliberately unreachable. `status`/`headers` are on `asApiResult()` and on `ResponseError` | + +Everything in the left column that you actually need, you build on `fetch`. The rest of this skill is +those patterns and the traps in them. + +**What the SDK does own**, so that you do not rebuild it: URL and server resolution, request +encoding and schema validation on the way out, auth application and OAuth token caching, response +decoding, the two-family error mapping, the `Idempotency-Key` on every non-GET call, the retry +loop with its per-attempt timeout, and request/response logging with redaction. + +## The `fetch` seam + +```ts +type FetchLike = typeof fetch; +``` + +The SDK calls it with **one argument, the attempt's `Request`, and no `init`**. The method, the +headers (the credential included), the body and the signal are all on that `Request`. **Forward that +`Request`, or at least its `signal`**, to whatever performs the request: drop the signal and both each +attempt's `retry.timeout` and per-call cancellation go inert β€” the call neither aborts nor times out. A +wrapper written against `(input, init)` still type-checks, but `init` is always `undefined`: whatever it +reads from `init` is nothing, and whatever it passes as `init` **replaces** what the `Request` carries, +so `fetch(input, { headers })` drops every header the SDK set. On an abort a replacement may reject with +whatever it likes: a deadline is still a `TimeoutError` the retry loop retries, and a caller abort still +rejects with the caller's own `reason`. + +Four facts that decide how you write a wrapper: + +- **It sees every request the client makes, including the OAuth token request** β€” a wrapper that logs + requests logs the token POST, `client_secret` and all, where the built-in logger (*Logging*) masks it. + Filter by URL where that matters. +- **It sits under the retry loop, so it sees every attempt.** A call retried three times is four + requests through your wrapper, each a fresh `Request` with its own signal, and a wrapper that logs + writes four lines. To see one line per logical call, log around the SDK call instead; to see why an + attempt was abandoned, use `retry.onRetry` or the built-in logger's retry line. +- **A plain `Error` thrown from a wrapper reaches the caller as `ConnectionError` with your error on + `.cause` β€” and is retried like a dropped connection** for a method in the retry gate, so a + deliberate refusal is thrown again on every attempt and surfaces only after the backoff. A member of + the `{Api}Error` family (`TimeoutError`, `ConnectionError`, …) passes through unwrapped, and is + retried only if its `kind` is `connection` or `timeout`. +- **Wrappers compose by nesting, innermost-first at the network:** in + `new {Api}Client({ fetch: tracingFetch(proxiedFetch) })` the tracer sees the request before the + proxy dispatches it. Do not add a retrying wrapper to the stack β€” see *Retries*. + +## `serverOptions` configuration for each environment + +The base URL is resolved **per server group and per environment**. `src/servers.ts` declares one +options type per group, and each carries a nested object for **every environment** the API declares: + +```ts +export type ServerOptions = { + {group}?: { + {environment}?: { baseUrl?: string; {variable}?: string }; + }; +}; +``` + +Each environment's entry exposes what the SDK substitutes into that group's URL: the **`baseUrl` +template** (always present and settable) plus any **template variables** that server's URL declares +β€” a region, a subdomain, a port, an API version. Names and counts vary per API and per +environment; a group with no variables exposes `baseUrl` alone. Read the real group keys, environment +keys, URL templates and variable defaults from `DEFAULT_SERVER_OPTIONS` in `src/servers.ts`, or from +the **Base URLs and overrides** table in `sdk-map.md`. + +```ts +const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, + + serverOptions: { + // Fill one template variable, keeping the declared baseUrl template: + {group}: { {environment}: { {variable}: "eu" } }, + + // Or replace the template outright β€” a mock server, a proxy, a self-hosted gateway. + // A literal URL with no {placeholders} is used as-is: + // {group}: { {environment}: { baseUrl: "http://localhost:3000" } }, + }, +}); +``` + +How this resolves: + +- **Overrides merge with the declared defaults per group-and-environment pair, key by key** + (`{ ...DEFAULT_SERVER_OPTIONS.{group}.{environment}, ...yours }`). Setting `{variable}` alone keeps + the declared `baseUrl`; setting `baseUrl` alone keeps the declared variables. Naming one group or + one environment leaves every other untouched. +- **Only the selected environment's options are read.** Set `baseUrl` on the wrong environment and + your value is silently ignored in favour of the selected one's default. +- **`baseUrl` is a template, not a URL.** Variables are percent-encoded into their `{placeholder}` at + request time. A placeholder in a `baseUrl` you supply is only substituted if a declared variable of + that exact name exists β€” otherwise it survives into the URL verbatim, and nothing rejects it the way + an unfilled path parameter is rejected. + +### βš β–Άβ–Ά Always pass `serverEnvironment` explicitly + +`serverEnvironment` has a default, so `new {Api}Client()` compiles and reaches a real host: the +**first** member of `ServerEnvironment` in `src/servers.ts` β€” often sandbox. Production credentials +then fail against it as if they were wrong. **Pass it in every environment, production included.** + +`ServerEnvironment` is a **closed** union β€” no `| (string & {})` tail β€” so a mistyped literal fails to +compile; a value read from configuration does not. An unknown string throws `ConfigurationError` from +the constructor, but `undefined` silently selects the default β€” so map configuration values explicitly +and **fail on a miss**: + +```ts +const ENVIRONMENTS: Record = { + sandbox: ServerEnvironment.{Sandbox}, + live: ServerEnvironment.{Production}, +}; + +const serverEnvironment = ENVIRONMENTS[process.env.API_ENV ?? ""]; +if (!serverEnvironment) throw new Error(`unknown API_ENV: ${process.env.API_ENV}`); +``` + +### What is captured when + +`serverEnvironment` and `serverOptions` are resolved **once**, in the constructor β€” so a bad value +throws there, never from a call, and mutating the `serverOptions` object you passed in, replacing a +whole group (`serverOptions.{group} = …`) or assigning `serverEnvironment` afterwards has no effect. +**Configure the server before you construct, and construct a new client to change environment.** + +### Redirecting at a mock + +⚠ **An OAuth token endpoint may live on its own server group.** Redirecting `{group}` at a mock does +**not** move the token request, which still reaches the real host and fails or, worse, succeeds against +production. Override every group the run touches. See **typescript-authentication**. + +This is a real base-URL override, not a proxy β€” the SDK builds requests against the URL you give it. +For a proxy, see *Proxy or custom agent* below. + +## Retries β€” on by default + +**A client built without `retry` retries.** With the defaults: + +- A `GET`, `HEAD`, `PUT` or `OPTIONS` call that gets a `408`, `429`, `500`, `502`, `503` or `504`, or + whose attempt fails with a `ConnectionError` or a `TimeoutError`, is re-sent up to **3** more times, + backing off roughly 1 s, 2 s and 4 s, and honouring `Retry-After`. +- A `POST`, `PATCH` or `DELETE` is sent **once**, whatever went wrong. The method gate covers every + failure, faults included. +- **A `401` is not in the default status set**: the failing call rejects, and the *next* call + re-acquires a fetched credential (**typescript-authentication**). + +Its defaults live in `src/core/retry.ts`. Configure it with any subset of `RetryOptions` β€” a field you +leave out keeps its default: + +```ts +new {Api}Client({ retry: { maxRetries: 0 } }); // off: every call is sent exactly once +new {Api}Client({ retry: { timeout: 10_000 } }); // the defaults, with a tighter per-attempt budget +new {Api}Client({ + retry: { + maxRetries: 5, + httpMethodsToRetry: ["GET", "HEAD", "PUT", "OPTIONS", "POST"], // see *Making a write safe* + }, +}); +``` + +| Field | Default | Meaning | +| --- | --- | --- | +| `timeout` | `60_000` | Milliseconds **one attempt** may take, up to its response headers. Not a budget for the call | +| `statusCodesToRetry` | `[408, 429, 500, 502, 503, 504]` | Exactly these and no others β€” there is no family rule, so `[429, 503]` stops retrying `500`. `[]` retries no status, while faults still retry | +| `httpMethodsToRetry` | `["GET", "HEAD", "PUT", "OPTIONS"]` | The gate: a call outside it is never re-sent. `[]` turns retrying off. Upper case only | +| `maxRetries` | `3` | Retries *after* the first attempt, so `3` is up to four sends. `0` turns retrying off | +| `delay` | `1000` | Milliseconds before the first retry, and the base the curve grows from | +| `backoffFactor` | `2` | What each successive delay is multiplied by, between `1` and `100` | +| `useExponentialBackoff` | `true` | `false` repeats `delay` unchanged | +| `maxJitter` | `0.25` | A **fraction**, `0` to `1`, and additive: a wait lands between its delay and `delay Γ— (1 + maxJitter)`. `0` makes every wait exact | +| `onRetry` | none | `(attempt: RetryAttempt) => void`, called before each retry's wait | + +`0`, `false` and `[]` are values, never misses. Anything else a field cannot honour β€” a negative, +fractional, `NaN`, `Infinity` or above-`2_147_483_647` `timeout`, a `maxJitter` of `50`, a list +carrying a lower-cased `"get"` β€” **silently falls back to that field's default** (on a call, to the +client's value), and the client still constructs. `timeout: 0` is kept: it is a zero-length deadline +that fails every attempt, not "no timeout" β€” an unbounded attempt is not expressible. + +The wait before retry *n* (counting from 0) is +`min(Retry-After ?? delay Γ— backoffFactor ** n, 60_000) Γ— (1 + random Γ— maxJitter)`, rounded up. The +60-second ceiling is the engine's, not an option, and jitter sits on top of it, so the longest single +wait is 75 s at the defaults. + +What the loop does: + +- **`Retry-After` is honoured** in both its delta-seconds and HTTP-date forms, capped at 60 s, with + jitter added on top, so the SDK never wakes before the time the server named. A malformed value + falls back to the curve. +- **Never retried:** a caller abort (which also cuts a backoff wait short), a streaming request body + whatever the method, and a failure to obtain a credential β€” the token request is itself a `POST` + under this policy (**typescript-authentication**). +- **A rejection from your `fetch` that is not an SDK error counts as a dropped connection** β€” see + *The `fetch` seam*. +- **The call rejects with the last attempt's failure** β€” an `ApiError` (or the operation's subclass) + for a status, a `ConnectionError` or `TimeoutError` for a fault β€” the same shapes as with retrying + off (**typescript-error-handling**). Only a retried-away response's headers reach `onRetry`, and + `.asApiResult()` sees the last response. + +### Per-call overrides + +`RequestOptions.retry` (`RequestRetryOptions`) takes only `maxRetries`, `timeout` and +`statusCodesToRetry`, each merged over the client's value for that one call (syntax: +**typescript-calling-endpoints**). Backoff, the method gate and `onRetry` are client-only β€” but only by +type: an object carrying another `RetryOptions` field (from JavaScript, or a constant typed as the +wider record) still has it honoured, so do not rely on the type to keep a write out of the gate. + +### Observing retries + +`onRetry` is the one built-in callback. It runs before each retry's wait β€” not for the first attempt, +and not on success β€” with the retry about to be made: + +```ts +const client = new {Api}Client({ + retry: { + onRetry: ({ attemptNumber, delay, reason }) => { + if (reason.kind === "status") log.warn({ attemptNumber, delay, status: reason.status }, "retrying"); + else log.warn({ attemptNumber, delay, kind: reason.error.kind }, "retrying after a fault"); + }, + }, +}); +``` + +Narrow `RetryReason` on `kind` before reading the rest: a `"status"` carries the `status` and the +discarded response's `headers`, a `"fault"` carries the `connection` or `timeout` error and no response +at all. A fault from an OAuth token request carries the token endpoint's `uri`, not the operation's. +**Keep the callback to logging and metrics** β€” a throw from it ends the call with the error it threw. +With the built-in logger on at `warn` or below, the SDK writes its own retry line before `onRetry` +runs, so you need the callback only for metrics or a sink of your own. + +### When the SDK's policy is not enough + +Rules to hold to: + +- **Retry idempotent reads freely; treat every write as a separate decision.** A write that timed out + may have succeeded β€” a reset after the bytes reached the server is indistinguishable from one before. + The default gate encodes exactly this. +- **Do not stack a second retry layer** β€” a retrying `fetch` wrapper, a `withRetry` around the call, a + job that re-runs on failure β€” on top of the SDK's without turning the SDK's off for that path. + Attempts multiply (four inside four is sixteen round-trips, and up to sixteen times the timeout), and + every attempt of a caller-level layer is a new SDK call that mints a **new** `Idempotency-Key`. +- **Never retry an `EncodeError`, a `DecodeError`, an `AuthError` or a 4xx other than `408`/`429`** β€” + the loop retries none of them, and what each means is in **typescript-error-handling**. + +When a decision genuinely needs something the policy cannot express β€” a retry that depends on the +decoded error payload, or on which operation it is β€” turn the SDK's loop off for that call and own +retrying entirely at the caller: + +```ts +import { {Api}Error } from "{package-name}"; + +async function withRetry(work: () => Promise, attempts = 3): Promise { + for (let attempt = 1; ; attempt++) { + try { + return await work(); + } catch (err) { + if (attempt === attempts || !isTransient(err)) throw err; + await new Promise((r) => setTimeout(r, 2 ** (attempt - 1) * 500 * (1 + Math.random()))); + } + } +} + +function isTransient(err: unknown): boolean { + if (!(err instanceof {Api}Error)) return false; // an abort's reason, a configuration fault, a bug: no + return err.kind === "api" && isRetryableConflict(err); // your decision, on the decoded payload +} + +await withRetry(() => client.{resource}.{operation}(request, { retry: { maxRetries: 0 } })); +``` + +`{Api}Error` is the whole error family, narrowed on its closed `kind`; its real name is in +**typescript-error-handling**. + +### Making a write safe under retries + +A write is exposed to a resend only when you widen `httpMethodsToRetry` or add a retry layer of your +own β€” and through two things no setting fixes: + +- **`PUT` is in the default gate β€” idempotent by HTTP's definition, not necessarily by your + provider's.** A `PUT` can still carry a per-call side effect β€” an audit row, an outbound webhook, a + metered charge β€” that a resend duplicates. Where yours does, drop `"PUT"` from + `httpMethodsToRetry`. +- **"It was not resent" is not "the write did not happen."** A transport failure on a `POST` leaves the + outcome *unknown*: the bytes may have reached the provider before the socket died. That is a + reconciliation problem, and no retry setting solves it. + +**Decide which requirement you are meeting before you pick a remedy β€” they are not interchangeable.** +*"A duplicate must be harmless"* is options 1–2, and the provider still receives more than one write. +*"At most one write may reach the provider"* is option 4, the only one that holds the count at one +regardless of how the client is later configured. The four, **weakest guarantee first** β€” so do not +read the numbering as a recommendation order: + +1. **Make the write idempotent at the provider.** Makes a resend *harmless* rather than rarer; the send + count stays above one. + + **The SDK sends an `Idempotency-Key` header on every operation other than a `GET`** β€” a UUID minted + once per call, unless the operation declares a header of that name itself, in which case that one is + a field you set. **Every attempt of the SDK's retry loop re-sends the same key**, because the loop + re-sends the request the SDK already built. That is what makes naming a write in + `httpMethodsToRetry` safe β€” **where the provider deduplicates on it**. A retry of your own is a new + call and mints a new key: the provider sees two submissions. + + Where the operation declares its own key β€” a header, a query parameter, a form field or a body + member, all alike in the flat, channel-blind request object; the `Channel` column of its **Fields** + table tells them apart (**typescript-calling-endpoints**) β€” **generate it once per logical action and + reuse it across every attempt you make**: + + ```ts + const requestId = crypto.randomUUID(); // once per logical action, NOT per attempt + await withRetry(() => + client.{resource}.{operation}({ ...body, {idempotencyKey}: requestId }, { retry: { maxRetries: 0 } }), + ); + ``` + + A key generated inside the retried function is a fresh key on every attempt, which deduplicates + nothing while looking exactly like a solution. + + Two cautions, neither of them visible in the type: + + - **Whether the provider actually enforces it is not visible in the SDK.** The header being sent, + or a declared key field existing, is not a guarantee that a resend is deduplicated. Verify + against the provider's documentation or live traffic before widening the gate. + - **Keys expire, and the retention window differs per API.** A resend after a long backoff may fall + outside it and be treated as a new request. Only the provider's documentation carries that number. +2. **Reconcile after a failure** β€” on a transport failure on a write, re-read provider state to + establish what actually happened instead of assuming nothing did. Detects a duplicate; does not + prevent one. Same reflex as an unreadable write response β€” see **typescript-error-handling**. +3. **Send the write once, explicitly** β€” `{ retry: { maxRetries: 0 } }` on that call, or a separate + client for writes built with `retry: { maxRetries: 0 }`: + + ```ts + const reads = new {Api}Client({ ...shared }); + const writes = new {Api}Client({ ...shared, retry: { maxRetries: 0 } }); // no resends + ``` + + Holds at one send today; stops holding the moment someone removes the override or widens the gate + on `writes`, and that failure does not announce itself. +4. **A guard wrapper that refuses a re-send it did not authorise** β€” the only option that holds the + count at one no matter how the client is configured, because a blocked attempt never reaches the + network. Reach for it whenever a duplicate would be externally visible or costly to undo, and + combine it with option 2 to settle the outcome of the one send you allowed. + + Three details decide whether it works: + + - **A `fetch` wrapper sees every attempt of the SDK's loop**, and its refusal is retried as a + `ConnectionError` (*The `fetch` seam*). Pair the guard with option 3 on the guarded write, so the + refusal is immediate, and recognise it with `err.cause instanceof YourRefusal`. + - **Keep the "already sent" marker in state that outlives one attempt** β€” an `AsyncLocalStorage` + scope the caller opens around the write, or a closure created per unit of work. A marker hung off + `init` is per-attempt bookkeeping only, and a caller-level retry (a job re-running after a + timeout) creates a fresh one either way. + - **Key it on method and URL**, so the OAuth token request it also sees is not counted as the + write. + + Count the send **before** it goes out. A request that failed on the way out may still have been + received, so "this may already have taken effect" is the only safe reading β€” surface it as an + **unknown outcome** to be settled by re-reading provider state (option 2), not as a definite failure. + +**Where the provider ignores the key, retrying a write is a real duplication risk.** Keep the write +out of the gate; the safe recovery is option 2 β€” re-read state and decide β€” not a resend. Some +operations are naturally idempotent anyway (cancelling an already-cancelled resource is usually +harmless), but that is a **per-operation judgement** made against the provider's documentation, not a +property you can assume for the whole class of writes. + +**All four bound the call YOU make to the provider. None of them is a reason to change the contract +your own callers see.** Key the guard on something you already hold β€” a reference derived +deterministically from what the caller sent. A caller that sent a well-formed request and got a `4xx` +because your guard wanted an extra field is a defect you introduced, not a duplicate you prevented. +And **a guard needs a release**: a claim or marker with no expiry turns one transient failure into a +permanent refusal. Clear it once the outcome is settled, and expire it when it never is. + +## Bounding a call β€” the two layers, and which one is a total + +| Layer | Scope | Default | Bounds a whole call? | +| --- | --- | --- | --- | +| `retry.timeout` | one attempt, up to its response headers | `60_000` ms | **No** β€” an idempotent call gets up to `maxRetries + 1` attempts, plus the waits between them | +| the `signal` you pass to the call | everything from dispatch to resolution, retries and waits included | none | **Yes** β€” the only one | + +### `retry.timeout` + +In **milliseconds**, bounding **one attempt**, each with its own deadline; the last attempt's expiry +rejects as a `TimeoutError` (`err.kind === "timeout"`). It starts once the credential is in hand and +stops at the response headers, so obtaining a credential and reading the body are bounded only by your +`signal` (a built-in OAuth token request runs on its own timer). A per-call value replaces the +client's for that call, still per attempt. A value a timer cannot hold silently falls back to the +default, and `0` fails every attempt (*Retries*). + +### Cancellation and real deadlines + +Per-call cancellation is `RequestOptions.signal` (**typescript-calling-endpoints**). An abort rejects +with the signal's own `reason`, outside the `{Api}Error` family (**typescript-error-handling**) β€” so +pass something diagnostic, and rethrow it from a family `catch`. + +For a **deadline across a whole call or unit of work** β€” one call and its retries, or several calls β€” +`AbortSignal.timeout` and `AbortSignal.any` compose (both on Node 20+): + +```ts +async function withDeadline(work: (signal: AbortSignal) => Promise, ms: number, caller?: AbortSignal) { + const deadline = AbortSignal.timeout(ms); + return await work(caller ? AbortSignal.any([deadline, caller]) : deadline); +} +``` + +That is how you give one operation a ceiling that `retry.timeout` cannot: the deadline covers every +attempt and every wait, and the earlier of the two wins. + +⚠ **Several calls in one handler add up** β€” a loop over records, a fan-out, a create-then-confirm +pair β€” and a swallowed timeout still costs its full bound before the next call runs: `calls Γ— +(maxRetries + 1) Γ— timeout`, plus the waits, must fit the budget you make a caller wait. Count the +calls in the loop, not in the snippet, and bound the handler with one signal. + +**Give the total bound one home, not one per call site.** A controller written out at each call is the +layer that gets skipped, so any operation added later silently has no ceiling. Put it at the one +service that fronts the SDK and route every operation through it: + +```ts +export class {Api}Gateway { + constructor(private readonly client: {Api}Client, private readonly budgetMs = 20_000) {} + + private bounded(work: (signal: AbortSignal) => Promise, caller?: AbortSignal): Promise { + const deadline = AbortSignal.timeout(this.budgetMs); + return work(caller ? AbortSignal.any([deadline, caller]) : deadline); + } + + {operation}(request: {Resource}.{Operation}Request, caller?: AbortSignal) { + return this.bounded((signal) => this.client.{resource}.{operation}(request, { signal }), caller); + } +} +``` + +Link the caller's own signal in β€” a request whose client has disconnected should stop the outbound work +too. + +> **Browser floor.** Cancellation needs `AbortController.abort(reason)` and `AbortSignal.reason` +> (Chrome 98, Firefox 97, Safari 15.4). The SDK's own module-load floor is lower, so between the two +> the engine still aborts the request but **produces no typed error at all**. + +## Pagination β€” drive it yourself + +Nothing is paginated, and the only async-iterable is an `EventStream`, which is not a page. When an +operation returns a page, advance its page or cursor field in a loop and stop on the API's own end +signal: + +```ts +// Offset/page style: +const PER_PAGE = 100; +for (let page = 1; ; page++) { + const result = await client.{resource}.{operation}({ page, perPage: PER_PAGE }); + for (const item of result.{items}) process(item); + if (result.{items}.length < PER_PAGE) break; // short page β€” usually the last +} + +// Cursor style: +let cursor: string | undefined; +do { + const result = await client.{resource}.{operation}({ ...(cursor ? { cursor } : {}) }); + for (const item of result.{items}) process(item); + cursor = result.{nextCursor}; +} while (cursor); +``` + +Read the actual page/cursor field names off the operation's **Fields** table and its response model β€” +they vary per API, and the SDK sends a defaulted `page`/`perPage` whether or not you set it. Prefer +the API's explicit end signal (a null next-cursor, a `hasMore` flag) over inferring from a short page +where one exists. + +### ⚠⚠ Never leave a page loop unbounded + +**Both loops above are wrong as written**, and the reason is the same one that makes hand-driven paging +riskier than the auto-paging this SDK does not have: *the only stop condition is the provider's +cooperation*. A provider that keeps returning a next cursor, a cursor that fails to advance, a filter +the provider quietly ignores, or a `perPage` it silently caps below what you asked for will each spin +forever. The failure does not look like a slow request. It looks like a request that **never returns**, +with tens of thousands of provider calls billed behind it and no error the provider can be blamed for. + +**Every page loop needs at least one bound that does not depend on the provider.** Pick what matches +the use case; a page cap is the cheapest and is never wrong: + +| Bound | Use when | Shape | +| --- | --- | --- | +| **page cap** | always β€” the backstop | `if (++pages >= MAX_PAGES) break;` | +| **item cap** | the caller wants "the first N" | `if (out.length >= max) break;` | +| **deadline** | the loop sits behind a request timeout | one `AbortSignal.timeout` passed to every call | +| **no-progress guard** | cursor or offset paging | stop if the cursor did not change between pages | + +```ts +const MAX_PAGES = 100; +const signal = AbortSignal.timeout(30_000); // bounds the whole walk, not one page +let cursor: string | undefined; +let seen: string | undefined; + +for (let pages = 0; pages < MAX_PAGES; pages++) { + const result = await client.{resource}.{operation}({ ...(cursor ? { cursor } : {}) }, { signal }); + for (const item of result.{items}) process(item); + + cursor = result.{nextCursor}; + if (!cursor) return; // the provider's end signal β€” the happy path + if (cursor === seen) throw new Error("cursor did not advance"); + seen = cursor; +} +throw new Error(`stopped after ${MAX_PAGES} pages`); // truncating silently is its own defect +``` + +Prefer to **narrow the query before you page it** β€” a provider-side date range, status filter, or a +`perPage` matching what the caller needs turns "walk everything" into a handful of pages. And **a bound +that silently truncates is a different defect from one that hangs**: when you hit the cap, surface it +or log it; never return a partial set that reads like a complete one. + +Wrapping a bounded loop in an async generator gives you the paging `for await` the SDK does not: + +```ts +async function* all{Items}(signal?: AbortSignal): AsyncGenerator<{Item}> { + let cursor: string | undefined; + for (let pages = 0; pages < MAX_PAGES; pages++) { + const result = await client.{resource}.{operation}({ ...(cursor ? { cursor } : {}) }, { signal }); + yield* result.{items}; + cursor = result.{nextCursor}; + if (!cursor) return; + } + throw new Error(`stopped after ${MAX_PAGES} pages`); +} +``` + +A failed page rejects mid-enumeration like any other call β€” see **typescript-error-handling**. + +## Event streams and XML bodies + +An operation with a `text/event-stream` success response **is** carried: its **Returns** bullet reads +`EventStream`, the call resolves at the response headers, and you iterate the stream with +`for await` β€” load **typescript-event-streams** before you write the loop. Its one setting is +`eventStream.idleTimeout` on the client, which bounds the silence between events; `retry.timeout` +stops at the headers and never reaches the loop. + +Outbound, an operation with an XML request body has no body field. Call it with `fetch` directly β€” +the same base URL, and the credential header the SDK would have sent; no option makes the SDK carry +it. A **binary** success, and multipart and binary request bodies, **are** carried β€” see +**typescript-calling-endpoints**. + +## Logging + +**The SDK has a built-in request/response logger, and it is off by default.** Turn it on through +`ClientOptions.logging`, a `LoggingOptions` exported from the package root with `Logger`, `LogLevel`, +`LogDetails` and the value `defaultLoggingOptions`: + +```ts +import { {Api}Client, defaultLoggingOptions } from "{package-name}"; + +const client = new {Api}Client({ + logging: { + logger: appLogger, // any { error, warn, info, debug }; default `console` + level: "info", // "off" | "error" | "warn" | "info" | "debug" + logRequestHeaders: false, // default + logResponseHeaders: false, // default + logRequestBody: false, // default β€” read the warning below before enabling + redactedKeys: [...defaultLoggingOptions.redactedKeys, "card_number"], + }, +}); +``` + +How it turns on β€” the first row that applies wins: + +| what is set | what is logged | +| --- | --- | +| `logging.logger` | everything the switches allow, at `debug` unless `level` says otherwise β€” your logger's own level does the filtering | +| `logging.level`, and no `logger` | lines at that level and above, to `console` | +| neither, and `{APICLIENTTYPENAME}_LOG` holds a level | see the warning below | +| nothing | nothing β€” `level` is `"off"` | + +What it writes, and at which level. The SDK filters by level itself, then calls your logger's method of +the same name: + +| Line | `event` | Level | Written when | +| --- | --- | --- | --- | +| `sending {METHOD} {url}` | `HttpRequest` | `info` | every attempt | +| `request headers` | `RequestHeaders` | `debug` | `logRequestHeaders` | +| `request body` | `RequestBody` | `debug` | `logRequestBody`, and the media type is in `loggableContentTypes` | +| `{METHOD} {url} succeeded with status {n} in {ms}ms` | `HttpResponse` | `info` | a `2xx` | +| `{METHOD} {url} failed with status {n} in {ms}ms` | `HttpResponse` | `warn` | any other status | +| `response headers` | `ResponseHeaders` | `debug` | `logResponseHeaders` | +| `{METHOD} {url} retrying in {ms}ms (retry {n} of {max}) after {reason}` | `HttpRetrying` | `warn` | before each retry, ahead of `onRetry` | +| `{METHOD} {url} failed after {ms}ms: {ErrorName}` | `HttpFailed` | `error` | a call that ends on a fault or on a credential it could not obtain | + +Each line is `logger.{level}(message, details)`: the message is complete text, and `details` is a +`LogDetails`, a union you narrow on `event`. The SDK calls the method **on** your logger, so one that +reads its own state through `this` works as it is. `console` and winston take `(message, meta)` and +plug in directly; pino takes `(details, message)` and needs a four-line adapter: + +```ts +import pino from "pino"; +import type { Logger } from "{package-name}"; + +const p = pino(); +const appLogger: Logger = { + error: (message, details) => p.error(details, message), + warn: (message, details) => p.warn(details, message), + info: (message, details) => p.info(details, message), + debug: (message, details) => p.debug(details, message), +}; +``` + +**Keep the logger non-throwing.** An error thrown from it ends the call with that error β€” on a +response line, after the request already reached the server. + +**The URL and the headers are redacted by allow-list, which is the safe direction.** A query value is +masked unless its key is on a short known-safe list (`cursor`, `dates`, `datetime`, `limit`, +`offset`, `page`, `since`, `size`, `strings`); a header is masked unless it is an ordinary transport +header β€” `authorization`, `cookie` and every api-key header are not on that list, so they are masked. +`redactedKeys` (default: `sig`, `signature`, `access_token`, `apikey`, `api_key`, `client_secret`, +`password`, `refresh_token`, `code`, `assertion`, `client_assertion`) adds keys to the masked set, +`redactedHeaders` forces a header in, and `unmaskHeaders` opts one out; the mask is +`redactionPlaceholder` (`"***"`). The URL **path** is not masked β€” an id or reference in the route is +logged as it is. `redactedKeys` and `loggableContentTypes` **replace** their default rather than add to +it, so spread `defaultLoggingOptions` to extend one, as above. + +βš β–Άβ–Ά **`logRequestBody: true` does not redact a JSON body**, and only half-redacts a form body. A body +is logged only when its media type is in `loggableContentTypes` (`application/json`, +`application/x-www-form-urlencoded`; add others there), truncated at `bodySizeLimit` bytes (32 KB). A +JSON body is logged **verbatim**. A form body gets key masking, but by **deny-list**, not by the +allow-list the URL uses: only keys in `redactedKeys` are masked, so `card_number` in a form body prints +in the clear although the same key in a query string would not. On any endpoint that carries card data, +bank details, personal data or a credential in the body, switching this on writes that data to your +logs. Leave it off in production; turn it on for one endpoint at a time +behind a flag you can prove is off. + +βš β–Άβ–Ά **An environment variable turns logging on without a code change.** When code sets neither a +`logger` nor a valid `level`, the client reads `{APICLIENTTYPENAME}_LOG` once, as it is built, through +`process.env` β€” a runtime without `process` reads nothing. It takes a level name (`error`, `warn`, +`info`, `debug`, or `off`) and writes to `console`. `debug` also turns **both header switches** on; the +request body it never turns on β€” only code can. A value that is not a level warns once on `console`, +and logging stays off. Setting `logger` or `level` in code disables the variable outright β€” which is the +reason to set `logging: { level: "off" }` explicitly where you must guarantee silence. + +**Per-call override.** `RequestOptions.logging`, typed `RequestLoggingOptions`, takes one field, `level`, +for that call alone. Two things about it are easy to get wrong: + +- It **replaces** the client's settings for that call rather than raising them. A per-call `level` + decides every line by itself with **all three switches on**, so `{ logging: { level: "debug" } }` + writes both header lines **and the request body**, whatever the client set. `{ logging: { level: + "off" } }` silences that one call. +- It cannot lift your logger's own filter. With a `logger` supplied, a per-call `debug` hands it `debug` + lines, and a logger filtering at `info` drops them; `console` has no filter of its own. + +A per-call value that is not a level is ignored, and the call logs under the client's settings. + +**Resolution.** A field whose value the SDK cannot honour falls back to its default, silently, and the +client still constructs. A `logging` record whose read throws β€” a throwing getter, a revoked proxy β€” is +the one failure: a `ConfigurationError` from the constructor, or the rejection of the one call that +passed it. + +**OpenTelemetry spans, metrics and request-id propagation are not logging** β€” they belong on the +`fetch` seam, the one place that sees every outbound request, its status and its duration. + +### Verify on the wire (first run of a new integration) + +On a **successful** call the SDK returns only the decoded body β€” it never surfaces the request URL, the +verb or the status. So a wrong verb, a mis-serialized path segment or a dropped query parameter +**compiles cleanly** and produces no in-band signal; the only symptom is a runtime `404` or `422`. Turn +logging on at `info` for the first execution of any new call β€” `{APICLIENTTYPENAME}_LOG=info`, or +`logging: { level: "info" }` β€” and check five things in its `sending` line: + +1. The **verb** matches the operation's **Wire** bullet in `map/operations/{resource}.md`. +2. The **base URL** is fully expanded β€” no leftover `{region}`-style server variable, which means a + `baseUrl` you supplied carries a placeholder no declared variable fills. +3. The **path** is fully formed: a path field set to `null` collapses to **nothing**, silently, + leaving a doubled slash (`/orders//confirm`). (One left `undefined` rejects with `EncodeError` + before anything is sent.) +4. Each **path segment** holds the value you meant β€” for an enum that is the **wire value**, not the + member name. +5. The **query parameters** you set are present, spelled as their `Wire` names. A `null` or `undefined` + query value is dropped entirely rather than sent empty, so an absent parameter usually means an + absent field, not an encoding bug. The line masks a value whose key is off the safe list, so it + shows that a parameter was sent, not what it held. + +Then turn the level back down. + +## Other work on the `fetch` seam + +### Header injection + +```ts +const client = new {Api}Client({ + fetch: (input, init) => { + const request = new Request(input, init); + request.headers.set("x-correlation-id", currentCorrelationId()); + return fetch(request); + }, +}); +``` + +Set the header on a copy of the `Request` β€” the SDK has already put the content type and the +credential on it, and the copy keeps them, the body and the signal. Passing `{ headers }` as `init` +instead **replaces** every header the SDK set, the credential included. This is the only client-wide +header mechanism: `ClientOptions` has no header option. + +### Rate limiting + +There is no built-in limiter. The default `429` retry absorbs an occasional limit but does nothing to +stop you starting more calls than the provider allows. Put a concurrency/rate limiter around your +calls for that. Client-side limiting is the more predictable of the two under load β€” retrying into a rate limit +converts a fast failure into a slow one, and a fan-out of retrying calls keeps the pressure on. + +### Circuit breaking + +Count consecutive `ConnectionError`s, `TimeoutError`s and 5xx `ApiError`s, open the circuit for a +cool-off window, and fail fast while it is open. **Put the breaker around the SDK call, not in `fetch`**, +where its refusal would be retried as a `ConnectionError` (*The `fetch` seam*). Around the call, the +breaker sees one outcome per call, after the retries, and its refusal never enters the loop. + +## Proxies and TLS (Node) + +Neither is a `ClientOptions` field. Both are properties of the **dispatcher** the `fetch` seam hands to +undici, so both are configured the same way: + +```ts +import { ProxyAgent } from "undici"; + +const dispatcher = new ProxyAgent(process.env.HTTPS_PROXY!); +const client = new {Api}Client({ + fetch: (request) => fetch(request, { dispatcher } as RequestInit), +}); +``` + +`dispatcher` is an undici extension, not standard `RequestInit`, hence the cast. The same seam takes a +custom TLS configuration, a connection-pool setting, or a unix-socket dispatcher. + +βš β–Άβ–Ά **Node's global `fetch` ignores the proxy environment variables.** `HTTP_PROXY`, `HTTPS_PROXY` and +`NO_PROXY` do **nothing** to a `fetch` call by default β€” unlike most HTTP clients in other ecosystems, +and unlike `curl` in the same container. Undici reads them only through `EnvHttpProxyAgent`, and only +once you register it: + +```ts +import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici"; + +setGlobalDispatcher(new EnvHttpProxyAgent()); // now http_proxy / https_proxy / no_proxy apply +``` + +This is the usual reason an SDK call fails inside a corporate network while every other tool works. +`setGlobalDispatcher` affects the whole process, so in a library prefer the per-request `dispatcher` +above. + +**Private CAs and client certificates** go on the dispatcher's `connect` options: + +```ts +import { Agent } from "undici"; +import { readFileSync } from "node:fs"; + +const dispatcher = new Agent({ + connect: { + ca: readFileSync("/etc/ssl/corporate-root.pem"), // a private CA bundle + // cert / key: for mutual TLS, where the provider requires a client certificate + }, +}); +``` + +`NODE_EXTRA_CA_CERTS` is the deployment-side equivalent for the CA case and needs no code change, but +it is read **once at process start** and is ignored if set later. + +⚠ **Never reach for `rejectUnauthorized: false` or `NODE_TLS_REJECT_UNAUTHORIZED=0`.** They disable +certificate verification for traffic that carries a live credential, and the second one disables it +**process-wide**, for every other outbound call your service makes. Fix the trust store instead. + +## Connection pooling + +Pooling belongs to `fetch`, not to the client: on Node the global `fetch` shares one process-wide +undici dispatcher, so several SDK clients built over the default `fetch` share one pool and cost +nothing extra. Two consequences: + +- **A dispatcher you create is a pool you own.** Build it once, beside the client, and reuse it. A + `new Agent(...)` or `new ProxyAgent(...)` per request opens a fresh pool β€” and therefore fresh TCP + and TLS handshakes β€” on every call. +- **Pool limits live there too** β€” `connections`, `keepAliveTimeout`, `pipelining` on the `Agent`. + Raise `connections` before assuming a provider is slow under a fan-out; the default cap can be the + actual bottleneck. + +Everything else about client lifetime β€” why the client itself must be long-lived, and what a per-request +client costs under OAuth β€” is **typescript-client-initialization**. + +## What to reach for, by symptom + +| Symptom | Where to look | +| --- | --- | +| calls never time out | a `fetch` replacement that does not forward the `Request`, or its `signal` | +| a header added in a `fetch` wrapper arrives, but every call is now a `401` | the wrapper passed `{ headers }` as `init`, which replaces the SDK's headers β€” copy the `Request` instead (*Header injection*) | +| request lines in the logs with no logging code | `{APICLIENTTYPENAME}_LOG` is set in that environment β€” set `logging.level` in code to own it | +| a JSON body or a card number in the logs | `logRequestBody`, or a per-call `level`, which turns the body on β€” see *Logging* | +| a failing call takes seconds longer to reject than the first failure | expected on an idempotent call: the default policy retried it β€” watch it with `retry.onRetry` | +| a page loop never returns | no provider-independent bound β€” see *Never leave a page loop unbounded* | +| requests still hit the real host under test | the wrong server **group** or **environment** overridden β€” check `src/servers.ts` | +| auth fails everywhere with credentials you know are right | `serverEnvironment` left to its default, the **first** `ServerEnvironment` member β€” often sandbox | +| the call works locally, fails behind a corporate proxy | Node's `fetch` ignores `HTTP_PROXY` β€” register `EnvHttpProxyAgent` or pass a `dispatcher` | +| a duplicate write despite an idempotency key | the key is generated inside the retried function β€” hoist it out; or your own retry layer re-calls the SDK, which sends a new `Idempotency-Key` per call | +| ~16Γ— the expected round-trips | a retry layer of your own stacked on the SDK's β€” set `retry: { maxRetries: 0 }` where you own retrying | +| a test with one stubbed `503` hangs, or runs out of queued responses | the default policy fetched it four times across ~7 s of backoff β€” see **typescript-testing** | +| a `{placeholder}` survives into the URL | a `baseUrl` you supplied names a variable the group does not declare | +| `EncodeError` whose `uri` still shows a `{placeholder}` | a path field was `undefined` β€” a required field was omitted | +| `ConfigurationError` about an unknown environment, thrown by the constructor | `serverEnvironment` is not a declared member | +| an event-stream loop rejects with `TimeoutError` while the server is still connected | the server stayed silent past `eventStream.idleTimeout` β€” raise it, or set it to `null` and bound the wait with a `signal` (**typescript-event-streams**) | +| a token is fetched on every call | a client built per request β€” see **typescript-client-initialization** | + +## Next + +- Injecting a fake `fetch` in tests β†’ **typescript-testing** +- What each failure kind means β†’ **typescript-error-handling** +- Options and client lifetime β†’ **typescript-client-initialization** +- Credentials and the token endpoint's server group β†’ **typescript-authentication** diff --git a/plugins/authlete/skills/typescript/typescript-error-handling/SKILL.md b/plugins/authlete/skills/typescript/typescript-error-handling/SKILL.md new file mode 100644 index 00000000..eb5f7b16 --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-error-handling/SKILL.md @@ -0,0 +1,548 @@ +--- +name: typescript-error-handling +description: Load before writing a try/catch around an APIMatic-generated TypeScript SDK call, translating its errors into your own, or reading a status code or error body β€” the SDK's one error family and its closed `err.kind`, the throwables outside it, per-operation typed error arms and the `"undeclared"` arm, and what `.asApiResult()` still rejects. +--- + +# Error handling for an APIMatic TypeScript SDK + +> `{...}` marks a name to take from your SDK (`{Api}Client`, `{Api}Error`, `{package-name}`, +> `{Resource}`, `{Operation}`, `{arm}`). Whether an operation has typed error arms, what they are +> called and which statuses they cover is per operation: read its page in +> `map/operations/{resource}.md` and **apply only the guidance that matches**. + +Operations are **throw-based** (for a non-throwing alternative, see **The non-throwing form** below), +and every **operational** failure belongs to **one family**: `{Api}Error`, a union over six leaves. +One `instanceof {Api}Error` sees all of them, and `err.kind` says which one it is. Four throwables sit +**outside** the family β€” a configuration fault, a codec called directly, a bug, and a caller abort β€” +so a `catch` that tests the family has to rethrow whatever is left. + +```ts +import { {Api}Error } from "{package-name}"; + +try { + await client.{resource}.{operation}({ /* ... */ }); +} catch (err) { + if (err instanceof {Api}Error) { + switch (err.kind) { + case "api": + // The API answered with an error status β€” read err.status and err.payload. + break; + case "decode": + // The answer did not fit its declared type β€” read err.status and err.cause. + break; + case "encode": + // Nothing was sent β€” err.cause is the SchemaError that rejected the value. + break; + case "connection": + case "timeout": + case "auth": + // No response was produced β€” err.kind says which. + break; + } + } else { + throw err; // outside the family: an abort's reason, a configuration fault, or a bug + } +} +``` + +Every member of the family names the call it raised: `err.method` and `err.uri`, and `err.message` +opens with that pair β€” `"POST https://api.example.com/v1/{resource} failed with 422"`. + +## What to import, and from where + +Everything below comes from **the package root** β€” deep imports do not resolve +(**typescript-client-initialization**). + +| import | what it is | +| --- | --- | +| `{Api}Error` | the whole family β€” a type **and** a value. The type is the union of the six leaves; the value is the abstract class they all extend, so `instanceof` and `err.kind` select the same set. It cannot be constructed or extended. Its real name β€” usually the API name plus `Error` β€” is on the `CoreError as …Error` line in `src/index.ts`; `CoreError` itself is not exported | +| `ResponseError` | the part of the family the server answered on β€” `ApiError \| DecodeError`, the two that carry `status` and `headers`. A type and a value, like `{Api}Error` | +| `ApiError` | `kind: "api"` β€” the API answered with an error status. The base every operation without declared error bodies rejects with, and the class every per-operation error extends | +| `{Resource}.{Operation}Error` | the generated `ApiError` subclass for an operation that declares error bodies, on the **resource class's merged namespace** β€” not a free-standing export | +| `DecodeError` `EncodeError` `ConnectionError` `TimeoutError` `AuthError` | the other five leaves | +| `ConfigurationError` `SchemaError` | outside the family: a constructor fault, and a codec called directly | +| `type ErrorKind` `Declared` `Undeclared` `HttpMethod` | type-only helpers, if you name them in your own signatures | + +## The family β€” every `err.kind` + +`ErrorKind` is closed, so a `switch` over `err.kind` is exhaustive: + +| `err.kind` | Class | What happened | Adds | +| --- | --- | --- | --- | +| `"api"` | `ApiError` | the API answered with an error status | `status` Β· `headers` Β· `payload` | +| `"decode"` | `DecodeError` | the answer could not be turned into the declared value β€” the body was not JSON, failed its schema, arrived where none is declared, or died mid-read after the response line | `status` Β· `headers` | +| `"encode"` | `EncodeError` | a request value did not match its declared type, so **nothing was sent** | β€” | +| `"connection"` | `ConnectionError` | `fetch` rejected before a response line arrived | β€” | +| `"timeout"` | `TimeoutError` | an attempt ran out its `retry.timeout`, and no retry was left | `timeout` β€” the budget that ran out | +| `"auth"` | `AuthError` | a credential could not be **obtained** | β€” | + +Every one carries `kind`, `method`, `uri`, `message` and, where there was an underlying failure, +`cause`. `uri` is the absolute URL the call dialled, with the server variables expanded and the path +parameters filled. It carries no query, fragment or userinfo, so no query parameter reaches it β€” a +credential sent in the query included. One failure names an unresolved URI: a path parameter rejected +by its schema arrives as an `EncodeError` whose `uri` still shows the unfilled `{braces}`. + +⚠ **`err.message` never carries a value from the wire.** It is the call plus what went wrong β€” +`"GET https://… failed with 404"`, `"… failed: Response body could not be decoded."` β€” never a body, +a header or a value that failed its schema. That keeps response bodies and secrets out of logs and +stack traces by default, which is a sound default and a trap: a handler that logs `err.message` and +nothing else records that something failed and discards every diagnostic. Read `err.payload` (or +`err.cause`) explicitly and log the fields you choose. + +`err.headers` is the `fetch` `Headers` object, not a plain object β€” look a header up with +`err.headers.get("x-request-id")` (case-insensitive). Indexing it (`err.headers["x-request-id"]`) is +always `undefined`, and spreading it gives you nothing. + +## Four throwables outside the family + +`instanceof {Api}Error` is `false` on each of these: + +- **`ConfigurationError`** comes out of the **client constructor**, synchronously and before any + call exists: no reachable `fetch`, a basic-auth username containing a colon (RFC 7617 Β§2), or an + unknown `serverEnvironment`. It is not a call failure, so it names no call. Wrap construction too + if it runs on a path that must not crash. +- **`SchemaError`** is what a model's schema value throws when **you** call it directly β€” + `{model}Schema.decode(json)`. Through an operation the same failure arrives one level down, as the + `cause` of a `DecodeError` or `EncodeError` that does name the call. Its `message` names the field + and the type expected, never the value; `cause` carries the issue list. A `serverOptions` override + whose value is not a string raises it from the constructor too. +- **A bug reaches you raw** β€” a `baseUrl` that does not parse, or a non-file value where a `FileInput` + was declared, is a plain `TypeError` naming no call. +- **A caller abort arrives as the signal's own `reason`**, unwrapped β€” whatever you passed to + `abort()`, or the `DOMException` a bare `abort()` supplies. It is yours, not the SDK's, so it is + deliberately not in the family, and never retried. The per-attempt `retry.timeout` is the SDK's own + and **does** stay in it, as a `TimeoutError`. + +`ConfigurationError` and `SchemaError` are exported from the package root; the other two are not the +SDK's to export. + +## `"api"` β€” the API answered + +```ts +class ApiError { + readonly kind: "api"; + readonly status: number; // the HTTP status + readonly headers: Headers; // the response headers + readonly payload: /* the decoded error body, as a discriminated union */; + readonly method: HttpMethod; // the call it names + readonly uri: string; +} +``` + +`status` and `headers` are always there, for any error status, declared or not β€” you never have to +choose between a typed body and knowing the status. Anything outside `200`–`299` is an error status, +so a `3xx` lands here too. + +### Which error does an operation reject with? + +Take the case from the operation's **Error** bullet in `map/operations/{resource}.md`: for a typed +operation its **Error arms** bullet lists every arm with the status it covers and whether it has a +body, and the **Signature** bullet shows the same as `E` in `ApiPromise`. `api-reference.md` +says what an arm *means*; the source is `src/resources/{resource}.ts`. All of these ship inside +`node_modules/{package-name}/` β€” read them there, not in the compiled `dist/`. + +- **Case A β€” typed arms.** The operation has an `{Operation}Error` subclass of `ApiError` on the + **enclosing class's merged namespace** β€” catch `{Resource}.{Operation}Error`, or + `{Api}Client.{Operation}Error` for an operation that sits directly on the client. It redeclares + `payload` with the operation's own arms; narrow on `err.payload.kind`. +- **Case B β€” no typed arms.** `E` is the **base `ApiError`**, its payload is always the + `"undeclared"` arm, and there is no subclass to catch. + +Take the case and the arms from that operation's own page every time, never a neighbour's: a +neighbouring operation's error class compiles cleanly and never matches at runtime. + +### Case A β€” reading the typed payload + +`err.payload` is a discriminated union: one arm per declared failure response, plus `"undeclared"`. +Arm names come from the body's schema, not the status β€” a model reference gives the model's name in +lower camel (`validationError`, then `validationError2` for a second status returning the same model), +anything else gives `error{Status}` (`error400`, `error4XX`) β€” so read them off the map. One arm can +cover many statuses: `4XX` covers `[400, 499]`, so check `err.status` for the exact one. + +**A spec's `default:` response is not an arm.** Every status the other arms miss is `"undeclared"` +with its bytes intact β€” the `520` a CDN, the `502` a gateway or the `403` a WAF answers on the API's +behalf included, whose HTML page the `default` schema never described. The model the spec named under +`default` is still under `src/models/`; decode `rawBody` through its schema by hand when the status is +one the API itself sent. + +```ts +import { {Resource} } from "{package-name}"; + +try { + await client.{resource}.{operation}({ /* ... */ }); +} catch (err) { + if (err instanceof {Resource}.{Operation}Error) { + switch (err.payload.kind) { + case "{arm}": + // err.payload.body is the declared model for this arm (or undefined if the arm declares none) + console.error(err.status, err.payload.body); + break; + // ... KEEP GOING: one case for EVERY arm the map lists β€” do not stop early ... + case "undeclared": + console.error(err.status, new TextDecoder().decode(err.payload.rawBody)); + break; + } + } else { + throw err; // every other failure, for the rest of your ladder + } +} +``` + +**`"undeclared"` is always an arm, and it is not a catch-all.** It carries +`{ kind: "undeclared"; rawBody: ArrayBuffer }` and fires **only** for a status no declared arm covers, +so any operation can hand you raw bytes, and a `catch` handling only `"undeclared"` drops every typed +body. Handle every arm the map lists, and put `"undeclared"` last. An arm can declare **no body** +(`body: undefined`); it accepts only an **empty** body β€” any body on a status it covers (a JSON error +message is typical) rejects with `DecodeError`, even through `.asApiResult()`, and `err.status` still +names it (see **`"decode"`**). + +Where your boundary treats several arms alike, group them with fall-through cases rather than +repeating the body β€” but only over fields they **all** declare: + +```ts +switch (err.payload.kind) { + case "{arm1}": + case "{arm2}": + // narrowed to {arm1} | {arm2} β€” only their common fields are reachable here + report(err.status, err.payload.body); + break; + case "undeclared": + report(err.status, new TextDecoder().decode(err.payload.rawBody)); + break; +} +``` + +### Case B β€” the base `ApiError` + +Nothing to narrow β€” and the obvious test narrows **nothing**. On the base `ApiError` the payload's +`kind` is typed `string`, so `err.payload.kind === "undeclared"` leaves `payload` unnarrowed and +`rawBody` unreachable. Test for the member instead: + +```ts +import { ApiError } from "{package-name}"; + +try { + await client.{resource}.{operation}({ /* ... */ }); +} catch (err) { + if (err instanceof ApiError) { + console.error(err.status, err.headers.get("x-request-id")); + if ("rawBody" in err.payload) { + console.error(new TextDecoder().decode(err.payload.rawBody)); + } + } else { + throw err; + } +} +``` + +`rawBody` is bytes, not text, and a Case B body may not be JSON at all β€” a gateway or proxy can answer +with HTML or plain text. Decode it to a string before you try to parse it, and expect the parse to fail. + +### Matcher precedence + +An exact numeric status wins over a covering range, so a specific `404` arm beats a `4XX` arm +regardless of declaration order. + +A body that does not fit the arm it matched is a **`DecodeError`**. A status no arm covers is the +`"undeclared"` arm, which every subclass ends in; a spec's `default:` response is never a row, so +nothing a middleware answers on the API's behalf can fail a schema the API wrote. + +### Catch order + +`instanceof` narrows from most specific to least, and every generated subclass extends `ApiError`, +which is itself in the family β€” so the subclass test must come first or it is unreachable: + +```ts +try { + await client.{resource}.{operation}({ /* ... */ }); +} catch (err) { + if (err instanceof {Resource}.{Operation}Error) { + // typed arms available on err.payload.kind + } else if (err instanceof ApiError) { + // any other operation's error status β€” payload is "undeclared" + } else if (err instanceof {Api}Error) { + // every other kind: decode, encode, connection, timeout, auth + } else { + throw err; + } +} +``` + +## `"decode"` β€” the answer did not fit + +A `DecodeError` means the API **did** answer β€” it carries `status` and `headers` β€” but the body could +not be turned into the declared value. `cause` says how: a `SchemaError` when the body did not fit its +contract, a `SyntaxError` when it was not JSON, or the platform's read fault when the body died +mid-stream after the response line. A body arriving where the operation declares none has no `cause`. + +Which status it came on matters a lot, and `err.status` tells you: + +- **On a success status** β€” the API answered `2xx` and the SDK could not decode the body. The outcome + is genuinely **unknown**: the call may have taken effect. +- **On a declared error arm** β€” the API rejected the request and only the *detail* was lost. The + status survives on the `DecodeError`, so you still know it was, say, a `422`. + +Mapping both onto one 5xx is wrong half the time β€” it tells a retrying caller to keep retrying +something that can never succeed. Only a **declared** arm can decode-fail: the `"undeclared"` arm reads +raw bytes and never decodes. + +```ts +if (err instanceof DecodeError) { + console.error(err.status, err.message); // the call, and that the body did not decode + console.error(err.cause); // the SchemaError naming the failing field, or the parse fault +} +``` + +## `"encode"` β€” nothing was sent + +An `EncodeError` means a request field failed encoding, so **no request reached the network**. This is +a bug in your code (a wrong type, a bad date format), not an API failure. Never retry it, and do not +let a production ladder quietly absorb it β€” it should fail loudly in development. Its `cause` is the +`SchemaError` that rejected the value, and that error's `message` names the field. + +## `"auth"` β€” a credential could not be obtained + +**`AuthError` is about obtaining a credential, not about being refused one.** A 401 *from the API* is +an `ApiError` like any other status, so one `catch` arm cannot absorb the other. + +A 401 from the API also invalidates the scheme's cached credential, so the **next** call re-acquires +(**typescript-authentication**). A single `401` is therefore not a permanent credential failure: do not +tear down the client or alert on one. + +`AuthError` means the token grant failed, every configured branch of an alternatives requirement +failed, PKCE was disabled without a client secret, or your own authorization-code prompt rejected. Its +`method` and `uri` name **the operation you called**, not the token endpoint. When the grant failed, +**`.cause` is the token endpoint's own `ApiError`** β€” that is where the token endpoint's status and +body are, and the only place a "bad client credentials" diagnosis can be read. Never log an `AuthError` +without its `cause`. + +An **alternatives** requirement falls through: a configured scheme that throws is not the end of it, +the next configured one is tried, and only when all of them have failed does it throw. When exactly +one was configured, its failure surfaces as it would from that scheme alone β€” a refused token endpoint +is still an `AuthError` with the endpoint's `ApiError` on `.cause`, never the bare `ApiError`. +Otherwise it is an `AuthError` whose `cause` is an `AggregateError` holding what each branch threw, in +the order tried. An abort or a timeout escapes immediately rather than being collected. + +⚠ **An `AuthError` means nothing was sent, and it surfaces from an operation call, not from client +construction.** The token is acquired lazily, before the request exists, so an auth misconfiguration +rejects whichever operation ran first β€” reading like a problem with that operation β€” and rejects in +`.asApiResult()` mode too. Check it early in your ladder. + +## A missing field is not an error β€” it is `undefined` + +A model member not marked **required** is `member?: T`, so a response that simply **omits** it +validates cleanly. No `DecodeError` is thrown, and nothing warns you: + +```ts +const value = await client.{resource}.{operation}({ /* ... */ }); // 200, truncated body +value.{member}; // undefined β€” not an error, and not a null you declared +``` + +A `DecodeError` on a success status means a **type** mismatch or an unparseable body, not an absent +field. So the guard belongs on the value, not in the `catch`: + +```ts +const value = await client.{resource}.{operation}({ /* ... */ }); +if (value.{member} === undefined) { + throw new MyProviderUnreadable("{operation} returned no {member}; outcome unknown"); +} +``` + +**Assert on the members you actually depend on, right after every call that matters.** For a write, an +absent identifier has the same "outcome unknown" character as a decode failure β€” the call may have +taken effect and you cannot name what it created. Which members are required is the `Req` column of the +operation's **Fields** table; nothing in the SDK checks the rest for you. The same is true of a **typed +error arm**: an arm whose fields are all optional will accept almost any JSON object, so +`err.payload.kind === "{arm}"` does not guarantee `err.payload.body.{member}` is there. See +**typescript-models**. + +## The non-throwing form + +`.asApiResult()` turns the **HTTP error status** into a value. It does **not** make the call +non-throwing: every other failure still rejects. + +```ts +try { + const outcome = await client.{resource}.{operation}({ /* ... */ }).asApiResult(); + + if (outcome.ok) { + use(outcome.value); + } else { + // outcome.status, outcome.headers, outcome.message, outcome.method, outcome.uri, outcome.payload + } +} catch (err) { + // still needed β€” decode, encode, connection, timeout, auth and a caller abort land here +} +``` + +The split is mechanical: **`200`–`299` is `ok: true`, every other status is `ok: false`.** No nuance, +no per-status judgement. Reach for it when an error status is an expected outcome (a `404` meaning "not +found yet", a `409` meaning "already exists") and you would otherwise be using exceptions for control +flow, or when you need `status` and `headers` on the *success* path. + +The failure branch is a plain object carrying **the error's own members under their own names** β€” +`status`, `headers`, `message`, `method`, `uri` and `payload` β€” never the error instance, so you narrow +on `outcome.payload.kind` exactly as you would on `err.payload.kind`, and a handler moves between the +two forms by swapping the prefix. Call `.asApiResult()` on the value the operation returned, **before +any `.then()`** (**typescript-calling-endpoints**). + +⚠ **Every failure other than an error status still rejects** β€” a `DecodeError` (on a `2xx` body or on +a **declared error arm**), a failed token acquisition and a caller abort included β€” so keep the +`try`/`catch` around `.asApiResult()`. + +## Transport failures, and guarding every call site + +`"api"` covers only the case where the API answered. It does **not** cover a host that is unreachable, +DNS that failed, a connection that dropped, or an attempt's timeout elapsing β€” those are +`"connection"` and `"timeout"`, and a `catch` whose only arm is `err instanceof ApiError` lets them +escape and take down whatever was running the call. + +**Each reaches you after the SDK's retries are spent** β€” as the **last** attempt's failure, in the +shape it would have had with retrying off; do not retry it again (**typescript-configuration-resilience** +owns the policy). + +Whatever your `fetch` rejects with becomes a `ConnectionError` with the original on `.cause`, so your +boundary never imports transport exception types. A family error and a cancellation (your abort +`reason`, or the SDK's `TimeoutError` once an attempt's timeout fired, whatever your `fetch` rejected +with) pass through unwrapped β€” and a plain error your own `fetch` wrapper throws is a `ConnectionError` +the retry loop retries. + +**Convert the family to your own error type in one place.** If you wrap the SDK behind your own +abstraction (a client interface, a service, a repository), do the conversion at that boundary so the +rest of the code has one failure type to handle: + +```ts +class MyUpstreamError extends Error { status: number; reason: string | undefined; constructor(status: number, reason: string | undefined, opts?: ErrorOptions) { super(reason, opts); this.status = status; this.reason = reason; } } + +function translate(err: unknown): never { + // Most specific first β€” the typed subclass, then any other error status. + if (err instanceof {Resource}.{Operation}Error && err.payload.kind === "{arm}") { + throw new MyUpstreamError(err.status, err.payload.body?.{reasonField}, { cause: err }); // one branch per arm the map lists; join a list of reasons + } + if (err instanceof {Api}Error) { + switch (err.kind) { + case "api": { + if (err.status === 429) throw new MyRateLimitedError(err.headers.get("retry-after"), { cause: err }); + // Every arm without its own branch lands here β€” raw bytes, or a declared arm's body. `reasonIn` is YOUR + // helper: parse, take the reason field, join a list, cap the length; nothing usable (an HTML page) β†’ undefined. + const reason = reasonIn("rawBody" in err.payload ? err.payload.rawBody : (err.payload as { body?: unknown }).body); + throw new MyUpstreamError(err.status, reason, { cause: err }); + } + case "timeout": + case "connection": + // Nothing was learned about the request's fate. For a write, that is "unknown", not "failed" β€” + // the bytes may have reached the API before the failure. Tell the caller that, don't say it failed. + throw new MyTransientError({ cause: err }); + case "decode": + // The API answered and the body was unreadable β€” err.status says whether it was a success. + throw new MyProviderUnreadable(err.status, { cause: err }); + case "encode": + case "auth": + throw new MyIntegrationDefect(err.message, { cause: err }); + } + } + throw err; // an abort's reason, a configuration fault, or a bug +} +``` + +Always pass `{ cause: err }` β€” the SDK's own errors chain their underlying failure that way (an +`AuthError` hides the token endpoint's `ApiError` there, a `DecodeError` or `EncodeError` the +`SchemaError` naming the field), and dropping the link is what makes production diagnosis hard. + +**Guard every call site, not just the ones that change data.** It is easy to wrap the calls that create +or modify something and overlook the calls that only read β€” especially reads on a routine path (loading +a screen, a scheduled job, a startup or health check). A connection failure during a read fails just as +hard as one during a write. A call left unguarded next to one that is guarded is the one that breaks. + +## Presenting failures at your boundary β€” coherent, distinct, leak-free + +The catches above decide what you catch; this decides what the caller (an HTTP response, a UI layer, +another service) sees. Get it wrong and every failure looks the same, or an internal diagnostic ends up +on the wire. Three rules, applied at the one boundary where SDK failures become your own error type: + +**Handle each failure kind the same way everywhere.** One mapping from failure kind β†’ outcome, applied +identically at every call site β€” same order, same conversion. When the same kind of failure becomes a +different result on a different operation, callers cannot reason about it. + +**Keep distinct failures distinct.** An `ApiError` always hands you `status`, so that is your +discriminator β€” and where the operation declares arms, `payload.kind` says more than the status does, +because it names the *body* the API sent rather than the class of failure. Carry whichever you keyed on +into your own error type; a status dropped at this boundary cannot be recovered downstream. Collapsing +everything into one blanket status (502 for all of it) throws away the only signal that separates "you +sent something invalid" from "the provider is down". + +One ladder, in the single place where your error type becomes a caller-facing status. This is where the +discriminator you carried gets read back β€” a ladder with **no branch reading it** is incomplete: + +```ts +function toHttp(err: unknown): { status: number; message: string } { + // OUR quota is spent β€” the caller did nothing wrong and cannot fix it. + if (err instanceof MyRateLimitedError) return { status: 503, message: "Temporarily unavailable." }; + + if (err instanceof MyUpstreamError) { + // OUR credentials β€” likewise not the caller's to fix. + if (err.status === 401 || err.status === 403) return { status: 502, message: "Provider unavailable." }; + // The provider rejected THE CALLER'S request β€” hand back the same status and its reason. + if (err.status >= 400 && err.status < 500) return { status: err.status, message: err.reason ?? "Request rejected." }; + return { status: 502, message: "Provider error." }; + } + + // Transport, timeout, provider 5xx β€” no meaningful caller status. + if (err instanceof MyTransientError) return { status: 502, message: "Provider unreachable." }; + return { status: 500, message: "Unexpected error." }; +} +``` + +**Not every provider failure is the caller's fault.** A `401`/`403` means *your* credentials are wrong +and a `429` means *your* quota is spent β€” passing either straight through tells the caller they are +unauthenticated or throttled when they are neither. Those belong in the 5xx bucket; validation, +conflict and not-found are the caller's to fix. And keep the default arm at 5xx: a status you have not +mapped is an unknown, not a caller error β€” the provider can add one without warning you. + +**An unreadable body is not one case but two** (*`"decode"` β€” the answer did not fit*): a `DecodeError` on a **success** +status is genuinely unknown, so 5xx; one on an **error** status is the provider's rejection with the +detail lost, so map it by `err.status`. + +**Never map a decode failure onto a domain absence.** "I could not read the answer" is not "the +provider said no." It is tempting on a lookup β€” an unreadable body and a genuine miss both leave you +without a record β€” but only one of them is a *fact*. Where a lookup gates a create, that conversion +turns a corrupt response into a spurious create; more generally it produces a confident wrong answer, +which is worse than an error. If the operation's miss really is signalled by an empty body, match on +*empty*, not on *unparseable*. + +**Never put an SDK `message` on the wire.** It names the call β€” the method and the URL your service +dialled, path parameters included β€” and that is a diagnostic for your logs, not copy for your caller. +Log the SDK error with its `cause`; return a message you wrote β€” or, for a `4xx` the caller caused, the +API's own reason from `err.payload`. + +## `instanceof` across the ESM/CJS boundary + +`instanceof` is reliable **within one dialect**. A process that loads both β€” `import` in one file, +`require` in another β€” gets **two independent copies** of every error class, and `instanceof` across +that boundary is `false`. It fails silently: a `catch` arm simply never matches. + +If your process might do that, narrow on a value instead β€” every member of the family carries `kind`: + +```ts +// The family, dialect-safe: +if (typeof err === "object" && err !== null && "kind" in err && "uri" in err) { /* err.kind */ } +``` + +`err.name` is also stable across copies (each class sets it from its own constructor name). + +## Notes + +- **The SDK logs a failure only when its logging is on**, which it is not by default + (**typescript-configuration-resilience**) β€” log what your own code decided in your `catch`. +- **A missing credential is not an error**: the call goes out without it and the API answers `401` + (**typescript-authentication**). +- **SDK errors do not survive a worker or process boundary intact.** `structuredClone` and + `postMessage` reduce an `Error` subclass to a plain `Error` β€” `name`, `message` and `cause` survive; + `kind`, `method`, `uri`, `status` and `payload` do not. Convert to your own serializable shape + *before* you post it, not after. + +## Next + +- Timeouts, cancellation, the retry policy β†’ **typescript-configuration-resilience** +- Asserting error paths in tests β†’ **typescript-testing** +- Which arms an operation declares β†’ `map/operations/{resource}.md`, the **Error arms** bullet diff --git a/plugins/authlete/skills/typescript/typescript-event-streams/SKILL.md b/plugins/authlete/skills/typescript/typescript-event-streams/SKILL.md new file mode 100644 index 00000000..385baf1e --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-event-streams/SKILL.md @@ -0,0 +1,144 @@ +--- +name: typescript-event-streams +description: Consuming a server-sent-events (text/event-stream) operation on an APIMatic-generated TypeScript SDK β€” what EventStream yields (each event's decoded data, nothing else), how the loop must end so the connection is released, narrowing a union on the yielded value, what rejects and when (the loop raises the same error family as the call, and there is no SSE error class), and bounding the wait with the client's idleTimeout. Load before writing the loop over an operation that resolves to an event stream, in addition to `typescript-calling-endpoints`. +--- + +# Event streams on an APIMatic TypeScript SDK + +> Throughout this skill, `{...}` is a placeholder for a name you take from your SDK (e.g. `{resource}`, +> `{operation}`, `{Api}Error`) β€” replace it with the concrete identifier from the source. + +An operation whose success response is `text/event-stream` returns `ApiPromise, E>` +rather than a value. The promise resolves as soon as the response **headers** arrive, before anything +is read β€” what is deferred is the body, and the stream holds its connection until it is released. +`T` is the type the spec gives every event's `data`: a string, a model, an array, or a union. + +```ts +const stream = await client.{resource}.{operation}({ /* ... */ }); +for await (const event of stream) { + event; // T β€” one event's data, already decoded +} +``` + +Iterating yields `T` itself, each event's decoded `data` and nothing else: the `event:` name and +`id:` lines of the wire format are not surfaced, so there is no envelope to unwrap and no `kind` +beyond what `T` declares. Nothing is read ahead of your loop, so a slow loop body slows delivery +rather than buffering events in memory. Where the operation declares a terminating event (the map's +**Returns** bullet names it, such as a `[DONE]` data line), the SDK consumes it, closes the stream and +never yields it; a body that closes before that line arrived is a clean end too. + +## Start here + +**Drain the loop.** Reaching the end releases the connection, so the shortest correct code is a plain +`for await`: + +```ts +for await (const event of await client.{resource}.{operation}({ /* ... */ })) { + handle(event); +} +``` + +**Leaving the loop early releases it too** β€” `break`, `return`, or a `throw` inside the body all +close the stream before control leaves. A stream you decide not to read at all is released with +`await stream.close()`, and where your runtime declares `Symbol.asyncDispose`, `await using` does +the same at block exit: + +```ts +await using stream = await client.{resource}.{operation}({ /* ... */ }); +for await (const event of stream) { + if (done(event)) break; // the stream is released either way +} +``` + +## How the iteration ends + +| You want | Write | Releases | +|---|---|---| +| the whole stream | a plain `for await` | at the end of the stream | +| to stop at a condition | `break` / `return` inside the loop | as the loop exits | +| a stream you will not read | `await stream.close()` | your `close()` | +| release at block exit | `await using stream = …` | the block | +| to stop from elsewhere | abort the `signal` you passed | the SDK cancels its own reader | + +`close()` is idempotent, and a pending `next()` ends with `done` rather than an error. A stream is +**single-use**: a second `for await` over the same `EventStream` throws a `TypeError`, exactly as a +second read of a web `ReadableStream` does. `stream.toReadableStream()` hands you the same values as a +web `ReadableStream` and takes the stream's one iteration. + +## What the loop yields + +| The success schema is | each `event` is | Narrow with | +|---|---|---| +| `type: string` | `string` β€” the event's text, verbatim | nothing β€” it is the value | +| an object schema | `{Model}` | nothing β€” it is the model | +| an array schema | `{Model}[]` β€” one batch per event, no wrapper model | nothing | +| a `oneOf` with a discriminator | `{Union}` | `switch (event.kind)` | + +Every event decodes through the **one** schema the spec declares for the stream, whatever name the +server gave it, so a union is narrowed on its own discriminant. An error the API reports *inside* the +stream is an event like any other: the spec declares its shape as an arm of the union, the loop +yields it, and the stream goes on unless you `break`. + +## What can go wrong + +| What failed | Direct call | `.asApiResult()` | +|---|---|---| +| a non-2xx status (the headers) | rejects at the call with `ApiError` (or the operation's typed subclass) | resolves `{ ok: false }` β€” no rejection | +| a 2xx that is not `text/event-stream` | nothing checks the media type: the call resolves and the body is read as event frames, so a JSON body usually ends the loop cleanly with no event | same | +| an event whose `data` does not fit the schema | the loop rejects with `DecodeError`, stream released | same | +| the connection dies mid-read | the loop rejects with `ConnectionError` | same | +| the body closes before the declared sentinel arrived | the loop ends cleanly; every event the server sent was delivered | same | +| the server sends nothing for `idleTimeout` | the loop rejects with `TimeoutError`, `timeout` = the idle bound | same | +| you abort the `signal` | the pending `next()` rejects with the signal's own `reason` β€” **not** an `{Api}Error` | same | +| a second iteration of one stream | `TypeError` at the `for await`, a bug | same | + +**There is no SSE error class.** Everything the loop raises is a leaf of the one `{Api}Error` family, +the same `DecodeError`, `ConnectionError` and `TimeoutError` the call itself can raise, narrowed on +`err.kind` exactly as `typescript-error-handling` describes. Put that boundary around the **loop**, not +only the call β€” the call settles as soon as the headers arrive: + +```ts +try { + const stream = await client.{resource}.{operation}({ /* ... */ }); + for await (const event of stream) { + handle(event); + } +} catch (err) { + if (err instanceof {Api}Error) { + // err.kind is "api" before the stream existed, and "decode", "connection" or "timeout" from the loop + } else { + throw err; // a caller abort or a bug β€” not ours + } +} +``` + +## Resuming + +The SDK does not read the `id:` lines and the stream carries no resume point. Where the spec declares +a `Last-Event-ID` header, the operation's request type carries it as an optional field the SDK sends +verbatim; what to put in it is yours to track, from the data the events carry, because no event id +reaches your loop. + +## Bounding the wait + +The client's `retry.timeout` stops at the response headers; an event stream has its own clock. +`ClientOptions.eventStream.idleTimeout` (default `60_000` ms) is how long the server may stay silent +while your loop waits for the next event β€” any bytes reset it, a heartbeat comment included, and it +runs only while an iteration is waiting, so time your loop body spends on an event never counts. +`null` turns it off; `0` is a zero-length window. It is resolved once, as the client is built, and no +call overrides it. For a total budget, pass a `signal` and abort it: the SDK cancels its own reader, +so the stream stops whatever your `fetch` does with the body. + +## The non-throwing form + +`.asApiResult()` resolves to `{ ok: true, status, headers, value }` with `value` the `EventStream` +β€” the only way to read the opening status and headers of a stream β€” or `{ ok: false, … }` for an +error status. The loop over `result.value` still rejects for the failures in the table above. + +## Testing an event stream + +Fake `fetch` (`typescript-testing`) and hand the SDK a `Response` whose body is a `ReadableStream` of +event frames under `content-type: text/event-stream` β€” `data: {...}\n\n`, one blank line ending each +event. The SDK reads the body in the transport's own chunks, so split a frame across two `enqueue` +calls to prove the loop does not depend on where the network cuts, and record what the body's +`cancel` receives to prove that `break` released the connection. diff --git a/plugins/authlete/skills/typescript/typescript-getting-started/SKILL.md b/plugins/authlete/skills/typescript/typescript-getting-started/SKILL.md new file mode 100644 index 00000000..569bab98 --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-getting-started/SKILL.md @@ -0,0 +1,192 @@ +--- +name: "typescript-getting-started" +description: "Load before answering any Authlete API TypeScript SDK question or writing SDK code β€” the lookup layer for `authlete-api`: install, imports, environments, auth, and how to read the SDK map that ships in the package (`sdk-map.md`, `map/operations/`). TypeScript/JavaScript only." +--- + +# Getting started with the Authlete API TypeScript SDK + +> **The lookup layer** for Authlete API TypeScript SDK code: ground every contract fact here β€” in the SDK map and the source files it names β€” never in recall, and carry those facts onto a contract sheet before you implement. `typescript-integrate-authlete-api` holds the workflow around it. + +This page is authoritative for the SDK's identity and surface. The API-agnostic companion skills β€” `typescript-client-initialization`, `typescript-authentication`, `typescript-calling-endpoints`, `typescript-models`, `typescript-error-handling`, `typescript-configuration-resilience` and `typescript-testing` β€” are the *usage layer* on top: the way to call each piece and the gotchas a signature cannot show. **Load both:** at each **Integration workflow** step, load the companion it names even after reading the file it covers, and confirm names against the installed package. + +## SDK identity + +Verified against `package.json` and `sdk-map.md` of the package at version `3.0.16`. **Re-verify after a version bump** β€” this page is a snapshot, not a live read. + +| Fact | Value | +| --- | --- | +| API | Authlete API | +| Package name (what you install, and what you import) | `authlete-api` β€” **not on npm**; it ships in this plugin (see *Install*) | +| SDK source | `sdk/typescript/` inside this plugin β€” nothing to clone | +| Version | `3.0.16` (API spec version `3.0.16`) | +| Client class | `AuthleteApiClient` (`src/client.ts`) | +| Options type | `ClientOptions` (`src/client-options.ts`) | +| Client construction | `new AuthleteApiClient(options: ClientOptions = {})` β€” the argument is optional, as is **every** field on it, so `new AuthleteApiClient()` compiles. Fields: `serverEnvironment` Β· `serverOptions` Β· `retry` Β· `fetch` Β· `bearer`. `retry` is the `RetryOptions` policy | +| Auth | **Bearer token** β€” set `ClientOptions.bearer` | +| Environments | 5 environments (`ServerEnvironment.Production` *(default)*, `ServerEnvironment.Environment2`, `ServerEnvironment.Environment3`, `ServerEnvironment.Environment4`, `ServerEnvironment.Environment5`) Γ— 1 server group | +| Base-URL config | `serverOptions.baseUrl` (`src/servers.ts`), defaulting to `https://us.authlete.com` | +| Log environment variable | `AUTHLETE_API_CLIENT_LOG` | +| Node floor | `>=20.3` (`engines.node`) | +| Runtime dependency | `zod` (`^4.1.0`) β€” the only one | +| Module format | dual ESM + CommonJS folder dialects (`dist/esm`, `dist/commonjs`) behind one export | +| Typing | the package ships its own `.d.ts` β€” **a type error against this SDK is a real contract violation, not noise** | +| Surface | 93 operations Β· 23 resources Β· 147 models Β· 71 open enums Β· 92 per-operation error subclasses | + +## Install + +Install the package from the copy that ships inside this plugin, at `sdk/typescript/` relative to the plugin root (the directory holding `skills/`), as it is not published to npm: + +```bash +npm --prefix /sdk/typescript/ install +npm --prefix /sdk/typescript/ run build +npm install /sdk/typescript/ +``` + +**That dependency is local to this machine.** `npm install ` records the package in `package.json` and the lockfile as a `file:` path from your project into this plugin's install directory, and links it in `node_modules` to that directory, so the project installs only where this plugin is installed at that path β€” a teammate needs the plugin installed and all three commands run against their copy, which ships unbuilt and without its dependencies, and a CI runner without the plugin cannot install it. Say so when you hand the work over. A plugin reinstalled or updated somewhere else replaces that directory, so run all three commands again against the new copy. + +The installed package carries everything its `files` list packs β€” `src/`, `sdk-map.md` and the pages under `map/operations/` β€” so **every lookup on this page works as soon as the install finishes**. The copy ships as source, not pre-built, which is why the commands build it where it sits before installing it. + +Do not vendor its `src/` into your project, point `tsconfig` `paths` at a throwaway clone, or import from `dist/` directly: the `exports` map, the shipped `.d.ts` chain, the dual-dialect resolution and the SDK map in `node_modules` all depend on a real install. + +## Imports β€” one entry, and only one + +**Every** public name is re-exported from the package root β€” the client, `ClientOptions`, `ServerEnvironment`, 218 model types with the schema value beside each, the error classes, and the runtime types (`ApiPromise`, `ApiResult`, `RequestOptions`, `RetryOptions`, `RequestRetryOptions`, `Declared`, `Undeclared`, `Schema`, `EnumSchema`, `Encoded`). + +```ts +import { AuthleteApiClient, ServerEnvironment, ApiError, AuthleteApiError } from "authlete-api"; +import type { ClientOptions, Action } from "authlete-api"; +``` + +Things the specifier alone will not tell you: + +- **Deep imports do not resolve.** The `exports` map exposes `.` and `./package.json` and nothing else, so `authlete-api/models/…` fails (`TS2307`) even though the file exists in the shipped `src/`. Every `Source` path on the SDK map is where to **read** a shape, never what to import. +- **From CommonJS**, the typed spelling is `import sdk = require("authlete-api")`. A plain `require` destructure runs but yields `any`. A process that loads both dialects gets two copies of every error class, so `instanceof` fails across them (`typescript-error-handling`). + +Under `verbatimModuleSyntax`, names carrying no runtime value (`ClientOptions`, every model type) must be imported with `import type`. Under `exactOptionalPropertyTypes`, **omit or spread** an absent optional field rather than assigning `undefined` to it. + +## Environments + +`ClientOptions.serverEnvironment` selects one environment for the whole client (`src/servers.ts`). `ServerEnvironment` is a `const` object with a derived union type β€” not a TypeScript `enum` β€” and unlike the model enums it is **closed**, so only its declared members are assignable. The base URL of every server group in every environment, with its template variables, is in `sdk-map.md` Β§ *Servers & auth*; override one at `serverOptions.baseUrl`. + +Consequences to state on every contract sheet that touches configuration: + +- Constructing the client with no options selects **`ServerEnvironment.Production`**, silently. +- An override merges with the built-in default **per group-and-environment pair, key by key**; a `baseUrl` override replaces the whole template. +- Each operation belongs to one server group; a map block carries a **Server** bullet only when its group is not `default`. +- An environment value the SDK does not know throws `ConfigurationError` **from the constructor**. + +## Auth pattern (1 scheme) + +Authentication is **per operation**: every operation declares the requirement it enforces and the SDK sends exactly that. Each block on a map page carries an **Auth** bullet, `none` included. There is no client-global switch and no per-call override. 91 of the 93 operations require a credential and 2 are public. + +| `ClientOptions` field | Scheme kind | What the SDK sends | +| --- | --- | --- | +| `bearer` | Bearer token | `Authorization: Bearer ` | + +```ts +const client = new AuthleteApiClient({ + bearer: process.env.API_TOKEN!, +}); +``` + +**Every credential field is optional.** Omit one and the request goes out without it, with no error at construction; the server answers `401`, or `200` if it serves anonymous traffic. On an unexpected `401`, check the operation's **Auth** bullet against the fields you set. + +Credential functions, scheme composition, 401 invalidation: `sdk-map.md` Β§ *Servers & auth* and `typescript-authentication`. + +## Resources + +Resources are **memoized lazy getters** on the client (`client.`); reach one only through its getter. Its class is exported for its merged namespace β€” the per-operation request and error types β€” and for `instanceof`, not for construction. + +23 resources: `sdk-map.md` Β§ *Operations β€” by resource* names every operation, with the page that documents it. + +Every operation has the same call shape β€” `op(request, options?)`, one **flat, channel-blind** request object first and `RequestOptions` (`{ signal, retry, logging }`) second β€” and returns `ApiPromise`. + +## SDK map β€” look up first, open the file second + +The map sits at the package root, the directory holding `package.json` and the `src/` tree: + +- **`sdk-map.md`** β€” the index: client construction with the full `ClientOptions` table, the *Not on this SDK* table, the one error family with `ApiResult` and `.asApiResult()`, wire serialization for every channel, servers and auth, runtime and packaging, and the link table into the operations pages. +- **`map/operations/.md`** β€” one page per resource, one `###` block per operation, with bullets in the fixed order **Server**, **Signature**, **Wire** (verb and route), **Auth**, **Request body**, **SDK-sent**, **Returns**, **Error**, **Error arms** β€” then a **Fields** table giving every request field its channel, wire name, type, required flag and default, and a **Type sources** table naming the declaring file and schema value of every type the operation mentions. + +Locate the installed package before you rely on a lookup: + +```bash +node -e "console.log(require.resolve('authlete-api/package.json'))" +``` + +Failing that it is at `node_modules/authlete-api/`. **If the resolve fails, or the directory it prints carries no `sdk-map.md`, the map is still in the SDK source this plugin ships** β€” at `sdk/typescript/`. That is the directory you installed from. Read it there, and if neither route yields a map, mark the fact `UNVERIFIED` and say what would settle it rather than answering from memory. + +Every `Source` path on the map is relative to that package root (`node_modules/authlete-api/`). An import specifier ending `.js` inside that source is the NodeNext spelling of the sibling `.ts` file. + +**Read `sdk-map.md` before any `map/operations/` page.** It states what holds for every operation, and **silence means the default**: a block departs from it only by saying so, so take the default rather than opening the source to confirm it. + +**The map carries shapes; what an operation *means* lives elsewhere.** When *what* to pass depends on meaning β€” which values a field accepts beyond its type, a rule that couples two fields, what a defaulted header actually selects β€” the map will not settle it. Read that operation's entry in `api-reference.md` at the package root, keyed by the same signature, *before* writing the sheet row, and record what you found. A value you already "know" for a field the map types as a plain `string` is a lookup, not a recall β€” the memory ban applies to it. + +## Contract facts β€” the map first, then the source file + +**The map is the locator; the source files are the shapes.** These are map lookups β€” don't open a source file for them: an operation's signature and route; its request fields with channel, wire name, required flag and default; its return type; its error subclass and the arms with the status each covers; the `ClientOptions` fields and their defaults; the environments, base URLs and auth wiring; and which file declares a type. + +Open the one file the map's **Type sources** table names β€” never derive the path from the type name β€” for what the map does not carry: a model's members and whether each is required, optional or nullable, and an enum's members with their wire values β€” the `as const` object in the enum's own file (for an enum inside a model, the file the model imports it from). The table below covers that, everything else, and the full body behind a map row. Paths are relative to `node_modules/authlete-api/`: + +| Question | File | +| --- | --- | +| A model's members, required (`f: T`) vs optional (`f?: T`) vs required-nullable (`f: T \| null`) | `src/models/.ts` | +| The operation method body and the request it builds | `src/resources/.ts` | +| The per-operation request and error types (merged namespace) | the `export namespace ` block at the foot of the same file | +| Client construction, resource getters | `src/client.ts` | +| `ClientOptions` fields | `src/client-options.ts` | +| Environments, base URLs, override merging | `src/servers.ts` | +| Auth scheme wiring, token endpoint, credential placement | `src/auth-schemes.ts`, `src/core/auth/credentials.ts`, `src/core/auth/oauth2-strategies.ts` | +| The transport: the attempt loop, `fetch` resolution, 401 invalidation, 2xx-vs-error split | `src/core/raw-client.ts` | +| Error classes and `ErrorKind` | `src/core/errors.ts`, `src/core/api-error.ts` | +| `ApiPromise`, `ApiResult`, `.asApiResult()`, the `Symbol.species` behaviour | `src/core/api-promise.ts` | +| `RequestOptions` (it is `{ signal, retry, logging }` and nothing else) | `src/core/api-request.ts` | +| `RetryOptions` and its defaults, `RequestRetryOptions`, backoff, `Retry-After`, `RetryAttempt` | `src/core/retry.ts` | +| `LoggingOptions` and its defaults (`defaultLoggingOptions`), `RequestLoggingOptions`, `Logger`, `LogDetails` | `src/core/logging.ts` | +| Schema decode/encode, `SchemaError`, `Encoded` | `src/core/validation/schema-error.ts` and its directory | +| Wire serialization per channel | `src/core/param-value.ts`, `src/core/url.ts`, `src/core/headers.ts`, `src/core/params.ts` | +| What an operation *means* β€” field semantics, coupling rules | `api-reference.md` at the package root | + +**Read scoped.** Search for the one symbol and read the lines around it rather than whole files, and never copy a design comment's rationale onto a contract sheet β€” the sheet carries facts an implementer must obey, not the reasoning behind them. + +Keep lookups cheap β€” the rules that keep a session's context small: + +- Collect the contracts for **every** in-scope operation in **one** pass β€” signature, request fields with channels and defaults, required members, the error arms, enum values β€” into a short **contract sheet** in your plan, then implement from the sheet. Don't re-open a map page per field, and never re-look-up a fact the sheet already carries. +- Recurse into a model's members only where the task actually sets them β€” a full transitive expansion is hundreds of rows nobody needs. +- **Never grep, glob or `find` the package to *locate* a type** β€” the map is the locator, and it says so. Grep only *inside* the file its **Type sources** table names, for the symbol. A sweep for a cross-cutting *shape* is a different question and is fine: "every field typed `unknown`", "every required-nullable member" are things nothing indexes, and one targeted `grep -rn` over `src/models/` is the right tool β€” record what it found on the sheet. +- Trust the compiler over this page: if a name here ever fails to type-check, re-read the file the table above names and report the drift; never patch around it from memory. + +## Sensitive data β€” check the request models before you log anything + +Whether this API's **request** models carry fields you must never log β€” card or bank numbers, personal data, message content β€” is a shape question: each request model's declaring file (the map's **Type sources** table names it) lists every field. If the scope touches such a field: `logRequestBody` stays off, because it logs a JSON request body **verbatim** and masks a form body only by deny-list; no call on those paths passes a per-call `RequestOptions.logging` level, which turns the request body on for that call whatever the client set; `ClientOptions.logging.level` is set explicitly in production, because without a `logger` or a `level` the `AUTHLETE_API_CLIENT_LOG` environment variable turns logging and both header switches on with no code change; and your own diagnostics never echo a request body on those paths (`typescript-configuration-resilience` has the details). + +The SDK never logs a response body, but your own code may. Responses are often safer β€” many APIs return masked variants of what requests carry raw β€” but that too is a model-shape fact: check the response model's file before assuming. + +## Integration workflow β€” load the companion skill at each step + +Before you write the code for each step, load the named companion skill β€” even if you have already read the relevant file. A typical integration reaches them in this order: + +1. **Client construction & lifetime** β€” load **typescript-client-initialization** before you write `new AuthleteApiClient(…)`. The client is **long-lived and app-scoped**, never rebuilt per request, because the resource getters live on it. +2. **Authentication** β€” load **typescript-authentication** before you set credentials. The scheme is `bearer` on `ClientOptions`. Load secrets from the environment or a secret store, never hardcode. +3. **Calling an endpoint** β€” load **typescript-calling-endpoints** before the first `client..(…)` call. +4. **Models** β€” load **typescript-models** the moment a request/response member is not a plain string or number. +5. **Error handling** β€” load **typescript-error-handling** before you write any `try/catch`. Every operational failure is **one family**, `AuthleteApiError`, narrowed on `err.kind`. +6. **Configuration & resilience** β€” load **typescript-configuration-resilience** when you set the base URL, timeouts, retries, proxies, TLS, or logging. +7. **Testing** β€” load **typescript-testing** before you stub the SDK. The seam is **`ClientOptions.fetch`**. + +## What a contract sheet must carry for this SDK + +Beyond the usual signatures and model members, a contract sheet for the Authlete API TypeScript SDK is incomplete without these, because each one is a decision the implementer cannot make correctly from the signature alone. + +1. **Which host each deployment talks to**, and where that is set. The members are `ServerEnvironment.Production`, `ServerEnvironment.Environment2`, `ServerEnvironment.Environment3`, `ServerEnvironment.Environment4`, `ServerEnvironment.Environment5`, defaulting to `ServerEnvironment.Production` when the field is unset. +2. **6 operations resolve to `undefined` (`serviceManagement.serviceDeleteApi`, `serviceManagement.serviceRemoveIdpApi`, `clientManagement.clientDeleteApi`, `clientManagement.clientExtensionRequestablesScopesDeleteApi`, `tokenOperations.authTokenDeleteApi`, `utilityEndpoints.miscEchoApi`)** β€” `await` gives you nothing to inspect, so **`.asApiResult()` is the only way to observe their status and headers** β€” decide the mode at write time, not by retrofit. +3. **The exact request type name per operation**, taken from the **Signature** bullet. +4. **Every request field with its channel, wire name and default**, because the request object is flat and channel-blind and the SDK fans fields out. An omitted field that has a default is still sent with that default, so a defaulted header shapes the response whether or not the sheet mentions it. Every non-GET operation sends an `Idempotency-Key` header, minted once per call and invisible to you, unless it takes that header as a request field β€” `typescript-configuration-resilience` has what that key does and does not deduplicate. Any caller-supplied idempotency or request-id field is the only idempotency that spans calls, and `RequestOptions` is `{ signal, retry, logging }` only. +5. **Required vs optional vs required-nullable** for every model member the task sets β€” `f: T` required, `f?: T` omit the key, `f: T | null` required and nullable. +6. **The error arms for each operation in scope, with the status each covers β€” and the warning that arm tags are schema-derived, not status codes.** Every operation rejects with its own `ApiError` subclass narrowed on `err.payload.kind`. A tag comes from the arm's body schema and is resolved per operation, so the same tag means different statuses on different operations, and a shared helper that switches on `kind` across operations is a bug β€” `typescript-error-handling` has the naming rule, the always-present `"undeclared"` arm and the matcher precedence. 92 of 93 operations declare typed error bodies; the rest reject with the base `ApiError`. +7. **That a malformed or drifted 2xx body rejects with `DecodeError`, not `ApiError`, in both response modes** β€” and the members the implementer has to assert on, because a thin or truncated body decodes without complaint. +8. **The retry policy this integration needs.** Retrying is on by default β€” `ClientOptions.retry` sets it for the client and `RequestOptions.retry` overrides it per call β€” so the sheet says whether the defaults fit, which writes (if any) join `httpMethodsToRetry`, since a write is repeated only when the policy names its method, and the per-attempt timeout. +9. **The logging this integration needs.** Logging is off by default β€” `ClientOptions.logging` sets it for the client and `RequestOptions.logging` sets one call's level β€” so the sheet says whether it stays off and, where it is on, the level, the logger, and whether headers and the request body are logged (**Sensitive data**, above). +10. **That the SDK performs no pagination and no streaming at all**, and that `ClientOptions.fetch` is the one seam where either can be added β€” so whatever the task needs there is yours to build or deliberately omit. Say which. +11. A **REQUIRED READING** block naming the `typescript-*` companions that govern the steps, with inline `MUST load` pointers. + diff --git a/plugins/authlete/skills/typescript/typescript-integrate-authlete-api/SKILL.md b/plugins/authlete/skills/typescript/typescript-integrate-authlete-api/SKILL.md new file mode 100644 index 00000000..fd398b89 --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-integrate-authlete-api/SKILL.md @@ -0,0 +1,150 @@ +--- +name: "typescript-integrate-authlete-api" +description: "MANDATORY FIRST STEP for Authlete API TypeScript SDK (`authlete-api`) work in a TypeScript or JavaScript project: load before writing any code β€” integrating Authlete API (service configuration api, service create api, service create idp api), or when the SDK errors, fails to compile or behaves unexpectedly. Sets the plan-first workflow, the contract sheet and which companion skills to load. TypeScript/JavaScript only; never load it for another language." +--- + +# Authlete API TypeScript SDK β€” Integration workflow (lookup layer + contract sheet) + +## Your lookup layer + +- **`typescript-getting-started`** is the entry point for every SDK need: identity, install, imports, environments, auth, resources, and a **file table** naming the one file that owns each kind of fact. Load it first, always. +- **The SDK map is your lookup surface.** The SDK ships `sdk-map.md` and `map/operations/` at its package root β€” and because `package.json`'s `files` list includes them, **installing the package puts the map in `node_modules/authlete-api/`**. `typescript-getting-started` tells you how to traverse it: read `sdk-map.md` once, then the one resource page your operations live on. +- **The installed package is the ground truth; the file table is only the locator.** Confirm it resolves before you rely on a lookup β€” `node -e "console.log(require.resolve('authlete-api/package.json'))"`. **If the resolve fails, or the directory it prints carries no `sdk-map.md`, the map is still in the SDK source this plugin ships** β€” at `sdk/typescript/`. Read it there, and only if neither route yields a map mark the fact `UNVERIFIED` and say what would settle it. This SDK is **not published to a registry**; it ships inside this plugin, at `sdk/typescript/`, and installs from there. `typescript-getting-started`'s *Install* section carries what to run. +- **Write a contract sheet with no open lookups** before you implement: exact signatures with the right request type name, every request field with its channel, wire name and default, required-vs-optional-vs-nullable members, the error arms per operation with the status each covers, and enum members for the operations in scope. `typescript-getting-started` ends with a checklist of what a sheet for this SDK is incomplete without β€” treat that as the checklist for your own sheet, and collect every in-scope operation in ONE pass rather than re-opening a map page per field. + +## Workflow + +**If the user opens with a reported SDK error, a compile failure, or unexpected Authlete API behaviour** (not new feature work), skip the plan-first flow: load `typescript-getting-started` and `typescript-error-handling`, look the failing symbol up on the map page or in the file the table names, and fix from what you find. Otherwise, for implementation work: + +### Step 1 β€” Plan first (always, for any implementation work) + +Your FIRST action is to load `typescript-getting-started` and work through the user's full request (all features in scope β€” one plan covers the whole implementation). Load `typescript-error-handling` before you write the plan too: table 1b.4 is decided from it. Then write the plan and its contract sheet to `/authlete-api-plan.md` β€” that absolute path, not a location you pick later. + +**Do the read-only prerequisites in the same pass β€” they need no SDK knowledge and touch no project file:** + +- the repo survey (read-only exploration of conventions, layering, **module format β€” ESM or CJS, which changes how the SDK is imported and whether `instanceof` on its error classes is trustworthy**, and the runtime: Node, a bundled browser app, or an edge runtime) β€” capture each convention as *pattern + the ONE exemplar file path to imitate*, NOT inline code snippets: you will read the exemplar at edit time anyway, so a snippet dump gets paid for twice; +- **establish the toolchain before you need it**: the package manager, whether `authlete-api` is already a dependency and at what version, the `tsconfig` flags that change what compiles here (`exactOptionalPropertyTypes`, `verbatimModuleSyntax`, `strict`), and the exact commands that run the project's build, type check, lint and tests. Getting this wrong later costs a broken install mid-implementation β€” and an uninstalled package costs you the lookup layer itself, since the map ships inside it; +- a baseline run of the project's checks (`tsc --noEmit`, the test runner, the linter β€” whatever it has) on the UNTOUCHED tree, so later failures are attributable to your changes; +- credentials/environment verification (per the task's secret-handling rules) β€” **including which host this deployment will actually talk to**, since constructing the client with no options silently selects `ServerEnvironment.Production`, and the other 4 environments are reached only by setting it; +- **a read-only smoke of each in-scope operation against the real credential**, run from your scratchpad, not the project, once the map has given you the signatures and before you finalise the sheet. A plan-, region- or entitlement-gated endpoint answers `403` here β€” the map presents every operation uniformly and cannot know which ones your credential may call β€” and learning that now reorders the plan rather than the build. Skip only operations whose side effects you cannot reverse; +- setting up your task tracking. + +**HARD GATE β€” no project-file creation or edits until:** `authlete-api-plan.md` EXISTS at the repo root, its contract sheet has **no open lookups**, and you have read it. **Creating or editing ANY project file before then is a defect**, however obvious the code seems; the read-only prerequisite work above is allowed meanwhile. + +**What the gate does not cover:** installing `authlete-api` into the project, installing the toolchain, and the files those writes produce (`node_modules/`, a lockfile the installer updates) are **prerequisites, not project-file edits** β€” the map lives inside the installed package β€” and they are the only writes permitted before the gate. + +### Step 1b β€” The tables the contract sheet must carry (fill these in before you write any code) + +The per-operation rows describe **what each operation is**. The tables below carry what those rows cannot: a write that may reach the provider twice or whose outcome you may not learn, a rule that spans two operations, and a field nobody asked for. Fill in every one that applies. + +#### 1b.1 β€” DURABLE WRITES + +One row in each table below per operation in scope that creates, sends or changes something on the provider's side; `none` if the scope has no such write. Every row follows one order: **claim β†’ SDK call β†’ record the result**. The claim is a row of its own, committed to a store this application owns *before* the call β€” not a field filled in afterwards on the record the write is about β€” so a second caller is stopped before it reaches the provider, and a lost answer leaves something to settle. A test that reads the store from inside the faked `fetch` shows the claim was there when the request left. + +**DUPLICATE CLAIMS** β€” what stops the same operation reaching the provider twice. + +| Write | Where the claim is stored | What rejects the second one | Where that rejection is caught | Where in the code | +| --- | --- | --- | --- | --- | + +⚠⚠ **What rejects the second one**: the store refusing the second claim β€” a unique constraint or a conditional write on a value every caller doing the same operation computes the same (the order being paid, the signup being welcomed), never on a value minted per attempt, which is unique by construction and refuses nothing. A read before the write, an in-process lock, or an assumption that one process runs lets both callers through. Release the claim when the provider refuses the call, so a later attempt is not blocked. + +**Where that rejection is caught**: the `catch` for what the store raises on the second claim, recognised by the driver's error code rather than its message; the losing caller then uses the record the winner wrote. + +The SDK's own `Idempotency-Key` is new on every call (*Making a write safe under retries* in `typescript-configuration-resilience`), so it goes beside the claim, never in place of it; where the operation declares its own key, send the reference stored on the claim. + +**UNKNOWN OUTCOMES** β€” what settles a write the provider may have acted on without your code learning the answer. + +| Write | The operation you re-read with | The reference you search by | Where in the code | The test that fails the connection | +| --- | --- | --- | --- | --- | + +⚠⚠ A `ConnectionError` or `TimeoutError` on a write is *unknown*, not *failed*: the provider may already have acted. Reporting a failure is not a legal entry, and neither is leaving the caller to check or retry. Settle it in that write's own `catch`: re-read by the reference you sent β€” found, settle from what the provider holds; not found, or the re-read itself fails, still unknown, never failed. Telling the caller the outcome is unknown comes after the re-read, never instead of it. If the provider offers no way to find the write, that is a **Blocker** in Assumptions & Blockers, not a row. + +A write that never left β€” a credential that could not be obtained (`AuthError`) or a value that would not encode (`EncodeError`) β€” has nothing to re-read and gets its own outcome. **The test that fails the connection** asserts the re-read request was made; a second test, a never-sent failure on the same write, asserts it was not. + +Write `TBD` in **Where in the code** and **The test that fails the connection** while you plan β€” every row. Once the code compiles, replace each `TBD` individually: the claim write, then the SDK call it precedes; the `catch`, then the re-read it makes. ⚠⚠ A `TBD` left behind, a name that is not in the code, or one note covering several rows is **not addressed**; if the code does not do what the row says, fix the code, then the cell. + +#### 1b.2 β€” CROSS-OPERATION INVARIANTS + +**CROSS-OPERATION INVARIANTS** β€” copy this header into the contract sheet and add one row per value a caller supplies (an identifier, a code, a reference) whose legal set something other than that call defines: a list the provider returns, or records this application keeps. `none` if the task restricts no such value. ⚠⚠ The map never states these rules; derive them from the task. + +| Invariant | Where the legal set comes from | Enforced where | The test that proves it | +| --- | --- | --- | --- | + +⚠⚠ **The legal set is the one this task offers, not everything the account holds.** Asking the provider whether a value exists is not this check: the provider already rejects what exists nowhere, so a lookup of one record by its id enforces nothing new β€” internal records, test records and records meant for something else all exist, and all pass it. Check membership of the offered set itself, read in full (every page). If neither the provider nor this application can name that set, that is a **Blocker** in Assumptions & Blockers, not a row. + +The check runs before the write is attempted and before its **DUPLICATE CLAIMS** claim is inserted β€” a value rejected after the claim exists leaves the claim behind to block a later, legitimate attempt. **The test that proves it** asserts a value outside the set is rejected **and** the write is never called. + +#### 1b.3 β€” OPTIONAL REQUEST FIELDS + +For every write operation in scope, the contract sheet carries an **OPTIONAL FIELDS** table: one row per optional field on the request, and what **requires** it. + +| Field | What requires it | Set it? | +| --- | --- | --- | +| `{optionalField}` | nothing | ❌ leave it out | +| `{optionalField}` | the words in the task that supply it | βœ… set it from those words | +| `{optionalField}` | a named row of this sheet β€” e.g. the idempotency key a DUPLICATE CLAIMS row sends, or the reference an UNKNOWN OUTCOMES row searches by | βœ… set it, and name that row here | + +⚠⚠ **A field nothing requires is not yours to choose** β€” and "something requires it" means the task's own words or a row of this sheet, never your own sense that it looks sensible. Leaving it out *is* the decision: the provider's own default then applies, whether that is the account's configuration or the API's documented behaviour, and neither is yours to replace with a value you invented. + +⚠⚠ **This rule never removes a field another row requires.** An idempotency key, and the reference you look an unknown outcome up by, are required by the DUPLICATE CLAIMS and UNKNOWN OUTCOMES rows rather than by the task β€” the task will never mention either. Dropping one to satisfy this table is a defect, not compliance. And because a header, a query parameter and a body field all look alike on this SDK's flat request object, "it is only a header" is not a reason to leave a field off the table. + +⚠⚠ **The map does not state defaults, and its silence is not permission to pick one.** A member typed `field?: T` tells you the field may be omitted. It tells you nothing about what omitting it does. If you cannot state what the provider will apply when the field is absent, that is a reason to leave it absent, not a reason to fill it. + +**The test that proves it:** assert the request the SDK sent carries **no key your own code set without a row that requires it** β€” headers and query parameters included, not only the body. The SDK adds its own (authorization, content type, its defaults); those are not yours and need no row. A test that only checks the fields you meant to send cannot see an invented one. + +#### 1b.4 β€” WHEN THE API SAYS NO + +Add one table per operation that uses the caller's data β€” what they typed, IDs they supply, and data your app stores for them (their email, their name). An empty request body does not make a refusal yours: when the API refuses the data you sent for them, only its reason tells them what to change. An operation with no typed arms still gets one. + +| status | your app answers | the caller reads | test | +| --- | --- | --- | --- | +| a `4xx` other than 401 / 403 / 429 | the same `4xx` | the API's reason text from `err.payload` | fake it with a reason; assert the reason is in your response | +| 401 / 403 | 502 | a fixed message β€” our credentials | fake a 401; assert 502 and the fixed message | +| 429 | 503 | a fixed message β€” our quota | β€” | +| 5xx | 502 | a fixed message | β€” | +| no response, on a write | per UNKNOWN OUTCOMES | unknown β€” never "failed" | β€” | +| no response, on a read | 502 | a fixed message | β€” | + +Logging the reason is not returning it: log it **and** put it in the response. + +### Step 1c β€” Required reading (do this before you write any code) + +End the contract sheet with a **REQUIRED READING** block whose rows carry inline `MUST load ` pointers. **Load every `typescript-*` skill the sheet names, now, before you start implementing** β€” not lazily at the step that needs it. The sheet deliberately does *not* carry the how-to: it names the hazard and hands you the skill that resolves it, so an unloaded pointer is a gap in what you know, not a formality. + +**A fixed floor applies regardless of what the sheet says** β€” the sheet can add to it, never remove from it: `typescript-error-handling` always (every integration writes an error boundary); `typescript-client-initialization` before the client is constructed; `typescript-testing` before the first test file you create or edit, *including* a throwaway verification script that fakes `fetch`. + +These are API-agnostic usage skills; loading them is not a substitute for the lookup layer, and reading a file in the installed package does not remove the need to load the skill for that step. + +Before implementing, check the plan's **Assumptions & Blockers** section: + +- Blocker or major assumption β†’ surface it to the user in plain language, get their answer, and revise `authlete-api-plan.md` in place. +- Minor assumptions only β†’ proceed. + +Full re-planning only on genuine scope change; for a single missing fact mid-implementation, do the lookup, never guess. + +### Step 2 β€” Implement from the contract sheet + +1. Read `authlete-api-plan.md` once and treat its contracts as authoritative. When a lookup revises a row, update the file so the sheet and the code never disagree. +2. **Decide which environment this deployment talks to, explicitly, before the first call** β€” the default is selected silently (Step 1). Bind it to configuration and fail startup if the (environment, credential) pair is not the one this deployment intends. +3. **Build the client once and hold it.** Resources are memoized lazy getters on it, so rebuilding one per request throws that state away. +4. **Pick the response mode per call, deliberately.** Every operation returns `ApiPromise`: `await` it and an error status rejects, or call `.asApiResult()` on the value the operation returned for the non-throwing `ApiResult` β€” the only way to see the status and headers of a *success*, and therefore the only way to observe anything at all from the **6 operations that resolve to `undefined`** (`serviceManagement.serviceDeleteApi`, `serviceManagement.serviceRemoveIdpApi`, `clientManagement.clientDeleteApi`, `clientManagement.clientExtensionRequestablesScopesDeleteApi`, `tokenOperations.authTokenDeleteApi`, `utilityEndpoints.miscEchoApi`). `typescript-calling-endpoints` has the mechanics. +5. Implement sequentially, following the repo's own conventions and layering and the companion skill that governs each step. +6. After every change: run the project's **type checker** (`tsc --noEmit`) as well as its tests. The package ships its own types, so a type error against it is a real contract violation, not noise β€” it is the compile-time backstop the whole map-first discipline leans on. **Treat a clean type check as the gate you do not skip.** If the project has no type check configured, run `tsc --noEmit` over the files you touched; if you genuinely cannot, say so in your final report rather than silently substituting a smoke run. Fix non-SDK errors yourself. +7. **Any error involving an SDK type or member** β€” `TS2339` (property does not exist), `TS2345`/`TS2322` (argument or assignment mismatch), `TS2551` (did you mean), `TS2307` (cannot find module β€” usually a deep import), `TS2739`/`TS2741` (missing required members), or an unexpected rejection at runtime β€” β†’ go back to `typescript-getting-started`'s map section and file table, and read the one page or file that owns the fact. Do not attempt more than one self-fix of an SDK-name error before doing that lookup: rewriting from the same knowledge that produced the error is guessing. A failure that is not an `ApiError` β€” `DecodeError`, `EncodeError`, `ConnectionError`, `TimeoutError`, `AuthError` β€” is still in the `AuthleteApiError` family, told apart by `err.kind` (`typescript-error-handling`). +8. Once the code is complete, open each member the **DUPLICATE CLAIMS** and **UNKNOWN OUTCOMES** rows name and confirm it runs when that write runs. Where the code falls short of a row, fix the code, not the row. +9. **Observability and sensitive data.** Logging is **off by default** and is configured through `ClientOptions.logging`: say whether the integration turns it on, at what level, and which correlation id from the provider's error bodies reaches your own logs. If the scope carries data you would not want in a log, follow the **Sensitive data** rules in `typescript-getting-started`. +10. Run the project's tests; verify the integration end to end the way the task demands. Retrying is **on by default**, so if the task needs a different policy, tune it through `ClientOptions.retry` rather than wrapping the call. The SDK has no hooks or middleware, so anything the task needs there is yours to build on the `ClientOptions.fetch` seam or to deliberately omit β€” say which you did for both. + +### Step 3 β€” Answering pure questions + +A standalone Authlete API question with no code change: look it up in `typescript-getting-started` (and the map page or file its table names), then give the grounded answer, citing where it came from. When several questions arrive batched, answer them in one pass. + +## Anti-patterns β€” never do these + +- **Never write or state any Authlete API or SDK fact from memory** β€” every signature, field name, wire header, enum member, and error arm must come from the contract sheet or a fresh lookup, never from recall or from a companion skill. And **never write a call from memory "to fix later".** +- **Never switch on an error arm tag across operations.** A tag comes from the arm's **body schema**, not its status, and is resolved **per operation**, so the same tag covers different statuses on different operations β€” a shared handler keyed on `err.payload.kind` is a bug (`typescript-error-handling` has the naming rule). Read the status from `err.status`, and narrow on `kind` only against the arm table of that one operation. +- **Don't web-search Authlete API topics to find an implementation detail** β€” the installed package's own map and source are the ground truth, and the file table tells you where to look. Public Authlete API docs describe the HTTP API, not this SDK's generated surface, and the two disagree on names, on which fields are required, and on what the SDK sends by default. +- **Never introspect the SDK at run time to discover its shape.** `Object.keys`, logging a client instance, or a REPL poke is the TypeScript-flavoured version of decompiling the package: it answers what exists, never what is *supported*, and it invites `#`-private fields and unexported engine types into your code. Read the map, then the file it names. +- **Don't grep the package to *locate* a type, deep-import from it (`authlete-api/models/…`), or vendor its `src/`** β€” `typescript-getting-started`'s *Contract facts*, *Imports* and *Install* sections say what to do instead. +- **Don't assume a name is a global.** The package root re-exports every public type, so an unaliased import shadows any same-named global for the rest of the file β€” check the name against the map before importing it plain. + diff --git a/plugins/authlete/skills/typescript/typescript-models/SKILL.md b/plugins/authlete/skills/typescript/typescript-models/SKILL.md new file mode 100644 index 00000000..b99eee8d --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-models/SKILL.md @@ -0,0 +1,320 @@ +--- +name: typescript-models +description: Load before building a request payload for an APIMatic-generated TypeScript SDK or mapping its models onto your own types β€” object-literal models, optional versus nullable fields, open enums, discriminated and plain unions, date codecs, bytes, unknown fields, and the schema companion beside every type. +--- + +# Working with models in an APIMatic TypeScript SDK + +Every model is a plain `type` β€” **not a class**. There is no constructor, no builder and no default +export: you build one with an object literal and read one with plain property access. + +```ts +import { type {Model}, {model}Schema } from "{package-name}"; + +const body: {Model} = { {requiredField}: "..." }; +``` + +An object literal is how every model is built, at every nesting level. **Nothing is `readonly` or +frozen**: a decoded response is an ordinary mutable object, and a change reaches the API only when you +pass the value to an operation, or re-encode it yourself. + +Two exports per model β€” the type and a `{model}Schema` value β€” both from the **package root** +(**typescript-client-initialization**). Take the real type and field names from the **Models** section +of `sdk-map.md` or an operation's **Type sources** table in `map/operations/*.md` β€” never from a +compiled `dist/`, and never from a path derived off the type name. The `Source` column is where to +**read** a shape, never what to import. + +> Throughout this skill, `{...}` is a placeholder for a name you take from your SDK (e.g. `{Model}`, +> `{Union}`, `{Variant}`, `{Enum}`, `{package-name}`) β€” replace it with the concrete identifier from +> the source. + +## Where a model lives + +| Group (as `sdk-map.md` names it) | Directory | What it is | +| --- | --- | --- | +| Objects, and `allOf` (flattened) | `src/models/` | one `type` with the properties, plus its schema | +| Enums (open; const companion plus schema) | `src/models/` | `as const` object + open type alias + schema | +| Discriminated unions (`oneOf`) | `src/models/unions/` | union of tag-narrowed arms + dispatching schema | +| Unions without a discriminant (`anyOf`) | `src/models/unions/` | a bare union type + an accepting schema | + +**`allOf` is flattened** into one plain object type β€” every constituent's properties on one type, with +no inheritance and no base type to assign to. **A polymorphic base** (an object that `allOf` subtypes +derive from) is a discriminated union under `unions/`, not a base type: there is nothing to upcast to, +and you narrow on the tag instead. + +## Required, optional, nullable + +```ts +type {Model} = { + id: string; // required + label?: string; // optional β€” omit the key + retiredAt: Date | null; // required AND nullable β€” null is a value, distinct from omitted + note?: string | null; // both β€” omit it, or send null +}; +``` + +Optional properties are declared `f?: T`, **not** `f?: T | undefined`. The SDK itself compiles under +`exactOptionalPropertyTypes` and `verbatimModuleSyntax`, and where your project does too you must +**omit or spread** an absent field rather than assign `undefined` to it: + +```ts +const bad: {Model} = { id: "1", label: undefined }; // does not compile +const ok: {Model} = { id: "1" }; // omit +const also: {Model} = { id: "1", ...(label !== undefined ? { label } : {}) }; // conditional +``` + +At run time an explicit `undefined` is treated as omitted anyway, so that one is a type-level +constraint. `null` is not: a nullable field set to `null` sends JSON `null`, which is a different +request from an absent key β€” the distinction that matters on a PATCH. + +**A model property whose schema is `s.defaulted(...)` has a default.** It is optional in the type; +omitting it, or passing `undefined`, still sends the default (on a partial update, send its current value +instead), and a response that omits it reads back as the default. + +### Reading one back + +An absent optional is simply `undefined` β€” there is no sentinel value to import and nothing to +compare against, so `?.` and `??` are the whole toolkit, and, on a property with no default, +`!== undefined` is the precise test when "present but empty" and "absent" are different facts. Under +`noUncheckedIndexedAccess` an array index is `T | undefined` too, so guard `items[0]` before reaching +through it. + +⚠ **A response type with no required properties decodes an empty or truncated body without +complaint** β€” every property reads `undefined`, or its default, and nothing throws; check the properties +you depend on. Request and response types for one concept can differ in requiredness, so never infer the +response shape from the request shape: read each from the map (the operation's **Fields** table for the +request, the **Type sources** file for the response). + +## Polymorphic union types: discriminated and undiscriminated + +A `oneOf` with a discriminator becomes a union whose arms are the variant types **intersected with +the tag literal that selects each one**, beside a schema that dispatches on that tag: + +```ts +// src/models/unions/{union}.ts +export type {Union} = ({VariantA} & { {tag}: "a" }) | ({VariantB} & { {tag}: "b" }); + +export const {union}Schema = s.discriminatedUnion<{Union}>("{wire_tag}", { + a: {variantA}Schema, + b: {variantB}Schema, +}); +``` + +An `anyOf` becomes a bare union with nothing to switch on β€” `export type {Union} = string | {Model}` +β€” and its schema tries the arms in **declaration order**, first match winning, so overlapping arms +are resolved by order. + +⚠ **A payload matching no arm throws** β€” through an operation as a `DecodeError` whose `cause` is the +`SchemaError`, through a schema you call directly as the `SchemaError` itself. There is no null or +unset variant, so a variant the provider adds later breaks a call that used to work. See +**typescript-error-handling** for where that surfaces in a catch ladder. + +### Construct + +Write the arm's object literal directly; there is no factory, no static helper and no wrapper type. + +```ts +const method: {Union} = { {tag}: "a", /* the rest of {VariantA} */ }; +``` + +**Include the tag.** It is part of the union arm, and the schema puts it on the wire from the value +you pass. The catch is that the tag lives on the *arm*, not on the variant type: a value typed +`{VariantA}` does not carry it, so building one and then passing it where `{Union}` is expected fails +to compile. Build against the union type, or spread the variant and add the tag. + +### Read / narrow + +```ts +switch (value.{tag}) { // discriminated: the arms are exhaustive, no fallback needed + case "a": return handleA(value); // narrowed to the {VariantA} arm + case "b": return handleB(value); +} + +if (typeof other === "string") { /* ... */ } else { /* the model arm */ } // undiscriminated +``` + +An undiscriminated union has nothing to switch on, so narrow it yourself with `typeof`, an `in` +check, or `Array.isArray`. + +The tag property name on the **decoded** object is not necessarily the wire discriminator β€” a variant +that renamed it has the rename applied on the way out, so the type alias spells what arrives while +the schema names what was sent. Take the exact tag and arm names from the **Unions** table in +`sdk-map.md` (`Union` Β· `Variants` Β· `Narrow with` Β· `Source`) rather than deriving them. + +A union with nothing constrained is typed `unknown`. + +## Collections + +Array properties are `T[]`; maps and free-form objects are `Record` (an API-definition map +is always string-keyed). Pass a plain array or a plain object β€” there is no wrapper and nothing to +convert. + +| Value | On the wire | +| --- | --- | +| omitted / `undefined` | key absent, or its default if it has one | +| `null` on a nullable field | `null` | +| `[]` | `[]` | +| `{}` | `{}` | + +## Dates & numbers + +- **A date-bearing field's wire format is fixed per field, and you cannot substitute another.** All + three instant kinds are typed `Date` while being three different things on the wire, so the type + alone does not tell you which β€” read the field's combinator in `src/models/{model}.ts`. + + | Field's schema | Wire form | Model type | + | --- | --- | --- | + | `s.dateTime()` | offset-bearing ISO 8601 β€” `"2024-06-17T15:30:00Z"` | `Date` | + | `s.rfc1123DateTime()` | HTTP-date β€” `"Mon, 17 Jun 2024 15:30:00 GMT"` | `Date` | + | `s.unixSecondsDateTime()` | epoch **seconds** β€” `1718638200` | `Date` | + | `s.dateOnly()` | `"2024-06-17"` | `string` β€” **not** a `Date` | + + Pass a `Date` and let the schema encode it; never hand-format one. Two traps: `s.dateTime()` + **requires the offset**, so a bare `"2024-06-17T15:30:00"` and a date-only `"2024-06-17"` both + fail; and a **`s.dateOnly()` field is a `string`**, so `new Date(...)` there is a type error and + formatting `"YYYY-MM-DD"` is yours to do. + +- **Every numeric kind is a `number`; the schema says which check runs.** `int32` and `int64` are + checked with `s.int()`, which rejects a fraction and anything outside the safe-integer range. + `float`, `double` and a big-decimal are checked with `s.float64()`, which rejects `NaN` and + `Β±Infinity`. Neither checks the `int32` or `float` range. An `int64` past + `Number.MAX_SAFE_INTEGER` is rejected rather than rounded, so treat a large-integer field as + something to verify against the provider rather than as a safe round-trip. + +- **There is no decimal type.** A `number` is an IEEE-754 double, so a spec that models money as a + number hands you binary floating point and the usual accumulation errors. Many APIs instead model + money as a **`string`** scaled to the currency (`"10.00"`) β€” where yours does, keep it a string end + to end: formatting it through `Number` and back is what silently turns `"10.10"` into `10.1`. + +- **A field pinned to one value** is typed as that literal and checked with + `s.literal(...)`: `status: "accepted"` admits that one value and nothing else, in either direction. + +## Bytes + +A binary or file-typed field is `Uint8Array` in the model and a base64 `string` on the wire; the codec +runs both ways, so pass bytes, not base64: + +```ts +const body: {Model} = { payload: new TextEncoder().encode("hello") }; +const text = new TextDecoder().decode(model.payload); +``` + +That is for base64-in-JSON fields only, and it is **not** the file-upload carrier: a raw-binary body +or multipart file takes a `FileInput` (**typescript-calling-endpoints**), and a decoded `Uint8Array` +field is not assignable to one β€” `new Uint8Array(value)` is the one-copy bridge. + +## What the schema checks β€” and what it does not + +Checked, in both directions: the **primitive type** of each property, the presence of required keys, +the four date formats, base64, a pinned literal, and a union's arms. **Not checked**: + +- **String formats.** A uuid, email, uri, hostname, ip, password, json-pointer, regex-constrained + string and a bare `time` are all plain `string` with `s.string()`. Nothing validates the shape in + either direction: an email field accepts `"not an email"` and sends it. +- **Bounds and lengths** β€” `minLength`, `maxLength`, `minimum`, `maximum`, `multipleOf`, + `uniqueItems`, `pattern`. +- **`additionalProperties: false`** β€” see *Unknown / future fields* below. + +The provider is the only enforcer of those, as a `400` you have to interpret. Where you want a +client-side guard β€” worth it on user-supplied input before a write, where a rejected request costs a +round-trip and an error path β€” write it at your own boundary and read the constraints from +`api-reference.md`, which does carry the API's documented contract. + +An invalid model rejects the call with `EncodeError` β€” its `cause` the `SchemaError` naming the field β€” +and **nothing is sent**. In production, catch it at the boundary where *you* assemble a payload from +external input and treat it as a 4xx on your own API. + +## Enums + +Enums are **not** TypeScript `enum`s. Each is an `as const` companion object plus an open type alias +and a schema: + +```ts +export const {Enum} = { Active: "active", Retired: "retired" } as const; +export type {Enum} = (typeof {Enum})[keyof typeof {Enum}] | (string & {}); +export const {enum}Schema = s.enumOf<{Enum}>({Enum}); +``` + +- **The name is a value and a type at once.** Import it *without* `type` wherever you use its members + β€” `import { {Enum}, {enum}Schema } from "{package-name}"` β€” since a type-only import makes + `{Enum}.Active` a compile error. Prefer the member over the bare literal: an SDK update that + renames the value then fails to compile instead of failing in production. +- **The type is open.** The `(string & {})` tail β€” `(number & {})` on a number enum β€” means any value + of the base type is assignable. +- **The schema validates the base type only, never membership.** A string enum accepts any string, a + number enum any number, so a value newer than the SDK round-trips instead of throwing. That is deliberate, and it is why a `switch` over a **received** enum value needs a + `default` arm β€” the opposite of a discriminated union, whose unrecognized tag does throw. + +```ts +if ({enum}Schema.values.includes(received)) { /* a member this SDK knows */ } +``` + +`.values` is the declared set in declaration order, and the only runtime membership test on offer. +There is no coercion helper β€” no `fromValue`, no lookup function β€” because a member *is* its wire +value: `{Enum}.Active === "active"`, `` `${{Enum}.Active}` `` is `"active"`, and a value read off a +response compares directly against the member. Member *names*, on the other hand, are generated +identifiers rather than wire values β€” a number enum's members are named `_0`, `_1`, … β€” so take the +names and their values from the `as const` companion in the enum's own file. + +## JSON key renaming happens invisibly + +A model's property names are TypeScript-cased; the wire names may differ. The mapping is a `_keysMap` +entry inside the schema literal, applied on decode and encode both, and carrying only the properties +that actually moved: + +```ts +export const {model}Schema = s.object<{Model}>({ + createdAt: s.dateTime(), + _keysMap: { createdAt: "created_at" }, +}); +``` + +So you always write `createdAt` and the wire always carries `created_at`. You never spell a wire name +β€” except when you are staring at a captured HTTP body wondering why the key does not match. The +divergences are listed once, in the **Wire-name divergences** table in `sdk-map.md` (`Type` Β· +`Property` Β· `Wire key`); every property absent from that table uses its TypeScript name verbatim. + +## Unknown / future fields + +Model schemas are **loose**: keys the model does not declare are **kept**, not stripped, in both +directions, so a field added after this SDK survives decoding and is re-sent on encode. There is no +typed bag and no member on the type; read one by its **wire** name β€” nothing maps the casing here: + +```ts +const extra = (model as Record)["x_experimental"]; +``` + +The same holds on the way out: a **misspelled** property is not an error, it is an extra key that is +sent and ignored. Excess-property checking catches it only on a fresh object literal, never on a value +that reached the call through a variable β€” so when a value you set is not taking effect, check the +spelling against the type before suspecting the API. + +## The schema companion + +You do not need `{model}Schema` for ordinary calls β€” the SDK encodes requests and decodes responses +itself. It is there for when you hold the wire shape yourself: + +```ts +const model = {model}Schema.decode(JSON.parse(rawWebhookBody)); // validates and renames keys +const wire = {model}Schema.encode(model); // exactly what the SDK would send +const back = {model}Schema.decode(JSON.parse(JSON.stringify(wire))); +``` + +Both directions throw `SchemaError` on mismatch and produce nothing partial. Using a schema on +inbound data the SDK did not hand you β€” a webhook body, a queue message, a cached blob β€” is the +intended use, and gives you exactly the validation the SDK applies to a response. + +Do **not** `JSON.stringify` a model directly for storage, or hand a raw model to anything that will +encode it β€” a web framework's response serializer, a cache, a queue. `JSON.stringify` writes +TypeScript-cased keys, ISO strings for every date whatever the field's wire format, and turns a +`Uint8Array` into an index-keyed object (`{"0":104,"1":105}`) rather than base64. Decoding that back +fails on the renamed keys and the wrong date formats, silently, and the bytes are simply gone. Either +map the model into your own type at the boundary, or go through `encode` and hand out its result. + +See [reference.md](reference.md) for the per-shape reference: the full spec-kind-to-type table, the +schema surface, and the failures each date codec produces. + +## Next + +- Pass models to an operation β†’ **typescript-calling-endpoints** +- `SchemaError`, and the `DecodeError` or `EncodeError` it arrives on β†’ **typescript-error-handling** diff --git a/plugins/authlete/skills/typescript/typescript-models/reference.md b/plugins/authlete/skills/typescript/typescript-models/reference.md new file mode 100644 index 00000000..80bf50fb --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-models/reference.md @@ -0,0 +1,106 @@ +# Models reference (APIMatic TypeScript) + +Lookup tables beside **typescript-models**, which carries the rules. Model modules build their +schemas from the `s` namespace (`src/core/validation/`, the same in every APIMatic TypeScript SDK): +`zod/v4-mini`'s `string`, `number`, `boolean`, `array`, `record`, `union`, `nullable`, `optional`, +`literal`, `unknown`, plus the SDK's `object`, `enumOf`, `discriminatedUnion`, `of`, `lazy`, +`optionalNullable`, `defaulted`, `dateTime`, `rfc1123DateTime`, `unixSecondsDateTime`, `dateOnly` and +`bytes`. +Confirm concrete type and field names in `src/models/`. + +## Spec kind to TypeScript type + +| API definition kind | Model type | Schema entry | +| --- | --- | --- | +| string, password, regex-constrained, email, uri, ip, hostname, json-pointer, uuid | `string` | `s.string()` β€” **format unchecked** | +| `time` | `string` | `s.string()` β€” unchecked | +| `date` | `string` | `s.dateOnly()` | +| `date-time` (ISO 8601 / RFC 1123 / unix) | `Date` | `s.dateTime()` / `s.rfc1123DateTime()` / `s.unixSecondsDateTime()` | +| int32, int64 | `number` | `s.int()` | +| float, double, big-decimal | `number` | `s.float64()` | +| boolean | `boolean` | `s.boolean()` | +| binary, file β€” as a declared field | `Uint8Array` | `s.bytes()` (base64) | +| a pinned value | that literal type | `s.literal(value)` | +| free-form object | `Record` | `s.record(s.string(), s.unknown())` | +| array of `T` | `T[]` | `s.array(...)` | +| map of `T` | `Record` | `s.record(s.string(), ...)` | +| model / enum reference | the referenced type | `s.lazy(...)`, or the bare schema const | +| unconstrained / no schema | `unknown` | `s.unknown()` β€” rejects nothing | + +Nullability folds onto whatever the row produced: `T | null` with `s.nullable(...)`. It is never +folded onto `unknown`, which already admits `null`. + +## Field declaration cheat sheet + +| Declared | Schema entry | Building it | +| --- | --- | --- | +| `f: T` | the entry itself | always provide | +| `f?: T` | `s.optional(...)` | **omit the key** (not `f: undefined`) | +| `f?: T` | `s.defaulted(...)` | omitting it sends its default; set the value you mean | +| `f: T \| null` | `s.nullable(...)` | provide `null` to send JSON `null` | +| `f?: T \| null` | `s.optionalNullable(...)` | omit, or provide `null` | + +## Deriving a changed copy + +```ts +const updated = { ...model, {field}: "..." }; +``` + +Nothing validates a spread β€” the schema runs when you pass the value to an operation or call `encode` +yourself, and that is where a bad value surfaces. The copy is **shallow**: nested models and arrays +are shared with the original, so mutating one mutates both. Undeclared keys the source carried are +copied along, which is what keeps a read-modify-write round-trip from dropping server-side state the +SDK does not model. + +## Schema surface + +```ts +type Schema> = { + readonly decode: (value: unknown) => T; // wire shape -> model + readonly encode: (value: unknown) => W; // model shape -> wire +}; + +type EnumSchema = Schema & { readonly values: readonly T[] }; +``` + +`Schema`, `EnumSchema` and `Encoded` are re-exported from the package root as types. + +`Encoded` β€” the wire projection: + +| `T` | `Encoded` | +| --- | --- | +| `Date` | `string \| number` | +| `Uint8Array` | `string` (base64) | +| `E[]` | `Encoded[]` | +| object | each property projected | +| anything else | itself | + +A `SchemaError` message is prefixed `Wire value could not be decoded.` or `Type could not be encoded +for the wire.`, followed by `path: expected X, received T` per issue β€” `T` the type that arrived, +never the value (`` where there is no path). `.cause` is the underlying zod error with the +structured issue list. + +## Enums + +An `if`/`else if` chain over the members with no final `else` silently does nothing for a value newer +than the SDK. `type Known = (typeof {Enum})[keyof typeof {Enum}]` is the closed set at type level. + +## Dates + +- `s.dateTime()` requires an offset on decode; encodes via `toISOString()`. +- `s.rfc1123DateTime()` is strict IMF-fixdate (two-digit day, `GMT`); encodes via `toUTCString()`. +- `s.unixSecondsDateTime()` also decodes a numeric string; encodes floored to whole seconds. +- `s.dateOnly()` checks the `YYYY-MM-DD` shape. `s.bytes()` rejects non-base64 input on decode. + +Failures to expect: + +- `"2024-06-17"` or `"2024-06-17T15:30:00"` into an `s.dateTime()` field β†’ `SchemaError` (no offset). +- `"Mon, 7 Jun 2024 15:30:00 GMT"` into `s.rfc1123DateTime()` β†’ `SchemaError` (day must be two + digits). +- Epoch **milliseconds** into `s.unixSecondsDateTime()` β†’ no error; it decodes to a date ~50,000 + years out. Divide by 1000, or pass a `Date`. + +## Recursive and self-referential models + +A self-referential model (a tree node, a nested comment) needs no special handling: generated schemas +wrap a nested reference in `s.lazy(() => otherSchema)`. diff --git a/plugins/authlete/skills/typescript/typescript-testing/SKILL.md b/plugins/authlete/skills/typescript/typescript-testing/SKILL.md new file mode 100644 index 00000000..62d5f58c --- /dev/null +++ b/plugins/authlete/skills/typescript/typescript-testing/SKILL.md @@ -0,0 +1,543 @@ +--- +name: typescript-testing +description: Load before writing tests for code that calls an APIMatic-generated TypeScript SDK β€” faking `ClientOptions.fetch` or intercepting global `fetch`, wire-shaped stub bodies, asserting the request the SDK built, error, decode and auth failure paths, OAuth token stubs, and the on-by-default retries. +--- + +# Testing code that uses an APIMatic TypeScript SDK + +The client takes a `fetch` implementation on its options object, and that is the seam for testing: pass +a function that returns a `Response` and no real network calls happen. The SDK ships no mocking +helpers, no test doubles and no recorded fixtures β€” this is standard TypeScript. No interceptor +library, no `nock`, no `msw` required. + +```ts +const client = new {Api}Client({ fetch: async () => jsonResponse(200, { id: "1" }) }); +``` + +**Match the project's existing test stack β€” don't impose one.** Check the test dependencies and the +existing tests, then mirror both its **runner** (Vitest / Jest / `node:test`) and its **assertion +style**: if it uses `node:test` with `assert`, or an assertion library, write assertions that way +rather than switching. The samples below use a `describe`/`it`/`expect` style **purely for reference** +β€” they show the SDK testing seam and *what* to assert, not a mandated runner or matcher library. +Substitute your own `{Api}Client` and operation names as well. + +> Throughout this skill, `{...}` is a placeholder for a name you take from your SDK (e.g. +> `{Api}Client`, `{package-name}`, `{resource}`, `{Resource}`, `{Operation}`, `{scheme}`) β€” replace it +> with the concrete identifier from the source. + +## A reusable fake fetch + +`ClientOptions.fetch` is typed `typeof fetch`, so a fake needs no base class, no registration and no +mocking library β€” just a function: + +```ts +type Call = { url: URL; request: Request; body: string | undefined }; + +function stubFetch(responder: (url: URL, request: Request) => Response | Promise) { + // Every request, in order β€” the token fetch appends too, so this is what you count. + const calls: Call[] = []; + + // Typed as `typeof fetch` so a signature drift is a compile error, not a runtime surprise. The SDK + // calls it with one `Request` and no `init`; wrapping both in a `Request` also accepts a direct call. + const fetch: typeof globalThis.fetch = async (input, init) => { + const request = new Request(input, init); + const body = request.body === null ? undefined : await request.clone().text(); + const call = { url: new URL(request.url), request, body }; + calls.push(call); + return responder(call.url, request); + }; + + return { fetch, calls, last: () => calls[calls.length - 1]! }; +} + +function jsonResponse(status: number, body: unknown): Response { + return new Response(JSON.stringify(body), { status }); +} + +function emptyResponse(status = 204): Response { + return new Response(null, { status }); // what an operation returning `undefined` expects +} + +/** Answer requests in order β€” the shape most tests want. */ +function queue(...responses: Response[]) { + const pending = [...responses]; + return () => { + const next = pending.shift(); + if (next === undefined) throw new Error("stub fetch ran out of queued responses"); + return next; + }; +} +``` + +Wire it in and you have a real client with no network: + +```ts +function clientReturning(...responses: Response[]) { + const stub = stubFetch(queue(...responses)); + const client = new {Api}Client({ + fetch: stub.fetch, + retry: { maxRetries: 0 }, // one request per call β€” see *Notes* + {scheme}: { /* ... */ }, + }); + return { client, stub }; +} +``` + +**`retry: { maxRetries: 0 }` is load-bearing.** Retries are on by default, so without it a stubbed +`503` on a `GET` is fetched four times across about 7 s of backoff, and a queue holding one response +runs out on the second attempt. Leave it on every test client that is not *about* retrying. + +**Logging is off unless the environment turns it on.** A test client with no `logging` reads +`{APICLIENTTYPENAME}_LOG` (the client class name in upper snake case, plus `_LOG`) like any other, so a CI job +that exports it fills the test output with request lines. Set `logging: { level: "off" }` where that +matters, or pass a `logger` that records its calls when the test is *about* what gets logged. + +`Response` is global on Node 18+. Credentials can be dummy values, or omitted entirely β€” nothing +validates them when the transport is stubbed, unless the test is *about* the credential. + +**Fake `fetch`, not the client.** A hand-rolled double of `{Api}Client` or a resource class (which you +cannot construct β€” **typescript-calling-endpoints**) cannot catch the mistakes that actually happen β€” a +wrong path parameter, a body that does not encode, a header you forgot; a fake `fetch` runs the real +request pipeline. + +> **With an OAuth 2 scheme, the first request your stub sees is the token request**, not your +> operation. A single-response fake hands your operation's body to the token decoder, and the call +> fails with an `AuthError` (cause: a `DecodeError` about `access_token`) that reads like bad +> credentials. See **Fake an OAuth token endpoint** below. + +## Stub bodies are in WIRE shape, not model shape + +Every response is decoded through the operation's schema, so a stub body must be what the API would +actually put on the wire: + +- **wire key spellings**, not the camelCase members β€” `created_at`, not `createdAt` (the `_keysMap` in + `src/models/{model}.ts` gives the mapping); +- **wire date formats** β€” an offset-bearing ISO string, an HTTP-date, or epoch **seconds**, per the + field's schema combinator; +- **every required field present.** + +Get a required one wrong and the call rejects with `DecodeError` before your assertion runs β€” which +reads as a mysterious failure until you read its `cause`, a `SchemaError` whose message names the +failing path and the expected type exactly. Get an **optional** one wrong and nothing fails at all: model schemas are +**loose**, so an unrecognized key is preserved rather than rejected, the member reads back `undefined`, +and the test passes for the wrong reason. Two shortcuts close that gap: + +```ts +const wireBody = {model}Schema.encode(someModel); // round-trip a model to a valid wire body +expect(() => {model}Schema.decode(fixture)).not.toThrow(); // validate a hand-written fixture once +``` + +Prefer the first: build the fixture as a typed model and encode it, so a member rename breaks the +fixture instead of passing a stale test. Validate any fixture you hand-write and reuse β€” it catches +fixture rot when the SDK is updated. Both schema consts are exported from the package root. + +## Test a success path + +```ts +it("returns the decoded model", async () => { + const { client } = clientReturning(jsonResponse(200, { id: "1", created_at: "2024-06-17T15:30:00Z" })); + + const result = await client.{resource}.{operation}({ /* ... */ }); + + expect(result.id).toBe("1"); + expect(result.createdAt).toEqual(new Date("2024-06-17T15:30:00Z")); // Date, decoded for you +}); +``` + +An operation whose success type is `undefined` needs a genuinely **empty** body β€” `emptyResponse()`. +A response with a body makes the call fail with `DecodeError`; to test that, stub the body on a `200` β€” +`new Response(body, { status: 204 })` throws `TypeError`. + +## Test an error path + +A non-2xx status rejects with the operation's error class, and the reason lives in `err.payload` β€” a +discriminated union keyed by the declared response **schema**, not by the status +(**typescript-error-handling**). Which class and which arms an operation has is the **Error arms** +bullet in `map/operations/{resource}.md`, so assert the pair that matches your operation. + +**Case A β€” the operation declares error bodies**, so it has a generated `{Resource}.{Operation}Error`: + +```ts +import { {Resource} } from "{package-name}"; + +it("surfaces the declared 400 arm", async () => { + const { client } = clientReturning(jsonResponse(400, { message: "bad input", code: "invalid" })); + + try { + await client.{resource}.{operation}({ /* ... */ }); + throw new Error("expected a rejection"); + } catch (err) { + expect(err).toBeInstanceOf({Resource}.{Operation}Error); + const e = err as {Resource}.{Operation}Error; + expect(e.status).toBe(400); // always present, whichever arm you got + expect(e.payload.kind).toBe("{arm}"); // narrow before reading the body + if (e.payload.kind === "{arm}") expect(e.payload.body.message).toBe("bad input"); + } +}); +``` + +The error body is decoded through **its** schema, so it is wire-shaped on the same terms as a success +body. + +**Case B β€” the operation declares no error bodies.** It rejects with the base `ApiError`, whose +payload is always the `"undeclared"` arm β€” **raw bytes**, not a decoded body. Its `kind` is typed +`string`, so test for `rawBody` to reach the bytes: + +```ts +import { ApiError } from "{package-name}"; + +const e = err as ApiError; +expect(e.status).toBe(422); +expect(e.payload.kind).toBe("undeclared"); +if ("rawBody" in e.payload) expect(new TextDecoder().decode(e.payload.rawBody)).toContain("bad input"); +``` + +**Take the arm union from the contract sheet per operation.** `"undeclared"` is reachable in Case A +too β€” any status the operation's matchers do not cover falls to it, a spec's `default` response +being no matcher β€” so an error test copied from a sibling operation can pass for the wrong reason, +landing in the `"undeclared"` branch and asserting nothing about the typed body you meant to check. + +Use `try`/`catch` rather than a rejection matcher: matcher support for narrowing a rejected value +differs between runners, and the `catch` form reads the same everywhere. + +These prove the reason is on the SDK error, not that the caller sees it. Also test through your own boundary: +fake the API's `4xx` with a reason and assert the reason is in **your** response β€” a test that checks +only the status passes on code that logs the reason and drops it. + +## Test the result-style (`asApiResult`) variant + +`.asApiResult()` turns the error-status branch into a value, so there is nothing to catch β€” stub the +response and assert on the returned `ApiResult` directly. The split is mechanical: **2xx is +`ok: true`, every other status is `ok: false`**, and both carry `status` and `headers`. + +```ts +it("reports the failure without throwing", async () => { + const { client } = clientReturning(jsonResponse(400, { message: "bad input" })); + + const outcome = await client.{resource}.{operation}({ /* ... */ }).asApiResult(); + + expect(outcome.ok).toBe(false); + if (!outcome.ok) { + expect(outcome.status).toBe(400); + expect(outcome.payload.kind).toBe("{arm}"); // the error's own members, not the error object + } +}); +``` + +`outcome.headers` is the only way to read **response headers** on the *success* path, so test in this +mode when your code depends on one. Everything in *Cover the failures that are not an `ApiError`* +still rejects in this mode. + +## Assert the outgoing request + +`expect(stub.calls).toHaveLength(1)` proves only that something was sent. Assert the things a +regression would actually change: + +```ts +it("sends the right request", async () => { + const { client, stub } = clientReturning(jsonResponse(200, { id: "1" })); + + await client.{resource}.{operation}({ {pathField}: "abc", {queryField}: 2, body: { name: "x" } }); + + const { url, request, body } = stub.last(); + expect(request.method).toBe("POST"); + expect(url.pathname).toBe("/{expected}/{route}/abc"); // path field substituted + expect(url.searchParams.get("{wire-query-name}")).toBe("2"); // query lives on the URL + expect(request.headers.get("content-type")).toBe("application/json"); + expect(JSON.parse(body!)).toEqual({ name: "x" }); // wire keys, wire dates +}); +``` + +- **The SDK hands the fake one `Request` and no `init`.** Its `url` is absolute, so + `new URL(request.url)` gives you `pathname` and `searchParams` for free rather than substring-matching + a URL string. A fake that reads `init` reads nothing. +- **Read headers through `request.headers.get(...)`** β€” `request.headers` is a `Headers` with + lowercased names, so a plain `request.headers["Authorization"]` is both the wrong type and the wrong + case. +- **The body is already serialized by the time the fake sees it.** The captured `body` is the request + body's **text** (or `undefined`), which is all a JSON, form-urlencoded or text body needs, with **wire + aliases** and wire dates, because that is what encoding produces β€” assert on the parsed string; do not + "fix" the aliases in the test. Read a multipart or binary body back off the captured `request` + instead: a multipart one with `await request.formData()`, and a binary one with + `new Uint8Array(await request.arrayBuffer())` β€” its text is lossy. A multipart `content-type` + carries a generated boundary, so match it as a prefix, never with `toBe`. Compare a file part by its + contents, `await new Response(form.get("{field}")).text()`: one sent without a file name may come + back as a string or a `File`. +- **A list-valued query or form parameter has a per-parameter wire style.** Its `StyledParam` in + `src/resources/{resource}.ts` carries a `style`: `plain` (the default β€” + `name=a&name=b`), `indexed` (`name[0]=a`, percent-encoded in the raw URL as `name%5B0%5D=a`), + `unindexed` (`name[]=a`), or `csv` / `tsv` / `psv` (one joined value). Read it before asserting: + + ```ts + expect(url.searchParams.getAll("{name}")).toEqual(["a", "b"]); // plain β€” get() returns only the FIRST + expect(url.searchParams.get("{name}[0]")).toBe("a"); // indexed + expect(url.searchParams.getAll("{name}[]")).toEqual(["a", "b"]); // unindexed + expect(url.searchParams.get("{name}")).toBe("a,b"); // csv + ``` + + Object-valued parameters flatten to `key[sub]=value`. Path and header arrays are not configurable β€” + both fold to one comma-separated value. +- The expected verb and route are the operation's **Wire** bullet in `map/operations/{resource}.md`, + and the channel each field travels on is its **Fields** table. Take them from there β€” a field's + channel is not derivable from its name. + +Worth asserting once, somewhere, because they are silent when wrong: + +- **The value actually reached the wire.** Request models encode loosely too, so an extra or misspelled + optional member is carried into the body rather than rejected. Only an assertion on the captured + `body` catches it. +- **An encode failure never reaches your fake** β€” a bad body and an `undefined` path parameter alike + reject with `EncodeError` and leave `stub.calls` **empty**; assert that in those tests. +- **`retry.timeout` never rides the request**; the fake sees it only as the `Request`'s `signal`. + +## Cover the failures that are not an `ApiError` + +Every other failure rejects with another member of the `{Api}Error` family β€” narrowed on its closed +`kind` β€” and **never** matches an `ApiError` check (**typescript-error-handling**). Each `kind` is +reachable from the fake, which makes these paths genuinely testable rather than hypothetical: + +| To produce | Make the fake `fetch` / the client | +| --- | --- | +| `kind: "connection"` | throw any error | +| `kind: "timeout"` | hang until aborted, with a tiny `retry.timeout` on the client | +| `kind: "decode"` | return a body that violates the schema (wrong type, bad date, a non-empty `200` where `undefined` is expected) | +| `kind: "encode"` | pass a request value its schema refuses β€” nothing reaches the fake | +| `kind: "auth"` | answer the **token** request with an error status (see below) | + +```ts +import { {Api}Error } from "{package-name}"; + +it("maps a transport failure", async () => { + const client = new {Api}Client({ + fetch: async () => { throw new Error("ECONNREFUSED"); }, + retry: { maxRetries: 0 }, // a connection failure is retried otherwise + }); + + try { + await client.{resource}.{operation}({ /* ... */ }); + throw new Error("expected a rejection"); + } catch (err) { + expect(err).toBeInstanceOf({Api}Error); + expect((err as {Api}Error).kind).toBe("connection"); + } +}); +``` + +⚠ **A timeout or abort test needs a fake that honours the `Request`'s `signal`**: one that ignores it +hangs the test until the runner kills it. What the fake rejects with does not matter β€” a timeout is a +`TimeoutError` the retry loop retries, which is why the test below sets `maxRetries: 0`. + +```ts +const hangingFetch: typeof globalThis.fetch = (input, init) => + new Promise((_resolve, reject) => { + const { signal } = new Request(input, init); + signal.addEventListener("abort", () => reject(signal.reason), { once: true }); + }); + +it("times out", async () => { + const client = new {Api}Client({ retry: { timeout: 10, maxRetries: 0 }, fetch: hangingFetch }); + + await expect(client.{resource}.{operation}({ /* ... */ })) + .rejects.toMatchObject({ kind: "timeout" }); +}); +``` + +A caller abort is **not** a `kind`: abort your own `AbortController` passed in `RequestOptions` and +assert the call rejects with exactly the `reason` you passed β€” not an `{Api}Error`, and never retried. + +**A decode failure bypasses *both* response modes.** A 2xx body that does not satisfy the schema +rejects with `DecodeError`; `.asApiResult()` does **not** turn it into an `ok: false` result. Test that your +boundary maps it: + +```ts +it("does not report an unreadable success body as a failure", async () => { + // A real decode failure needs a TYPE mismatch, not an absent optional member. + const { client } = clientReturning(jsonResponse(200, { id: 42 })); // schema says string + await expect(service.{operation}()).rejects.toBeInstanceOf(MyProviderUnreadable); +}); +``` + +**A truncated 2xx that decodes cleanly** is the mirror image. A member declared optional is absent +without complaint and reads back `undefined`, so only your own guard catches it β€” test the guard: + +```ts +it("does not report a truncated success body as a success", async () => { + const { client } = clientReturning(jsonResponse(200, {})); // no {wire_field} + await expect(service.{operation}()).rejects.toBeInstanceOf(MyProviderUnreadable); +}); +``` + +And the one people forget: **bad credentials**. A failed token fetch raises `AuthError` β€” `kind: +"auth"`, *not* an `ApiError` β€” carrying the token endpoint's `ApiError` on `.cause`. Return an +RFC 6749 error body from the **token** request and assert your configuration error, not a rejection +error: + +```ts +it("treats bad credentials as a config error", async () => { + const { client } = clientReturning(jsonResponse(401, { error: "invalid_client" })); // no tokenResponse() + await expect(service.{operation}()).rejects.toBeInstanceOf(MyProviderConfigError); +}); +``` + +Being *refused* by the API is the other case: that is an `ApiError` with status 401/403 from the +operation itself. + +## Fake an OAuth token endpoint + +An OAuth 2 client fetches its token **lazily, on the first authenticated call, through the same +`fetch`** β€” so the *first* request your stub sees is the `POST` to the token endpoint, not your +operation. Two ways to handle it. + +Queue a token response ahead of the operation's, and index from the end: + +```ts +function tokenResponse(): Response { + return jsonResponse(200, { access_token: "T", token_type: "Bearer", expires_in: 3600 }); +} + +const { client, stub } = clientReturning(tokenResponse(), jsonResponse(201, { id: "1" })); +await client.{resource}.{operation}({ /* ... */ }); + +expect(stub.calls).toHaveLength(2); +const { request } = stub.last(); // index from the end, not calls[0] +expect(request.headers.get("authorization")).toBe("Bearer T"); +``` + +Or route on the path, when the order is what you are testing: + +```ts +const stub = stubFetch((url) => + url.pathname.endsWith("/{token-path}") ? tokenResponse() : jsonResponse(200, { id: "1" }), +); +``` + +Or bypass acquisition entirely with a stub token strategy β€” a supported extension point +(**typescript-authentication**) β€” so the fake only ever sees your operation: + +```ts +const stubStrategy = { // keep it a variable: inline, tryRefreshToken is a type error on the other grants + getToken: async () => ({ accessToken: "T", tokenType: "Bearer" }), + tryRefreshToken: async () => null, // the authorization-code grant requires it +}; + +const client = new {Api}Client({ + {scheme}: { clientId: "id", clientSecret: "secret" }, // still required β€” the scheme is off without it + {scheme}Strategy: stubStrategy, + fetch: stub.fetch, +}); +``` + +That credential fits the client-credentials grant. Match `{scheme}`'s type in `src/client-options.ts`: +`OAuth2PasswordCredentials` needs `username` and `password`, and `OAuth2AuthorizationCodeCredentials` +needs `redirectUri` and `promptForAuthorizationCode` (e.g. `async () => "code"`). + +Facts that make these tests behave: + +- The token **response** is wire-shaped (`access_token`, `token_type`, `expires_in`); the token + request's shape is in **typescript-authentication**. +- The token endpoint may live on a **different server group**, so match on the path, not the host β€” + read the group's URL from `DEFAULT_SERVER_OPTIONS` in `src/servers.ts`. +- The token is **cached on the scheme**, so only the first call fetches one: a test making two calls + against one client sees three requests, not four. Build a fresh client per test to reset it. +- The token request and the operation each run on their own `retry.timeout`, so a tiny timeout in an + OAuth test can fire on the token request β€” which ends the call with a `TimeoutError` for the token + endpoint, not retried unless `httpMethodsToRetry` names `POST`. + +## The alternative: intercepting global `fetch` + +When your production code constructs the client itself and you cannot inject a `fetch`, intercept one +layer down instead β€” undici's `MockAgent` with `setGlobalDispatcher`, or `msw`. Your production code +runs unmodified: + +```ts +import { MockAgent, setGlobalDispatcher } from "undici"; + +const agent = new MockAgent(); +setGlobalDispatcher(agent); +agent.get("{base-origin}").intercept({ path: "{operation-path}", method: "POST" }) + .reply(201, { id: "1" }, { headers: { "content-type": "application/json" } }); +``` + +Pick one approach. An interceptor sees nothing once a `fetch` option is supplied, and one that patches +`globalThis.fetch` must be installed before the client is built (**typescript-client-initialization**). + +## Keeping tests independent of SDK internals + +- **Import only from the package root** (**typescript-client-initialization**); read `src/` to learn a + shape, never to import from it. +- **Never assert on an error's `message`.** An `ApiError`'s is the call and its status β€” + `POST https://… failed with 400` β€” which moves whenever the base URL or the route does. It is not a + contract. Assert `err.status` and the narrowed `err.payload`. +- **Never derive test data from the schema's own definition** β€” reading `_keysMap` to generate a + payload means the fixture cannot detect a wrong payload. Construct models explicitly and + `{model}Schema.encode` them. +- **Test your own boundary's output, not the SDK's.** The valuable assertions are that a provider 4xx + becomes your 4xx and a transport failure becomes your 5xx. That the SDK rejects with + `{Operation}Error` on a 400 is the SDK's own tested behaviour, not yours. +- Prefer this `fetch`-seam approach over wrapping the SDK in your own interface unless you need to + abstract the SDK for other reasons. + +## Integration tests + +Against a **local test server**, leave `fetch` alone and redirect the base URL: + +```ts +const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{Environment}, + serverOptions: { default: { {environment}: { baseUrl: server.url } } }, +}); +``` + +**Override every group the test touches** β€” a token endpoint on its own group otherwise reaches the +real host (**typescript-configuration-resilience**). `src/servers.ts` lists the groups and their +environments. + +Against a **live sandbox**, keep those tests separate from unit tests, skip them when credentials are +absent, and never assert on ids or timestamps the provider generates: + +```ts +const hasCredentials = Boolean(process.env.{API}_CLIENT_ID); + +describe.skipIf(!hasCredentials)("integration", () => { + it("{operation}", async () => { + const client = new {Api}Client({ + serverEnvironment: ServerEnvironment.{SandboxEnvironment}, // state it, never rely on the default + {scheme}: { /* from env */ }, + retry: { timeout: 15_000 }, // per attempt; retries stay on + }); + const result = await client.{resource}.{operation}({ /* ... */ }); + expect(result.{member}).toBeDefined(); + }); +}); +``` + +Expect flakiness from the provider, not from your code, and never gate CI on a third party's uptime +unless you mean to. + +## Notes + +- **The response's `content-type` picks no decoder.** The decoder is fixed by the operation, so a stubbed + `Response` needs no `content-type` header; setting one is harmless documentation. What matters is + that the body parses and satisfies the schema. A binary response is the exception: its result + reports `content-type` and the `content-disposition` file name, so stub them when your code reads them. +- A stubbed `401` invalidates the cached OAuth token, so the *next* call in the same test fetches a new + one β€” queue another `tokenResponse()` for it. +- **Testing the retry policy itself**: drop `maxRetries: 0`, queue one response per attempt (a queue + that runs out throws, and the loop retries that as a connection failure), pass `delay: 0`, and count + requests: + ```ts + const stub = stubFetch(queue(jsonResponse(503, {}), jsonResponse(503, {}), jsonResponse(200, { id: "1" }))); + const client = new {Api}Client({ fetch: stub.fetch, retry: { delay: 0 } }); + + await client.{resource}.{operation}({ /* ... */ }); // an operation whose method is in the gate + expect(stub.calls).toHaveLength(3); + ``` + A stubbed `Retry-After` overrides `delay`; a backoff test wants `maxJitter: 0` and fake timers. Worth + covering: a write outside the gate is sent **once**, a widened write re-sends the **same** + `Idempotency-Key`, and an abort stops retrying. +- **Never guess a base URL.** With a fake `fetch` nothing is dialled and the host is irrelevant; if you + match on URLs, take the prefix from `DEFAULT_SERVER_OPTIONS` rather than from memory β€” environments + differ per API. +- Mocking libraries work too β€” the seam is a plain function, so a `vi.fn()` / `jest.fn()` returning a + `Response` satisfies it. The hand-written stub above gives you typed captured requests and ordered + responses for free. diff --git a/plugins/cloudinary-analysis/.claude-plugin/plugin.json b/plugins/cloudinary-analysis/.claude-plugin/plugin.json new file mode 100644 index 00000000..f81c723d --- /dev/null +++ b/plugins/cloudinary-analysis/.claude-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "cloudinary-analysis", + "displayName": "Cloudinary Analysis", + "version": "0.1.0", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/cloudinary-analysis/.codex-plugin/plugin.json b/plugins/cloudinary-analysis/.codex-plugin/plugin.json new file mode 100644 index 00000000..e335965a --- /dev/null +++ b/plugins/cloudinary-analysis/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "cloudinary-analysis", + "version": "0.1.0", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks.", + "skills": "./skills", + "interface": { + "displayName": "Cloudinary Analysis", + "developerName": "APIMatic" + } +} diff --git a/plugins/cloudinary-analysis/.cursor-plugin/plugin.json b/plugins/cloudinary-analysis/.cursor-plugin/plugin.json new file mode 100644 index 00000000..f81c723d --- /dev/null +++ b/plugins/cloudinary-analysis/.cursor-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "cloudinary-analysis", + "displayName": "Cloudinary Analysis", + "version": "0.1.0", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/cloudinary-analysis/README.md b/plugins/cloudinary-analysis/README.md new file mode 100644 index 00000000..ae2fda85 --- /dev/null +++ b/plugins/cloudinary-analysis/README.md @@ -0,0 +1,11 @@ +# Analyze API (Beta) SDK Plugin + +A plugin whose skills teach a coding agent to install and use the APIMatic-generated **Analyze API (Beta) SDK**, in TypeScript. Every SDK fact the skills state is grounded in the SDK's own source and generated documentation, not in what a model remembers about this API. + +## What's inside + +One skill set per language. The entry point is that language's getting-started skill, which carries what is specific to this SDK; the rest are API-agnostic and describe how to use any SDK the same generator produces. + +| Language | Skill prefix | Skills | +| --- | --- | --- | +| TypeScript | `typescript-` | `typescript-authentication`, `typescript-calling-endpoints`, `typescript-client-initialization`, `typescript-configuration-resilience`, `typescript-error-handling`, `typescript-event-streams`, `typescript-getting-started`, `typescript-integrate-analyze-api-beta`, `typescript-models`, `typescript-testing` | diff --git a/plugins/cloudinary-analysis/plugin.json b/plugins/cloudinary-analysis/plugin.json new file mode 100644 index 00000000..f81c723d --- /dev/null +++ b/plugins/cloudinary-analysis/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "cloudinary-analysis", + "displayName": "Cloudinary Analysis", + "version": "0.1.0", + "description": "Analyze images with Cloudinary AI: tagging, captioning, moderation, and quality checks.", + "author": { + "name": "APIMatic", + "email": "developer@apimatic.io" + }, + "license": "MIT", + "homepage": "https://context.apimatic.io/", + "repository": "https://github.com/context-plugins/plugin-marketplace", + "skills": [ + "./skills/typescript" + ] +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/.gitignore b/plugins/cloudinary-analysis/sdk/typescript/.gitignore new file mode 100644 index 00000000..b079e0f3 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/.gitignore @@ -0,0 +1,8 @@ +node_modules/ +dist/ +.tshy/ +.tshy-build/ +*.tsbuildinfo +*.log +.env +.DS_Store diff --git a/plugins/cloudinary-analysis/sdk/typescript/.oxfmtrc.json b/plugins/cloudinary-analysis/sdk/typescript/.oxfmtrc.json new file mode 100644 index 00000000..8e9159d1 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 110, + "sortPackageJson": false +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/.oxlintrc.json b/plugins/cloudinary-analysis/sdk/typescript/.oxlintrc.json new file mode 100644 index 00000000..8cdcf059 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/.oxlintrc.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/oxc-project/oxc/main/npm/oxlint/configuration_schema.json", + "categories": { + "correctness": "error" + }, + "rules": { + "@typescript-eslint/no-unnecessary-type-assertion": "error" + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/LICENSE b/plugins/cloudinary-analysis/sdk/typescript/LICENSE new file mode 100644 index 00000000..5de12eaa --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 APIMatic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/cloudinary-analysis/sdk/typescript/README.md b/plugins/cloudinary-analysis/sdk/typescript/README.md new file mode 100644 index 00000000..9ec06766 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/README.md @@ -0,0 +1,132 @@ +# Analyze API (Beta) + +[![Built with APIMatic][apimatic-badge]][apimatic-url] [![License: MIT][license-badge]][license-url] + +The Analyze API (Beta) SDK for TypeScript provides typed access to the Analyze API (Beta) REST APIs from Node.js and the browser. + +> [!TIP] +> **Looking for a specific signature, request field, model, enum or error type?** This SDK ships a generated, machine-readable **[SDK map](sdk-map.md)** β€” a lookup index of the whole TypeScript surface. Consult it **before** grepping or scanning the source tree; it answers most contract questions directly and, where a source file is genuinely needed, names the exact one to open. Details under [SDK map](#sdk-map). + +Use the Analyze API to analyze any external asset and return details based on the type of analysis requested. + +Currently supports the following analysis options: + * [AI Vision - Tagging](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#tagging_mode) + * [AI Vision - Moderation](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#moderation_mode) + * [AI Vision - General](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#general_mode) + * [Captioning](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) + * [Cld Fashion](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Cld Text](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Coco](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Google Tagging](https://cloudinary.com/documentation/google_auto_tagging_addon) + * [Human Anatomy](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Image Quality Analysis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#image_quality_analysis) + * [Lvis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Shop Classifier](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Unidet](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * [Watermark Detection](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#watermark_detection) + + **Notes**: + * The Analyze API is currently in development and is available as a Public Beta, which means we value your feedback, so please feel free to [share any thoughts with us](https://support.cloudinary.com/hc/en-us/requests/new). + * The analysis options require an active subscription to the relevant add-on. Learn more about [registering for add-ons](https://cloudinary.com/documentation/cloudinary_add_ons#registering_for_add_ons). + + The API supports both Basic Authentication using your Cloudinary API Key and API Secret (which can be found on the Dashboard page of your [Cloudinary Console](https://console.cloudinary.com/pm)) or OAuth2 ([Contact support](https://support.cloudinary.com/hc/en-us/requests/new) for more information regarding OAuth). + + +--- + +## Installation + +The SDK compiles to `dist/` before it can be referenced β€” run its `build` script once in the SDK folder, then add it to your project by path: + +```bash +npm install +``` + +--- + +## Quick Start + +Create one client and reuse it. Configure its behaviour through [ClientOptions](src/client-options.ts). + +```ts +import { AnalyzeApiBetaClient, PkceMethod } from "analyze-api-beta"; + +const client = new AnalyzeApiBetaClient({ + basicAuth: { username: "YOUR_USERNAME", password: "YOUR_PASSWORD" }, + oAuth2: { + clientId: "YOUR_CLIENT_ID", + redirectUri: "YOUR_REDIRECT_URI", + pkce: PkceMethod.S256, + promptForAuthorizationCode: (authorizationUrl, signal) => Promise.resolve("YOUR_AUTHORIZATION_CODE"), + }, +}); +``` + +Nothing in `ClientOptions` is required β€” `new AnalyzeApiBetaClient()` compiles β€” and each option left out falls back to its default. This API declares one environment, so there is no environment to select and no host to state at the construction site. + +### From CommonJS + +The package ships both dialects from a single entry, so `require` works with full types. In a TypeScript CommonJS file use the `import ... = require(...)` form β€” a plain destructuring `require` runs fine but gives you `any`. + +```ts +import sdk = require("analyze-api-beta"); + +const client = new sdk.AnalyzeApiBetaClient({ + basicAuth: { username: "YOUR_USERNAME", password: "YOUR_PASSWORD" }, + oAuth2: { + clientId: "YOUR_CLIENT_ID", + redirectUri: "YOUR_REDIRECT_URI", + pkce: sdk.PkceMethod.S256, + promptForAuthorizationCode: (authorizationUrl, signal) => Promise.resolve("YOUR_AUTHORIZATION_CODE"), + }, +}); +``` + +--- + +## Usage + +For code examples and error responses, see [API Reference](api-reference.md). + +--- + +## SDK map + +This SDK ships a generated **SDK map** β€” [`sdk-map.md`](sdk-map.md) plus the pages under [`map/operations/`](map/operations/analysis.md) β€” a deterministic, lookup-oriented table of contents of the TypeScript surface, generated alongside the SDK. + +**Read it before scanning the source.** Whether you are an AI coding assistant or searching by hand, the map answers "what is the exact …" by lookup for every call-level contract, and for anything it does not carry it names the one file that does: + +- **[`sdk-map.md`](sdk-map.md)** β€” the index: client construction, the one error family, the non-throwing `.asApiResult()` form, servers, environments and auth, the model locator, the runtime facts, and the SDK-wide defaults every operation relies on. +- **[`map/operations/`](map/operations/analysis.md)** β€” one page per resource: the exact signature and return type, the verb and route, the request body and its media type, a **Fields** table giving every request field its channel, and a **Type sources** table naming the file and schema value of every type the operation mentions. + +Model shapes β€” object properties with their wire names, enum member names and wire values, union variants β€” are **not** duplicated in the map, and not in the API reference either. Both name the type and the file to read; take the pair from the operation's **Type sources** table and read the declaring file. That file is the single source of truth and cannot go stale against the code. + +**Each operation block states only what is specific to it.** The SDK-wide defaults are stated once in [`sdk-map.md`](sdk-map.md) β€” the call shape, the base `ApiError`, the default server group β€” and a block departs from one only by saying so, so a block silent on a point is telling you the default applies. Take it and move on rather than opening the source to confirm. + +### Which one to reach for + +The map and the [API reference](api-reference.md) answer different questions, and both are generated from this SDK so they stay in lockstep with the code. + +| Use | For | +| --- | --- | +| **[`sdk-map.md`](sdk-map.md) + [`map/operations/`](map/operations/analysis.md)** | Traversing the SDK and working out its surface β€” locating the operation you need (this SDK exposes **16 operations**), its exact signature, which credential it sends, which channel every request field travels on, which error type it rejects with and how to read it, and the file behind any type. This is the index to consume the SDK from, and the one to reach for first. | +| **[`api-reference.md`](api-reference.md)** | Usage guidance for a single operation once you know which one you want β€” a code sample for each of the two call forms (awaiting it, and the non-throwing `.asApiResult()`), per-parameter descriptions, and the success and error types it resolves or rejects with. | + +--- + +## License + +This SDK is distributed under the [MIT License](LICENSE). + +--- + +## Support + +Refer to the [API reference](api-reference.md) for detailed information on available operations with code samples. + +--- + +[license-url]: LICENSE +[license-badge]: https://img.shields.io/badge/License-MIT-blue.svg +[apimatic-url]: https://www.apimatic.io +[apimatic-badge]: https://www.apimatic.io/hubfs/Built-with-APIMatic-badge.svg diff --git a/plugins/cloudinary-analysis/sdk/typescript/api-reference.md b/plugins/cloudinary-analysis/sdk/typescript/api-reference.md new file mode 100644 index 00000000..f8810f6e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/api-reference.md @@ -0,0 +1,1516 @@ +# Reference + +> Source: [AnalyzeApiBetaClient](src/client.ts) + +## Analysis + +> Source: [Analysis](src/resources/analysis.ts) + +
+analyzeAiVisionGeneral(request: Analysis.AnalyzeAiVisionGeneralRequestParams, options?: RequestOptions): ApiPromise<AnalyzeAiVisionGeneralResponse | null, Analysis.AnalyzeAiVisionGeneralError> + +
+
+ +### Description + +
+
+ +The General mode serves a wide array of applications by providing detailed answers to diverse questions about an image. Users can inquire about any aspect of an image, such as identifying objects, understanding scenes, or interpreting text within the image. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeAiVisionGeneral({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + prompts: ["Describe this image in detail", "Does this image contain an insect?"], + }, + }); + // TODO: Handle 'response' of type AnalyzeAiVisionGeneralResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeAiVisionGeneralError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeAiVisionGeneral({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + prompts: ["Describe this image in detail", "Does this image contain an insect?"], + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeAiVisionGeneralResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [AnalyzeAiVisionGeneralRequest](src/models/analyze-ai-vision-general-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeAiVisionGeneral(request)` + +- **OnSuccess**: [AnalyzeAiVisionGeneralResponse](src/models/analyze-ai-vision-general-response.ts) +- **OnError**: throws [Analysis.AnalyzeAiVisionGeneralError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeAiVisionGeneral(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeAiVisionGeneralResponse, Analysis.AnalyzeAiVisionGeneralError>, with `result.value` of type [AnalyzeAiVisionGeneralResponse](src/models/analyze-ai-vision-general-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeAiVisionModeration(request: Analysis.AnalyzeAiVisionModerationRequestParams, options?: RequestOptions): ApiPromise<AnalyzeAiVisionModerationResponse | null, Analysis.AnalyzeAiVisionModerationError> + +
+
+ +### Description + +
+
+ +The Moderation mode accepts multiple questions about an image, to which the response provides concise answers of "yes," "no," or "unknown." This functionality allows for a nuanced evaluation of whether the image adheres to specific content policies, creative specs, or aesthetic criteria. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeAiVisionModeration({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + rejectionQuestions: [ + "Does this image contain any violent activity?", + "Is there any nudity in the image?", + ], + }, + }); + // TODO: Handle 'response' of type AnalyzeAiVisionModerationResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeAiVisionModerationError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeAiVisionModeration({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + rejectionQuestions: [ + "Does this image contain any violent activity?", + "Is there any nudity in the image?", + ], + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeAiVisionModerationResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [AnalyzeAiVisionModerationRequest](src/models/analyze-ai-vision-moderation-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeAiVisionModeration(request)` + +- **OnSuccess**: [AnalyzeAiVisionModerationResponse](src/models/analyze-ai-vision-moderation-response.ts) +- **OnError**: throws [Analysis.AnalyzeAiVisionModerationError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeAiVisionModeration(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeAiVisionModerationResponse, Analysis.AnalyzeAiVisionModerationError>, with `result.value` of type [AnalyzeAiVisionModerationResponse](src/models/analyze-ai-vision-moderation-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeAiVisionTagging(request: Analysis.AnalyzeAiVisionTaggingRequestParams, options?: RequestOptions): ApiPromise<AnalyzeAiVisionTaggingResponse | null, Analysis.AnalyzeAiVisionTaggingError> + +
+
+ +### Description + +
+
+ +The Tagging mode accepts a list of tag names along with their corresponding descriptions. If the image matches the description, which may encompass various elements, it will be appropriately tagged. This approach enables customers to align with their own brand taxonomy, offering a dynamic, flexible, and open method for image classification. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeAiVisionTagging({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + tagDefinitions: [ + { name: "cigarettes", description: "Does the image contain a pack of cigarettes?" }, + { name: "furniture", description: "Does the image contain any tables, chairs, couches or sofas?" }, + ], + }, + }); + // TODO: Handle 'response' of type AnalyzeAiVisionTaggingResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeAiVisionTaggingError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeAiVisionTagging({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + tagDefinitions: [ + { name: "cigarettes", description: "Does the image contain a pack of cigarettes?" }, + { name: "furniture", description: "Does the image contain any tables, chairs, couches or sofas?" }, + ], + }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeAiVisionTaggingResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [AnalyzeAiVisionTaggingRequest](src/models/analyze-ai-vision-tagging-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeAiVisionTagging(request)` + +- **OnSuccess**: [AnalyzeAiVisionTaggingResponse](src/models/analyze-ai-vision-tagging-response.ts) +- **OnError**: throws [Analysis.AnalyzeAiVisionTaggingError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeAiVisionTagging(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeAiVisionTaggingResponse, Analysis.AnalyzeAiVisionTaggingError>, with `result.value` of type [AnalyzeAiVisionTaggingResponse](src/models/analyze-ai-vision-tagging-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeCaptioning(request: Analysis.AnalyzeCaptioningRequest, options?: RequestOptions): ApiPromise<AnalyzeCaptioningResponse | null, Analysis.AnalyzeCaptioningError> + +
+
+ +### Description + +
+
+ +Provides a caption for an image. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeCaptioning({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeCaptioningResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeCaptioningError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeCaptioning({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeCaptioningResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeCaptioning(request)` + +- **OnSuccess**: [AnalyzeCaptioningResponse](src/models/analyze-captioning-response.ts) +- **OnError**: throws [Analysis.AnalyzeCaptioningError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeCaptioning(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeCaptioningResponse, Analysis.AnalyzeCaptioningError>, with `result.value` of type [AnalyzeCaptioningResponse](src/models/analyze-captioning-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeCldFashion(request: Analysis.AnalyzeCldFashionRequest, options?: RequestOptions): ApiPromise<AnalyzeCldFashionResponse | null, Analysis.AnalyzeCldFashionError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Cld-Fashion](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. Cloudinary's fashion model is specifically dedicated to items of clothing. The response includes attributes of the clothing identified, for example whether the garment contains pockets, its material and the fastenings used. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeCldFashion({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeCldFashionResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeCldFashionError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeCldFashion({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeCldFashionResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeCldFashion(request)` + +- **OnSuccess**: [AnalyzeCldFashionResponse](src/models/analyze-cld-fashion-response.ts) +- **OnError**: throws [Analysis.AnalyzeCldFashionError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeCldFashion(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeCldFashionResponse, Analysis.AnalyzeCldFashionError>, with `result.value` of type [AnalyzeCldFashionResponse](src/models/analyze-cld-fashion-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeCldText(request: Analysis.AnalyzeCldTextRequest, options?: RequestOptions): ApiPromise<AnalyzeCldTextResponse | null, Analysis.AnalyzeCldTextError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Cld-Text](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. Cloudinary's text model tells you if your image includes text, and where it's located. Used with image tagging, you can then search for images that contain blocks of text. Used with object-aware cropping, you can choose to keep only the text part, or specify a crop that avoids the text. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeCldText({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeCldTextResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeCldTextError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeCldText({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeCldTextResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeCldText(request)` + +- **OnSuccess**: [AnalyzeCldTextResponse](src/models/analyze-cld-text-response.ts) +- **OnError**: throws [Analysis.AnalyzeCldTextError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeCldText(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeCldTextResponse, Analysis.AnalyzeCldTextError>, with `result.value` of type [AnalyzeCldTextResponse](src/models/analyze-cld-text-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeCoco(request: Analysis.AnalyzeCocoRequest, options?: RequestOptions): ApiPromise<AnalyzeCocoResponse | null, Analysis.AnalyzeCocoError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Coco](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. The [Common Objects in Context](https://cocodataset.org/) model contains just 80 common objects. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeCoco({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeCocoResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeCocoError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeCoco({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeCocoResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeCoco(request)` + +- **OnSuccess**: [AnalyzeCocoResponse](src/models/analyze-coco-response.ts) +- **OnError**: throws [Analysis.AnalyzeCocoError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeCoco(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeCocoResponse, Analysis.AnalyzeCocoError>, with `result.value` of type [AnalyzeCocoResponse](src/models/analyze-coco-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeGoogleLogoDetection(request: Analysis.AnalyzeGoogleLogoDetectionRequest, options?: RequestOptions): ApiPromise<AnalyzeGoogleLogoDetectionResponse | null, Analysis.AnalyzeGoogleLogoDetectionError> + +
+
+ +### Description + +
+
+ +Detects popular product logos within an image. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeGoogleLogoDetection({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeGoogleLogoDetectionResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeGoogleLogoDetectionError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeGoogleLogoDetection({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeGoogleLogoDetectionResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeGoogleLogoDetection(request)` + +- **OnSuccess**: [AnalyzeGoogleLogoDetectionResponse](src/models/analyze-google-logo-detection-response.ts) +- **OnError**: throws [Analysis.AnalyzeGoogleLogoDetectionError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeGoogleLogoDetection(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeGoogleLogoDetectionResponse, Analysis.AnalyzeGoogleLogoDetectionError>, with `result.value` of type [AnalyzeGoogleLogoDetectionResponse](src/models/analyze-google-logo-detection-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeGoogleTagging(request: Analysis.AnalyzeGoogleTaggingRequest, options?: RequestOptions): ApiPromise<AnalyzeGoogleTaggingResponse | null, Analysis.AnalyzeGoogleTaggingError> + +
+
+ +### Description + +
+
+ +Provides tags for an image using Google's tagging service. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeGoogleTagging({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeGoogleTaggingResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeGoogleTaggingError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeGoogleTagging({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeGoogleTaggingResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeGoogleTagging(request)` + +- **OnSuccess**: [AnalyzeGoogleTaggingResponse](src/models/analyze-google-tagging-response.ts) +- **OnError**: throws [Analysis.AnalyzeGoogleTaggingError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeGoogleTagging(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeGoogleTaggingResponse, Analysis.AnalyzeGoogleTaggingError>, with `result.value` of type [AnalyzeGoogleTaggingResponse](src/models/analyze-google-tagging-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeHumanAnatomy(request: Analysis.AnalyzeHumanAnatomyRequest, options?: RequestOptions): ApiPromise<AnalyzeHumanAnatomyResponse | null, Analysis.AnalyzeHumanAnatomyError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Human Anatomy](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. Cloudinary's human anatomy model identifies parts of the human body in an image. It works best when the majority of a human body is detected in the image. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeHumanAnatomy({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeHumanAnatomyResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeHumanAnatomyError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeHumanAnatomy({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeHumanAnatomyResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeHumanAnatomy(request)` + +- **OnSuccess**: [AnalyzeHumanAnatomyResponse](src/models/analyze-human-anatomy-response.ts) +- **OnError**: throws [Analysis.AnalyzeHumanAnatomyError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeHumanAnatomy(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeHumanAnatomyResponse, Analysis.AnalyzeHumanAnatomyError>, with `result.value` of type [AnalyzeHumanAnatomyResponse](src/models/analyze-human-anatomy-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeImageQuality(request: Analysis.AnalyzeImageQualityRequest, options?: RequestOptions): ApiPromise<AnalyzeImageQualityResponse | null, Analysis.AnalyzeImageQualityError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Image Quality Analysis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#image_quality_analysis) model. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeImageQuality({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeImageQualityResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeImageQualityError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeImageQuality({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeImageQualityResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeImageQuality(request)` + +- **OnSuccess**: [AnalyzeImageQualityResponse](src/models/analyze-image-quality-response.ts) +- **OnError**: throws [Analysis.AnalyzeImageQualityError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeImageQuality(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeImageQualityResponse, Analysis.AnalyzeImageQualityError>, with `result.value` of type [AnalyzeImageQualityResponse](src/models/analyze-image-quality-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeLvis(request: Analysis.AnalyzeLvisRequest, options?: RequestOptions): ApiPromise<AnalyzeLvisResponse | null, Analysis.AnalyzeLvisError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Lvis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. The [Large Vocabulary Instance Segmentation](https://www.lvisdataset.org/) model contains thousands of general objects. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeLvis({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeLvisResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeLvisError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeLvis({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeLvisResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeLvis(request)` + +- **OnSuccess**: [AnalyzeLvisResponse](src/models/analyze-lvis-response.ts) +- **OnError**: throws [Analysis.AnalyzeLvisError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeLvis(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeLvisResponse, Analysis.AnalyzeLvisError>, with `result.value` of type [AnalyzeLvisResponse](src/models/analyze-lvis-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeShopClassifier(request: Analysis.AnalyzeShopClassifierRequest, options?: RequestOptions): ApiPromise<AnalyzeShopClassifierResponse | null, Analysis.AnalyzeShopClassifierError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Shop Classifier](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. Cloudinary's shop classifier model detects if the image is a product image taken in a studio, or if it's a natural image. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeShopClassifier({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeShopClassifierResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeShopClassifierError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeShopClassifier({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeShopClassifierResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeShopClassifier(request)` + +- **OnSuccess**: [AnalyzeShopClassifierResponse](src/models/analyze-shop-classifier-response.ts) +- **OnError**: throws [Analysis.AnalyzeShopClassifierError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeShopClassifier(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeShopClassifierResponse, Analysis.AnalyzeShopClassifierError>, with `result.value` of type [AnalyzeShopClassifierResponse](src/models/analyze-shop-classifier-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeUnidet(request: Analysis.AnalyzeUnidetRequest, options?: RequestOptions): ApiPromise<AnalyzeUnidetResponse | null, Analysis.AnalyzeUnidetError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Unidet](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) content-aware detection model. The [UniDet](https://github.com/xingyizhou/UniDet) model is a unified model, combining a number of object models, including [Objects365](https://www.objects365.org/overview.html), which focuses on diverse objects in the wild. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeUnidet({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeUnidetResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeUnidetError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeUnidet({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeUnidetResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeUnidet(request)` + +- **OnSuccess**: [AnalyzeUnidetResponse](src/models/analyze-unidet-response.ts) +- **OnError**: throws [Analysis.AnalyzeUnidetError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeUnidet(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeUnidetResponse, Analysis.AnalyzeUnidetError>, with `result.value` of type [AnalyzeUnidetResponse](src/models/analyze-unidet-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +
+analyzeWatermarkDetection(request: Analysis.AnalyzeWatermarkDetectionRequest, options?: RequestOptions): ApiPromise<AnalyzeWatermarkDetectionResponse | null, Analysis.AnalyzeWatermarkDetectionError> + +
+
+ +### Description + +
+
+ +Analyze an image using the [Watermark Detection](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#watermark_detection) detection model. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.analysis.analyzeWatermarkDetection({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, + }); + // TODO: Handle 'response' of type AnalyzeWatermarkDetectionResponse +} catch (err) { + // TODO: Handle 'err' of type Analysis.AnalyzeWatermarkDetectionError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.analysis.analyzeWatermarkDetection({ + body: { source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" } }, +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type AnalyzeWatermarkDetectionResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| body | [BaseAnalyzeRequest](src/models/base-analyze-request.ts) | A JSON object containing request parameters | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.analysis.analyzeWatermarkDetection(request)` + +- **OnSuccess**: [AnalyzeWatermarkDetectionResponse](src/models/analyze-watermark-detection-response.ts) +- **OnError**: throws [Analysis.AnalyzeWatermarkDetectionError](src/resources/analysis.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.analysis.analyzeWatermarkDetection(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<AnalyzeWatermarkDetectionResponse, Analysis.AnalyzeWatermarkDetectionError>, with `result.value` of type [AnalyzeWatermarkDetectionResponse](src/models/analyze-watermark-detection-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ +## Tasks + +> Source: [Tasks](src/resources/tasks.ts) + +
+getAnalysisTaskStatus(request: Tasks.GetAnalysisTaskStatusRequest, options?: RequestOptions): ApiPromise<GetTaskStatusResponse, Tasks.GetAnalysisTaskStatusError> + +
+
+ +### Description + +
+
+ +Get the status of an analysis task. + +
+
+ +### Direct Usage + +
+
+ +```ts +try { + const response = await client.tasks.getAnalysisTaskStatus({ + taskId: + "053f4bde4b933c8ecef23724ecde63b667c1ea21816d56c161c7ec1df6297da4b43109625650e9edf0f42152cc4cc32c8ad57824ac75ba8e05020f827c415559ac1248076a2d72c0a73af0479cca77eb", + }); + // TODO: Handle 'response' of type GetTaskStatusResponse +} catch (err) { + // TODO: Handle 'err' of type Tasks.GetAnalysisTaskStatusError, discriminated with 'err.payload.kind' +} +``` + +
+
+ +### Usage as ApiResult + +
+
+ +```ts +const result = await client.tasks.getAnalysisTaskStatus({ + taskId: + "053f4bde4b933c8ecef23724ecde63b667c1ea21816d56c161c7ec1df6297da4b43109625650e9edf0f42152cc4cc32c8ad57824ac75ba8e05020f827c415559ac1248076a2d72c0a73af0479cca77eb", +}).asApiResult(); +// TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers +if (result.ok) { + // TODO: Use 'result.value' of type GetTaskStatusResponse +} else { + // TODO: Handle 'result', discriminated with 'result.payload.kind' +} +``` + +
+
+ +### Parameters + +
+
+ +| Name | Type | Description | +| --- | --- | --- | +| taskId | string | The ID of the analysis task | + +
+
+ +### Response + +
+
+ +**Direct**: `await client.tasks.getAnalysisTaskStatus(request)` + +- **OnSuccess**: [GetTaskStatusResponse](src/models/get-task-status-response.ts) +- **OnError**: throws [Tasks.GetAnalysisTaskStatusError](src/resources/tasks.ts), with `err.payload` discriminated on `kind` + +**As ApiResult**: `await client.tasks.getAnalysisTaskStatus(request).asApiResult()` + +- **OnSuccess**: [ApiResult](src/core/api-promise.ts)<GetTaskStatusResponse, Tasks.GetAnalysisTaskStatusError>, with `result.value` of type [GetTaskStatusResponse](src/models/get-task-status-response.ts) +- **OnError**: `result.payload` discriminated on `kind`, with `result.message` + +**Thrown**: [AnalyzeApiBetaError](src/core/errors.ts) on any operational failure β€” a caller abort and a programmer error stay outside the family and reach you raw + +
+
+ +
+
+ +
+ diff --git a/plugins/cloudinary-analysis/sdk/typescript/map/operations/analysis.md b/plugins/cloudinary-analysis/sdk/typescript/map/operations/analysis.md new file mode 100644 index 00000000..3956e160 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/map/operations/analysis.md @@ -0,0 +1,368 @@ + + +# Analysis β€” operations + +Accessor: `client.analysis` Β· Source: `src/resources/analysis.ts` Β· 15 operations Β· Request and error types: namespace `Analysis` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `analyze-api-beta`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### analyzeAiVisionGeneral + +- **Signature**: `analyzeAiVisionGeneral(request: Analysis.AnalyzeAiVisionGeneralRequestParams, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/ai_vision_general` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeAiVisionGeneralResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeAiVisionGeneralError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeAiVisionGeneralRequestParams` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `AnalyzeAiVisionGeneralRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AnalyzeAiVisionGeneralRequest` | `analyzeAiVisionGeneralRequestSchema` | `src/models/analyze-ai-vision-general-request.ts` | +| `AnalyzeAiVisionGeneralResponse` | `analyzeAiVisionGeneralResponseSchema` | `src/models/analyze-ai-vision-general-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeAiVisionModeration + +- **Signature**: `analyzeAiVisionModeration(request: Analysis.AnalyzeAiVisionModerationRequestParams, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/ai_vision_moderation` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeAiVisionModerationResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeAiVisionModerationError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeAiVisionModerationRequestParams` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `AnalyzeAiVisionModerationRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AnalyzeAiVisionModerationRequest` | `analyzeAiVisionModerationRequestSchema` | `src/models/analyze-ai-vision-moderation-request.ts` | +| `AnalyzeAiVisionModerationResponse` | `analyzeAiVisionModerationResponseSchema` | `src/models/analyze-ai-vision-moderation-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeAiVisionTagging + +- **Signature**: `analyzeAiVisionTagging(request: Analysis.AnalyzeAiVisionTaggingRequestParams, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/ai_vision_tagging` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeAiVisionTaggingResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeAiVisionTaggingError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeAiVisionTaggingRequestParams` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `AnalyzeAiVisionTaggingRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `AnalyzeAiVisionTaggingRequest` | `analyzeAiVisionTaggingRequestSchema` | `src/models/analyze-ai-vision-tagging-request.ts` | +| `AnalyzeAiVisionTaggingResponse` | `analyzeAiVisionTaggingResponseSchema` | `src/models/analyze-ai-vision-tagging-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeCaptioning + +- **Signature**: `analyzeCaptioning(request: Analysis.AnalyzeCaptioningRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/captioning` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeCaptioningResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeCaptioningError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeCaptioningRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeCaptioningResponse` | `analyzeCaptioningResponseSchema` | `src/models/analyze-captioning-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeCldFashion + +- **Signature**: `analyzeCldFashion(request: Analysis.AnalyzeCldFashionRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/cld_fashion` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeCldFashionResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeCldFashionError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeCldFashionRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeCldFashionResponse` | `analyzeCldFashionResponseSchema` | `src/models/analyze-cld-fashion-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeCldText + +- **Signature**: `analyzeCldText(request: Analysis.AnalyzeCldTextRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/cld_text` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeCldTextResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeCldTextError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeCldTextRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeCldTextResponse` | `analyzeCldTextResponseSchema` | `src/models/analyze-cld-text-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeCoco + +- **Signature**: `analyzeCoco(request: Analysis.AnalyzeCocoRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/coco` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeCocoResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeCocoError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeCocoRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeCocoResponse` | `analyzeCocoResponseSchema` | `src/models/analyze-coco-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeGoogleLogoDetection + +- **Signature**: `analyzeGoogleLogoDetection(request: Analysis.AnalyzeGoogleLogoDetectionRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/google_logo_detection` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeGoogleLogoDetectionResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeGoogleLogoDetectionError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeGoogleLogoDetectionRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeGoogleLogoDetectionResponse` | `analyzeGoogleLogoDetectionResponseSchema` | `src/models/analyze-google-logo-detection-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeGoogleTagging + +- **Signature**: `analyzeGoogleTagging(request: Analysis.AnalyzeGoogleTaggingRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/google_tagging` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeGoogleTaggingResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeGoogleTaggingError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeGoogleTaggingRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeGoogleTaggingResponse` | `analyzeGoogleTaggingResponseSchema` | `src/models/analyze-google-tagging-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeHumanAnatomy + +- **Signature**: `analyzeHumanAnatomy(request: Analysis.AnalyzeHumanAnatomyRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/human_anatomy` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeHumanAnatomyResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeHumanAnatomyError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeHumanAnatomyRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeHumanAnatomyResponse` | `analyzeHumanAnatomyResponseSchema` | `src/models/analyze-human-anatomy-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeImageQuality + +- **Signature**: `analyzeImageQuality(request: Analysis.AnalyzeImageQualityRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/image_quality` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeImageQualityResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeImageQualityError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeImageQualityRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeImageQualityResponse` | `analyzeImageQualityResponseSchema` | `src/models/analyze-image-quality-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeLvis + +- **Signature**: `analyzeLvis(request: Analysis.AnalyzeLvisRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/lvis` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeLvisResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeLvisError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeLvisRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeLvisResponse` | `analyzeLvisResponseSchema` | `src/models/analyze-lvis-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeShopClassifier + +- **Signature**: `analyzeShopClassifier(request: Analysis.AnalyzeShopClassifierRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/shop_classifier` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeShopClassifierResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeShopClassifierError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeShopClassifierRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeShopClassifierResponse` | `analyzeShopClassifierResponseSchema` | `src/models/analyze-shop-classifier-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeUnidet + +- **Signature**: `analyzeUnidet(request: Analysis.AnalyzeUnidetRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/unidet` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeUnidetResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeUnidetError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeUnidetRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeUnidetResponse` | `analyzeUnidetResponseSchema` | `src/models/analyze-unidet-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + +### analyzeWatermarkDetection + +- **Signature**: `analyzeWatermarkDetection(request: Analysis.AnalyzeWatermarkDetectionRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `POST /analyze/watermark_detection` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: `application/json` β€” the `body` field +- **SDK-sent**: `header Idempotency-Key` (minted per call) +- **Returns**: `AnalyzeWatermarkDetectionResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Analysis.AnalyzeWatermarkDetectionError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Analysis.AnalyzeWatermarkDetectionRequest` (1): + +| Field | Channel | Type | Req | +| --- | --- | --- | --- | +| `body` | `body` | `BaseAnalyzeRequest` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `BaseAnalyzeRequest` | `baseAnalyzeRequestSchema` | `src/models/base-analyze-request.ts` | +| `AnalyzeWatermarkDetectionResponse` | `analyzeWatermarkDetectionResponseSchema` | `src/models/analyze-watermark-detection-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + diff --git a/plugins/cloudinary-analysis/sdk/typescript/map/operations/tasks.md b/plugins/cloudinary-analysis/sdk/typescript/map/operations/tasks.md new file mode 100644 index 00000000..83264f3e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/map/operations/tasks.md @@ -0,0 +1,30 @@ + + +# Tasks β€” operations + +Accessor: `client.tasks` Β· Source: `src/resources/tasks.ts` Β· 1 operation Β· Request and error types: namespace `Tasks` + +**Type sources**: every type an operation names, with the file that declares it and the schema value exported beside it. Import every name from `analyze-api-beta`; the `Source` path is where to **read** the shape, never what to import. `ApiError`, the runtime error family and the file vocabulary are excluded β€” see sdk-map.md. + +### getAnalysisTaskStatus + +- **Signature**: `getAnalysisTaskStatus(request: Tasks.GetAnalysisTaskStatusRequest, options?: RequestOptions): ApiPromise` +- **Wire**: `GET /tasks/{task_id}` +- **Auth**: any of `basicAuth`, `oAuth2` β€” the first one configured is sent +- **Request body**: none β€” no `Content-Type` header is sent +- **Returns**: `GetTaskStatusResponse` +- **Error**: `AnalyzeApiBetaError` with `kind: "api"`, an instance of `Tasks.GetAnalysisTaskStatusError` β€” **typed arms**, narrowed on `err.payload.kind` +- **Error arms**: `"errorResponse"` [400] `ErrorResponse` Β· `"errorResponse2"` [401] `ErrorResponse` Β· `"errorResponse3"` [403] `ErrorResponse` Β· `"errorResponse4"` [404] `ErrorResponse` Β· `"rateLimitedResponse1"` [429] `RateLimitedResponse1` Β· `"errorResponse5"` [500] `ErrorResponse` Β· `"undeclared"` [any other] `rawBody: ArrayBuffer` + +**Fields** β€” `Tasks.GetAnalysisTaskStatusRequest` (1): + +| Field | Channel | Wire | Type | Req | +| --- | --- | --- | --- | --- | +| `taskId` | `path` | `task_id` | `string` | yes | + +| Type | Schema value | Source | +| --- | --- | --- | +| `GetTaskStatusResponse` | `getTaskStatusResponseSchema` | `src/models/get-task-status-response.ts` | +| `ErrorResponse` | `errorResponseSchema` | `src/models/error-response.ts` | +| `RateLimitedResponse1` | `rateLimitedResponse1Schema` | `src/models/rate-limited-response1.ts` | + diff --git a/plugins/cloudinary-analysis/sdk/typescript/package.json b/plugins/cloudinary-analysis/sdk/typescript/package.json new file mode 100644 index 00000000..8ca5d244 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/package.json @@ -0,0 +1,70 @@ +{ + "name": "analyze-api-beta", + "version": "0.2.0", + "description": "Use the Analyze API to analyze any external asset and return details based on the type of analysis requested.\n\nCurrently supports the following analysis options:\n * [AI Vision - Tagging](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#tagging_mode)\n * [AI Vision - Moderation](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#moderation_mode)\n * [AI Vision - General](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#general_mode)\n * [Captioning](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning)\n * [Cld Fashion](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Cld Text](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Coco](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Google Tagging](https://cloudinary.com/documentation/google_auto_tagging_addon)\n * [Human Anatomy](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Image Quality Analysis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#image_quality_analysis)\n * [Lvis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Shop Classifier](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Unidet](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models)\n * [Watermark Detection](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#watermark_detection)\n\n **Notes**: \n * The Analyze API is currently in development and is available as a Public Beta, which means we value your feedback, so please feel free to [share any thoughts with us](https://support.cloudinary.com/hc/en-us/requests/new).\n * The analysis options require an active subscription to the relevant add-on. Learn more about [registering for add-ons](https://cloudinary.com/documentation/cloudinary_add_ons#registering_for_add_ons).\n\n The API supports both Basic Authentication using your Cloudinary API Key and API Secret (which can be found on the Dashboard page of your [Cloudinary Console](https://console.cloudinary.com/pm)) or OAuth2 ([Contact support](https://support.cloudinary.com/hc/en-us/requests/new) for more information regarding OAuth).\n", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "dialects": [ + "esm", + "commonjs" + ], + "project": "./tsconfig.tshy.json", + "selfLink": false + }, + "main": "./dist/commonjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/commonjs/index.d.ts", + "files": [ + "dist", + "src", + "tsconfig.json", + "tsconfig.tshy.json", + "README.md", + "api-reference.md", + "sdk-map.md", + "map" + ], + "sideEffects": false, + "engines": { + "node": ">=20.3" + }, + "scripts": { + "prepare": "tshy", + "build": "tshy", + "smoke": "node -e \"require('analyze-api-beta')\" && node --input-type=module -e \"import('analyze-api-beta')\"", + "typecheck": "tsc --noEmit", + "typecheck:dom": "tsc --noEmit -p tsconfig.dom.json", + "lint": "oxlint --type-aware --max-warnings=0 src", + "format": "oxfmt src", + "format:check": "oxfmt --check src" + }, + "license": "MIT", + "devDependencies": { + "@types/node": "^20.14.0", + "oxfmt": "^0.61.0", + "oxlint": "^1.76.0", + "oxlint-tsgolint": "^7.0.2001", + "tshy": "^4.1.3", + "typescript": "^7.0.2" + }, + "dependencies": { + "zod": "^4.1.0" + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/sdk-map.md b/plugins/cloudinary-analysis/sdk/typescript/sdk-map.md new file mode 100644 index 00000000..31e94365 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/sdk-map.md @@ -0,0 +1,378 @@ + + +# SDK map β€” Analyze API (Beta) (TypeScript) + +> A generated table of contents for this SDK. Consult this map and its sub-pages to learn signatures, request-field placement, error types and server wiring **by lookup**. Model shapes and enum values are *not* duplicated here β€” the map names the file declaring each type and the schema value exported beside it; read the shape there. The compiler is the backstop: a wrong name fails to build. + +| | | +| --- | --- | +| SDK display name | Analyze API (Beta) | +| Package | `analyze-api-beta` | +| Package version | `0.2.0` | +| API spec version | `0.2.0` | +| Import specifier | `analyze-api-beta` β€” the package root is the **only** entry. Deep imports (`analyze-api-beta/models/...`) do not resolve; the `exports` map exposes `.` and `./package.json` and nothing else | +| Module format | dual ESM + CommonJS, as folder dialects (`dist/esm`, `dist/commonjs`), each with its own `package.json` marker. No `.mjs`, `.cjs`, `.d.mts` or `.d.cts` files exist | +| Node floor | `>=20.3` (`engines.node`) | +| TypeScript floor | a resolver that reads `exports` (4.7+), plus whatever the pinned `zod` requires β€” `zod@4` needs 5.5 or later. The public `.d.ts` chain reaches `zod/v4-mini`, so this is a real constraint rather than a build-tool version | +| Runtime dependency | `zod` (`^4.1.0`), imported as `zod/v4-mini`. The only runtime dependency | +| Generator | APIMatic | + +Staleness check: the API spec version above changes when the SDK is regenerated from a new spec. If a lookup here fails to compile, trust the compiler and re-read the source file named in the row. + +All `Source` paths on this map and its sub-pages are relative to the **SDK root** β€” the directory holding this file and `package.json` β€” never to the page that carries them: a page two directories deep writes exactly what a page at the root would. The package ships its `src/` tree, so the same paths resolve inside `node_modules/analyze-api-beta/` too. An import specifier ending `.js` inside that source is the NodeNext spelling of the sibling `.ts` file. + +--- + +## Getting a client + +```ts +import { AnalyzeApiBetaClient, PkceMethod } from "analyze-api-beta"; + +const client = new AnalyzeApiBetaClient({ + basicAuth: { username: "YOUR_USERNAME", password: "YOUR_PASSWORD" }, + oAuth2: { + clientId: "YOUR_CLIENT_ID", + redirectUri: "YOUR_REDIRECT_URI", + pkce: PkceMethod.S256, + promptForAuthorizationCode: (authorizationUrl, signal) => Promise.resolve("YOUR_AUTHORIZATION_CODE"), + }, +}); +``` + +The only constructor is `new AnalyzeApiBetaClient(options: ClientOptions = {})`, so `new AnalyzeApiBetaClient()` is the minimum. Resources are memoized lazy getters on the client β€” `client.analysis`, `client.tasks` β€” and their classes are exported only for their merged namespaces and for `instanceof`; their constructors take engine internals that are not exported, so reach a resource only through its getter. + +All `ClientOptions` fields (source: `src/client-options.ts`; every field is `readonly`): + +| Field | Type | Default | +| --- | --- | --- | +| `serverOptions` | the server's base-URL and template-variable overrides | `{}` β€” the resolver merges the declared defaults in | +| `retry` | `RetryOptions` | the `RetryOptions` defaults below | +| `logging` | `LoggingOptions` | off β€” the `LoggingOptions` defaults below | +| `fetch` | `FetchLike \| undefined` | the global `fetch`, resolved by the transport | +| `basicAuth` | `BasicAuthCredentials \| undefined` | unset | +| `oAuth2` | `OAuth2AuthorizationCodeCredentials \| undefined` | unset | +| `oAuth2Strategy` | `OAuth2RefreshableTokenStrategy \| undefined` | the built-in grant | + +The 3 auth fields are all optional, and an unset one is not an error β€” the operation that wanted it simply sends no credential. What each one puts on the wire, and which operations require it, are under Servers & auth. + +When no `fetch` is reachable the **constructor** throws `ConfigurationError`, not the first call. + +`RetryOptions` fields (source: `src/core/retry.ts`; exported from `analyze-api-beta` as a type). Every field is optional, so pass only the fields you change β€” each one left out takes its default: + +| Field | Type | Default | +| --- | --- | --- | +| `timeout` | `number` (ms) | `60_000` | +| `statusCodesToRetry` | `readonly number[]` | `[408, 429, 500, 502, 503, 504]` | +| `httpMethodsToRetry` | `readonly HttpMethod[]` | `["GET", "HEAD", "PUT", "OPTIONS"]` | +| `maxRetries` | `number` | `3` | +| `delay` | `number` (ms) | `1000` | +| `backoffFactor` | `number` | `2` | +| `useExponentialBackoff` | `boolean` | `true` | +| `maxJitter` | `number` (a fraction, `0` to `1`) | `0.25` | +| `onRetry` | `((attempt: RetryAttempt) => void) \| undefined` | unset | + +`retry: { maxRetries: 0 }` turns retries off. + +A call may override three of these through `RequestOptions.retry`, typed `RequestRetryOptions`: `maxRetries`, `timeout` and `statusCodesToRetry`. + +Retry types named by the fields above β€” public members with their **declared types**, verbatim from source; every member is `readonly`, and both are exported as types: + +| Type | Public members | Source | +| --- | --- | --- | +| `RetryAttempt` β€” the `onRetry` callback argument | `attemptNumber: number` Β· `delay: number` Β· `reason: RetryReason` | `src/core/retry.ts` | +| `RetryReason` β€” narrow on `kind` | `{ kind: "status"; status: number; headers: Headers }` or `{ kind: "fault"; error: ConnectionError \| TimeoutError }`, the two retryable leaves of `AnalyzeApiBetaError` | `src/core/retry.ts` | + +`LoggingOptions` fields (source: `src/core/logging.ts`; exported from `analyze-api-beta` as a type). Every field is optional, so pass only the fields you change β€” each one left out takes its default: + +| Field | Type | Default | +| --- | --- | --- | +| `logger` | `Logger` | `console` | +| `level` | `LogLevel` β€” `"off" \| "error" \| "warn" \| "info" \| "debug"` | `"off"`; `"debug"` when `logger` is set | +| `logRequestHeaders` | `boolean` | `false` | +| `logResponseHeaders` | `boolean` | `false` | +| `logRequestBody` | `boolean` | `false` | +| `bodySizeLimit` | `number` (bytes of UTF-8) | `32_768` | +| `loggableContentTypes` | `readonly string[]` | `["application/json", "application/x-www-form-urlencoded"]` | +| `redactedHeaders` | `readonly string[]` | `[]` | +| `unmaskHeaders` | `readonly string[]` | `[]` | +| `redactedKeys` | `readonly string[]` | `["sig", "signature", "access_token", "apikey", "api_key", "client_secret", "password", "refresh_token", "code", "assertion", "client_assertion"]` | +| `redactionPlaceholder` | `string` | `"***"` | + +A call may override one of these through `RequestOptions.logging`, typed `RequestLoggingOptions`: `level`. + +Logging types named by the fields above β€” their **declared types**, verbatim from source; both are exported as types: + +| Type | Public members | Source | +| --- | --- | --- | +| `Logger` β€” the `logger` field | `error(message: string, details: LogDetails): void` Β· `warn(message: string, details: LogDetails): void` Β· `info(message: string, details: LogDetails): void` Β· `debug(message: string, details: LogDetails): void` | `src/core/logging.ts` | +| `LogDetails` β€” narrow on `event` | `HttpRequestDetails \| RequestHeadersDetails \| RequestBodyDetails \| HttpResponseDetails \| ResponseHeadersDetails \| HttpRetryingDetails \| HttpFailedDetails`, each exported as a type | `src/core/logging.ts` | + +**`ClientOptions.fetch` is the one transport extension point** β€” there are no hooks, no middleware and no interceptors, so a proxy, a custom agent and extra headers all go here. The SDK calls a replacement with one argument, the `Request` of the attempt, and no `init`. A replacement **must forward that `Request`, or its `signal`,** to whatever actually performs the request. Drop the signal and both the per-call signal and `retry.timeout` go inert β€” the call neither aborts nor times out. To add a header, copy the `Request` with `new Request(request)` and set it on the copy's `headers`: passing `{ headers }` to the copy replaces every header the SDK set rather than adding to them. A replacement may reject with an error of its own when that signal aborts: a timeout is still a `TimeoutError` that is retried, and an abort of `RequestOptions.signal` still rejects with the caller's own `reason`. + +**Cancellation.** The `signal` on `RequestOptions` is the per-request cancellation surface. Aborting rejects with the signal's **own `reason`** β€” whatever you passed to `abort()`, or the platform `DOMException` a bare `abort()` supplies β€” unwrapped, so it is **not** an `AnalyzeApiBetaError` and a `catch` that tests the family must rethrow it. An already-aborted signal rejects immediately. The `retry.timeout` that bounded the attempt is the SDK's own and does stay in the family, as `err.kind === "timeout"`. It starts once the credential is in hand and covers the request up to its response headers β€” not obtaining the credential and not reading the body, which only the signal bounds, so a body that stalls after its headers holds a call with no signal until the transport gives up. A built-in OAuth2 token request is timed on its own, so a slow token endpoint ends the call with a `TimeoutError` whose `uri` is the token endpoint. + +The entire per-request surface is the optional second argument of every operation: + +| Type | Members | Source | +| --- | --- | --- | +| `RequestOptions` | `signal?: AbortSignal \| undefined` Β· `retry?: RequestRetryOptions` Β· `logging?: RequestLoggingOptions` | `src/core/api-request.ts` | +| `RequestRetryOptions` β€” `Pick` | `maxRetries?: number` Β· `timeout?: number` Β· `statusCodesToRetry?: readonly number[]` | `src/core/retry.ts` | +| `RequestLoggingOptions` β€” `Pick` | `level?: LogLevel` | `src/core/logging.ts` | + +**A per-call `retry` is merged field by field over the client's resolved policy**, so `{ retry: { maxRetries: 0 } }` changes that one field for that one call and leaves every other call alone. + +**Not on this SDK.** These are absent by design, not undocumented. This table ships with `src/core/` and is versioned with it. + +| You might reach for | Reality | +| --- | --- | +| hooks, middleware, interceptors, `onRequest`/`onResponse` | none. `fetch` is the one transport extension point | +| pagination, `for await`, auto-paging helpers | no operation is paginated and nothing is async-iterable | +| SSE, `text/event-stream` | no event streams. Every decoder reads the body to completion, bar a binary success, which hands its stream over unread | +| multipart responses, XML bodies | none. A multipart reply is not decoded and an XML body is not sent β€” an operation declaring either is still emitted, with no body to supply or read | +| per-request `headers`, `baseUrl`, idempotency key | none. `RequestOptions` is `{ signal, retry, logging }`; a header, a base URL and a caller-supplied idempotency key are not on it | +| the raw `fetch` `Response` | deliberately unreachable. `status` and `headers` are on `asApiResult()` and on a thrown `ResponseError` | + +--- + +## Error-handling model (read once β€” applies to every operation) + +Operations are **throw-based**, and every **operational** failure belongs to **one family**: `AnalyzeApiBetaError`, a union over six leaves, so one `instanceof AnalyzeApiBetaError` sees all of them. It is not the whole escape set β€” four throwables sit outside it, enumerated below. Every leaf names the call it raised β€” `err.method` and `err.uri` β€” and `message` opens with that name. `instanceof` is reliable **within one dialect**: a process that loads both β€” `import` in one file, `require` in another β€” gets two independent copies of every error class, and `instanceof` across that boundary is `false`. Narrow on `err.kind` there, or on `err.name`, which is stable across copies. + +Core types (public members with their declared types; all are `readonly`): + +| Type | Public members | Source | +| --- | --- | --- | +| `AnalyzeApiBetaError` (declared as `CoreError`) | `kind: ErrorKind` Β· `method: HttpMethod` Β· `uri: string` Β· `message` Β· `cause` β€” the union every failure below belongs to | `src/core/errors.ts` | +| `ResponseError` | the rung the server answered on, `ApiError \| DecodeError`; adds `status: number` Β· `headers: Headers` | `src/core/errors.ts` | +| `ApiError` | `kind: "api"` Β· `payload` β€” the open arm, whose `kind` is `string`. **Not generic**: a typed operation's subclass redeclares `payload` with its own literal arms | `src/core/api-error.ts` | +| `TimeoutError` | `kind: "timeout"` Β· `timeout: number` | `src/core/errors.ts` | +| `DecodeError`, `EncodeError`, `ConnectionError`, `AuthError` | their `kind`, and nothing beyond the two rows above | `src/core/errors.ts` | +| `Declared` | `kind: K` Β· `body: B` | `src/core/api-error.ts` | +| `Undeclared` | `kind: "undeclared"` Β· `rawBody: ArrayBuffer` β€” the last arm of every error class, carrying the untouched bytes of a status no arm covers, a middleware's included | `src/core/api-error.ts` | +| `ApiResult` | on success `{ ok: true; status; headers; value: T }`, on failure `{ ok: false; status; headers; message: string; method: HttpMethod; uri: string; payload: P }`, `P` being the error class's payload union β€” the failure branch carries the error's own members, never the error object | `src/core/api-promise.ts` | + +`AnalyzeApiBetaError` and `ResponseError` are each a **type and a value**: the type is the union, the value is the abstract class every leaf extends, so `instanceof` and `err.kind` select the same set. Neither can be constructed or extended. `uri` is the absolute URL the call dialled, with the server variables expanded and the path parameters filled. It carries no query, fragment or userinfo, so no query parameter reaches it. One failure names an unresolved URI: a path parameter rejected by its schema arrives as an `EncodeError` whose `uri` still shows the unfilled `{braces}` β€” an `undefined` one included, since a path parameter is always required, so its schema rejects it first. On an `AuthError` the pair names the **operation you called**, not the token endpoint, which appears after it in the message. + +`ErrorKind` is closed, so a `switch` over `err.kind` is exhaustive: + +| `err.kind` | What happened | Adds | +| --- | --- | --- | +| `"api"` | the API answered with an error status | `status` Β· `headers` Β· `payload` | +| `"decode"` | the answer could not be turned into the declared value β€” the body was not JSON, failed its schema, arrived where none is declared, or died mid-read after the response line; `cause` carries the underlying failure | `status` Β· `headers` | +| `"encode"` | a request value did not match its declared type, so **nothing was sent**. `cause` is the `SchemaError` that rejected it | β€” | +| `"connection"` | `fetch` rejected before a response line arrived | β€” | +| `"timeout"` | `ClientOptions.retry.timeout` elapsed. `timeout` is the budget that ran out | `timeout` | +| `"auth"` | a credential could not be **obtained**, per the paragraph below | β€” | + +**Four throwables sit outside the family**, so `instanceof AnalyzeApiBetaError` is `false` on each and a `catch` that tests it has to rethrow what is left. `ConfigurationError` comes out of the **`AnalyzeApiBetaClient` constructor**, synchronously and before any `ApiPromise` exists: no reachable `fetch`, a `basicAuth.username` containing a colon (invalid per RFC 7617 Β§2, because the first colon separates user-id from password), or a `ClientOptions.retry` or `ClientOptions.logging` whose reads throw. One call can reject with it too: a `RequestOptions.retry` or `RequestOptions.logging` whose reads throw. Either way the failed read is on `cause`. `SchemaError` is what a codec throws when called directly β€” `aiVisionGeneralAnalysisDataSchema.decode(json)` β€” so it names no call; through an operation the same failure arrives one level down, on `DecodeError.cause` or `EncodeError.cause`, and a `serverOptions` override whose value is not a string raises it from the constructor too. Bugs stay outside the family and reach you raw β€” an unparseable `baseUrl` is a `TypeError`. And a caller abort arrives as the signal's own `reason`, unwrapped. The first two are exported from the package root; the other two are not ours to export. + +**`AuthError` is about obtaining a credential, never about being refused one** β€” a refused token endpoint, every configured branch of an alternatives requirement failing, PKCE disabled with no client secret, or your own `promptForAuthorizationCode` rejecting. A 401 *from the API* is an `ApiError` like any other status. A 401 does have one auth consequence: it invalidates whatever that operation's scheme had cached, so the **next** call re-acquires. An alternatives requirement **falls through**: a configured scheme that throws is not the end of it, the next configured one is tried, and only when all of them have failed does it throw. When exactly one was configured, its failure surfaces as it would from that scheme alone β€” a refused token endpoint is still an `AuthError` with the endpoint's `ApiError` on `cause`, never the bare `ApiError`. Otherwise it is an `AuthError` whose `cause` is an `AggregateError` holding what each branch threw, in the order tried. Cancellation is the exception: an abort or a timeout escapes immediately rather than being collected. + +```ts +try { + const response = await client.analysis.analyzeAiVisionGeneral({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + prompts: ["Describe this image in detail", "Does this image contain an insect?"], + }, + }); +} catch (err) { + if (err instanceof AnalyzeApiBetaError) { + switch (err.kind) { + case "api": + // TODO: the API answered with an error status β€” read err.status and err.payload + break; + case "decode": + // TODO: the answer did not fit the spec β€” read err.status and err.cause + break; + case "encode": + // TODO: nothing was sent β€” err.cause is the SchemaError that rejected the value + break; + case "connection": + case "timeout": + case "auth": + // TODO: no response was produced β€” err.kind says which + break; + } + } else { + throw err; + } +} +``` + +**Narrowing the payload.** A typed subclass declares its arms as literals, so `switch (err.payload.kind)` narrows `payload.body` to exactly one model. The `kind` is named after the arm's **body**, *not* its status code: a body that references a model takes that model's name in lower camel, any other body `error{Status}`, and a second arm that would land on the same name takes a numeric suffix. On the base `ApiError` β€” what an operation with no declared error bodies rejects with β€” `payload.kind` is `string`, so comparing it to `"undeclared"` narrows **nothing**: use `"rawBody" in err.payload`. Which arms an operation declares, with the status each covers, is the **Error arms** bullet on its page below. + +**Matcher precedence** for a subclass with several arms, in two passes: an exact numeric status is looked up across the whole table **first**, then the first covering `[lo, hi]` range. A body that does not fit the arm it matched is a `DecodeError`. A status no row covers is the `"undeclared"` arm with its bytes untouched β€” a spec's `default` response is not a row, so a gateway's or a WAF's own status lands there rather than failing the `default` schema. + +**The non-throwing form exists on every operation.** `.asApiResult()` returns `ApiResult` and does **not** reject for an HTTP error status β€” every other failure still rejects, `DecodeError` included, so the `catch` stays. It must be called on the value the operation returned: `ApiPromise` overrides `Symbol.species`, so `.then()`, `.catch()` and `.finally()` hand back a plain `Promise` and the method is gone. + +```ts +try { + const result = await client.analysis.analyzeAiVisionGeneral({ + body: { + source: { uri: "https://res.cloudinary.com/demo/image/upload/sample.jpg" }, + prompts: ["Describe this image in detail", "Does this image contain an insect?"], + }, + }).asApiResult(); + // TODO: Use 'result.status' and 'result.headers' to read the raw response status and headers + if (result.ok) { + // TODO: Use 'result.value' β€” what this operation resolves to + } else { + // TODO: Use 'result.message', 'result.method' and 'result.uri', and narrow 'result.payload' + } +} catch (err) { + if (err instanceof AnalyzeApiBetaError) { + // TODO: no error status was produced β€” err.kind says which failure this is + } else { + throw err; + } +} +``` + +`result.payload` is the same payload union a thrown `ApiError` carries on `err.payload`, and `result.message`, `result.method` and `result.uri` are that error's own members β€” so the **Error arms** bullet on an operation's page enumerates the payload either way, and the `catch` above it is for the rest of the family. `result.method` and `result.uri` are named on this map alone. + +Of **16 operations**, **16** declare typed error bodies and **0** reject with the base `ApiError`, whose payload is always the `"undeclared"` arm. + +--- + +## Operations β€” by resource (2 groups, 16 operations) + +Each page below carries one block per operation, with bullets in the fixed order **Server**, **Signature**, **Wire**, **Auth**, **Request body**, **SDK-sent**, **Returns**, **Error**, **Error arms**, then a **Fields** table mapping every request field to the channel it travels on, and a **Type sources** table naming the declaring file and schema value of every type the operation mentions. With `api-reference.md` documenting operations only, that table is the route from an operation to the file declaring what it takes. + +**Each block states what is specific to its operation. Everything in the table below holds for EVERY operation unless that operation says otherwise, so a block silent on one of these points is telling you the default here applies β€” take it and move on rather than opening the source to confirm it.** + +| Applies to every operation | Stated where | A block departs from it only by | +| --- | --- | --- | +| **Call shape `op(request, options?)`** β€” one flat request object first, the per-call options second. There is no positional overload. What the second argument carries is a `signal`, a `retry` override and a log level; a per-call base URL, header or auth override does not exist | here, Getting a client | never β€” it always holds | +| **The request object is flat and channel-blind.** A field named `body` *is* the whole request body; every other field is fanned out to path, query, header or form by the SDK. Nothing in the object is nested by channel | here | never β€” the **Fields** table `Channel` column always resolves it | +| **Throw-based, returning `ApiPromise`.** `await` it for `T`; call `.asApiResult()` on the returned value for the non-throwing `ApiResult`. No operation is result-only | here, Error-handling model | never | +| **`E` is the base `ApiError`** and the payload is always the `"undeclared"` arm | Error-handling model | the spec declared error bodies β€” the **Error** bullet names a subclass and an **Error arms** bullet gives each arm's tag, status and body | +| **The request body and its media type are stated on every block**, by a **Request body** bullet that is never omitted. `none` means no body **and no `Content-Type` header**, and a named media type means the body is **required** β€” the request type's field is not optional | here | the spec declared the body optional β€” the bullet adds **Optional**, the field is `field?:`, and omitting it sends no body and no `Content-Type` header at all | +| **Resolves once, to one whole value** β€” except a binary body, which resolves to a stream the caller reads. No pagination, no SSE, no async iterables and no partial results | here, Not on this SDK | never at this SDK version | +| **Six identity headers ride every request** β€” `User-Agent`, `X-APIMatic-Lang`, `X-APIMatic-Package-Version`, `X-APIMatic-Gen-Version`, `X-APIMatic-OS` and `X-APIMatic-Runtime`. They identify the generated SDK, so **no option configures them** | here | the operation declared a header of the same name β€” the operation's layer is folded after the client's, so its value wins | +| **A fresh `Idempotency-Key` rides every non-GET call that does not declare that header itself**, minted per call in the operation's own header layer. It makes a *replayed* request safe, not a repeated one β€” a value that changes per call deduplicates nothing, so it is no substitute for a key the API documents. **No option sets it**, and once minted it is always sent β€” a runtime with no `crypto` global mints it from `Math.random` mixed with the clock and a per-process counter | here | the operation is a GET, or declared that header itself β€” then its own value stands and nothing is minted | +| **Server group `default`** | here, Servers & auth | the operation is on another group β€” its block carries a **Server** bullet | +| **Every operation states its auth requirement**, by an **Auth** bullet that is never omitted β€” one scheme, a composition over schemes, or `none` for a public operation | here, Servers & auth | never β€” the bullet is always present | +| **Every value is schema-encoded before the request is built** β€” a wrong type or format rejects and nothing is sent. **An omitted field that has a default is still sent, with that default**, filled by the SDK rather than by the server | here | the field has a default β€” it appears in the **Fields** table `Default` column | +| **Field names are TypeScript camelCase and the wire name is the same** | here | some field differs β€” the **Fields** table gains a `Wire` column, where an em dash means "same as the field name" | +| **Arrays repeat their key and objects bracket-expand** | the serialization block below | never β€” this SDK declares no per-field serialization style, so every array takes this one | + +**Wire serialization, once, for every channel** (source: `src/core/param-value.ts`, `src/core/url.ts`, `src/core/headers.ts`, `src/core/params.ts`). This block ships with `src/core/` and is versioned with it: + +- **`path`** takes no style. An array is comma-joined with each element percent-encoded **separately**; an object becomes one percent-encoded JSON document inside the segment. A field whose encoded value is `undefined` throws `TypeError` naming the unfilled placeholder β€” a guard no operation reaches, since a path parameter is always required and its schema rejects `undefined` first, as an `EncodeError`; `null` collapses the segment. +- **`header`** takes no style. An array is comma-joined un-encoded (OpenAPI `simple`). `undefined` says nothing, while `null` and an empty array are tombstones that remove the header. Later layers win by **lowercased** name, in the order body content type, then client defaults, then operation. +- **`query`** and **`form`** repeat an array's key and bracket-expand an object at any depth (`filter[status]=open`, `ranges[amount][min]=10`). An array of *objects* bracket-expands per element with **no index**, so element boundaries collapse. +- Nullish **fields** are dropped from every channel except `path`, where `null` collapses the segment. A nullish array **element** is dropped, so an all-nullish array emits no key at all. +- `form` bodies use RFC 1866 encoding (space becomes `+`); `query` uses `%20`. On the wire both key and value go through `encodeURIComponent`, plus a further escape of `!`, `'`, `(`, `)` and `*`. + +**The verb and route are on the pages below**, where a map for a language whose method names are derived from the route can leave them to the source. A TypeScript method name carries none of it, and a `path` field row is unreadable without the route template it fills. + +**Endpoint prose is not on this map.** Where the *semantics* of an operation decide what you must pass β€” a field whose value changes server-side behaviour, an ordering or exclusivity rule between fields β€” read `api-reference.md`, whose entries are keyed by the same signature these pages print. Blocks here give you the contract: names, channels, types, defaults, errors. + +| Resource (`client.X`) | Ops | Operations | Page | +| --- | --- | --- | --- | +| `analysis` | 15 | `analyzeAiVisionGeneral` Β· `analyzeAiVisionModeration` Β· `analyzeAiVisionTagging` Β· `analyzeCaptioning` Β· `analyzeCldFashion` Β· `analyzeCldText` Β· `analyzeCoco` Β· `analyzeGoogleLogoDetection` Β· `analyzeGoogleTagging` Β· `analyzeHumanAnatomy` Β· `analyzeImageQuality` Β· `analyzeLvis` Β· `analyzeShopClassifier` Β· `analyzeUnidet` Β· `analyzeWatermarkDetection` | [map/operations/analysis.md](map/operations/analysis.md) | +| `tasks` | 1 | `getAnalysisTaskStatus` | [map/operations/tasks.md](map/operations/tasks.md) | + +--- + +## Models β€” where they live, how to build them + +**Shapes live only in the source.** Every module under `src/models/` declares exactly one model type and the schema value beside it, and both are re-exported from the package root. Take the pair from an operation's **Type sources** table, or build the directory from the kind below. **Do not derive the path from the type name** β€” the transform is not reversible in general, and the table is the authority. Never grep for a type. + +| Group | Count | Directory | +| --- | --- | --- | +| Objects (plain `type`, no class) | 77 | `src/models/` | +| Enums (open; const companion plus schema) | 5 | `src/models/` | +| Unions | 1 | `src/models/unions/` | +| Typed error classes (`ApiError` subclass, one per typed operation) | 16 | `src/resources/`, in the declaring module's namespace | + +Conventions: every model is a plain `type`, not a class β€” build one with an object literal; there is no constructor and no builder. `f: T` is required, `f?: T` is optional (omit the key), and `f: T | null` is a **required, nullable** field where `null` is a value distinct from an omitted key. Optional properties are declared `f?: T`, not `f?: T | undefined`, so under `exactOptionalPropertyTypes` you must **omit or spread** an absent field rather than assign `undefined` to it. A schema value is directly usable both ways: `Schema>` is `{ decode(v: unknown): T; encode(v: unknown): W }`, and `Encoded` is the wire projection β€” a `Date` becomes `string | number`, a `Uint8Array` becomes a base64 `string`, recursing through arrays and objects. `EnumSchema` adds `readonly values: readonly T[]`, so an enum's known set is testable at run time. Enums are **not** TypeScript `enum`s and are open: a `const` companion plus a union that includes `(string & {})` or `(number & {})`, so **any** value of the base type is assignable and the schema validates the base type only, never membership β€” read the member names and the values they send off the companion, and use `.values` to test membership yourself. A discriminated union is narrowed with an exhaustive `switch` on its tag, with no fallback arm and no type guard to import; one without a discriminant is narrowed on the shape of its arms, which its declaration spells out. A property default is filled by the SDK on **encode as well as decode**, so omitting one still sends it β€” read it off the `defaulted(…)` entry in the schema, or off the property's `@default`. A numeric property is a `number` whatever its format, and its schema follows the type. `type: integer` with no format, `int32` or `int64` rejects a fraction and any value outside the safe-integer range; `type: number` with no format, `float`, `double` or `bigdecimal` rejects a non-finite value. A property's wire name is its `_keysMap` entry in the schema and may differ from the TypeScript name β€” read it there rather than deriving it. A named spec schema whose resolved form is a bare container, or which is used only as a form-encoded body, gets no model file and no exported name: the first is written inline at each use site, the second is flattened onto the operation's request type, one field per property, so read that field list from the request type. + +Every name comes from the package root β€” there is no default export, and no deep imports: + +```ts +import { type AiVisionGeneralAnalysisData, aiVisionGeneralAnalysisDataSchema } from "analyze-api-beta"; +``` + +--- + +## Servers & auth + +**Authentication is per operation.** Every operation declares the requirement it enforces and the SDK sends exactly that: **16 of the 16 operations** require a credential and **0** are public. Each block on a page above carries an **Auth** bullet naming its requirement, `none` included. There is no client-global switch and no per-call override. + +| Scheme (as an **Auth** bullet names it) | Configured with | What the SDK sends | +| --- | --- | --- | +| `basicAuth` | `basicAuth: { username, password }` | `Authorization: Basic `, UTF-8 | +| `oAuth2` | `oAuth2: { clientId, clientSecret?, redirectUri, scope?, state?, pkce?, promptForAuthorizationCode }` | `Authorization: Bearer ` | + +A scheme **contributes** headers, query parameters and cookies rather than mutating the request, so a credential is encoded by exactly the code that encodes an operation's own parameters. The auth layer goes on **last**, which means a scheme's `Authorization` wins over one the operation declared. + +**Composition is emitted, not configured.** Where the spec puts two schemes in one requirement the SDK sends **both**; where it lists alternatives the SDK sends the **first configured** one, in the order the **Auth** bullet prints them. The combinators that express this (`allAuth`, `anyAuth`, `noneAuth`) live in the generated resource modules and are **not exported**. + +**A credential may be a function.** Every field typed `TokenProvider` is re-read on **every** request with no caching, so a key can rotate without rebuilding the client. An empty string counts as absent, and a function is treated as present without being invoked. The function is handed the call's `signal`, or one that never aborts when the call was given none, and the SDK waits for it to settle β€” so a function that fetches its credential should pass that signal on, or a cancelled call waits for the fetch to finish. + +**An unconfigured scheme does not throw.** The request goes out without that credential and the server decides. So a 401 on a call you believed was authenticated is usually an unset credential field rather than an SDK failure β€” check the operation's **Auth** bullet against what the client was given. + +**OAuth2 fetches and caches its own token.** The token request goes through the same client as every other call β€” same `fetch`, same `retry` policy. It sends a form-urlencoded body, and **decodes** the response against a schema rather than casting it. An access token is cached until 30 seconds before it expires, or for half its lifetime when that is longer, so a token issued for 30 seconds or less is reused rather than refetched on every call; a response carrying no `expires_in` is treated as never expiring (RFC 6749 Β§5.1); concurrent callers share one in-flight fetch, and one of them aborting ends only its own wait β€” unless it is the caller that started the fetch, whose signal the fetch runs under. A refused token endpoint rejects with `AuthError` wrapping the underlying `ApiError` as `cause` β€” untyped, so its payload is the `"undeclared"` arm β€” so it never looks like the business call failing. + +| Flow | Token endpoint | Client credentials travel | +| --- | --- | --- | +| `oAuth2` | `authServer` + `/token` | as `Authorization: Basic`; with no `clientSecret`, as a `client_id` form field | + +The authorization-code grant differs in three ways. It needs a human, so `promptForAuthorizationCode` is a **required** member of its credentials: the SDK builds the authorization URL from `authServer` + `/auth` and hands it over, and never opens it itself. PKCE is on by default β€” `pkce` defaults to `PkceMethod.S256`, and `null` disables it, which then requires a client secret or the scheme throws `AuthError` before prompting. And it is the only grant that can refresh, so acquisition is a ladder: live cache, then a refresh attempt, then a full re-authorization. **A prompt that rejects is an `AuthError`, not a transport failure** β€” the user declined, closed the window, or your callback threw β€” and it carries your error as `cause` with no token request sent. That is the distinction to catch on: a `ConnectionError` invites a retry, a declined consent needs the user asked again. Two rejections pass through as themselves rather than being wrapped: a `ConnectionError` or `TimeoutError` raised while acquiring, and β€” when you cancelled the call underneath the prompt β€” the signal's own `reason`. No timeout covers the prompt; the call's signal is the only thing that ends it. + +**Replacing a grant.** Each OAuth2 scheme's token request is a strategy you can substitute β€” `oAuth2Strategy` on `ClientOptions`. A strategy is one method, `getToken(credentials, signal)`, plus `tryRefreshToken(...)` for the refreshable one. `signal` is the one the call was given, or one that never aborts when it was given none; nothing times a strategy of your own. Supply it and the built-in token request is not used, while the caching, the expiry buffer and the single-flight behaviour above still apply. + +**The auth types you can name.** Every row below is exported from the package root. `Source` is where to read the declaration, never what to import β€” the credential shapes themselves are already spelled in the scheme table above. + +| Type | Source | +| --- | --- | +| `OAuthToken` | `src/core/auth/oauth2-strategies.ts` | +| `OAuth2CredentialPlacement` | `src/core/auth/oauth2-strategies.ts` | +| `OAuthTokenRefreshable` | `src/core/auth/oauth2-strategies.ts` | +| `AuthorizationCodePrompt` | `src/core/auth/credentials.ts` | +| `PkceMethod` | `src/core/auth/credentials.ts` | + +**A 401 invalidates the cached credential.** On a **401** β€” 401 only, not 403 β€” the SDK clears whatever that operation's scheme had cached, so the *next* call re-acquires. The current request still rejects with the operation's `ApiError`. The credential fields are on `ClientOptions`. + +**serverOptions.** 2 logical servers; each operation is bound to one at generation time, and a block carries a **Server** bullet only when its group is not `default`. Override `serverOptions.default` and `serverOptions.authServer`. + +**Base URLs and overrides.** One row per group, and every cell is overridden at `serverOptions..`, where `` is `baseUrl` for the whole template or the variable name for one substitution. An override merges with the built-in defaults **key by key**. + +| Group | Base URL template | Template variables (default) | +| --- | --- | --- | +| `default` | `https://api.cloudinary.com/v2/analysis/{cloud_name}` | `cloudName` = `"CLOUD_NAME"` | +| `authServer` | `https://oauth.cloudinary.com/oauth2` | β€” | + +A `baseUrl` override replaces the template verbatim; variable values are percent-encoded into it. Server variables are filled in once, as the client is built; only the path parameters are expanded per request. + +Retries are configurable via `ClientOptions.retry` (`RetryOptions`, source `src/core/retry.ts`) β€” the field table is under Getting a client. + +--- + +## Runtime & packaging + +The facts that change what you type, and the floors that decide whether the package loads at all. This section is the home for all of them. + +| | | +| --- | --- | +| One entry, two dialects | `import` resolves `dist/esm`, `require` resolves `dist/commonjs`, both through the single `.` export. In a TypeScript CommonJS file the typed spelling is `import sdk = require("analyze-api-beta")`; a plain `require` destructure works at run time but yields no types. `instanceof` is reliable **within** one dialect β€” if your app loads both, the two copies declare separate error classes | +| Consumer compiler settings | Under `exactOptionalPropertyTypes`, **omit or spread** an absent optional rather than assigning `undefined` to it. Under `verbatimModuleSyntax`, names that carry no runtime value (the options types, every model type) must be imported with `import type` | +| Required globals, and only these | Always: `fetch` (or a replacement passed as the `fetch` option), `AbortController`, `AbortSignal.any`, `Headers`, `Request`, `URL`, `setTimeout` and `clearTimeout`, `JSON`, `BigInt`, and `console`, which is read as the package loads. Auth adds more, each reached only once the credential needing it is configured. `TextEncoder` and `btoa` build every `Authorization: Basic` value, sent on every request that authenticates with basic and on every OAuth2 token request from a client that has a secret, whose client credentials travel as Basic by default. `crypto.subtle.digest` belongs to the authorization-code grant's PKCE alone. `crypto.randomUUID` or `crypto.getRandomValues` mints the `Idempotency-Key` a non-GET call carries β€” **read and never required**, since a runtime offering neither fills the bytes from `Math.random` mixed with the clock and a per-process counter, so the header is always sent. Three more are **read and never required** β€” `process`, `navigator` and `EdgeRuntime`, which name the host in `X-APIMatic-OS` and `X-APIMatic-Runtime`. A runtime offering none of them sends neither header and works unchanged. `process.env.ANALYZE_API_BETA_CLIENT_LOG` is read at most once, when the client is built and code sets neither a `logger` nor a valid `level`. Logging writes to `console` only as the default logger, and for the warning on a `ANALYZE_API_BETA_CLIENT_LOG` value that is not a level. | +| Values that cross the boundary | `Date` for `date-time`, `string` for `date`, `ArrayBuffer` for an undeclared error body, `Headers` on a result and on a thrown `ResponseError`. The engine also carries a `bigint` int64 path and a base64 `bytes()` codec, reached only where a model uses them | +| Browser distribution | The package ships `dist/esm` and `dist/commonjs` and nothing else β€” **no bundle, no UMD file, no CDN artifact**. Use it through a bundler, which resolves `zod/v4-mini`, deduplicates it against your own copy and tree-shakes the rest | +| Other runtimes | Deno, Bun, Cloudflare Workers and Vercel Edge are all likely to work β€” the SDK needs only the globals above and imports no Node built-in β€” but **none of them is tested for this package**, so nothing here claims support for them | + +The browser floor is set by `AbortSignal.any`, which every call uses to combine `RequestOptions.signal`, or a signal that never aborts when there is none, with the attempt's timer. The emitted output needs less: `tshy` builds at `target: ES2022`, so native `#private` fields and methods survive into `dist/`, and those load from Chrome 85, Firefox 90 and Safari 15. + +| Browser | Minimum | +| --- | --- | +| Chrome / Edge | **116** | +| Firefox | **124** | +| Safari / iOS Safari | **17.4** | + +Below that table the module still loads, down to the emitted-output floor, but every call rejects with a `TypeError`. + +One auth floor is not a version at all. `crypto.subtle` is `undefined` on an **insecure origin** in every browser, so the authorization-code grant's default `S256` PKCE fails on a page served over plain `http` β€” a LAN IP included, since only `localhost` is treated as secure. HTTPS is the fix; `PkceMethod.Plain` avoids `crypto.subtle` entirely. + diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/auth-schemes.ts b/plugins/cloudinary-analysis/sdk/typescript/src/auth-schemes.ts new file mode 100644 index 00000000..48419c52 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/auth-schemes.ts @@ -0,0 +1,32 @@ +import type { ClientOptions } from "./client-options.js"; +import type { AuthScheme } from "./core/api-request.js"; +import { oauth2RefreshableScheme } from "./core/auth/oauth2-schemes.js"; +import { oauth2AuthorizationCodeStrategy } from "./core/auth/oauth2-strategies.js"; +import { basicAuth } from "./core/auth/schemes.js"; +import type { RawClient } from "./core/raw-client.js"; +import type { Servers } from "./servers.js"; + +export type AuthSchemes = { + readonly basicAuth: AuthScheme; + readonly oAuth2: AuthScheme; +}; + +export function buildAuthSchemes( + options: ClientOptions, + servers: Servers, + rawClient: RawClient, +): AuthSchemes { + return { + basicAuth: basicAuth(options.basicAuth), + oAuth2: oauth2RefreshableScheme( + options.oAuth2, + options.oAuth2Strategy ?? + oauth2AuthorizationCodeStrategy({ + authorizationUrl: servers.authServer("/auth"), + tokenUrl: servers.authServer("/token"), + rawClient, + placement: "header", + }), + ), + }; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/client-options.ts b/plugins/cloudinary-analysis/sdk/typescript/src/client-options.ts new file mode 100644 index 00000000..af69cab3 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/client-options.ts @@ -0,0 +1,24 @@ +import type { BasicAuthCredentials, OAuth2AuthorizationCodeCredentials } from "./core/auth/credentials.js"; +import type { OAuth2RefreshableTokenStrategy } from "./core/auth/oauth2-strategies.js"; +import type { CoreClientOptions } from "./core/client-options.js"; + +export type ClientOptions = SdkClientOptions & CoreClientOptions; + +type SdkClientOptions = ServerOptions & { + readonly basicAuth?: BasicAuthCredentials | undefined; + readonly oAuth2?: OAuth2AuthorizationCodeCredentials | undefined; + readonly oAuth2Strategy?: OAuth2RefreshableTokenStrategy | undefined; +}; + +type ServerOptions = { + readonly serverOptions?: { + default?: { + baseUrl?: string; + /** Your Cloud Name. @default "CLOUD_NAME" */ + cloudName?: string; + }; + authServer?: { + baseUrl?: string; + }; + }; +}; diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/client.ts b/plugins/cloudinary-analysis/sdk/typescript/src/client.ts new file mode 100644 index 00000000..ab1336e9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/client.ts @@ -0,0 +1,99 @@ +import { buildAuthSchemes, type AuthSchemes } from "./auth-schemes.js"; +import type { ClientOptions } from "./client-options.js"; +import { buildCoreClientOptions } from "./core/client-options.js"; +import { RawClient } from "./core/raw-client.js"; +import * as host from "./core/runtime-environment.js"; +import * as s from "./core/validation/index.js"; +import { Analysis } from "./resources/analysis.js"; +import { Tasks } from "./resources/tasks.js"; +import { buildServers, type Servers } from "./servers.js"; + +/** + * Use the Analyze API to analyze any external asset and return details based on the type of + * analysis requested. + * + * Currently supports the following analysis options: + * * [AI Vision - + * Tagging](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#tagging_mode) + * * [AI Vision - + * Moderation](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#moderation_mode) + * * [AI Vision - + * General](https://cloudinary.com/documentation/cloudinary_ai_vision_addon#general_mode) + * * + * [Captioning](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#ai_based_image_captioning) + * * [Cld + * Fashion](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * [Cld + * Text](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * + * [Coco](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * [Google Tagging](https://cloudinary.com/documentation/google_auto_tagging_addon) + * * [Human + * Anatomy](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * [Image Quality + * Analysis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#image_quality_analysis) + * * + * [Lvis](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * [Shop + * Classifier](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * + * [Unidet](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#supported_content_aware_detection_models) + * * [Watermark + * Detection](https://cloudinary.com/documentation/cloudinary_ai_content_analysis_addon#watermark_detection) + * + * **Notes**: + * * The Analyze API is currently in development and is available as a Public Beta, which means we + * value your feedback, so please feel free to [share any thoughts with + * us](https://support.cloudinary.com/hc/en-us/requests/new). + * * The analysis options require an active subscription to the relevant add-on. Learn more about + * [registering for + * add-ons](https://cloudinary.com/documentation/cloudinary_add_ons#registering_for_add_ons). + * + * The API supports both Basic Authentication using your Cloudinary API Key and API Secret (which + * can be found on the Dashboard page of your [Cloudinary + * Console](https://console.cloudinary.com/pm)) or OAuth2 ([Contact + * support](https://support.cloudinary.com/hc/en-us/requests/new) for more information regarding + * OAuth). + */ +export class AnalyzeApiBetaClient { + readonly #rawClient: RawClient; + readonly #servers: Servers; + readonly #auth: AuthSchemes; + #analysis?: Analysis; + #tasks?: Tasks; + + constructor(options: ClientOptions = {}) { + this.#rawClient = new RawClient({ + ...buildCoreClientOptions(options, "ANALYZE_API_BETA_CLIENT_LOG"), + defaultHeaders: [ + { name: "User-Agent", value: "AnalyzeApiBetaClient/0.2.0 TypeScript", schema: s.string() }, + { name: "X-APIMatic-Lang", value: "TypeScript", schema: s.string() }, + { name: "X-APIMatic-Package-Version", value: "0.2.0", schema: s.string() }, + { name: "X-APIMatic-Gen-Version", value: "4.0.0", schema: s.string() }, + { name: "X-APIMatic-OS", value: host.operatingSystem(), schema: s.optional(s.string()) }, + { name: "X-APIMatic-Runtime", value: host.runtimeDescription(), schema: s.optional(s.string()) }, + ], + defaultQuery: [], + defaultPathParams: [], + }); + + this.#servers = buildServers(options); + + this.#auth = buildAuthSchemes(options, this.#servers, this.#rawClient); + } + + /** + * Use the Analyze API to analyze any external asset and return details based on the type of + * analysis requested. + */ + get analysis(): Analysis { + return (this.#analysis ??= new Analysis(this.#rawClient, this.#servers, this.#auth)); + } + + /** + * Query the status of analysis tasks. + */ + get tasks(): Tasks { + return (this.#tasks ??= new Tasks(this.#rawClient, this.#servers, this.#auth)); + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/api-error.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-error.ts new file mode 100644 index 00000000..145fed81 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-error.ts @@ -0,0 +1,58 @@ +import { ResponseErrorBase, type ResponseErrorInit } from "./errors.js"; + +/** + * An error body the spec describes, tagged with the arm name to narrow on. + * + * @remarks + * `kind` is the arm name the operation's error class declares. Narrow `err.payload.kind` and + * `body` is typed for that arm, with no cast. + */ +export type Declared = { + readonly kind: K; + readonly body: B; +}; + +/** + * An error body the spec does not describe: the server answered with a status no matcher covers, + * and the operation declares no `default` response. + * + * @remarks + * The untouched bytes come back as `rawBody` so the failure stays diagnosable. A status the spec + * *does* describe whose body fails to decode is **not** this and throws `DecodeError`, because + * shape drift on a declared status is a contract violation rather than an open error. A `default` + * response describes every status the spec does not list, so an operation that declares one never + * produces this arm: its body either decodes or is that same contract violation. + */ +export type Undeclared = { + readonly kind: "undeclared"; + readonly rawBody: ArrayBuffer; +}; + +/** What an {@link ApiError} is built with: the call it names, and the answer the API gave. */ +export type ApiErrorInit = ResponseErrorInit & { + readonly payload: Payload; +}; + +/** + * An error answer from the API. + * + * @remarks + * Thrown when the server responded with an error status. It is a `ResponseError`, so a `catch` for + * everything the server had a say in sees it beside `DecodeError`, and it is a `CoreError`, so one + * `instanceof AcmeError` catches it too. An operation that declares error bodies throws a subclass + * redeclaring `payload` as a typed union; the rest throw this class directly. + * + * On this class `payload.kind` is `string`, so comparing it to `"undeclared"` does not narrow and + * `"rawBody" in payload` does. A subclass declares a union of literals, which narrows on its own. + */ +export class ApiError extends ResponseErrorBase { + readonly kind = "api" as const; + + /** The error body, as the arm the spec declared for this status, or `"undeclared"`. */ + readonly payload: Declared | Undeclared; + + constructor(init: ApiErrorInit | Undeclared>) { + super(`${init.method} ${init.uri} failed with ${init.status}`, init); + this.payload = init.payload; + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/api-promise.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-promise.ts new file mode 100644 index 00000000..f3ba789b --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-promise.ts @@ -0,0 +1,92 @@ +import type { ApiError } from "./api-error.js"; +import type { HttpMethod } from "./api-request.js"; +import type { PayloadOf } from "./response-decoder.js"; + +export type RequestOutcome = + | { ok: true; status: number; headers: Headers; data: T } + | { ok: false; status: number; headers: Headers; error: E }; + +/** + * The non-throwing view of a call, returned by {@link ApiPromise.asApiResult}. + * + * @remarks + * Narrow on `ok`. The success branch carries `value`. The failure branch is a plain object rather + * than the error instance, carrying its `status`, `headers`, `message` and `payload` plus the + * `method` and `uri` of the call, and `result.payload` narrows exactly as `err.payload` would. + */ +export type ApiResult = + | { ok: true; status: number; headers: Headers; value: T } + | { + ok: false; + status: number; + headers: Headers; + message: string; + method: HttpMethod; + uri: string; + payload: PayloadOf; + }; + +/** + * The promise every operation returns: resolves with `T`, rejects with `E` or a `CoreError`. + * + * @remarks + * It is a real `Promise`, so `await`, `.then` and `Promise.all` all work. Awaiting it throws `E` on + * an error status and a `CoreError` when no usable response was produced, so a complete `catch` + * needs both. A bug rejects as the raw error it threw, outside the family. Call + * {@link ApiPromise.asApiResult} for a non-throwing {@link ApiResult} instead. + * + * `.then`, `.catch` and `.finally` hand back a plain `Promise`, which has no `asApiResult` on it. + * Call that method on the value the operation returned. + * + * Nothing here suppresses an unhandled rejection. An abandoned call that fails is reported by the + * runtime: `unhandledRejection` on Node, which by default ends the process, and + * `unhandledrejection` in a browser. + */ +export class ApiPromise extends Promise { + readonly #outcome: Promise>; + + constructor(outcome: Promise>) { + super((resolve, reject) => { + outcome.then((o) => (o.ok ? resolve(o.data) : reject(o.error)), reject); + }); + this.#outcome = outcome; + } + + static override get [Symbol.species](): PromiseConstructor { + return Promise; + } + + /** + * Resolves to an {@link ApiResult} instead of throwing: an error status arrives as a value, not + * as a rejection. + * + * @remarks + * It is also the only way to read the HTTP status and response headers of a successful call. An + * error status is the only failure it turns into a value. A request value that would not encode, + * auth, a dropped connection, a timeout and a body that would not decode all still reject, and a + * caller abort rejects with the signal's own `reason`. + * + * Call it on the value the operation returned, in the same turn: marking this promise handled is + * its first act, so a call deferred past a turn leaves the rejection unhandled until it runs. + */ + async asApiResult(): Promise> { + this.catch(() => {}); + const o = await this.#outcome; + return o.ok + ? { + ok: true, + value: o.data, + status: o.status, + headers: o.headers, + } + : { + ok: false, + status: o.error.status, + headers: o.error.headers, + message: o.error.message, + method: o.error.method, + uri: o.error.uri, + payload: o.error.payload, + }; + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/api-request.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-request.ts new file mode 100644 index 00000000..561c19f1 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/api-request.ts @@ -0,0 +1,89 @@ +import type { RequestBody } from "./request-body.js"; +import type { RequestRetryOptions } from "./retry.js"; +import type { RequestLoggingOptions } from "./logging.js"; +import type { ResolvedCoreClientOptions } from "./client-options.js"; +import type { StyledParam, Param, ParamPair } from "./param-value.js"; + +/** HTTP method of a call. Closed, and the type of `method` on every failure the SDK raises. */ +export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS"; + +/** + * Per-call options, the optional second argument of every operation. + * + * @remarks + * Credentials and the server are configured once on the client rather than per call. Three fields + * of the retry policy and the log level are the two things a single call may shape for itself. + */ +export type RequestOptions = { + /** Cancels the call. Aborting rejects the returned promise with the signal's own `reason`. */ + signal?: AbortSignal | undefined; + + /** + * Shapes the retry policy for this call alone, leaving every other call on the client's. + * + * @remarks + * Three fields of `RetryOptions`, the ones {@link RequestRetryOptions} names, merged field by + * field over what the client resolved: a field left out β€” or passed as `undefined` β€” keeps the + * client's value, and `0` and an empty list are values rather than misses. `timeout` bounds one + * attempt here exactly as it does there, so `{ retry: { timeout: 5000 } }` is a per-attempt + * budget and not a budget for the call. + * + * A value the engine cannot honour is dropped rather than raising: that field keeps the client's + * value, and the fields beside it in the same literal still apply. A record whose reads throw + * rejects the call with a `ConfigurationError` before anything is sent. + */ + retry?: RequestRetryOptions; + + /** + * Sets the log level for this call alone, leaving every other call on the client's. + * + * @remarks + * One field, {@link RequestLoggingOptions}. A level named here decides every line of this call + * by itself, the client's `level` and its three switches set aside, so `{ logging: { level: + * "debug" } }` logs this one call in full and `{ logging: { level: "off" } }` silences it. A + * value that is not a level is dropped and the call logs under the client's settings. + */ + logging?: RequestLoggingOptions; +}; + +export type RawClientOptions = ResolvedCoreClientOptions & { + readonly defaultHeaders: readonly Param[]; + readonly defaultQuery: readonly StyledParam[]; + readonly defaultPathParams: readonly Param[]; +}; + +export type UrlTemplate = { + readonly baseUrl: string; + readonly subPath: string; +}; + +/** + * A named server with its options already decoded: the base URL template and the variables that + * fill it, still unexpanded. + */ +export type ServerBase = { + baseUrl: string; + variables?: Record | undefined; +}; + +export type AuthParams = { + readonly headers: readonly ParamPair[]; + readonly query: readonly ParamPair[]; + readonly cookies: readonly ParamPair[]; +}; + +export type AuthScheme = { + resolve(signal: AbortSignal): Promise | AuthParams; + hasCredentials(): boolean; + invalidate?(): void; +}; + +export type ApiRequest = { + readonly method: HttpMethod; + readonly urlTemplate: UrlTemplate; + readonly auth: AuthScheme; + readonly pathParams: readonly Param[]; + readonly query: readonly StyledParam[]; + readonly headers: readonly Param[]; + readonly body: RequestBody; +}; diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/credentials.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/credentials.ts new file mode 100644 index 00000000..26f0bc59 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/credentials.ts @@ -0,0 +1,80 @@ +/** + * A credential, or a function that produces one. + * + * @remarks + * A function is called on **every** request and its result is never cached, so a key or token can + * be rotated without rebuilding the client. An empty string counts as absent. + * + * The function is handed the call's `signal`, or one that never aborts when the call was given + * none. The SDK waits for the function to settle, so a function that does I/O should honour that + * signal for a cancelled call to end promptly. + */ +export type TokenProvider = string | ((signal: AbortSignal) => string | Promise); + +/** Username and password for HTTP Basic, sent base64 in the Authorization header. */ +export type BasicAuthCredentials = { + readonly username: string; + readonly password: string; +}; + +/** Credentials for the OAuth 2.0 client-credentials grant (RFC 6749 section 4.4). */ +export type OAuth2ClientCredentials = { + readonly clientId: string; + readonly clientSecret: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; +}; + +/** + * Credentials for the OAuth 2.0 resource-owner password grant (RFC 6749 section 4.3). + * + * @remarks + * Legacy: the grant hands the user password to the client. Prefer the authorization-code grant. + */ +export type OAuth2PasswordCredentials = { + readonly clientId: string; + readonly clientSecret?: string | undefined; + readonly username: string; + readonly password: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; +}; + +/** PKCE challenge method (RFC 7636). {@link PkceMethod.S256} unless the server cannot do it. */ +export const PkceMethod = { + S256: "S256", + Plain: "plain", +} as const; +export type PkceMethod = (typeof PkceMethod)[keyof typeof PkceMethod]; + +/** + * Sends the user to the authorization URL and returns the code they come back with. + * + * @remarks + * The SDK builds the URL and hands it over; it never opens it itself. Typically this opens a + * browser, waits for the redirect, and returns its `code` query parameter. + * + * `signal` is the one the call was given, or one that never aborts when it was given none. No + * timeout covers the prompt, so it may wait on the user for as long as that signal allows. + */ +export type AuthorizationCodePrompt = (authorizationUrl: string, signal: AbortSignal) => Promise; + +/** + * Credentials for the OAuth 2.0 authorization-code grant (RFC 6749 section 4.1) with PKCE. + * + * @remarks + * The only grant that needs a human β€” supply {@link AuthorizationCodePrompt} as + * `promptForAuthorizationCode` β€” and the only one whose token response may carry a refresh token. + * PKCE is on by default (`pkce` defaults to `S256`); setting it to `null` disables it and then a + * `clientSecret` is required. + */ +export type OAuth2AuthorizationCodeCredentials = { + readonly clientId: string; + readonly clientSecret?: string | undefined; + readonly redirectUri: string; + /** Space-separated scopes to request. Omit to accept the server default. */ + readonly scope?: string | undefined; + readonly state?: string | undefined; + readonly pkce?: PkceMethod | null | undefined; + readonly promptForAuthorizationCode: AuthorizationCodePrompt; +}; diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-schemes.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-schemes.ts new file mode 100644 index 00000000..01735f49 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-schemes.ts @@ -0,0 +1,81 @@ +import type { AuthParams, AuthScheme } from "../api-request.js"; +import type { + OAuth2RefreshableTokenStrategy, + OAuth2TokenStrategy, + OAuthToken, + OAuthTokenRefreshable, +} from "./oauth2-strategies.js"; +import { header, noneAuth } from "./schemes.js"; + +const EXPIRY_BUFFER_MS = 30_000; + +export function oauth2Scheme( + credentials: TCredentials | undefined, + strategy: OAuth2TokenStrategy, +): AuthScheme { + if (credentials === undefined) return noneAuth; + return cachedTokenScheme((signal) => strategy.getToken(credentials, signal)); +} + +export function oauth2RefreshableScheme( + credentials: TCredentials | undefined, + strategy: OAuth2RefreshableTokenStrategy, +): AuthScheme { + if (credentials === undefined) return noneAuth; + return cachedTokenScheme(async (signal, previous) => { + const refreshToken = previous?.refreshToken; + if (refreshToken === undefined) return strategy.getToken(credentials, signal); + + const refreshed = await strategy.tryRefreshToken(credentials, refreshToken, signal); + if (refreshed === null) return strategy.getToken(credentials, signal); + return refreshed.refreshToken === undefined ? { ...refreshed, refreshToken } : refreshed; + }); +} + +function cachedTokenScheme( + acquire: (signal: AbortSignal, previous?: TToken) => Promise, +): AuthScheme { + let cached: { token: TToken; expiresAt: number } | undefined; + let inflight: Promise | undefined; + + async function obtainAndCacheToken(signal: AbortSignal): Promise { + try { + const token = await acquire(signal, cached?.token); + cached = { token, expiresAt: expiryOf(token) }; + return token; + } finally { + inflight = undefined; + } + } + + return { + async resolve(signal) { + if (cached !== undefined && Date.now() < cached.expiresAt) return bearer(cached.token); + inflight ??= obtainAndCacheToken(signal); + return bearer(await untilAborted(inflight, signal)); + }, + hasCredentials: () => true, + invalidate: () => { + cached = undefined; + }, + }; +} + +function untilAborted(promise: Promise, signal: AbortSignal): Promise { + return new Promise((resolve, reject) => { + const onAbort = (): void => reject(signal.reason); + signal.addEventListener("abort", onAbort, { once: true }); + if (signal.aborted) onAbort(); + promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort)); + }); +} + +function bearer(token: OAuthToken): AuthParams { + return header("Authorization", `Bearer ${token.accessToken}`); +} + +function expiryOf(token: OAuthToken): number { + if (token.expiresIn === undefined || token.expiresIn <= 0) return Number.POSITIVE_INFINITY; + const lifetimeMs = token.expiresIn * 1000; + return Date.now() + Math.max(lifetimeMs - EXPIRY_BUFFER_MS, lifetimeMs / 2); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-strategies.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-strategies.ts new file mode 100644 index 00000000..e721e919 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/oauth2-strategies.ts @@ -0,0 +1,365 @@ +import type { UrlTemplate } from "../api-request.js"; +import type { Param, StyledParam } from "../param-value.js"; +import type { RawClient } from "../raw-client.js"; +import type { Entry, Schema } from "../validation/schema.js"; +import type { + OAuth2AuthorizationCodeCredentials, + OAuth2ClientCredentials, + OAuth2PasswordCredentials, +} from "./credentials.js"; +import { PkceMethod } from "./credentials.js"; +import { base64, basicCredential, noneAuth } from "./schemes.js"; +import { CoreError } from "../errors.js"; +import { ApiError } from "../api-error.js"; +import { buildQueryUrl, templateUri } from "../url.js"; +import * as s from "../validation/index.js"; + +/** + * An OAuth 2.0 token response (RFC 6749 section 5.1). + * + * @remarks + * Wire names are snake_case; these are the decoded SDK names. Returned by an + * {@link OAuth2TokenStrategy}, and cached by the scheme that owns it until it expires. + */ +export type OAuthToken = { + /** The token to send in `Authorization`. */ + accessToken: string; + + /** How the token is presented, normally `Bearer`. */ + tokenType: string; + + /** + * Lifetime in seconds. RECOMMENDED, not required (RFC 6749 section 5.1) β€” a token with no + * `expiresIn` is treated as never expiring. + */ + expiresIn?: number; + + /** Scopes the server actually granted, which may be narrower than those requested. */ + scope?: string; +}; + +export const oauthTokenSchema: Schema = s.object({ + accessToken: s.string(), + tokenType: s.string(), + expiresIn: s.optional(s.int()), + scope: s.optional(s.string()), + _keysMap: { + accessToken: "access_token", + tokenType: "token_type", + expiresIn: "expires_in", + }, +}); + +/** + * An OAuth 2.0 token response that may carry a refresh token. + * + * @remarks + * Only the authorization-code grant issues one β€” RFC 6749 forbids it for the implicit grant and + * says client-credentials SHOULD NOT issue one. + */ +export type OAuthTokenRefreshable = OAuthToken & { + /** + * Issued at the server's discretion (RFC 6749 sections 4.1.4 and 4.3.3). When a refresh response + * omits it, the previously issued one is carried forward. + */ + refreshToken?: string; +}; + +export const oauthTokenRefreshableSchema: Schema = s.object({ + accessToken: s.string(), + tokenType: s.string(), + expiresIn: s.optional(s.int()), + scope: s.optional(s.string()), + refreshToken: s.optional(s.string()), + _keysMap: { + accessToken: "access_token", + tokenType: "token_type", + expiresIn: "expires_in", + refreshToken: "refresh_token", + }, +}); + +/** + * How a token is obtained for a grant. The replaceable half of the OAuth2 design. + * + * @remarks + * The scheme owns caching, single-flight and expiry; a strategy owns only the exchange. Supply one + * on {@link ClientOptions} to talk to a server whose token endpoint does not follow the built-in + * shape; the SDK then never builds the request itself. + * + * `signal` is the one the call was given, or one that never aborts when it was given none. + * Nothing times a strategy you supply; the built-in ones time their own token request. + */ +export type OAuth2TokenStrategy = { + getToken(credentials: TCredentials, signal: AbortSignal): Promise; +}; + +/** + * A token strategy that can also refresh, used by the authorization-code grant. + * + * @remarks + * `tryRefreshToken` returns `null` rather than throwing when the refresh is rejected, which is what + * makes "refresh failed, re-authorize" a normal path instead of an error path. Both methods get + * `signal` as {@link OAuth2TokenStrategy} does. + */ +export type OAuth2RefreshableTokenStrategy = { + getToken(credentials: TCredentials, signal: AbortSignal): Promise; + tryRefreshToken( + credentials: TCredentials, + refreshToken: string, + signal: AbortSignal, + ): Promise; +}; + +/** + * Where client credentials travel on a token request. + * + * @remarks + * `"header"` sends them as `Authorization: Basic`; `"body"` sends them as form fields, the + * `client_secret_post` style. A client with no secret is a public client: under either + * placement it sends only `client_id`, as a form field, and no `Authorization` header. + */ +export type OAuth2CredentialPlacement = "header" | "body"; + +export function oauth2ClientCredentialsStrategy(config: { + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2TokenStrategy { + return { + getToken(credentials, signal: AbortSignal) { + const placed = placeCredentials( + config.placement ?? "header", + credentials.clientId, + credentials.clientSecret, + ); + return requestToken( + config, + oauthTokenSchema, + placed.headers, + [ + { name: "grant_type", value: "client_credentials", schema: s.string() }, + ...scopeField(credentials.scope), + ...placed.fields, + ], + signal, + ); + }, + }; +} + +export function oauth2PasswordStrategy(config: { + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2TokenStrategy { + return { + getToken(credentials, signal: AbortSignal) { + const placed = placeCredentials( + config.placement ?? "header", + credentials.clientId, + credentials.clientSecret, + ); + return requestToken( + config, + oauthTokenSchema, + placed.headers, + [ + { name: "grant_type", value: "password", schema: s.string() }, + { name: "username", value: credentials.username, schema: s.string() }, + { name: "password", value: credentials.password, schema: s.string() }, + ...scopeField(credentials.scope), + ...placed.fields, + ], + signal, + ); + }, + }; +} + +export function oauth2AuthorizationCodeStrategy(config: { + authorizationUrl: UrlTemplate; + tokenUrl: UrlTemplate; + rawClient: RawClient; + placement?: OAuth2CredentialPlacement; +}): OAuth2RefreshableTokenStrategy { + const placementOf = ( + credentials: OAuth2AuthorizationCodeCredentials, + ): { headers: Param[]; fields: StyledParam[] } => + placeCredentials(config.placement ?? "header", credentials.clientId, credentials.clientSecret); + + return { + async getToken(credentials, signal: AbortSignal) { + const method = credentials.pkce === undefined ? PkceMethod.S256 : credentials.pkce; + if (method === null && (credentials.clientSecret === undefined || credentials.clientSecret === "")) { + throw new Error( + "A client secret is required when PKCE is disabled. Set pkce to PkceMethod.S256 for a public client.", + ); + } + + const pkce = method === null ? undefined : await generatePkce(method); + const authorizationUri = buildQueryUrl(new URL(templateUri(config.authorizationUrl)), [ + authorizationFields(credentials, pkce), + ]); + const code = await promptForCode(credentials, authorizationUri.href, signal); + const placed = placementOf(credentials); + + return requestToken( + config, + oauthTokenRefreshableSchema, + placed.headers, + [ + { name: "grant_type", value: "authorization_code", schema: s.string() }, + { name: "code", value: code, schema: s.string() }, + { name: "redirect_uri", value: credentials.redirectUri, schema: s.string() }, + ...(pkce === undefined + ? [] + : [{ name: "code_verifier", value: pkce.verifier, schema: s.string() }]), + ...placed.fields, + ], + signal, + ); + }, + + async tryRefreshToken(credentials, refreshToken, signal: AbortSignal) { + const placed = placementOf(credentials); + const outcome = await config.rawClient + .execute( + { + method: "POST", + urlTemplate: config.tokenUrl, + auth: noneAuth, + pathParams: [], + query: [], + headers: placed.headers, + body: { + kind: "formUrlEncoded", + value: [ + { name: "grant_type", value: "refresh_token", schema: s.string() }, + { name: "refresh_token", value: refreshToken, schema: s.string() }, + ...placed.fields, + ], + }, + }, + { + success: { kind: "json", schema: oauthTokenRefreshableSchema }, + errorFactory: ApiError, + }, + { signal }, + ) + .asApiResult(); + + return outcome.ok ? outcome.value : null; + }, + }; +} + +function scopeField(scope: string | undefined): StyledParam[] { + if (scope === undefined || scope === "") return []; + return [{ name: "scope", value: scope, schema: s.string() }]; +} + +function placeCredentials( + placement: OAuth2CredentialPlacement, + clientId: string, + clientSecret: string | undefined, +): { headers: Param[]; fields: StyledParam[] } { + const clientIdField: StyledParam = { name: "client_id", value: clientId, schema: s.string() }; + if (clientSecret === undefined || clientSecret === "") { + return { headers: [], fields: [clientIdField] }; + } + + if (placement === "header") { + return { + headers: [ + { + name: "Authorization", + value: basicCredential(clientId, clientSecret), + schema: s.string(), + }, + ], + fields: [], + }; + } + + return { + headers: [], + fields: [clientIdField, { name: "client_secret", value: clientSecret, schema: s.string() }], + }; +} + +async function requestToken( + config: { tokenUrl: UrlTemplate; rawClient: RawClient }, + schema: Entry, + headers: readonly Param[], + fields: readonly StyledParam[], + signal: AbortSignal, +): Promise { + return config.rawClient.execute( + { + method: "POST", + urlTemplate: config.tokenUrl, + auth: noneAuth, + pathParams: [], + query: [], + headers, + body: { kind: "formUrlEncoded", value: fields }, + }, + { + success: { kind: "json", schema }, + errorFactory: ApiError, + }, + { signal }, + ); +} + +async function promptForCode( + credentials: OAuth2AuthorizationCodeCredentials, + authorizationUrl: string, + signal: AbortSignal, +): Promise { + try { + return await credentials.promptForAuthorizationCode(authorizationUrl, signal); + } catch (err) { + if (!(err instanceof CoreError) && signal.aborted) throw signal.reason; + throw err; + } +} + +function authorizationFields( + credentials: OAuth2AuthorizationCodeCredentials, + pkce: PkceValues | undefined, +): StyledParam[] { + const fields: StyledParam[] = [ + { name: "response_type", value: "code", schema: s.string() }, + { name: "client_id", value: credentials.clientId, schema: s.string() }, + { name: "redirect_uri", value: credentials.redirectUri, schema: s.string() }, + ...scopeField(credentials.scope), + ]; + if (credentials.state !== undefined && credentials.state !== "") { + fields.push({ name: "state", value: credentials.state, schema: s.string() }); + } + if (pkce !== undefined) { + fields.push( + { name: "code_challenge", value: pkce.challenge, schema: s.string() }, + { name: "code_challenge_method", value: pkce.method, schema: s.string() }, + ); + } + return fields; +} + +type PkceValues = { verifier: string; challenge: string; method: PkceMethod }; + +async function generatePkce(method: PkceMethod): Promise { + const entropy = new Uint8Array(32); + crypto.getRandomValues(entropy); + const verifier = base64Url(entropy); + if (method === PkceMethod.Plain) return { verifier, challenge: verifier, method }; + + const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)); + return { verifier, challenge: base64Url(new Uint8Array(digest)), method }; +} + +function base64Url(bytes: Uint8Array): string { + return base64(bytes).replace(/=+$/, "").replaceAll("+", "-").replaceAll("/", "_"); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/schemes.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/schemes.ts new file mode 100644 index 00000000..a01c35db --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/auth/schemes.ts @@ -0,0 +1,183 @@ +import type { AuthParams, AuthScheme, HttpMethod } from "../api-request.js"; +import type { BasicAuthCredentials, TokenProvider } from "./credentials.js"; +import type { RequestHeaders } from "../headers.js"; +import type { RequestUrl } from "../url.js"; +import type { HttpLogger } from "../http-logger.js"; +import { AuthError, ConfigurationError, ConnectionError, TimeoutError } from "../errors.js"; + +const NO_PARAMS: AuthParams = { headers: [], query: [], cookies: [] }; + +export const noneAuth: AuthScheme = { + resolve: () => NO_PARAMS, + hasCredentials: () => false, +}; + +export function bearerAuth(token: TokenProvider | undefined): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(token, signal); + return value === undefined ? NO_PARAMS : header("Authorization", `Bearer ${value}`); + }, + hasCredentials: () => hasToken(token), + }; +} + +export function basicAuth(credentials: BasicAuthCredentials | undefined): AuthScheme { + if (credentials !== undefined && credentials.username.includes(":")) { + throw new ConfigurationError("A basic-auth username cannot contain a colon (RFC 7617 section 2)."); + } + return { + resolve() { + if (credentials === undefined) return NO_PARAMS; + return header("Authorization", basicCredential(credentials.username, credentials.password)); + }, + hasCredentials: () => credentials !== undefined, + }; +} + +export function apiKeyHeaderAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + return value === undefined ? NO_PARAMS : header(config.name, value); + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function apiKeyQueryAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + if (value === undefined) return NO_PARAMS; + return { headers: [], query: [[config.name, value]], cookies: [] }; + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function apiKeyCookieAuth(config: { name: string; token: TokenProvider | undefined }): AuthScheme { + return { + async resolve(signal) { + const value = await resolveToken(config.token, signal); + if (value === undefined) return NO_PARAMS; + return { headers: [], query: [], cookies: [[config.name, value]] }; + }, + hasCredentials: () => hasToken(config.token), + }; +} + +export function allAuth(...schemes: readonly AuthScheme[]): AuthScheme { + return { + async resolve(signal) { + const parts: AuthParams[] = []; + for (const scheme of schemes) parts.push(await scheme.resolve(signal)); + return { + headers: parts.flatMap((part) => part.headers), + query: parts.flatMap((part) => part.query), + cookies: parts.flatMap((part) => part.cookies), + }; + }, + hasCredentials: () => schemes.length > 0 && schemes.every((scheme) => scheme.hasCredentials()), + invalidate() { + for (const scheme of schemes) scheme.invalidate?.(); + }, + }; +} + +export function anyAuth(...schemes: readonly AuthScheme[]): AuthScheme { + return { + async resolve(signal) { + const failures: unknown[] = []; + for (const scheme of schemes) { + if (!scheme.hasCredentials()) continue; + try { + return await scheme.resolve(signal); + } catch (err) { + if (signal.aborted) throw signal.reason; + if (err instanceof TimeoutError) throw err; + failures.push(err); + } + } + if (failures.length === 0) return NO_PARAMS; + if (failures.length === 1) throw failures[0]; + throw new AggregateError(failures, "No authentication scheme succeeded."); + }, + hasCredentials: () => schemes.some((scheme) => scheme.hasCredentials()), + invalidate() { + for (const scheme of schemes) scheme.invalidate?.(); + }, + }; +} + +export type AuthenticatedRequest = { + readonly url: URL; + readonly headers: Headers; +}; + +export async function authenticateRequest( + method: HttpMethod, + auth: AuthScheme, + url: RequestUrl, + headers: RequestHeaders, + signal: AbortSignal, + startedAt: number, + logger: HttpLogger, +): Promise { + try { + const credential = await auth.resolve(signal); + return { + url: url.withQuery(credential.query), + headers: headers.withCredential(credential.headers, credential.cookies), + }; + } catch (err) { + if (signal.aborted) throw signal.reason; + if (err instanceof ConnectionError || err instanceof TimeoutError) throw err; + if (err instanceof AuthError) { + logger.logFailure(err, startedAt); + throw err; + } + const { uri } = url; + const error = new AuthError( + `${method} ${uri} failed: ${ + err instanceof Error && err.message !== "" ? err.message : "A credential could not be obtained." + }`, + { cause: err, method, uri }, + ); + logger.logFailure(error, startedAt); + throw error; + } +} + +export function header(name: string, value: string): AuthParams { + return { headers: [[name, value]], query: [], cookies: [] }; +} + +export function basicCredential(username: string, password: string): string { + return `Basic ${base64(new TextEncoder().encode(`${username}:${password}`))}`; +} + +export function base64(bytes: Uint8Array): string { + let binary = ""; + for (const byte of bytes) binary += String.fromCharCode(byte); + return btoa(binary); +} + +async function resolveToken( + token: TokenProvider | undefined, + signal: AbortSignal, +): Promise { + const value: unknown = typeof token === "function" ? await token(signal) : token; + if (value !== undefined && typeof value !== "string") { + throw new TypeError(`A credential must be a string, received ${typeof value}.`); + } + return present(value); +} + +function hasToken(token: TokenProvider | undefined): boolean { + return typeof token === "function" || present(token) !== undefined; +} + +function present(value: string | undefined): string | undefined { + return value !== undefined && value !== "" ? value : undefined; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/binary.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/binary.ts new file mode 100644 index 00000000..18e9a4d9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/binary.ts @@ -0,0 +1,428 @@ +import { CoreError, DecodeError } from "./errors.js"; +import type { HttpMethod } from "./api-request.js"; +import type { BinaryResponseDecoder } from "./response-decoder.js"; + +/** + * Bytes, in any form the platform can send. + * + * @remarks + * Bytes, not a file: nothing here carries a name or a media type. Wrap them in {@link FileData} + * to attach either. + * + * A `ReadableStream` streams the request rather than buffering it: the fetch is sent with + * `duplex: "half"`, and the body cannot be replayed, so a retry would have nothing left to send. + * That holds for a multipart file part too: one streaming part makes the SDK frame the whole form + * itself as a single chunked stream, taking the `Content-Length` and the replayability with it. + * + * If the request is aborted or times out mid-upload the SDK stops reading within one chunk and + * hands the lock back, so the source is yours again to cancel or dispose from the `catch`. It is + * never cancelled on your behalf β€” the SDK closes only what it opened. A stream that was fully + * sent stays locked, which is the platform's own guard against sending a spent body twice. + * + * The async-iterable arm is what a Node stream satisfies, so `createReadStream(path)` can be + * passed straight in and is forwarded chunk by chunk rather than buffered, under that same + * one-shot rule. It is spelled structurally on purpose: naming Node's `Readable` would put + * `@types/node` in the published declarations, which the DOM typecheck lane exists to prevent. + */ +export type BinaryData = + | Blob + | Uint8Array + | ArrayBuffer + | ReadableStream + | AsyncIterable>; + +/** + * Bytes plus the name and media type to declare for them β€” what turns {@link BinaryData} into a + * file, as the platform's own `File` does to a `Blob`. + * + * @remarks + * Wrap a {@link BinaryData} in this to send a `Content-Disposition` filename, or to override the + * media type the operation declares. Both are optional, and a `Blob` that already carries a `type` + * or a `name` supplies them on its own. + */ +export type FileData = { + readonly data: BinaryData; + readonly fileName?: string | undefined; + readonly contentType?: string | undefined; +}; + +/** A file to send: bare {@link BinaryData} bytes, or a {@link FileData} that names them. */ +export type FileInput = BinaryData | FileData; + +export type BufferedResolvedFile = { + readonly body: Blob | Uint8Array; + readonly contentType: string | undefined; + readonly fileName: string | undefined; + readonly streaming: false; +}; + +export type StreamingResolvedFile = { + readonly body: ReadableStream; + readonly contentType: string | undefined; + readonly fileName: string | undefined; + readonly streaming: true; +}; + +export type ResolvedFile = BufferedResolvedFile | StreamingResolvedFile; + +export type ByteSource = Blob | Uint8Array | ReadableStream; + +/** + * An error response whose body is bytes rather than JSON, buffered in full. + * + * @remarks + * Unlike a binary *success* body this is a value, not a resource: a thrown error's payload has no + * lifetime, so the bytes are read before the rejection surfaces and there is nothing to release. + */ +export type BinaryErrorContent = { + readonly bytes: Uint8Array; + readonly contentType: string | undefined; + readonly fileName: string | undefined; +}; + +/** + * A binary response body you own β€” a handle over the still-open connection, not bytes. + * + * @remarks + * The body arrives as an unread `ReadableStream` and is handled the way any web stream is: buffer + * it with `new Response(file.stream)` and one of `bytes()`, `text()` or `blob()`, read it chunk by + * chunk with a reader or `for await`, or release it unread with `file.stream.cancel()`. Once a + * reader holds the lock, cancel through the reader instead. The stream is the response body + * itself, so an abort or a dropped connection surfaces on it as the platform reports it: an abort + * as the signal's `reason` when `fetch` ties the body to the signal, a dropped connection as the + * platform's own error. + */ +export type BinaryContent = { + /** + * The body, unread β€” a web `ReadableStream` whose lifetime is yours. + */ + readonly stream: ReadableStream>; + + /** The media type the server declared, or the operation's declared type when it declared none. */ + readonly contentType: string; + + /** + * The name from `Content-Disposition`, when the response carried one. + * + * @remarks + * Server-chosen, and therefore **untrusted input**. Never use it as a path: sanitize it, or name + * the file yourself. + */ + readonly fileName: string | undefined; +}; + +export function readBinary(decoder: BinaryResponseDecoder, response: Response): BinaryContent { + const contentType = response.headers.get("content-type") ?? decoder.contentType; + const fileName = parseFileName(response.headers.get("content-disposition")); + return { stream: response.body ?? emptyStream(), contentType, fileName }; +} + +export async function readBinaryError( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let buffer: ArrayBuffer; + try { + buffer = await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return { + bytes: new Uint8Array(buffer), + contentType: response.headers.get("content-type") ?? undefined, + fileName: parseFileName(response.headers.get("content-disposition")), + }; +} + +export function resolveFile( + input: FileInput, + declaredContentType: string | undefined, + signal?: AbortSignal, +): ResolvedFile { + const file: FileData = isFileData(input) ? input : { data: input }; + if (!isBinaryData(file.data)) throw unusableBinaryData(file.data); + const declared = file.contentType ?? blobContentType(file.data); + const contentType = declared === undefined || declared === "" ? declaredContentType : declared; + const fileName = file.fileName ?? blobFileName(file.data); + const body = toBody(file.data, signal); + if (body instanceof ReadableStream) return { body, contentType, fileName, streaming: true }; + return { body, contentType, fileName, streaming: false }; +} + +export function concatStream( + sources: readonly ByteSource[], + signal: AbortSignal | undefined, +): ReadableStream { + return streamFrom(concatenate(sources), signal); +} + +export function attachmentDisposition(fileName: string | undefined): string | undefined { + if (fileName === undefined || fileName === "") return undefined; + return `attachment; filename="${asciiFileName(fileName)}"; filename*=UTF-8''${encodeRfc5987(fileName)}`; +} + +const EXTENDED_FILENAME = /;[ \t]*filename\*[ \t]*=[ \t]*([^;]*)/i; +const QUOTED_FILENAME = /;[ \t]*filename[ \t]*=[ \t]*"((?:[^"\\]|\\[\s\S])*)"/i; +const TOKEN_FILENAME = /;[ \t]*filename[ \t]*=[ \t]*([^;",]+)/i; +const ESCAPED_PAIR = /\\([\s\S])/g; + +export function parseFileName(disposition: string | null): string | undefined { + if (disposition === null) return undefined; + + const extended = EXTENDED_FILENAME.exec(disposition); + if (extended !== null) { + const decoded = decodeRfc5987((extended[1] ?? "").trim()); + if (decoded !== undefined && decoded !== "") return decoded; + } + + const quoted = QUOTED_FILENAME.exec(disposition); + if (quoted !== null) { + const unescaped = (quoted[1] ?? "").replace(ESCAPED_PAIR, "$1"); + if (unescaped !== "") return unescaped; + } + + const token = TOKEN_FILENAME.exec(disposition); + const value = (token?.[1] ?? "").trim(); + return value === "" ? undefined : value; +} + +function emptyStream(): ReadableStream> { + return new ReadableStream>({ start: (controller) => controller.close() }); +} + +function toBody(data: BinaryData, signal: AbortSignal | undefined): ResolvedFile["body"] { + if (data instanceof ArrayBuffer) return new Uint8Array(data); + if (data instanceof ReadableStream) return signal === undefined ? data : borrowStream(data, signal); + if (isAsyncIterable(data)) return streamFrom(data, signal); + return data; +} + +function streamFrom( + iterable: AsyncIterable, + signal: AbortSignal | undefined, +): ReadableStream { + const iterator = iterable[Symbol.asyncIterator](); + let sink: ReadableStreamDefaultController | undefined; + let settled = false; + + const settle = (): boolean => { + if (settled) return false; + settled = true; + return true; + }; + + const onAbort = (): void => { + if (!settle()) return; + sink?.error(signal?.reason); + void iterator.return?.(signal?.reason).catch(() => {}); + }; + + signal?.addEventListener("abort", onAbort, { once: true }); + + return new ReadableStream({ + start: (controller) => { + sink = controller; + if (signal?.aborted) onAbort(); + }, + pull: async (controller) => { + const { done, value } = await iterator.next(); + if (settled) return; + if (done) { + settle(); + controller.close(); + return; + } + controller.enqueue(value); + }, + cancel: async (reason) => { + settle(); + await iterator.return?.(reason); + }, + }); +} + +function borrowStream(source: ReadableStream, signal: AbortSignal): ReadableStream { + let reader: ReadableStreamDefaultReader | undefined; + let sink: ReadableStreamDefaultController | undefined; + let settled = false; + + const settle = (): boolean => { + if (settled) return false; + settled = true; + return true; + }; + + const onAbort = (): void => { + if (!settle()) return; + sink?.error(signal.reason); + reader?.releaseLock(); + }; + + signal.addEventListener("abort", onAbort, { once: true }); + + return new ReadableStream( + { + start: (controller) => { + sink = controller; + if (signal.aborted) onAbort(); + }, + pull: async (controller) => { + try { + reader ??= source.getReader(); + const { done, value } = await reader.read(); + if (settled) return; + if (done) { + settle(); + controller.close(); + return; + } + controller.enqueue(value); + } catch (err) { + if (settle()) controller.error(err); + } + }, + cancel: () => { + if (settle()) reader?.releaseLock(); + }, + }, + { highWaterMark: 0 }, + ); +} + +async function* concatenate(sources: readonly ByteSource[]): AsyncGenerator { + for (const source of sources) { + if (source instanceof Uint8Array) { + yield source; + } else if (source instanceof Blob) { + const reader = source.stream().getReader(); + try { + yield* readChunks(reader); + } finally { + await reader.cancel().catch(() => {}); + } + } else { + const reader = source.getReader(); + try { + yield* readChunks(reader); + } finally { + reader.releaseLock(); + } + } + } +} + +async function* readChunks(reader: ReadableStreamDefaultReader): AsyncGenerator { + for (;;) { + const { done, value } = await reader.read(); + if (done) return; + yield value; + } +} + +function isFileData(input: FileInput): input is FileData { + return typeof input === "object" && input !== null && "data" in input; +} + +function isBinaryData(data: unknown): data is BinaryData { + return ( + data instanceof Blob || + data instanceof Uint8Array || + data instanceof ArrayBuffer || + data instanceof ReadableStream || + isAsyncIterable(data) + ); +} + +function isAsyncIterable(data: unknown): data is AsyncIterable> { + return typeof data === "object" && data !== null && Symbol.asyncIterator in data; +} + +function unusableBinaryData(data: unknown): TypeError { + return new TypeError( + `A file body must be a Blob, Uint8Array, ArrayBuffer, ReadableStream or an async iterable of ` + + `bytes, or a { data } wrapper around one β€” received ${receivedType(data)}. A filesystem path is ` + + `not a file body: open it with createReadStream(path), or read it with await openAsBlob(path).`, + ); +} + +function receivedType(data: unknown): string { + if (data === null) return "null"; + if (typeof data !== "object") return typeof data; + return data.constructor?.name ?? "object"; +} + +function blobContentType(data: BinaryData): string | undefined { + return data instanceof Blob ? data.type : undefined; +} + +function blobFileName(data: BinaryData): string | undefined { + if (!(data instanceof Blob) || !("name" in data)) return undefined; + const name = data.name; + return typeof name === "string" && name !== "" ? name : undefined; +} + +const DISPOSITION_UNSAFE = /[^ -~]|["\\;]/g; + +function asciiFileName(fileName: string): string { + return fileName.replace(DISPOSITION_UNSAFE, "_"); +} + +const RFC5987_ATTR_CHAR = /^[A-Za-z0-9!#$&+\-.^_`|~]$/; + +function encodeRfc5987(value: string): string { + let encoded = ""; + for (const byte of new TextEncoder().encode(value)) { + const char = String.fromCharCode(byte); + encoded += RFC5987_ATTR_CHAR.test(char) ? char : `%${byte.toString(16).toUpperCase().padStart(2, "0")}`; + } + return encoded; +} + +const EXT_VALUE = /^([^']*)'[^']*'([\s\S]*)$/; + +function decodeRfc5987(value: string): string | undefined { + const parts = EXT_VALUE.exec(value); + if (parts === null) return undefined; + + const charset = (parts[1] ?? "").toLowerCase(); + const label = charset === "" ? "utf-8" : charset; + if (label !== "utf-8" && label !== "iso-8859-1") return undefined; + + const bytes = percentDecode(parts[2] ?? ""); + if (bytes === undefined) return undefined; + + try { + return new TextDecoder(label, { fatal: true }).decode(bytes); + } catch { + return undefined; + } +} + +const HEX_PAIR = /^[0-9A-Fa-f]{2}$/; + +function percentDecode(value: string): Uint8Array | undefined { + const bytes: number[] = []; + for (let index = 0; index < value.length; index += 1) { + const char = value[index] ?? ""; + if (char !== "%") { + const code = char.charCodeAt(0); + if (code > 0xff) return undefined; + bytes.push(code); + continue; + } + const hex = value.slice(index + 1, index + 3); + if (!HEX_PAIR.test(hex)) return undefined; + bytes.push(Number.parseInt(hex, 16)); + index += 2; + } + return new Uint8Array(bytes); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/client-options.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/client-options.ts new file mode 100644 index 00000000..23c803b6 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/client-options.ts @@ -0,0 +1,90 @@ +import { ConfigurationError } from "./errors.js"; +import { buildLoggingOptions, type LoggingOptions, type ResolvedLoggingOptions } from "./logging.js"; +import { buildRetryOptions, type RetryOptions, type ResolvedRetryOptions } from "./retry.js"; +import { + buildEventStreamOptions, + type EventStreamOptions, + type ResolvedEventStreamOptions, +} from "./event-stream.js"; + +/** The signature a replacement `fetch` must have, which is the global `fetch`'s own. */ +export type FetchLike = typeof fetch; + +/** + * What the caller configures the engine with: every field optional, and one left out resolved to + * the default its own block names. + * + * @remarks + * The shell's `ClientOptions` intersects this directly, so a new engine option costs no emitted + * line. {@link buildCoreClientOptions} fills every field in, and a value the engine cannot honour + * falls back to its own default rather than being rejected. + */ +export type CoreClientOptions = { + /** + * How a request is timed and how a failed one is retried. + * + * @remarks + * Any subset of {@link RetryOptions}. Retries are **on** by default β€” that type says what is + * retried, what is not, and which value each field falls back to. Its `timeout` is the budget + * for one attempt, so it applies whether or not a request is ever re-sent. + */ + readonly retry?: RetryOptions | undefined; + + /** + * How an event stream is watched for a silent server. + * + * @remarks + * Any subset of {@link EventStreamOptions}, resolved once when the client is built and read only + * by an operation that resolves to an `EventStream`. A field left out takes its default. + */ + readonly eventStream?: EventStreamOptions | undefined; + + /** Logging of requests, responses and retries. Off by default, see {@link LoggingOptions}. */ + readonly logging?: LoggingOptions | undefined; + + /** + * Replaces the global `fetch`. The one extension point for proxying or mocking. + * + * @remarks + * It is called with one argument, the attempt's `Request`, and no `init`. A replacement must + * forward that `Request`, or at least its `signal`, to whatever performs the request: one that + * reads `init` forwards nothing, and drops the call's abort and its timeout. To add a header, + * copy the `Request` with `new Request(request)` and set it on the copy's `headers`: passing + * `{ headers }` to the copy replaces every header the SDK set. + */ + readonly fetch?: FetchLike | undefined; +}; + +export type ResolvedCoreClientOptions = { + readonly retry: ResolvedRetryOptions; + readonly eventStream: ResolvedEventStreamOptions; + readonly logging: ResolvedLoggingOptions; + readonly fetch: FetchLike; +}; + +/** + * Resolves the engine-wide options once, when the client is built, from what the caller passed. + * + * @remarks + * `loggingEnvironmentVariable` is the name of the environment variable that can turn logging on. + * It is read here, once, and only when `logging` sets neither `logger` nor `level`. + * + * @throws {@link ConfigurationError} when no `fetch` was supplied and the global one is missing, + * or when reading `retry`, `eventStream` or `logging` throws. No value throws: every bounded field + * resolves, its own default standing in for a value the engine cannot honour. + */ +export function buildCoreClientOptions( + options: CoreClientOptions, + loggingEnvironmentVariable: string, +): ResolvedCoreClientOptions { + const fetchImpl = options.fetch ?? globalThis.fetch; + if (typeof fetchImpl !== "function") { + throw new ConfigurationError("No fetch implementation available; pass ClientOptions.fetch."); + } + return { + retry: buildRetryOptions(options.retry), + eventStream: buildEventStreamOptions(options.eventStream), + logging: buildLoggingOptions(options.logging, loggingEnvironmentVariable), + fetch: fetchImpl, + }; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/errors.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/errors.ts new file mode 100644 index 00000000..d600fb4e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/errors.ts @@ -0,0 +1,253 @@ +import type { HttpMethod } from "./api-request.js"; +import type { ApiError } from "./api-error.js"; + +/** Discriminant of the {@link CoreError} family. Closed, so a `switch` over it is exhaustive. */ +export type ErrorKind = "api" | "decode" | "encode" | "connection" | "timeout" | "auth"; + +/** + * What every {@link CoreError} is built with: the call it names, and an optional `cause`. + * + * @remarks + * It widens `ErrorOptions`, so `cause` is the platform's own and reaches the instance through + * `Error` rather than being copied. The message is not here: each throw site writes its own. + */ +export type CoreErrorInit = ErrorOptions & { + readonly method: HttpMethod; + readonly uri: string; +}; + +/** + * The runtime root every failure in the family extends. Its public face is {@link CoreError}. + * + * @remarks + * Not exported from the package and never named in a signature: a caught failure is one of the + * leaves, and {@link CoreError} is how they are spelled together. + */ +export abstract class CoreErrorBase extends Error { + abstract readonly kind: ErrorKind; + + /** HTTP method of the call that failed. */ + readonly method: HttpMethod; + + /** + * Absolute URI of the call that failed, as `https://us.api.acme.com/v1/accounts/acc_1/payments`. + * + * @remarks + * Path parameters are filled in and server variables expanded, so it names the resource actually + * dialled. It carries no query, fragment or userinfo, so no query parameter reaches it, not even + * an `apiKeyQuery` credential. With `method` it opens `message`. + * + * One failure names an unresolved URI: a path parameter rejected by its schema arrives as an + * `EncodeError` whose URI still shows the unfilled `{braces}` β€” an `undefined` one included, + * since a path parameter is always required, so its schema rejects it first. A `baseUrl` that + * is not a URL is a bug, and leaves as a raw `TypeError` naming no call. + */ + readonly uri: string; + + protected constructor(message: string, init: CoreErrorInit) { + super(message, init); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + this.method = init.method; + this.uri = init.uri; + } +} + +/** What a {@link ResponseError} adds to {@link CoreErrorInit}: the answer the API gave. */ +export type ResponseErrorInit = CoreErrorInit & { + readonly status: number; + readonly headers: Headers; +}; + +/** + * The runtime rung under {@link ResponseError}, carrying the two facts that exist exactly when + * the server answered. + * + * @remarks + * Not exported from the package. Its two arms are `ApiError` and {@link DecodeError}. + */ +export abstract class ResponseErrorBase extends CoreErrorBase { + abstract override readonly kind: "api" | "decode"; + + /** Status the API answered with. */ + readonly status: number; + + /** Headers the API answered with. The correlation id is read off these. */ + readonly headers: Headers; + + protected constructor(message: string, init: ResponseErrorInit) { + super(message, init); + this.status = init.status; + this.headers = init.headers; + } +} + +/** + * The server answered, and the answer could not be turned into the value the spec declares. + * + * @remarks + * `status` and `headers` are the ones it answered with, so the failure still says what came back + * and still carries the correlation id. `cause` says how it failed: a `SchemaError` when the body + * did not fit its contract, a `SyntaxError` when it was not JSON, or the platform's read fault + * when it died mid-stream. A body arriving where the operation declares none has no `cause`. + * + * Shape drift on a **declared** status is this, because the spec was wrong about the API. A status + * the spec does not describe arrives instead as an `ApiError` whose payload is the `"undeclared"` + * arm, which is the open world working as intended. + */ +export class DecodeError extends ResponseErrorBase { + readonly kind = "decode" as const; + + constructor(message: string, init: ResponseErrorInit) { + super(message, init); + } +} + +/** + * A request value did not match its declared type, so nothing was sent. + * + * @remarks + * The mirror of {@link DecodeError}, and it carries no `status` or `headers` because there is no + * response: the call never left. `cause` is the `SchemaError` that rejected the value, whose own + * `cause` is the issue list naming the field. + */ +export class EncodeError extends CoreErrorBase { + readonly kind = "encode" as const; + + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** + * `fetch` rejected before a response line arrived β€” the request never reached the server, or the + * server never answered β€” or an event stream's connection failed while you read it. + * + * @remarks + * Once a response line has arrived this is no longer the kind: a body that then dies mid-read is a + * {@link DecodeError}, carrying the status and headers the server did send. A binary success body + * is the exception, since it is yours from the moment it resolves, so a socket dying under it + * surfaces on that stream as the platform's own error, not as this. An event stream is the other: + * its loop raises this when a read fails, with the fault on `cause`. + */ +export class ConnectionError extends CoreErrorBase { + readonly kind = "connection" as const; + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** What a {@link TimeoutError} adds to {@link CoreErrorInit}: the budget that elapsed. */ +export type TimeoutErrorInit = CoreErrorInit & { + readonly timeout: number; +}; + +/** + * The request exceeded the client timeout, or an event stream stayed silent past its idle timeout. + * + * @remarks + * The client timeout starts once the credential is in hand and covers the request up to its + * response headers. Obtaining the credential and reading any response body are not covered. An + * OAuth2 token request is timed on its own, so a slow token endpoint raises this with the token + * endpoint's `uri`, not the operation's. An event stream's loop raises this when the server sends + * nothing for `eventStream.idleTimeout` while the loop waits. + */ +export class TimeoutError extends CoreErrorBase { + readonly kind = "timeout" as const; + + /** + * The budget that elapsed, in milliseconds: `ClientOptions.retry.timeout` as it resolved for this + * client, or the stream's `idleTimeout` when an event stream raised it, so a handler reads the + * limit off the failure rather than the configuration. + */ + readonly timeout: number; + + constructor(message: string, init: TimeoutErrorInit) { + super(message, init); + this.timeout = init.timeout; + } +} + +/** + * A credential could not be obtained, so the call was never sent. + * + * @remarks + * Raised when the token exchange answered with an error status, returned a body that would not + * decode, or could not be encoded, and when every branch of an `anyAuth` failed. `cause` carries + * that failure, and the operation's retry loop never re-sends it: a refusal is not transient. A + * dropped connection or a timeout during the exchange keeps its own kind instead, names the token + * endpoint, and ends the call without the operation being sent. + * + * `method` and `uri` name the operation you called; the token endpoint appears in the message + * after them. A 401 answered by the operation itself is an `ApiError`, not this. + */ +export class AuthError extends CoreErrorBase { + readonly kind = "auth" as const; + constructor(message: string, init: CoreErrorInit) { + super(message, init); + } +} + +/** + * The API answered, and the answer is the failure. + * + * @remarks + * The one branch of the family that reaches a response: an error status arrives as `ApiError` and + * a body that would not decode as {@link DecodeError}. Catch it to handle everything the server + * had a say in, and read `headers` for the correlation id either way. `err.kind` tells the two + * arms apart and narrows to the one you have. + */ +export type ResponseError = ApiError | DecodeError; + +/** + * The value behind the {@link ResponseError} type: `err instanceof ResponseError` narrows to that + * union. It cannot be constructed and cannot be extended. + */ +export const ResponseError = ResponseErrorBase as unknown as abstract new (...args: never) => ResponseError; + +/** + * Root of every failure the SDK raises. Exported as `AcmeError`. + * + * @remarks + * One `catch (err) { if (err instanceof AcmeError) }` sees everything: the answers the API gave and + * the bodies it could not decode, both {@link ResponseError}, plus a request value that would not + * encode, a dropped connection, a timeout and auth that could not be obtained. Every one names the + * call that raised it, and `message` opens with that name. + * + * Narrow with `instanceof` on a leaf, or on `err.kind`, which is exhaustive and selects the leaf + * with its own members: `status` and `headers` on `"api"` and `"decode"`, `payload` on `"api"`, + * `timeout` on `"timeout"`. + * + * Bugs stay outside the family and reach you raw. An unparseable base URL and a non-file value + * for a file are both `TypeError`. A caller abort is outside it too, arriving as the `reason` of + * the signal you aborted. + */ +export type CoreError = ApiError | DecodeError | EncodeError | ConnectionError | TimeoutError | AuthError; + +/** + * The value behind the {@link CoreError} type, exported as `AcmeError`: `err instanceof AcmeError` + * narrows to that union. It cannot be constructed and cannot be extended. + */ +export const CoreError = CoreErrorBase as unknown as abstract new (...args: never) => CoreError; + +/** + * The client, or one call's options, could not be resolved. + * + * @remarks + * Not a member of the {@link CoreError} family: it carries no `method` or `uri`. The constructor + * throws it synchronously for three defects: no `fetch`, a basic-auth username containing a + * colon, and an unknown `serverEnvironment`. A `serverOptions` override of the wrong type is a + * `SchemaError` instead, raised by the codec that rejected it. + * + * A `retry` field the engine cannot honour is **not** a fourth: it falls back to its default. The + * `retry` and `logging` records reach this error only when reading one throws, as a getter that + * throws or a revoked proxy does, and that failure is on `cause`. Passed to the constructor, such a + * record is thrown from it. Passed to one call, it rejects that call before anything is sent. + */ +export class ConfigurationError extends Error { + constructor(message: string, options?: ErrorOptions) { + super(message, options); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream-reader.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream-reader.ts new file mode 100644 index 00000000..500cfd7a --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream-reader.ts @@ -0,0 +1,248 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConnectionError, TimeoutError } from "./errors.js"; +import type { ResolvedEventStreamOptions } from "./event-stream.js"; + +const LINE_FEED = "\n"; +const CARRIAGE_RETURN = "\r"; +const LINE_FEED_CODE = 0x0a; +const SPACE_CODE = 0x20; +const BYTE_ORDER_MARK_CODE = 0xfeff; +const DATA_FIELD = "data"; + +type Failure = { readonly error: unknown }; + +type ReadResult = Awaited["read"]>>; + +export class EventStreamReader { + readonly #response: Response; + readonly #method: HttpMethod; + readonly #uri: string; + readonly #signal: AbortSignal; + readonly #abort: () => void; + readonly #sentinel: string | undefined; + readonly #clock: IdleClock; + readonly #parser = new EventStreamParser(); + #reader: ReadableStreamDefaultReader | undefined; + #released: Promise | undefined; + #failure: Failure | undefined; + #onAbort: (() => void) | undefined; + + constructor( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, + sentinel: string | undefined, + ) { + this.#response = response; + this.#method = method; + this.#uri = uri; + this.#signal = signal; + this.#abort = abort; + this.#sentinel = sentinel; + this.#clock = new IdleClock(options.idleTimeout, (bound) => this.#lapse(bound)); + } + + readonly close = (): Promise => this.#release(undefined); + + async *frames(): AsyncGenerator { + try { + this.#watchSignal(); + for (;;) { + if (this.#halted()) return undefined; + const bytes = await this.#readChunk(); + if (bytes === undefined) return undefined; + for (const data of this.#parser.parse(bytes)) { + if (this.#halted()) return undefined; + if (data === this.#sentinel) return undefined; + yield data; + } + } + } finally { + void this.#release(undefined); + } + } + + #halted(): boolean { + if (this.#failure !== undefined) throw this.#failure.error; + return this.#released !== undefined; + } + + async #readChunk(): Promise { + const body = this.#response.body; + if (body === null) return undefined; + let result: ReadResult | undefined; + let fault: unknown; + this.#clock.start(); + try { + result = await (this.#reader ??= body.getReader()).read(); + } catch (error) { + fault = error; + } finally { + this.#clock.pause(); + } + if (this.#halted()) return undefined; + const lapsed = this.#clock.take(); + if (lapsed !== undefined) throw lapsed; + if (result === undefined) { + const signal = this.#signal; + throw signal.aborted && fault === signal.reason ? fault : this.#readError(fault); + } + return result.done ? undefined : result.value; + } + + #watchSignal(): void { + const signal = this.#signal; + if (this.#released !== undefined) return; + if (signal.aborted) { + void this.#release({ error: signal.reason }); + return; + } + const onAbort = (): void => { + void this.#release({ error: signal.reason }); + }; + this.#onAbort = onAbort; + signal.addEventListener("abort", onAbort, { once: true }); + } + + #lapse(bound: number): TimeoutError { + const error = new TimeoutError( + `${this.#method} ${this.#uri} failed: Event stream was idle for ${bound}ms.`, + { + method: this.#method, + uri: this.#uri, + timeout: bound, + }, + ); + void this.#drop(error); + return error; + } + + #release(failure: Failure | undefined): Promise { + if (this.#released !== undefined) return this.#released; + this.#failure = failure; + this.#clock.stop(); + if (this.#onAbort !== undefined) this.#signal.removeEventListener("abort", this.#onAbort); + this.#onAbort = undefined; + void this.#drop(failure?.error); + this.#released = Promise.resolve(); + return this.#released; + } + + #drop(reason: unknown): Promise { + const reader = this.#reader; + const cancelled = Promise.resolve( + reader === undefined ? this.#response.body?.cancel(reason) : reader.cancel(reason), + ).catch(() => {}); + this.#abort(); + return cancelled; + } + + #readError(error: unknown): ConnectionError { + return new ConnectionError(`${this.#method} ${this.#uri} failed: Event stream could not be read.`, { + method: this.#method, + uri: this.#uri, + cause: error, + }); + } +} + +class IdleClock { + readonly #bound: number | null; + readonly #lapse: (bound: number) => TimeoutError; + #timer: ReturnType | undefined; + #waitingSince: number | undefined; + #lapsed: TimeoutError | undefined; + + constructor(bound: number | null, lapse: (bound: number) => TimeoutError) { + this.#bound = bound; + this.#lapse = lapse; + } + + start(): void { + const bound = this.#bound; + if (bound === null) return; + this.#waitingSince = performance.now(); + this.#timer ??= setTimeout(() => this.#tick(bound), bound); + } + + pause(): void { + this.#waitingSince = undefined; + } + + stop(): void { + clearTimeout(this.#timer); + this.#timer = undefined; + } + + take(): TimeoutError | undefined { + const lapsed = this.#lapsed; + this.#lapsed = undefined; + return lapsed; + } + + #tick(bound: number): void { + this.#timer = undefined; + const since = this.#waitingSince; + if (since === undefined) return; + const remaining = bound - (performance.now() - since); + if (remaining > 0) { + this.#timer = setTimeout(() => this.#tick(bound), Math.ceil(remaining)); + return; + } + this.#lapsed = this.#lapse(bound); + } +} + +class EventStreamParser { + readonly #decoder = new TextDecoder("utf-8", { ignoreBOM: true }); + #started = false; + #rest = ""; + + parse(bytes: Uint8Array): readonly string[] { + const chunk = this.#decode(bytes); + const text = this.#rest === "" ? chunk : this.#rest + chunk; + const frames: string[] = []; + let blockStart = 0; + let data = ""; + let position = 0; + let lineFeed = text.indexOf(LINE_FEED); + let carriageReturn = text.indexOf(CARRIAGE_RETURN); + while (lineFeed !== -1 || carriageReturn !== -1) { + const end = + lineFeed === -1 || (carriageReturn !== -1 && carriageReturn < lineFeed) ? carriageReturn : lineFeed; + const line = text.slice(position, end); + position = end + 1; + if (end === carriageReturn && text.charCodeAt(position) === LINE_FEED_CODE) position += 1; + if (line === "") { + if (data !== "") frames.push(data.slice(0, -1)); + data = ""; + blockStart = position; + } else { + const value = this.#dataOf(line); + if (value !== undefined) data += value + LINE_FEED; + } + if (lineFeed !== -1 && lineFeed < position) lineFeed = text.indexOf(LINE_FEED, position); + if (carriageReturn !== -1 && carriageReturn < position) + carriageReturn = text.indexOf(CARRIAGE_RETURN, position); + } + this.#rest = text.slice(blockStart); + return frames; + } + + #dataOf(line: string): string | undefined { + const colon = line.indexOf(":"); + if (colon === -1) return line === DATA_FIELD ? "" : undefined; + if (colon !== DATA_FIELD.length || !line.startsWith(DATA_FIELD)) return undefined; + return line.slice(line.charCodeAt(colon + 1) === SPACE_CODE ? colon + 2 : colon + 1); + } + + #decode(bytes: Uint8Array): string { + const text = this.#decoder.decode(bytes, { stream: true }); + if (this.#started || text === "") return text; + this.#started = true; + return text.charCodeAt(0) === BYTE_ORDER_MARK_CODE ? text.slice(1) : text; + } +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream.ts new file mode 100644 index 00000000..365de8dc --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/event-stream.ts @@ -0,0 +1,143 @@ +import * as s from "./validation/index.js"; +import type { Schema } from "./validation/schema.js"; +import { ConfigurationError } from "./errors.js"; + +const MAX_TIMEOUT_MS = 2_147_483_647; +const DEFAULT_EVENT_STREAM_OPTIONS: ResolvedEventStreamOptions = { + idleTimeout: 60_000, +}; + +type AsyncDisposeKey = SymbolConstructor extends { readonly asyncDispose: infer K extends symbol } + ? K + : never; + +type AsyncDisposeMember = { readonly [K in AsyncDisposeKey]: () => Promise }; + +const AsyncDisposable = class {} as new () => AsyncDisposeMember; + +/** + * A server-sent event stream you own: the still-open response, read one event at a time. + * + * @remarks + * The operation resolves as soon as the response headers arrive. Iterate the stream once with + * `for await`, or release it unread with `close()`. Each step is one event's `data`, decoded + * through the schema the operation declares, and nothing is read ahead of your loop. Leaving the + * loop early releases the connection. Aborting the call's `signal` ends the loop with the signal's + * own `reason`, and every other failure is a `CoreError`. Where the platform supports + * `await using`, disposing the stream releases the connection too. + */ +export class EventStream extends AsyncDisposable implements AsyncIterable { + readonly #close: () => Promise; + readonly #events: AsyncGenerator; + #taken = false; + + /** @internal Engine-only: the stream decoders in `response-decoder.ts` build it. */ + constructor(close: () => Promise, events: AsyncGenerator) { + super(); + this.#close = close; + this.#events = events; + } + + /** + * The events' data, in order. Callable once: a second call throws a `TypeError`, as a second read + * of a web stream does. + */ + [Symbol.asyncIterator](): AsyncIterator { + return this.#take(); + } + + /** Releases the connection, iterated or not. Safe to call more than once. */ + close(): Promise { + return this.#close(); + } + + /** + * The same events as a web `ReadableStream`, pulled one at a time; cancelling it releases the + * connection. It takes the stream's one iteration. + */ + toReadableStream(): ReadableStream { + return pullStream(this.#take()); + } + + #take(): EventIterator { + if (this.#taken) { + throw new TypeError("An event stream can be read once, and this one is already being read."); + } + this.#taken = true; + const events = this.#events; + return { + next: () => events.next(), + return: () => { + void this.#close(); + return events.return(undefined); + }, + }; + } +} + +const asyncDispose: unknown = Reflect.get(Symbol, "asyncDispose"); +if (typeof asyncDispose === "symbol") { + Object.defineProperty(EventStream.prototype, asyncDispose, { + value(this: EventStream): Promise { + return this.close(); + }, + }); +} + +/** + * How an event stream is watched for a silent server. + * + * @remarks + * Resolved once, when the client is built. A field left out takes its default. + */ +export type EventStreamOptions = { + /** + * Milliseconds the server may stay silent while your loop waits for the next event. When it runs + * out the loop ends with a `TimeoutError`. `null` disables it. + * + * @default 60000 + */ + readonly idleTimeout?: number | null; +}; + +export type ResolvedEventStreamOptions = Required; + +export function buildEventStreamOptions(options: EventStreamOptions | undefined): ResolvedEventStreamOptions { + const schema: Schema = s.of( + s.fallback( + s.object({ + idleTimeout: s.fallback( + s.nullable(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS))), + DEFAULT_EVENT_STREAM_OPTIONS.idleTimeout, + ), + }), + DEFAULT_EVENT_STREAM_OPTIONS, + ), + ); + try { + return schema.decode(options); + } catch (err) { + throw new ConfigurationError("The event stream options could not be resolved.", { cause: err }); + } +} + +type EventIterator = { + next(): Promise>; + return(): Promise>; +}; + +function pullStream(iterator: EventIterator): ReadableStream { + return new ReadableStream( + { + pull: async (controller) => { + const next = await iterator.next(); + if (next.done === true) controller.close(); + else controller.enqueue(next.value); + }, + cancel: async () => { + await iterator.return(); + }, + }, + { highWaterMark: 0 }, + ); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/headers.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/headers.ts new file mode 100644 index 00000000..9309491b --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/headers.ts @@ -0,0 +1,91 @@ +import type { ApiRequest, RawClientOptions } from "./api-request.js"; +import { EncodeError } from "./errors.js"; +import type { Param, ParamPair } from "./param-value.js"; +import { encodedParam, flattenValue } from "./param-value.js"; +import { percentEncode } from "./params.js"; +import type { BodyContent } from "./request-body.js"; +import { SchemaError } from "./validation/schema-error.js"; +import * as s from "./validation/index.js"; + +type HeaderDefaults = Pick; + +type HeadersRequest = Pick; + +type ContentHeaders = Pick; + +export class HeadersBuilder { + readonly #defaultHeaders: readonly Param[]; + + constructor(defaults: HeaderDefaults) { + this.#defaultHeaders = defaults.defaultHeaders; + } + + build({ method, headers }: HeadersRequest, uri: string, body: ContentHeaders): RequestHeaders { + try { + return new RequestHeaders(foldHeaders([contentHeaders(body), this.#defaultHeaders, headers])); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new EncodeError(`${method} ${uri} failed: Request header could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } +} + +export class RequestHeaders { + readonly #headers: Headers; + + constructor(headers: Headers) { + this.#headers = headers; + } + + withCredential(headers: readonly ParamPair[], cookies: readonly ParamPair[]): Headers { + const result = new Headers(this.#headers); + for (const [name, value] of headers) result.set(name, value); + + const cookie = mergeCookies(result.get("cookie"), cookies); + if (cookie !== undefined) result.set("cookie", cookie); + return result; + } +} + +const CONTENT_HEADER = s.optional(s.string()); + +function contentHeaders({ contentType, contentDisposition }: ContentHeaders): Param[] { + return [ + { name: "content-type", value: contentType, schema: CONTENT_HEADER }, + { name: "content-disposition", value: contentDisposition, schema: CONTENT_HEADER }, + ]; +} + +function foldHeaders(layers: ReadonlyArray): Headers { + const headers = new Headers(); + for (const layer of layers) { + for (const header of layer) { + const value = encodedParam(header); + if (value === undefined) continue; + const parts = flattenValue(value); + if (parts.length === 0) headers.delete(header.name); + else headers.set(header.name, parts.join(",")); + } + } + return headers; +} + +function mergeCookies(existing: string | null, cookies: readonly ParamPair[]): string | undefined { + if (cookies.length === 0) return existing ?? undefined; + + const pairs = new Map(); + for (const part of (existing ?? "").split(";")) { + const trimmed = part.trim(); + const separator = trimmed.indexOf("="); + if (separator <= 0) continue; + pairs.set(trimmed.slice(0, separator), trimmed.slice(separator + 1)); + } + for (const [name, value] of cookies) pairs.set(name, percentEncode(value)); + + const merged = [...pairs].map(([name, value]) => `${name}=${value}`).join("; "); + return merged === "" ? undefined : merged; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/http-logger.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/http-logger.ts new file mode 100644 index 00000000..b7fc6e18 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/http-logger.ts @@ -0,0 +1,338 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConfigurationError } from "./errors.js"; +import { + logLevels, + type HttpResponseDetails, + type Logger, + type LogLevel, + type RequestLoggingOptions, + type ResolvedLoggingOptions, +} from "./logging.js"; +import type { RetryAttempt } from "./retry.js"; + +const SAFE_HEADERS: ReadonlySet = new Set([ + "accept", + "accept-charset", + "accept-encoding", + "accept-language", + "accept-ranges", + "age", + "allow", + "cache-control", + "connection", + "content-disposition", + "content-encoding", + "content-language", + "content-length", + "content-md5", + "content-range", + "content-type", + "date", + "etag", + "expect", + "expires", + "host", + "idempotency-key", + "if-match", + "if-modified-since", + "if-none-match", + "if-range", + "if-unmodified-since", + "keep-alive", + "last-modified", + "max-forwards", + "origin", + "pragma", + "range", + "ratelimit-limit", + "ratelimit-policy", + "ratelimit-remaining", + "ratelimit-reset", + "request-id", + "retry-after", + "server", + "te", + "trailer", + "transfer-encoding", + "upgrade", + "user-agent", + "vary", + "via", + "warning", + "www-authenticate", + "traceparent", + "tracestate", + "x-correlation-id", + "x-ratelimit-limit", + "x-ratelimit-remaining", + "x-ratelimit-reset", + "x-request-id", + "x-apimatic-lang", + "x-apimatic-package-version", + "x-apimatic-gen-version", + "x-apimatic-os", + "x-apimatic-runtime", +]); + +const SAFE_QUERY_KEYS: ReadonlySet = new Set([ + "cursor", + "dates", + "datetime", + "limit", + "offset", + "page", + "since", + "size", + "strings", +]); + +export class HttpLogger { + readonly #logger: Logger; + readonly #logsInfo: boolean; + readonly #logsWarn: boolean; + readonly #logsError: boolean; + readonly #logsRequestHeaders: boolean; + readonly #logsRequestBody: boolean; + readonly #logsResponseHeaders: boolean; + readonly #loggableContentTypes: readonly string[]; + readonly #bodySizeLimit: number; + readonly #redaction: Redaction; + readonly #method: HttpMethod; + readonly #url: URL; + #sentAt = 0; + + constructor( + requestLoggingOptions: RequestLoggingOptions | undefined, + loggingOptions: ResolvedLoggingOptions, + method: HttpMethod, + url: URL, + ) { + this.#method = method; + this.#url = url; + const { level, logRequestHeaders, logResponseHeaders, logRequestBody } = applyRequestLoggingOptions( + requestLoggingOptions, + loggingOptions, + ); + this.#logger = loggingOptions.logger; + this.#logsInfo = enables(level, "info"); + this.#logsWarn = enables(level, "warn"); + this.#logsError = enables(level, "error"); + this.#logsRequestHeaders = enables(level, "debug") && logRequestHeaders; + this.#logsRequestBody = enables(level, "debug") && logRequestBody; + this.#logsResponseHeaders = enables(level, "debug") && logResponseHeaders; + this.#loggableContentTypes = loggingOptions.loggableContentTypes.map((type) => type.toLowerCase()); + this.#bodySizeLimit = loggingOptions.bodySizeLimit; + this.#redaction = new Redaction(loggingOptions); + } + + async logRequest(request: Request): Promise { + if (this.#logsInfo) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + this.#logger.info(`sending ${method} ${href}`, { event: "HttpRequest", method, url: href }); + } + + if (this.#logsRequestHeaders) { + this.#logger.debug("request headers", { + ...this.#redaction.redactHeaders(request.headers), + event: "RequestHeaders", + }); + } + + if (this.#logsRequestBody && request.body !== null) { + const mediaType = mediaTypeOf(request.headers.get("content-type")); + if (mediaType !== "" && this.#loggableContentTypes.some((type) => mediaType.startsWith(type))) { + this.#logger.debug("request body", { + event: "RequestBody", + mediaType, + body: await this.#renderBody(request, mediaType), + }); + } + } + this.#sentAt = Date.now(); + } + + logResponse(response: Response): void { + if (response.ok ? this.#logsInfo : this.#logsWarn) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const { status } = response; + const durationMs = Date.now() - this.#sentAt; + const details: HttpResponseDetails = { event: "HttpResponse", method, url: href, status, durationMs }; + if (response.ok) { + this.#logger.info(`${method} ${href} succeeded with status ${status} in ${durationMs}ms`, details); + } else { + this.#logger.warn(`${method} ${href} failed with status ${status} in ${durationMs}ms`, details); + } + } + + if (this.#logsResponseHeaders) { + this.#logger.debug("response headers", { + ...this.#redaction.redactHeaders(response.headers), + event: "ResponseHeaders", + }); + } + } + + logRetry(retry: RetryAttempt, maxRetries: number): void { + if (this.#logsWarn) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const { reason } = retry; + this.#logger.warn( + `${method} ${href} retrying in ${retry.delay}ms (retry ${retry.attemptNumber} of ${maxRetries}) after ${ + reason.kind === "status" ? `status ${reason.status}` : reason.error.name + }`, + { + event: "HttpRetrying", + method, + url: href, + delayMs: retry.delay, + retry: retry.attemptNumber, + maxRetries, + ...(reason.kind === "status" + ? { reason: "status", status: reason.status } + : { reason: "fault", errorName: reason.error.name }), + }, + ); + } + } + + logFailure(error: Error, startedAt: number): void { + if (this.#logsError) { + const method = this.#method; + const href = this.#redaction.redactUrl(this.#url); + const durationMs = Date.now() - startedAt; + this.#logger.error(`${method} ${href} failed after ${durationMs}ms: ${error.name}`, { + event: "HttpFailed", + method, + url: href, + durationMs, + errorName: error.name, + }); + } + } + + async #renderBody(request: Request, mediaType: string): Promise { + const body = await request.clone().text(); + const redacted = + mediaType === "application/x-www-form-urlencoded" ? this.#redaction.redactFormBody(body) : body; + return truncateToBytes(redacted, this.#bodySizeLimit); + } +} + +class Redaction { + readonly placeholder: string; + readonly #redactedHeaders: ReadonlySet; + readonly #unmaskedHeaders: ReadonlySet; + readonly #redactedKeys: ReadonlySet; + + constructor(options: ResolvedLoggingOptions) { + this.placeholder = options.redactionPlaceholder; + this.#redactedHeaders = new Set(options.redactedHeaders.map((name) => name.toLowerCase())); + this.#unmaskedHeaders = new Set(options.unmaskHeaders.map((name) => name.toLowerCase())); + this.#redactedKeys = new Set(options.redactedKeys.map((key) => key.toLowerCase())); + } + + redactHeaders(headers: Headers): Record { + const redacted: Record = {}; + for (const [name, value] of headers) { + if (this.#isRedactedHeader(name)) { + redacted[name] = this.placeholder; + continue; + } + const previous = redacted[name]; + redacted[name] = previous === undefined ? value : `${previous}, ${value}`; + } + return redacted; + } + + redactUrl(url: URL): string { + const query = maskPairs(url.search.slice(1), this.placeholder, (key) => this.#isRedactedQueryKey(key)); + return `${url.origin}${url.pathname}${query === "" ? "" : `?${query}`}${url.hash}`; + } + + redactFormBody(body: string): string { + return maskPairs(body, this.placeholder, (key) => this.isRedactedKey(key)); + } + + isRedactedKey(key: string): boolean { + return this.#redactedKeys.has(key.toLowerCase()); + } + + #isRedactedHeader(name: string): boolean { + if (this.#unmaskedHeaders.has(name)) return false; + return this.#redactedHeaders.has(name) || !SAFE_HEADERS.has(name); + } + + #isRedactedQueryKey(key: string): boolean { + return this.isRedactedKey(key) || !SAFE_QUERY_KEYS.has(key.toLowerCase()); + } +} + +type AppliedLoggingOptions = Pick< + ResolvedLoggingOptions, + "level" | "logRequestHeaders" | "logResponseHeaders" | "logRequestBody" +>; + +function applyRequestLoggingOptions( + requestLoggingOptions: RequestLoggingOptions | undefined, + loggingOptions: ResolvedLoggingOptions, +): AppliedLoggingOptions { + let override: LogLevel | undefined; + try { + override = logLevels.find((candidate) => candidate === requestLoggingOptions?.level); + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } + if (override === undefined) return loggingOptions; + return { level: override, logRequestHeaders: true, logResponseHeaders: true, logRequestBody: true }; +} + +function enables(level: LogLevel, threshold: LogLevel): boolean { + return logLevels.indexOf(level) >= logLevels.indexOf(threshold); +} + +function mediaTypeOf(contentType: string | null): string { + if (contentType === null) return ""; + return (contentType.split(";")[0] ?? "").trim().toLowerCase(); +} + +function truncateToBytes(body: string, limit: number): string { + const totalBytes = new TextEncoder().encode(body).length; + if (totalBytes <= limit) return body; + let bytesUsed = 0; + let charsKept = 0; + for (const char of body) { + const code = char.codePointAt(0) ?? 0; + const charBytes = code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4; + if (bytesUsed + charBytes > limit) break; + bytesUsed += charBytes; + charsKept += char.length; + } + return `${body.slice(0, charsKept)} …(${totalBytes} bytes total)`; +} + +function maskPairs(pairs: string, placeholder: string, isRedactedKey: (key: string) => boolean): string { + if (pairs === "") return pairs; + return pairs + .split("&") + .map((pair) => { + const equals = pair.indexOf("="); + if (equals === -1) return pair; + const rawKey = pair.slice(0, equals); + return isRedactedKey(paramName(rawKey)) ? `${rawKey}=${placeholder}` : pair; + }) + .join("&"); +} + +function paramName(rawKey: string): string { + let key: string; + try { + key = decodeURIComponent(rawKey.replaceAll("+", " ")); + } catch { + key = rawKey; + } + const bracket = key.indexOf("["); + return bracket === -1 ? key : key.slice(0, bracket); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/logging.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/logging.ts new file mode 100644 index 00000000..cace467d --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/logging.ts @@ -0,0 +1,206 @@ +import { ConfigurationError } from "./errors.js"; +import * as host from "./runtime-environment.js"; +import * as s from "./validation/index.js"; + +export const logLevels = ["off", "error", "warn", "info", "debug"] as const; + +/** The built-in defaults behind {@link LoggingOptions}; spread a list to extend it. */ +export const defaultLoggingOptions: ResolvedLoggingOptions = Object.freeze({ + logger: console, + level: "off", + logRequestHeaders: false, + logResponseHeaders: false, + logRequestBody: false, + bodySizeLimit: 32_768, + loggableContentTypes: Object.freeze(["application/json", "application/x-www-form-urlencoded"]), + redactedHeaders: Object.freeze([]), + unmaskHeaders: Object.freeze([]), + redactedKeys: Object.freeze([ + "sig", + "signature", + "access_token", + "apikey", + "api_key", + "client_secret", + "password", + "refresh_token", + "code", + "assertion", + "client_assertion", + ]), + redactionPlaceholder: "***", +}); + +/** How the client logs its HTTP traffic, set through `ClientOptions.logging`. */ +export type LoggingOptions = { + readonly logger?: Logger; + readonly level?: LogLevel; + readonly logRequestHeaders?: boolean; + readonly logResponseHeaders?: boolean; + readonly logRequestBody?: boolean; + readonly bodySizeLimit?: number; + readonly loggableContentTypes?: readonly string[]; + readonly redactedHeaders?: readonly string[]; + readonly unmaskHeaders?: readonly string[]; + readonly redactedKeys?: readonly string[]; + readonly redactionPlaceholder?: string; +}; + +/** The part of {@link LoggingOptions} a call may override through `RequestOptions.logging`. */ +export type RequestLoggingOptions = Pick; + +/** Where the client writes its log lines; `console` already has this shape. */ +export type Logger = { + error(message: string, details: LogDetails): void; + warn(message: string, details: LogDetails): void; + info(message: string, details: LogDetails): void; + debug(message: string, details: LogDetails): void; +}; + +/** The structured fields passed with every log line; narrow on `event` to read the rest. */ +export type LogDetails = + | HttpRequestDetails + | RequestHeadersDetails + | RequestBodyDetails + | HttpResponseDetails + | ResponseHeadersDetails + | HttpRetryingDetails + | HttpFailedDetails; + +/** Logged at `info` as a request is sent. */ +export type HttpRequestDetails = { + readonly event: "HttpRequest"; + readonly method: string; + readonly url: string; +}; + +/** Logged at `debug` with a request's headers, one field per header. */ +export type RequestHeadersDetails = { + readonly event: "RequestHeaders"; + readonly [header: string]: string; +}; + +/** Logged at `debug` with a request's body. */ +export type RequestBodyDetails = { + readonly event: "RequestBody"; + readonly mediaType: string; + readonly body: string; +}; + +/** Logged at `info` when a response arrives, or `warn` for an error status. */ +export type HttpResponseDetails = { + readonly event: "HttpResponse"; + readonly method: string; + readonly url: string; + readonly status: number; + readonly durationMs: number; +}; + +/** Logged at `debug` with a response's headers, one field per header. */ +export type ResponseHeadersDetails = { + readonly event: "ResponseHeaders"; + readonly [header: string]: string; +}; + +/** Logged at `warn` before a failed attempt is retried. */ +export type HttpRetryingDetails = { + readonly event: "HttpRetrying"; + readonly method: string; + readonly url: string; + readonly delayMs: number; + readonly retry: number; + readonly maxRetries: number; +} & ( + | { readonly reason: "status"; readonly status: number } + | { readonly reason: "fault"; readonly errorName: string } +); + +/** Logged at `error` when a call ends without a response. */ +export type HttpFailedDetails = { + readonly event: "HttpFailed"; + readonly method: string; + readonly url: string; + readonly durationMs: number; + readonly errorName: string; +}; + +export type LogSeverity = "error" | "warn" | "info" | "debug"; + +/** The most detailed level the client logs, or `"off"` to log nothing. */ +export type LogLevel = LogSeverity | "off"; + +export type ResolvedLoggingOptions = Required; + +export function buildLoggingOptions( + options: LoggingOptions | undefined, + environmentVariable: string, +): ResolvedLoggingOptions { + let logger: Logger | undefined; + let levelInCode: boolean; + try { + logger = options?.logger; + levelInCode = logLevels.some((level) => level === options?.level); + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } + if (logger !== undefined) { + return validateLoggingOptions(options, { ...defaultLoggingOptions, level: "debug" }); + } + if (levelInCode) { + return validateLoggingOptions(options, defaultLoggingOptions); + } + const level = environmentLogLevel(environmentVariable); + if (level === undefined) return validateLoggingOptions(options, defaultLoggingOptions); + const headers = level === "debug"; + return validateLoggingOptions(options, { + ...defaultLoggingOptions, + level, + logRequestHeaders: headers, + logResponseHeaders: headers, + }); +} + +function validateLoggingOptions( + options: LoggingOptions | undefined, + fallbacks: Omit, +): ResolvedLoggingOptions { + const schema = s.of( + s.fallback( + s.object>({ + level: s.fallback(s.literal(logLevels), fallbacks.level), + logRequestHeaders: s.fallback(s.boolean(), fallbacks.logRequestHeaders), + logResponseHeaders: s.fallback(s.boolean(), fallbacks.logResponseHeaders), + logRequestBody: s.fallback(s.boolean(), fallbacks.logRequestBody), + bodySizeLimit: s.fallback(s.int().check(s.gte(0)), fallbacks.bodySizeLimit), + loggableContentTypes: s.fallback(s.array(s.string()), fallbacks.loggableContentTypes), + redactedHeaders: s.fallback(s.array(s.string()), fallbacks.redactedHeaders), + unmaskHeaders: s.fallback(s.array(s.string()), fallbacks.unmaskHeaders), + redactedKeys: s.fallback(s.array(s.string()), fallbacks.redactedKeys), + redactionPlaceholder: s.fallback(s.string(), fallbacks.redactionPlaceholder), + }), + fallbacks, + ), + ); + try { + const logger = options?.logger; + return { + ...schema.decode(options), + logger: logger === undefined ? defaultLoggingOptions.logger : logger, + }; + } catch (err) { + throw new ConfigurationError("The logging options could not be resolved.", { cause: err }); + } +} + +function environmentLogLevel(name: string): LogLevel | undefined { + const value = host.environmentVariable(name); + if (value === undefined) return undefined; + const level = logLevels.find((candidate) => candidate === value.toLowerCase()); + if (level === undefined) { + console.warn( + `${name} is set to ${JSON.stringify(value)}, which is not a log level. ` + + `Expected one of: ${logLevels.join(", ")}. Logging stays off.`, + ); + } + return level; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/param-value.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/param-value.ts new file mode 100644 index 00000000..456fa994 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/param-value.ts @@ -0,0 +1,104 @@ +import type { Entry } from "./validation/schema.js"; +import { encodeEntry } from "./validation/schema-error.js"; + +export type ParamStyle = "plain" | "indexed" | "unindexed" | "csv" | "tsv" | "psv"; + +export type ParamValue = + | string + | number + | boolean + | bigint + | null + | undefined + | readonly ParamValue[] + | { readonly [key: string]: ParamValue }; + +export type Param = { + readonly name: Name; + readonly value: unknown; + readonly schema: Entry; +}; + +export type StyledParam = Param & { readonly style?: ParamStyle | undefined }; + +export type ParamPair = readonly [name: string, value: string]; + +const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER); + +export function flattenValue(value: ParamValue): string[] { + if (value === undefined || value === null) return []; + if (isScalar(value)) return [scalar(value)]; + if (!Array.isArray(value)) return [jsonValue(value)]; + + const folded: string[] = []; + for (const element of value) { + if (element === undefined || element === null) folded.push(""); + else if (isScalar(element)) folded.push(scalar(element)); + else return [jsonValue(value)]; + } + return folded; +} + +export function encodedParam(source: Param): ParamValue { + return encodeEntry(source.schema, source.value) as ParamValue; +} + +export function flattenParams(sources?: ReadonlyArray): ParamPair[] { + return (sources ?? []).flatMap((source) => + flattenParam(source.name, encodedParam(source), source.style ?? "plain"), + ); +} + +function flattenParam(key: string, value: ParamValue, style: ParamStyle): ParamPair[] { + if (value === undefined || value === null) return []; + if (isScalar(value)) return [[key, scalar(value)]]; + if (Array.isArray(value)) return flattenArray(key, value, style); + return Object.entries(value).flatMap(([sub, subValue]) => flattenParam(`${key}[${sub}]`, subValue, style)); +} + +function flattenArray(key: string, values: readonly ParamValue[], style: ParamStyle): ParamPair[] { + const present = values.filter((value) => value !== undefined && value !== null); + switch (style) { + case "plain": + return present.flatMap((value) => flattenParam(key, value, style)); + case "indexed": + return values.flatMap((value, index) => flattenParam(`${key}[${index}]`, value, style)); + case "unindexed": + return present.flatMap((value) => flattenParam(`${key}[]`, value, style)); + case "csv": + return [joined(key, values, ",")]; + case "tsv": + return [joined(key, values, "\t")]; + case "psv": + return [joined(key, values, "|")]; + default: + return unknownStyle(style); + } +} + +function joined(key: string, values: readonly ParamValue[], separator: string): ParamPair { + return [key, flattenValue(values).join(separator)]; +} + +function unknownStyle(style: never): never { + throw new TypeError(`Unknown parameter style: ${String(style)}.`); +} + +export function isScalar(value: ParamValue): value is string | number | boolean | bigint { + if (value === undefined || value === null) return false; + return typeof value !== "object"; +} + +export function scalar(value: string | number | boolean | bigint): string { + return String(value); +} + +export function jsonValue(value: ParamValue): string { + return JSON.stringify(value, bigintReplacer); +} + +function bigintReplacer(_key: string, value: unknown): unknown { + if (typeof value !== "bigint") return value; + if (value <= MAX_SAFE_BIGINT && value >= -MAX_SAFE_BIGINT) return Number(value); + return String(value); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/params.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/params.ts new file mode 100644 index 00000000..ab2904b7 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/params.ts @@ -0,0 +1,25 @@ +import type { ParamPair, StyledParam } from "./param-value.js"; +import { flattenParams } from "./param-value.js"; + +export function percentEncode(value: string): string { + return encodeURIComponent(value).replace( + /[!'()*]/g, + (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`, + ); +} + +export function formEncode(value: string): string { + return percentEncode(value).replace(/%20/g, "+"); +} + +export function queryString(layers: ReadonlyArray): string { + return serializePairs(layers.flatMap(flattenParams), percentEncode); +} + +export function formString(fields?: ReadonlyArray): string { + return serializePairs(flattenParams(fields), formEncode); +} + +export function serializePairs(pairs: ReadonlyArray, encode: (value: string) => string): string { + return pairs.map(([key, value]) => `${encode(key)}=${encode(value)}`).join("&"); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/raw-client.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/raw-client.ts new file mode 100644 index 00000000..15587535 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/raw-client.ts @@ -0,0 +1,191 @@ +import type { ApiRequest, HttpMethod, RequestOptions, RawClientOptions } from "./api-request.js"; +import { CoreError, ConnectionError, TimeoutError } from "./errors.js"; +import type { ApiError } from "./api-error.js"; +import { ResponseReader, type ResponseHandler } from "./response-decoder.js"; +import { ApiPromise, type RequestOutcome } from "./api-promise.js"; +import { buildBody } from "./request-body.js"; +import { RetryPolicy } from "./retry.js"; +import { UrlBuilder } from "./url.js"; +import { HeadersBuilder } from "./headers.js"; +import { HttpLogger } from "./http-logger.js"; +import { authenticateRequest } from "./auth/schemes.js"; + +export class RawClient { + readonly #config: RawClientOptions; + readonly #urlBuilder: UrlBuilder; + readonly #headersBuilder: HeadersBuilder; + readonly #responseReader: ResponseReader; + + constructor(config: RawClientOptions) { + this.#config = config; + this.#urlBuilder = new UrlBuilder(config); + this.#headersBuilder = new HeadersBuilder(config); + this.#responseReader = new ResponseReader(config); + } + + execute( + apiRequest: ApiRequest, + responseHandler: ResponseHandler, + options?: RequestOptions, + ): ApiPromise { + return new ApiPromise(this.#dispatch(apiRequest, responseHandler, options)); + } + + async #dispatch( + apiRequest: ApiRequest, + responseHandler: ResponseHandler, + options: RequestOptions | undefined, + ): Promise> { + const startedAt = Date.now(); + const callerSignal = options?.signal ?? neverAbortedSignal(); + const uploadController = new AbortController(); + const streamRequestController = new AbortController(); + const abortStreamRequest = (): void => streamRequestController.abort(); + + const url = this.#urlBuilder.build(apiRequest); + const { uri } = url; + + const body = buildBody( + apiRequest.method, + uri, + apiRequest.body, + AbortSignal.any([callerSignal, uploadController.signal]), + ); + + const logger = new HttpLogger( + options?.logging, + this.#config.logging, + apiRequest.method, + url.withQuery([]), + ); + const headers = this.#headersBuilder.build(apiRequest, uri, body); + const policy = new RetryPolicy(options?.retry, this.#config.retry); + const response = await policy.execute( + apiRequest.method, + callerSignal, + startedAt, + logger, + async (timeout) => { + const authenticated = await authenticateRequest( + apiRequest.method, + apiRequest.auth, + url, + headers, + callerSignal, + startedAt, + logger, + ); + + const isStreaming = body.streaming === true; + const timeoutController = new AbortController(); + const signal = AbortSignal.any([ + callerSignal, + timeoutController.signal, + streamRequestController.signal, + ]); + if (isStreaming) { + timeoutController.signal.addEventListener( + "abort", + () => uploadController.abort(timeoutController.signal.reason), + { once: true }, + ); + } + + const request = new Request(authenticated.url, { + method: apiRequest.method, + headers: authenticated.headers, + body: body.body, + signal, + ...(isStreaming ? { duplex: "half", window: null, redirect: "error" } : {}), + }); + await logger.logRequest(request); + + const timer = startTimeout(timeoutController, timeout, apiRequest.method, uri); + let response: Response; + try { + response = await this.#config.fetch.call(undefined, request); + } catch (err) { + if (err instanceof ConnectionError || err instanceof TimeoutError) + return { kind: "fault", error: err, isStreaming }; + if (err instanceof CoreError) throw err; + if (signal.reason instanceof TimeoutError) + return { kind: "fault", error: signal.reason, isStreaming }; + if (signal.aborted) throw signal.reason; + return { + kind: "fault", + error: new ConnectionError( + `${apiRequest.method} ${uri} failed: ${ + err instanceof Error && err.message !== "" ? err.message : "Connection error." + }`, + { cause: err, method: apiRequest.method, uri }, + ), + isStreaming, + }; + } finally { + clearTimeout(timer); + } + + const discard = (): void => { + void Promise.resolve(response.body?.cancel()).catch(() => {}); + }; + try { + logger.logResponse(response); + } catch (err) { + discard(); + throw err; + } + if (response.status === 401) apiRequest.auth.invalidate?.(); + return { kind: "response", response, isStreaming, discard }; + }, + ); + + if (isSuccess(response.status)) { + const data = await this.#responseReader.read( + responseHandler.success, + response, + apiRequest.method, + uri, + callerSignal, + abortStreamRequest, + ); + return { ok: true, status: response.status, headers: response.headers, data }; + } + + const error = await this.#responseReader.readError( + responseHandler.errorFactory, + response, + apiRequest.method, + uri, + callerSignal, + abortStreamRequest, + ); + return { ok: false, status: response.status, headers: response.headers, error }; + } +} + +function isSuccess(status: number): boolean { + return status >= 200 && status <= 299; +} + +function neverAbortedSignal(): AbortSignal { + return new AbortController().signal; +} + +function startTimeout( + controller: AbortController, + timeoutMs: number, + method: HttpMethod, + uri: string, +): ReturnType { + return setTimeout( + () => + controller.abort( + new TimeoutError(`${method} ${uri} failed: Request timed out after ${timeoutMs}ms.`, { + method, + uri, + timeout: timeoutMs, + }), + ), + timeoutMs, + ); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/request-body.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/request-body.ts new file mode 100644 index 00000000..9b871b7e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/request-body.ts @@ -0,0 +1,300 @@ +import type { HttpMethod } from "./api-request.js"; +import { + attachmentDisposition, + concatStream, + resolveFile, + type BufferedResolvedFile, + type ByteSource, + type FileInput, + type ResolvedFile, +} from "./binary.js"; +import { EncodeError } from "./errors.js"; +import type { Param, ParamValue, StyledParam } from "./param-value.js"; +import { flattenParams, isScalar, jsonValue, scalar } from "./param-value.js"; +import { formString } from "./params.js"; +import type { Entry } from "./validation/schema.js"; +import { SchemaError, encodeEntry } from "./validation/schema-error.js"; + +export type Json = string | number | boolean | null | readonly Json[] | JsonObject; + +export type JsonObject = { readonly [key: string]: Json | undefined }; + +export type ContentType = + | "application/x-www-form-urlencoded;charset=UTF-8" + | "application/json" + | "text/plain;charset=utf-8" + | "application/octet-stream" + | `multipart/form-data; boundary=${string}` + | (string & {}); + +export type BodyContent = { + body: string | FormData | Blob | Uint8Array | ReadableStream | null; + contentType?: ContentType; + contentDisposition?: string; + streaming?: true; +}; + +export type EmptyBody = { + readonly kind: "empty"; +}; + +export type JsonBody = { + readonly kind: "json"; + readonly value: unknown; + readonly schema: Entry; +}; + +export type FormUrlEncodedBody = { + readonly kind: "formUrlEncoded"; + readonly value: readonly StyledParam[]; +}; + +export type TextBody = { + readonly kind: "text"; + readonly value: unknown; + readonly schema: Entry; +}; + +export type MultipartTextPart = StyledParam & { + readonly kind: "text"; +}; + +export type MultipartJsonPart = Param & { + readonly kind: "json"; + readonly contentType: "application/json" | `application/${string}+json`; +}; + +export type MultipartFilePart = { + readonly kind: "file"; + readonly name: string; + readonly value: FileInput | readonly (FileInput | null | undefined)[] | null | undefined; + readonly contentType?: string | undefined; +}; + +export type MultipartPart = MultipartTextPart | MultipartJsonPart | MultipartFilePart; + +export type MultipartBody = { + readonly kind: "multipart"; + readonly value: readonly MultipartPart[]; +}; + +export type BinaryBody = { + readonly kind: "binary"; + readonly value: FileInput | null | undefined; + readonly contentType: string; +}; + +export type RequestBody = EmptyBody | JsonBody | FormUrlEncodedBody | TextBody | MultipartBody | BinaryBody; + +export function buildBody( + method: HttpMethod, + uri: string, + body: RequestBody, + signal?: AbortSignal, +): BodyContent { + switch (body.kind) { + case "empty": + return { body: null }; + case "json": + return buildJson(body, method, uri); + case "formUrlEncoded": + return buildFormUrlEncoded(body, method, uri); + case "text": + return buildText(body, method, uri); + case "multipart": + return buildMultipart(body, signal, method, uri); + case "binary": + return buildBinary(body, signal); + default: + return unknownBodyKind(body); + } +} + +function unknownBodyKind(body: never): never { + const kind = (body as { kind?: unknown }).kind; + throw new TypeError(`Unsupported request body kind: ${String(kind)}`); +} + +function encodeFailure(err: unknown, method: HttpMethod, uri: string): EncodeError { + if (!(err instanceof SchemaError)) throw err; + return new EncodeError(`${method} ${uri} failed: Request value could not be encoded.`, { + cause: err, + method, + uri, + }); +} + +function buildFormUrlEncoded(body: FormUrlEncodedBody, method: HttpMethod, uri: string): BodyContent { + let text: string; + try { + text = formString(body.value); + } catch (err) { + throw encodeFailure(err, method, uri); + } + return { body: text, contentType: "application/x-www-form-urlencoded;charset=UTF-8" }; +} + +function buildJson(body: JsonBody, method: HttpMethod, uri: string): BodyContent { + let encoded: unknown; + try { + encoded = encodeEntry(body.schema, body.value); + } catch (err) { + throw encodeFailure(err, method, uri); + } + const text = JSON.stringify(encoded); + if (text === undefined) return { body: null }; + return { body: text, contentType: "application/json" }; +} + +function buildBinary(body: BinaryBody, signal: AbortSignal | undefined): BodyContent { + if (body.value === undefined || body.value === null) return { body: null }; + + const file = resolveFile(body.value, body.contentType, signal); + const content: BodyContent = { body: file.body }; + if (file.contentType !== undefined) content.contentType = file.contentType; + const disposition = attachmentDisposition(file.fileName); + if (disposition !== undefined) content.contentDisposition = disposition; + if (file.streaming) content.streaming = true; + return content; +} + +function buildText(body: TextBody, method: HttpMethod, uri: string): BodyContent { + let encoded: ParamValue; + try { + encoded = encodeEntry(body.schema, body.value) as ParamValue; + } catch (err) { + throw encodeFailure(err, method, uri); + } + if (encoded === undefined) return { body: null }; + return { + body: isScalar(encoded) ? scalar(encoded) : jsonValue(encoded), + contentType: "text/plain;charset=utf-8", + }; +} + +type MultipartEntry = + | { readonly kind: "text"; readonly name: string; readonly value: string } + | { readonly kind: "json"; readonly name: string; readonly value: string; readonly contentType: string } + | { readonly kind: "file"; readonly name: string; readonly file: File }; + +function buildMultipart( + body: MultipartBody, + signal: AbortSignal | undefined, + method: HttpMethod, + uri: string, +): BodyContent { + let entries: readonly MultipartEntry[]; + try { + entries = body.value.flatMap(partEntries); + } catch (err) { + throw encodeFailure(err, method, uri); + } + if (allBuffered(entries)) return { body: toFormData(entries) }; + + const boundary = `----${crypto.randomUUID()}`; + return { + body: frameMultipart(entries, boundary, signal), + contentType: `multipart/form-data; boundary=${boundary}`, + streaming: true, + }; +} + +function partEntries(part: MultipartPart): readonly MultipartEntry[] { + switch (part.kind) { + case "text": + return flattenParams([part]).map(([name, value]) => ({ kind: "text", name, value })); + case "json": { + const encoded = encodeEntry(part.schema, part.value); + const entries: MultipartEntry[] = []; + for (const document of Array.isArray(encoded) ? encoded : [encoded]) { + const value = JSON.stringify(document); + if (value === undefined) continue; + entries.push({ kind: "json", name: part.name, value, contentType: part.contentType }); + } + return entries; + } + case "file": { + const entries: MultipartEntry[] = []; + for (const input of Array.isArray(part.value) ? part.value : [part.value]) { + if (input === undefined || input === null) continue; + entries.push({ kind: "file", name: part.name, file: resolveFile(input, part.contentType) }); + } + return entries; + } + default: + return unknownPartKind(part); + } +} + +function allBuffered( + entries: readonly MultipartEntry[], +): entries is readonly MultipartEntry[] { + return entries.every((entry) => entry.kind !== "file" || !entry.file.streaming); +} + +function toFormData(entries: readonly MultipartEntry[]): FormData { + const form = new FormData(); + for (const entry of entries) { + if (entry.kind === "text") { + form.append(entry.name, entry.value); + } else if (entry.kind === "json") { + form.append(entry.name, new Blob([entry.value], { type: entry.contentType }), ""); + } else { + const { body, contentType, fileName } = entry.file; + form.append(entry.name, new Blob([body], { type: contentType ?? "" }), fileName ?? ""); + } + } + return form; +} + +function frameMultipart( + entries: readonly MultipartEntry[], + boundary: string, + signal: AbortSignal | undefined, +): ReadableStream { + const encoder = new TextEncoder(); + const segments: ByteSource[] = []; + for (const entry of entries) { + const name = escapeParameter(normalizeNewlines(entry.name)); + const disposition = `--${boundary}\r\nContent-Disposition: form-data; name="${name}"`; + if (entry.kind === "text") { + segments.push(encoder.encode(`${disposition}\r\n\r\n${normalizeNewlines(entry.value)}\r\n`)); + } else if (entry.kind === "json") { + const contentType = partContentType(entry.contentType); + segments.push( + encoder.encode(`${disposition}\r\nContent-Type: ${contentType}\r\n\r\n${entry.value}\r\n`), + ); + } else { + const { body, contentType, fileName } = entry.file; + const filename = + fileName === undefined || fileName === "" ? "" : `; filename="${escapeParameter(fileName)}"`; + segments.push( + encoder.encode(`${disposition}${filename}\r\nContent-Type: ${partContentType(contentType)}\r\n\r\n`), + body, + encoder.encode("\r\n"), + ); + } + } + segments.push(encoder.encode(`--${boundary}--\r\n`)); + return concatStream(segments, signal); +} + +function normalizeNewlines(value: string): string { + return value.replace(/\r\n|\r|\n/g, "\r\n"); +} + +function escapeParameter(value: string): string { + return value.replaceAll('"', "%22").replaceAll("\r", "%0D").replaceAll("\n", "%0A"); +} + +const PRINTABLE_ASCII = /^[ -~]*$/; + +function partContentType(type: string | undefined): string { + if (type === undefined || type === "" || !PRINTABLE_ASCII.test(type)) return "application/octet-stream"; + return type.toLowerCase(); +} + +function unknownPartKind(part: never): never { + const kind = (part as { kind?: unknown }).kind; + throw new TypeError(`Unsupported multipart part kind: ${String(kind)}`); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/response-decoder.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/response-decoder.ts new file mode 100644 index 00000000..727116d2 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/response-decoder.ts @@ -0,0 +1,402 @@ +import type { Entry } from "./validation/schema.js"; +import type { HttpMethod, RawClientOptions } from "./api-request.js"; +import type { BinaryContent, BinaryErrorContent } from "./binary.js"; +import { readBinary, readBinaryError } from "./binary.js"; +import type { ResolvedEventStreamOptions } from "./event-stream.js"; +import { EventStream } from "./event-stream.js"; +import { EventStreamReader } from "./event-stream-reader.js"; +import { CoreError, DecodeError } from "./errors.js"; +import { SchemaError, decodeEntry } from "./validation/schema-error.js"; +import type { ApiError, ApiErrorInit, Declared, Undeclared } from "./api-error.js"; + +export type JsonResponseDecoder = { + readonly kind: "json"; + readonly schema: Entry; +}; + +export type TextResponseDecoder = { + readonly kind: "text"; + readonly schema: Entry; +}; + +export type EmptyResponseDecoder = { + readonly kind: "empty"; +}; + +export type BinaryResponseDecoder = { + readonly kind: "binary"; + readonly contentType: string; +}; + +export type BinaryErrorResponseDecoder = { + readonly kind: "binaryError"; +}; + +export type EventStreamTextResponseDecoder = { + readonly kind: "eventStreamText"; + readonly dataSchema: Entry; + readonly sentinel?: string | undefined; +}; + +export type EventStreamJsonResponseDecoder = { + readonly kind: "eventStreamJson"; + readonly dataSchema: Entry; + readonly sentinel?: string | undefined; +}; + +export type EventStreamResponseDecoder = + | EventStreamTextResponseDecoder + | EventStreamJsonResponseDecoder; + +export type ResponseDecoder = + | JsonResponseDecoder + | TextResponseDecoder + | ([T] extends [undefined] ? EmptyResponseDecoder : never) + | ([T] extends [BinaryContent] ? BinaryResponseDecoder : never) + | ([T] extends [EventStream] ? EventStreamResponseDecoder : never); + +export type ErrorResponseDecoder = + | JsonResponseDecoder + | TextResponseDecoder + | ([T] extends [undefined] ? EmptyResponseDecoder : never) + | ([T] extends [BinaryErrorContent] ? BinaryErrorResponseDecoder : never); + +export type StatusPattern = + | number + | "1XX" + | "2XX" + | "3XX" + | "4XX" + | "5XX" + | "default" + | readonly [number, number]; + +/** + * The payload union an operation's error class declares. + * + * @remarks + * It is the type of `err.payload` on the thrown error and of `result.payload` on the + * `ok: false` arm of `ApiResult`, so a narrowing written for one works unchanged for the other. + */ +export type PayloadOf = E["payload"]; + +type BodyByKind = { + [Arm in Extract> as Arm["kind"]]: Arm["body"]; +}; + +type KindOf = keyof BodyByKind & string; + +export type ErrorMatcher = KindOf> = { + readonly on: StatusPattern; + readonly kind: K; + readonly decode: ErrorResponseDecoder[K]>; +}; + +type ErrorMatcherOf = { [K in KindOf]: ErrorMatcher }[KindOf]; + +type DeclaredArmOf = { + [K in KindOf]: Declared[K]>; +}[KindOf]; + +export type ErrorDecoders = readonly ErrorMatcherOf>[]; + +export type ErrorFactory = (new (init: ApiErrorInit>) => E) & { + readonly errors?: ErrorDecoders | undefined; +}; + +export type ResponseHandler = { + success: ResponseDecoder; + errorFactory: ErrorFactory; +}; + +type ReaderOptions = Pick; + +export class ResponseReader { + readonly #eventStreamOptions: ResolvedEventStreamOptions; + + constructor(options: ReaderOptions) { + this.#eventStreamOptions = options.eventStream; + } + + async read( + decoder: ResponseDecoder | ErrorResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise { + switch (decoder.kind) { + case "json": + return decodeJson(decoder, response, method, uri, signal); + case "text": + return decodeText(decoder, response, method, uri, signal); + case "empty": + return (await decodeEmpty(response, method, uri, signal)) as T; + case "binary": + return readBinary(decoder, response) as T; + case "binaryError": + return (await readBinaryError(response, method, uri, signal)) as T; + case "eventStreamText": + return readEventStreamText( + decoder, + response, + method, + uri, + signal, + this.#eventStreamOptions, + abort, + ) as T; + case "eventStreamJson": + return readEventStreamJson( + decoder, + response, + method, + uri, + signal, + this.#eventStreamOptions, + abort, + ) as T; + default: { + await response.body?.cancel().catch(() => {}); + return unknownDecoderKind(decoder); + } + } + } + + async readError( + factory: ErrorFactory, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise { + const matchers = factory.errors; + const status = response.status; + const matcher = + matchers?.find((m) => m.on === status) ?? + matchers?.find((m) => typeof m.on !== "number" && m.on !== "default" && matchesStatus(m.on, status)) ?? + matchers?.find((m) => m.on === "default"); + const payload: DeclaredArmOf> | Undeclared = matcher + ? await this.#declared(matcher, response, method, uri, signal, abort) + : { kind: "undeclared", rawBody: await readRawBody(response, method, uri, signal) }; + return new factory({ status, headers: response.headers, method, uri, payload }); + } + + async #declared>( + matcher: ErrorMatcher, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + abort: () => void, + ): Promise[K]>> { + return { + kind: matcher.kind, + body: await this.read(matcher.decode, response, method, uri, signal, abort), + }; + } +} + +async function decodeJson( + decoder: JsonResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let text: string; + try { + text = await response.text(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return decodeJsonText(decoder.schema, text, "Response body", response, method, uri); +} + +async function decodeText( + decoder: TextResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let text: string; + try { + text = await response.text(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + return decodeTextValue(decoder.schema, text, "Response body", response, method, uri); +} + +function decodeJsonText( + schema: Entry, + text: string, + subject: string, + response: Response, + method: HttpMethod, + uri: string, +): T { + let parsed: unknown; + try { + parsed = JSON.parse(text); + } catch (err) { + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be parsed as JSON.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + try { + return decodeEntry(schema, parsed); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be decoded.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +function decodeTextValue( + schema: Entry, + text: string, + subject: string, + response: Response, + method: HttpMethod, + uri: string, +): T { + try { + return decodeEntry(schema, text); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + throw new DecodeError(`${method} ${uri} failed: ${subject} could not be decoded.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +async function decodeEmpty( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + let bytes: ArrayBuffer; + try { + bytes = await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } + if (bytes.byteLength > 0) + throw new DecodeError(`${method} ${uri} failed: Expected an empty response body.`, { + method, + uri, + status: response.status, + headers: response.headers, + }); + return undefined; +} + +async function readRawBody( + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, +): Promise { + try { + return await response.arrayBuffer(); + } catch (err) { + if (err instanceof CoreError || (signal.aborted && err === signal.reason)) throw err; + throw new DecodeError(`${method} ${uri} failed: Response body could not be read.`, { + cause: err, + method, + uri, + status: response.status, + headers: response.headers, + }); + } +} + +export function readEventStreamJson( + decoder: EventStreamJsonResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, +): EventStream { + const reader = new EventStreamReader(response, method, uri, signal, options, abort, decoder.sentinel); + const events = (async function* (): AsyncGenerator { + for await (const data of reader.frames()) { + yield decodeJsonText(decoder.dataSchema, data, "Event data", response, method, uri); + } + return undefined; + })(); + + return new EventStream(reader.close, events); +} + +export function readEventStreamText( + decoder: EventStreamTextResponseDecoder, + response: Response, + method: HttpMethod, + uri: string, + signal: AbortSignal, + options: ResolvedEventStreamOptions, + abort: () => void, +): EventStream { + const reader = new EventStreamReader(response, method, uri, signal, options, abort, decoder.sentinel); + const events = (async function* (): AsyncGenerator { + for await (const data of reader.frames()) { + yield decodeTextValue(decoder.dataSchema, data, "Event data", response, method, uri); + } + return undefined; + })(); + + return new EventStream(reader.close, events); +} + +function matchesStatus(pattern: StatusPattern, status: number): boolean { + if (typeof pattern === "number") return pattern === status; + if (typeof pattern === "string") return `${Math.floor(status / 100)}XX` === pattern; + return status >= pattern[0] && status <= pattern[1]; +} + +function unknownDecoderKind(decoder: never): never { + const kind = (decoder as { kind?: unknown }).kind; + throw new TypeError(`Unsupported response decoder kind: ${String(kind)}`); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/retry.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/retry.ts new file mode 100644 index 00000000..70b59af5 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/retry.ts @@ -0,0 +1,417 @@ +import type { HttpMethod } from "./api-request.js"; +import { ConfigurationError, type ConnectionError, type TimeoutError } from "./errors.js"; +import type { HttpLogger } from "./http-logger.js"; +import * as s from "./validation/index.js"; +import type { Schema } from "./validation/schema.js"; + +const MAX_DELAY_MS = 60_000; +const MAX_TIMEOUT_MS = 2_147_483_647; +const MAX_BACKOFF_FACTOR = 100; +const MIN_STATUS = 100; +const MAX_STATUS = 599; +const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const; +const DEFAULT_RETRY_OPTIONS: ResolvedRetryOptions = { + timeout: 60_000, + statusCodesToRetry: [408, 429, 500, 502, 503, 504], + httpMethodsToRetry: ["GET", "HEAD", "PUT", "OPTIONS"], + maxRetries: 3, + delay: 1000, + backoffFactor: 2, + useExponentialBackoff: true, + maxJitter: 0.25, +}; + +/** + * How a failed request is retried. + * + * @remarks + * Every field is optional, so any subset is accepted and "the defaults with two of them changed" + * is a two-field literal. What the engine reads is this type **resolved**, with every field filled + * in, so the policy can be read off the options rather than inferred from engine behaviour. It is + * resolved at two layers: once when the client is built, over the engine's defaults, and again on + * every call, over the client's resolved options, when a call passes its own `retry`, which is + * {@link RequestRetryOptions}, three of these fields. + * + * A field a call leaves out keeps the client's value. A field given a value the engine cannot + * honour falls back to what stands behind it β€” the default at the client, the client's value on a + * call β€” and takes none of the fields beside it with it. The per-field blocks say which values + * those are. `0`, `false` and an empty list are values, never misses. + * + * `timeout` is the one field that is not about retrying: it bounds every attempt, the only one + * under `maxRetries: 0` included, so this type is what a single request may do as well as what a + * retried one may. + * + * Two things are retried: a response **status**, and a **transport fault** β€” a connection failure + * or a per-attempt timeout. An abort is never retried, nor is a fault the SDK itself caused, nor a + * request whose body is a stream, because a stream cannot be replayed. + * + * Obtaining a credential comes before the attempt's timer starts, and a failure there is never + * retried by the operation. The OAuth2 token request is a request of its own, timed and retried + * under the client's policy, so a token endpoint that drops the connection or runs out its own + * `timeout` ends the call with that request's `ConnectionError` or `TimeoutError`. A token + * endpoint that answers with a refusal is an `AuthError` and ends the call too. + */ +export type RetryOptions = { + /** + * Milliseconds a single attempt may take, from sending the request to its response headers. + * + * @remarks + * A value `setTimeout` cannot honour β€” negative, fractional, `NaN`, `Infinity`, or above + * `2_147_483_647` β€” silently falls back to the default, or on a call to the client's value. + * `0` is passed through literally, a zero-length deadline rather than "no timeout". + * + * Every attempt gets its own deadline, so this is the budget for one attempt and not for the + * call. `maxRetries: 0` does not lift it β€” the single attempt is timed like any other. + * + * The timer starts once the credential is in hand and stops when the response headers arrive, so + * it covers neither obtaining the credential nor reading any response body. The call's `signal` + * bounds both, and an OAuth2 token request is also timed on its own. + * + * @default 60000 + */ + readonly timeout?: number; + + /** + * Response statuses that are retried. Exactly these and no others. + * + * @remarks + * There is no family rule: a status is retried if and only if it appears here. So `[429, 503]` + * stops retrying 500 and 502. An empty list retries no status at all, while a list carrying + * anything that is not a status code between 100 and 599 β€” and any value that is not a list of + * them at all β€” falls back to the default. + * + * The default is the C# SDK's own set, each with a transient story β€” a timeout, a rate limit, an + * overloaded or misbehaving origin. The rest of the 5xx family is deliberately not in it: 501 + * and 505 will not succeed on a second attempt, and 510 and 511 both mean the caller must change + * something first. 409 is absent for its own reason β€” two of the three `ConflictReason` values + * this API declares are permanent, and the engine cancels the body before it could tell which + * one arrived. + * + * @default [408, 429, 500, 502, 503, 504] + */ + readonly statusCodesToRetry?: readonly number[]; + + /** + * Request methods that are retried. Exactly these and no others. + * + * @remarks + * The default is the conservative policy the C# SDK defaults to, which never sends a POST, a + * PATCH or a DELETE twice. Every non-GET operation mints its own `Idempotency-Key`, which is + * what makes widening it safe: name all seven methods and a write is retried like anything + * else. An empty list disables retries entirely, while a list carrying an entry that is not one + * of the seven methods β€” a lower-cased `"get"` among them β€” is rejected whole and falls back to + * the default, exactly as a bad status list does. + * + * @default ["GET", "HEAD", "PUT", "OPTIONS"] + */ + readonly httpMethodsToRetry?: readonly HttpMethod[]; + + /** + * How many further attempts a failed request may get, on top of the first one. + * + * @remarks + * A value that is not a safe, non-negative integer falls back to the default. `0` sends every + * request exactly once. + * + * @default 3 + */ + readonly maxRetries?: number; + + /** + * Milliseconds to wait before the first retry, and the base the backoff curve grows from. + * + * @remarks + * A value `setTimeout` cannot honour β€” negative, fractional, `NaN`, `Infinity`, or above + * `2_147_483_647` β€” falls back to the default. `0` retries immediately. + * + * No computed wait exceeds **60 seconds**, whatever this and `backoffFactor` compute between + * them. That ceiling is the engine's own rather than an option, and it bounds a `Retry-After` + * by the same number, so a value above it is accepted here and then never reached. Jitter sits + * on top of that ceiling rather than inside it, so the longest single wait is + * `60_000 Γ— (1 + maxJitter)` β€” 75 seconds at the default. + * + * @default 1000 + */ + readonly delay?: number; + + /** + * What each successive delay is multiplied by, when `useExponentialBackoff` is set. + * + * @remarks + * A value below 1 or above 100, or one that is not finite, falls back to the default. Growth + * stops at the ceiling {@link RetryOptions.delay} describes. + * + * @default 2 + */ + readonly backoffFactor?: number; + + /** + * Grow the delay by `backoffFactor` per attempt, rather than repeating `delay` unchanged. + * + * @default true + */ + readonly useExponentialBackoff?: boolean; + + /** + * The largest **fraction** of a delay that jitter may move it by at random. + * + * @remarks + * A fraction between 0 and 1, and it is **additive**: a wait is never shorter than the delay it + * spreads. `0.25` spreads a 1000ms delay off the curve over 1000-1250ms, and a 30s `Retry-After` + * over 30-37.5s β€” a client that woke before the time the server named would be answering a + * request to back off by backing off less. + * + * It is not a number of milliseconds: a value outside 0 to 1 falls back to the default, which + * is the one mistake a reader of the C# `RetryOptions` is most likely to make, its `MaxJitter` + * being a `TimeSpan`. `0` makes both exact, which is what a test wanting a predictable delay + * should pass. + * + * @default 0.25 + */ + readonly maxJitter?: number; + + /** + * Called before each retry sleep, with the attempt about to be made. + * + * @remarks + * Runs after the failed attempt's release has begun β€” its response body's cancel started, its + * timer already cleared β€” so a callback that throws cannot leak a connection or orphan a timer, + * but it does end the call, surfacing as the error it threw. Keep it to logging and metrics. + * + * Called for a retried transport fault as well as a retried status; narrow on + * {@link RetryAttempt.reason} before reading either. + * + * Anything other than a function falls back to no callback at all, which is what leaving it + * out means too β€” the absence of a callback is this field's value rather than a missing one. + * + * @default undefined + */ + readonly onRetry?: (attempt: RetryAttempt) => void; +}; + +/** + * The part of {@link RetryOptions} a single call may override, through `RequestOptions.retry`. + * + * @remarks + * Three fields, the ones whose right value differs from one operation to the next: how many + * attempts this call gets, how long each may take, and which statuses are worth a second try + * for this resource. The backoff curve, the method gate and `onRetry` are how the client paces + * itself against one server, so they are set on the client alone. + * + * A field named here replaces the client's for this call, and one left out keeps the client's + * value. The restriction is the type's: a value carrying another `RetryOptions` field, from + * JavaScript or through a constant typed as the wider record, still has it honoured. + */ +export type RequestRetryOptions = Pick; + +/** + * {@link RetryOptions} with every field filled in: what the engine reads, never what a caller + * writes. + * + * @remarks + * `RetryOptions` is itself the partial β€” every field optional, so any subset is accepted β€” and this + * is its resolved twin, built once when the client is built and again, over that one, on every + * call. The engine therefore reads a policy rather than re-deciding what an absent field meant, and + * no read site needs a `!`. + * + * `onRetry` is the one field that stays optional: the absence of a callback is its value, so a + * resolved record may leave the key out, and a missing key means the same as `undefined`. + */ +export type ResolvedRetryOptions = Required> & { + readonly onRetry?: RetryOptions["onRetry"]; +}; + +export function buildRetryOptions( + retry: RetryOptions | undefined, + fallbacks: ResolvedRetryOptions = DEFAULT_RETRY_OPTIONS, +): ResolvedRetryOptions { + const schema: Schema = s.of( + s.fallback( + s.object({ + timeout: s.fallback(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS)), fallbacks.timeout), + statusCodesToRetry: s.fallback( + s.array(s.int().check(s.gte(MIN_STATUS), s.lte(MAX_STATUS))), + fallbacks.statusCodesToRetry, + ), + httpMethodsToRetry: s.fallback(s.array(s.literal(HTTP_METHODS)), fallbacks.httpMethodsToRetry), + maxRetries: s.fallback(s.int().check(s.gte(0)), fallbacks.maxRetries), + delay: s.fallback(s.int().check(s.gte(0), s.lte(MAX_TIMEOUT_MS)), fallbacks.delay), + backoffFactor: s.fallback( + s.float64().check(s.gte(1), s.lte(MAX_BACKOFF_FACTOR)), + fallbacks.backoffFactor, + ), + useExponentialBackoff: s.fallback(s.boolean(), fallbacks.useExponentialBackoff), + maxJitter: s.fallback(s.float64().check(s.gte(0), s.lte(1)), fallbacks.maxJitter), + onRetry: s.fallback(s.callback<(attempt: RetryAttempt) => void>(), fallbacks.onRetry), + }), + fallbacks, + ), + ); + try { + return schema.decode(retry); + } catch (err) { + throw new ConfigurationError("The retry options could not be resolved.", { cause: err }); + } +} + +/** + * One retry, as {@link RetryOptions.onRetry} sees it. + */ +export type RetryAttempt = { + /** Which retry this is: `1` for the first one, after the original attempt failed. */ + readonly attemptNumber: number; + + /** Milliseconds the engine is about to wait before re-sending. */ + readonly delay: number; + + /** What went wrong on the attempt being abandoned. */ + readonly reason: RetryReason; +}; + +/** + * Why an attempt is being retried β€” a response the engine rejected, or no response at all. + * + * @remarks + * Narrow on `kind` before reading the rest: a fault carries no status and no headers, because + * nothing came back. + */ +export type RetryReason = + | { + /** A response arrived and its status is retryable. */ + readonly kind: "status"; + + /** The status that triggered the retry. */ + readonly status: number; + + /** + * Headers of the response being discarded. + * + * @remarks + * The one place a retried-away response reaches the caller. Its body is already being + * cancelled by the time this runs, so only the headers survive. + */ + readonly headers: Headers; + } + | { + /** No response arrived: the request failed at the transport layer. */ + readonly kind: "fault"; + + /** + * The classified failure, always a `connection` or a `timeout`. + * + * @remarks + * An abort is never retried, and neither is a fault the SDK itself caused, so neither ever + * reaches this callback. + * + * The OAuth2 token request's own retries arrive here too, since it runs under the client's + * policy. A fault names the call that dialled, so one from the token request carries the + * token endpoint's `uri` rather than the operation's. + */ + readonly error: ConnectionError | TimeoutError; + }; + +export type RespondedAttempt = { + readonly kind: "response"; + readonly response: Response; + readonly isStreaming: boolean; + discard(): void; +}; + +export type FaultedAttempt = { + readonly kind: "fault"; + readonly error: ConnectionError | TimeoutError; + readonly isStreaming: boolean; +}; + +export class RetryPolicy { + readonly #options: ResolvedRetryOptions; + + constructor(retry: RetryOptions | undefined, fallbacks: ResolvedRetryOptions) { + this.#options = buildRetryOptions(retry, fallbacks); + } + + async execute( + method: HttpMethod, + callerSignal: AbortSignal, + startedAt: number, + logger: HttpLogger, + send: (timeout: number) => Promise, + ): Promise { + for (let retriesMade = 0; ; retriesMade += 1) { + const attempt = await send(this.#options.timeout); + if (!shouldRetry(this.#options, retriesMade, method, attempt)) { + if (attempt.kind === "fault") { + logger.logFailure(attempt.error, startedAt); + throw attempt.error; + } + return attempt.response; + } + let reason: RetryReason; + switch (attempt.kind) { + case "response": + attempt.discard(); + reason = { kind: "status", status: attempt.response.status, headers: attempt.response.headers }; + break; + case "fault": + reason = { kind: "fault", error: attempt.error }; + break; + } + if (callerSignal.aborted) throw callerSignal.reason; + const retry: RetryAttempt = { + attemptNumber: retriesMade + 1, + delay: retryDelayMs(this.#options, retriesMade, reason), + reason, + }; + logger.logRetry(retry, this.#options.maxRetries); + this.#options.onRetry?.(retry); + await sleep(retry.delay, callerSignal); + if (callerSignal.aborted) throw callerSignal.reason; + } + } +} + +function retryDelayMs(options: ResolvedRetryOptions, retriesMade: number, reason: RetryReason): number { + const hint = reason.kind === "status" ? delayFromRetryAfterHeader(reason.headers, Date.now()) : undefined; + const growth = options.useExponentialBackoff ? options.backoffFactor ** retriesMade : 1; + const delay = Math.min(Math.max(hint ?? options.delay * growth, 0), MAX_DELAY_MS); + return Math.ceil(delay * (1 + Math.random() * options.maxJitter)); +} + +function shouldRetry( + options: ResolvedRetryOptions, + attempt: number, + method: HttpMethod, + outcome: RespondedAttempt | FaultedAttempt, +): boolean { + if (attempt >= options.maxRetries || outcome.isStreaming) return false; + if (!options.httpMethodsToRetry.includes(method)) return false; + return outcome.kind === "fault" || options.statusCodesToRetry.includes(outcome.response.status); +} + +function delayFromRetryAfterHeader(headers: Headers, now: number): number | undefined { + const value = headers.get("retry-after"); + if (value === null || value === "") return undefined; + + if (value[0]! >= "0" && value[0]! <= "9") { + for (const c of value) if (c < "0" || c > "9") return undefined; + return Number(value) * 1000; + } + + const first = value[0]!.toLowerCase(); + if (first < "a" || first > "z") return undefined; + const date = Date.parse(value); + return Number.isNaN(date) ? undefined : date - now; +} + +function sleep(ms: number, signal: AbortSignal): Promise { + if (signal.aborted) return Promise.resolve(); + return new Promise((resolve) => { + const timer = setTimeout(done, ms); + signal.addEventListener("abort", done); + function done(): void { + clearTimeout(timer); + signal.removeEventListener("abort", done); + resolve(); + } + }); +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/runtime-environment.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/runtime-environment.ts new file mode 100644 index 00000000..fe6fd8c8 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/runtime-environment.ts @@ -0,0 +1,74 @@ +export function operatingSystem(): string | undefined { + const nodeProcess = runtimeGlobals.process; + const versions = nodeProcess?.versions; + if ( + present(versions?.node) !== undefined || + present(versions?.bun) !== undefined || + present(versions?.deno) !== undefined + ) { + return processPlatform(nodeProcess); + } + return present(runtimeGlobals.navigator?.userAgentData?.platform); +} + +export function runtimeDescription(): string | undefined { + const versions = runtimeGlobals.process?.versions; + const bun = present(versions?.bun); + if (bun !== undefined) return `Bun/${bun}`; + const deno = present(versions?.deno); + if (deno !== undefined) return `Deno/${deno}`; + const edge = present(runtimeGlobals.EdgeRuntime); + if (edge !== undefined) return edge; + + const userAgent = present(runtimeGlobals.navigator?.userAgent); + if (userAgent === CLOUDFLARE_WORKERS_USER_AGENT) return userAgent; + + const node = present(versions?.node); + return node === undefined ? userAgent : `Node.js/${node}`; +} + +export function environmentVariable(name: string): string | undefined { + try { + return present(runtimeGlobals.process?.env?.[name]?.trim()); + } catch { + return undefined; + } +} + +type RuntimeVersions = { + readonly node?: string; + readonly bun?: string; + readonly deno?: string; +}; + +type NodeProcess = { + readonly versions?: RuntimeVersions; + readonly platform?: string; + readonly arch?: string; + readonly env?: Readonly>; +}; + +type NavigatorLike = { + readonly userAgent?: string; + readonly userAgentData?: { readonly platform?: string }; +}; + +type RuntimeGlobals = { + readonly process?: NodeProcess; + readonly navigator?: NavigatorLike; + readonly EdgeRuntime?: string; +}; + +const runtimeGlobals: RuntimeGlobals = globalThis; + +const CLOUDFLARE_WORKERS_USER_AGENT = "Cloudflare-Workers"; + +const present = (value: string | undefined): string | undefined => + value === undefined || value === "" ? undefined : value; + +function processPlatform(nodeProcess: NodeProcess | undefined): string | undefined { + const platform = present(nodeProcess?.platform); + if (platform === undefined) return undefined; + const arch = present(nodeProcess?.arch); + return arch === undefined ? platform : `${platform} ${arch}`; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/url.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/url.ts new file mode 100644 index 00000000..ffa56466 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/url.ts @@ -0,0 +1,122 @@ +import type { ApiRequest, RawClientOptions, ServerBase, UrlTemplate } from "./api-request.js"; +import type { StyledParam, Param, ParamPair } from "./param-value.js"; +import { encodedParam, flattenValue } from "./param-value.js"; +import { percentEncode, queryString, serializePairs } from "./params.js"; +import { EncodeError } from "./errors.js"; +import { SchemaError } from "./validation/schema-error.js"; + +type UrlDefaults = Pick; + +type UrlRequest = Pick; + +export class UrlBuilder { + readonly #defaultPathParams: readonly Param[]; + readonly #defaultQuery: readonly StyledParam[]; + + constructor(defaults: UrlDefaults) { + this.#defaultPathParams = defaults.defaultPathParams; + this.#defaultQuery = defaults.defaultQuery; + } + + build(request: UrlRequest): RequestUrl { + return new RequestUrl(this.#applyQuery(this.#resolvePath(request), request)); + } + + #resolvePath({ method, urlTemplate, pathParams }: UrlRequest): URL { + try { + return new URL( + joinUrl(urlTemplate.baseUrl, expandPath(urlTemplate.subPath, pathParams, this.#defaultPathParams)), + ); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + const uri = templateUri(urlTemplate); + throw new EncodeError(`${method} ${uri} failed: Request path parameter could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } + + #applyQuery(path: URL, { method, query }: UrlRequest): URL { + try { + return buildQueryUrl(path, [this.#defaultQuery, query]); + } catch (err) { + if (!(err instanceof SchemaError)) throw err; + const uri = uriOf(path); + throw new EncodeError(`${method} ${uri} failed: Request query parameter could not be encoded.`, { + cause: err, + method, + uri, + }); + } + } +} + +export class RequestUrl { + readonly uri: string; + readonly #url: URL; + + constructor(url: URL) { + this.#url = url; + this.uri = uriOf(url); + } + + withQuery(pairs: readonly ParamPair[]): URL { + return copyWithQuery(this.#url, serializePairs(pairs, percentEncode)); + } +} + +export function buildQueryUrl(url: URL, queryInWireOrder: ReadonlyArray): URL { + return copyWithQuery(url, queryString(queryInWireOrder)); +} + +export function resolveBaseUrl(server: ServerBase): string { + let out = server.baseUrl; + for (const [key, value] of Object.entries(server.variables ?? {})) { + if (value === undefined) continue; + out = out.replaceAll(`{${key}}`, percentEncode(value)); + } + return out; +} + +export function templateUri(template: UrlTemplate): string { + return joinUrl(template.baseUrl, template.subPath); +} + +function uriOf(url: URL): string { + return `${url.origin}${url.pathname}`; +} + +function copyWithQuery(url: URL, added: string): URL { + const result = new URL(url); + if (added !== "") result.search = url.search === "" ? added : `${url.search.slice(1)}&${added}`; + return result; +} + +function expandPath(template: string, ...layers: ReadonlyArray): string { + let out = template; + const skipped: string[] = []; + for (const layer of layers) { + for (const source of layer) { + const value = encodedParam(source); + if (value === undefined) { + skipped.push(source.name); + continue; + } + const replacement = value === null ? "" : flattenValue(value).map(percentEncode).join(","); + out = out.replaceAll(`{${source.name}}`, replacement); + } + } + for (const name of skipped) { + if (!out.includes(`{${name}}`)) continue; + throw new TypeError(`Path parameter "${name}" resolved to undefined and left {${name}} unfilled.`); + } + return out; +} + +function joinUrl(base: string, path: string): string { + const left = base.replace(/\/+$/, ""); + const right = path.replace(/^\/+/, ""); + return `${left}/${right}`; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/uuid.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/uuid.ts new file mode 100644 index 00000000..de949470 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/uuid.ts @@ -0,0 +1,43 @@ +export function uuid(): string { + const webCrypto = cryptoGlobals.crypto; + if (webCrypto?.randomUUID !== undefined) return webCrypto.randomUUID(); + + const bytes = new Uint8Array(16); + if (webCrypto?.getRandomValues !== undefined) webCrypto.getRandomValues(bytes); + else fillPseudoRandomBytes(bytes); + bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40; + bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; + + const text = hex(bytes); + return `${text.slice(0, 8)}-${text.slice(8, 12)}-${text.slice(12, 16)}-${text.slice(16, 20)}-${text.slice(20)}`; +} + +type CryptoLike = { + readonly randomUUID?: () => string; + readonly getRandomValues?: (array: Uint8Array) => unknown; +}; + +type CryptoGlobals = { + readonly crypto?: CryptoLike; +}; + +const cryptoGlobals: CryptoGlobals = globalThis; + +let pseudoRandomCalls = 0; + +function fillPseudoRandomBytes(bytes: Uint8Array): void { + for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256); + + let time = Date.now(); + for (let i = 5; i >= 0; i--) { + bytes[i] = (bytes[i] ?? 0) ^ (time % 256); + time = Math.floor(time / 256); + } + + pseudoRandomCalls = (pseudoRandomCalls + 1) % 65536; + bytes[10] = (bytes[10] ?? 0) ^ Math.floor(pseudoRandomCalls / 256); + bytes[11] = (bytes[11] ?? 0) ^ (pseudoRandomCalls % 256); +} + +const hex = (bytes: Uint8Array): string => + Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(""); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/entries.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/entries.ts new file mode 100644 index 00000000..818442f1 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/entries.ts @@ -0,0 +1,162 @@ +import * as zod from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import { resolve, type Encoded, type Entry, type Schema } from "./schema.js"; + +export function lazy(target: () => Schema>): ZodMiniType> { + return zod.lazy(() => resolve(target())); +} + +export function defaulted(entry: Entry>, value: T): ZodMiniType> { + return zod.codec(zod.optional(resolve(entry)), zod.custom(), { + decode: (given: T | undefined) => (given === undefined ? value : given), + encode: (given: T | undefined) => (given === undefined ? value : given), + }) as unknown as ZodMiniType>; +} + +export function fallback(entry: Entry>, value: T): ZodMiniType> { + return zod.catch(resolve(entry), () => value); +} + +export function callback unknown>(): ZodMiniType { + return zod.custom((value) => typeof value === "function"); +} + +export function optionalNullable( + entry: Entry>, +): ZodMiniType | null | undefined> { + return zod.optional(zod.nullable(resolve(entry))); +} + +export const dateTime = (): ZodMiniType => + zod.codec(zod.iso.datetime({ offset: true }), zod.date(), { + decode: (value) => new Date(value), + encode: (date) => date.toISOString(), + }); + +const IMF_FIXDATE = + /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/; + +export const rfc1123DateTime = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(IMF_FIXDATE)), zod.date(), { + decode: (value) => new Date(value), + encode: (date) => date.toUTCString(), + }); + +const EPOCH_SECONDS = /^-?\d+(?:\.\d+)?$/; + +export const unixSecondsDateTime = (): ZodMiniType => + zod.codec(zod.union([zod.number(), zod.string().check(zod.regex(EPOCH_SECONDS))]), zod.date(), { + decode: (value) => new Date(Number(value) * 1000), + encode: (date) => Math.floor(date.getTime() / 1000), + }) as unknown as ZodMiniType; + +export const dateOnly = (): ZodMiniType => zod.iso.date(); + +const BYTES = zod.custom((value) => value instanceof Uint8Array); + +const BASE64URL = /^(?:[A-Za-z0-9_-]{4})*(?:[A-Za-z0-9_-]{2}(?:==)?|[A-Za-z0-9_-]{3}=?)?$/; +const BASE32 = + /^(?:[A-Za-z2-7]{8})*(?:[A-Za-z2-7]{2}={6}|[A-Za-z2-7]{4}={4}|[A-Za-z2-7]{5}={3}|[A-Za-z2-7]{7}=)?$/; +const BASE32HEX = + /^(?:[0-9A-Va-v]{8})*(?:[0-9A-Va-v]{2}={6}|[0-9A-Va-v]{4}={4}|[0-9A-Va-v]{5}={3}|[0-9A-Va-v]{7}=)?$/; +const BASE16 = /^(?:[0-9A-Fa-f]{2})*$/; + +const BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; +const BASE32HEX_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; + +export const bytes = (): ZodMiniType => + zod.codec(zod.base64(), BYTES, { + decode: (value) => decodeBase64(value), + encode: (value) => encodeBase64(value), + }); + +export const base64UrlBytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE64URL)), BYTES, { + decode: (value) => decodeBase64(padBase64(value.replace(/-/g, "+").replace(/_/g, "/"))), + encode: (value) => encodeBase64(value).replace(/\+/g, "-").replace(/\//g, "_"), + }); + +export const base32Bytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE32)), BYTES, { + decode: (value) => decodeBase32(value, BASE32_ALPHABET), + encode: (value) => encodeBase32(value, BASE32_ALPHABET), + }); + +export const base32HexBytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE32HEX)), BYTES, { + decode: (value) => decodeBase32(value, BASE32HEX_ALPHABET), + encode: (value) => encodeBase32(value, BASE32HEX_ALPHABET), + }); + +export const base16Bytes = (): ZodMiniType => + zod.codec(zod.string().check(zod.regex(BASE16)), BYTES, { + decode: (value) => decodeBase16(value), + encode: (value) => encodeBase16(value), + }); + +function decodeBase64(value: string): Uint8Array { + const latin1 = atob(value); + const bytes = new Uint8Array(latin1.length); + for (let index = 0; index < latin1.length; index++) bytes[index] = latin1.charCodeAt(index); + return bytes; +} + +function encodeBase64(value: Uint8Array): string { + let latin1 = ""; + for (const byte of value) latin1 += String.fromCharCode(byte); + return btoa(latin1); +} + +function padBase64(value: string): string { + const remainder = value.length % 4; + return remainder === 0 ? value : `${value}${"=".repeat(4 - remainder)}`; +} + +function decodeBase32(value: string, alphabet: string): Uint8Array { + const text = value.replace(/=+$/, "").toUpperCase(); + const bytes = new Uint8Array((text.length * 5) >> 3); + let accumulator = 0; + let bits = 0; + let offset = 0; + + for (const char of text) { + accumulator = (accumulator << 5) | alphabet.indexOf(char); + bits += 5; + if (bits < 8) continue; + bits -= 8; + bytes[offset] = (accumulator >> bits) & 0xff; + offset += 1; + } + return bytes; +} + +function encodeBase32(value: Uint8Array, alphabet: string): string { + let text = ""; + let accumulator = 0; + let bits = 0; + + for (const byte of value) { + accumulator = (accumulator << 8) | byte; + bits += 8; + while (bits >= 5) { + bits -= 5; + text += alphabet[(accumulator >> bits) & 0x1f]; + } + } + if (bits > 0) text += alphabet[(accumulator << (5 - bits)) & 0x1f]; + return text.padEnd(Math.ceil(text.length / 8) * 8, "="); +} + +function decodeBase16(value: string): Uint8Array { + const bytes = new Uint8Array(value.length / 2); + for (let index = 0; index < bytes.length; index++) { + bytes[index] = Number.parseInt(value.slice(index * 2, index * 2 + 2), 16); + } + return bytes; +} + +function encodeBase16(value: Uint8Array): string { + let text = ""; + for (const byte of value) text += byte.toString(16).toUpperCase().padStart(2, "0"); + return text; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/index.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/index.ts new file mode 100644 index 00000000..ada685e2 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/index.ts @@ -0,0 +1,32 @@ +export { + array, + boolean, + literal, + nullable, + optional, + record, + string, + union, + unknown, + float64, + gte, + int, + lte, +} from "zod/v4-mini"; +export { + lazy, + fallback, + callback, + optionalNullable, + defaulted, + dateTime, + rfc1123DateTime, + unixSecondsDateTime, + dateOnly, + bytes, + base64UrlBytes, + base32Bytes, + base32HexBytes, + base16Bytes, +} from "./entries.js"; +export { object, discriminatedUnion, enumOf, of } from "./schema.js"; diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema-error.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema-error.ts new file mode 100644 index 00000000..12a09ab9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema-error.ts @@ -0,0 +1,99 @@ +import * as schema from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import type { Entry, Schema } from "./schema.js"; + +/** + * A value did not match the schema that describes it. + * + * @remarks + * Thrown on both directions by a codec used directly, as in `paymentInputSchema.decode(json)`, so + * it names no call. Through an operation the same failure reaches you one level down, on the + * `cause` of the `DecodeError` or `EncodeError` that does name the call. + * + * `message` names the field and the type expected, never the value, and `cause` carries the issue + * list with its original paths. The value that failed is not kept. + */ +export class SchemaError extends Error { + constructor(message: string, cause?: unknown) { + super(message, cause === undefined ? undefined : { cause }); + this.name = new.target.name; + Object.setPrototypeOf(this, new.target.prototype); + } +} + +export function decodeWith(zodType: ZodMiniType, value: unknown): T { + const result = zodType.safeParse(value); + if (result.success) return result.data; + throw new SchemaError(issuesMessage(result.error, value), result.error); +} + +export function encodeWith(zodType: ZodMiniType, value: unknown): W { + const result = schema.safeEncode(zodType, value as T); + if (result.success) return result.data; + throw new SchemaError( + issuesMessage(result.error, value, "Type could not be encoded for the wire."), + result.error, + ); +} + +export function decodeEntry(schema: Entry, value: unknown): V { + return isSchemaEntry(schema) ? schema.decode(value) : decodeWith(schema, value); +} + +export function encodeEntry(schema: Entry, value: unknown): W { + return isSchemaEntry(schema) ? schema.encode(value) : encodeWith(schema, value); +} + +function isSchemaEntry(entry: Entry): entry is Schema { + return typeof (entry as { decode?: unknown }).decode === "function"; +} + +type SchemaIssue = { + readonly path: readonly PropertyKey[]; + readonly message: string; + readonly expected?: string | undefined; + readonly format?: string | undefined; + readonly options?: readonly unknown[] | undefined; +}; + +type SchemaIssues = { readonly issues: readonly SchemaIssue[] }; + +function issuesMessage( + error: SchemaIssues, + value: unknown, + prefix = "Wire value could not be decoded.", +): string { + const issues = error.issues.map((issue) => { + const path = issue.path.map(String).join(".") || ""; + return `${path}: ${issueReason(issue, valueAt(value, issue.path))}`; + }); + return `${prefix} ${issues.join("; ")}`; +} + +function issueReason(issue: SchemaIssue, actual: unknown): string { + if (issue.expected !== undefined) return `expected ${issue.expected}, received ${typeName(actual)}`; + if (issue.format !== undefined) return `expected ${issue.format} format, received ${typeName(actual)}`; + if (issue.options !== undefined) { + return `expected one of ${issue.options.map(literal).join(" | ")}, received ${typeName(actual)}`; + } + return issue.message; +} + +function valueAt(value: unknown, path: readonly PropertyKey[]): unknown { + let current = value; + for (const key of path) { + if (current === null || typeof current !== "object") return undefined; + current = (current as Record)[key]; + } + return current; +} + +function typeName(value: unknown): string { + if (value === null) return "null"; + if (Array.isArray(value)) return "array"; + return typeof value; +} + +function literal(value: unknown): string { + return JSON.stringify(value) ?? "undefined"; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema.ts b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema.ts new file mode 100644 index 00000000..cb2c33a7 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/core/validation/schema.ts @@ -0,0 +1,168 @@ +import * as zod from "zod/v4-mini"; +import type { ZodMiniType } from "zod/v4-mini"; +import { decodeWith, encodeWith } from "./schema-error.js"; + +/** + * What a value looks like on the wire. + * + * @remarks + * Only `Date` and `Uint8Array` change shape β€” a `Date` becomes its serialized form and a + * `Uint8Array` a string in the RFC 4648 alphabet its field declares β€” recursing through arrays and + * objects. Everything else is unchanged, because the schema layer stops at *values*: how a number + * is written into a query string versus a JSON body is the engine's decision, not the schema's. + */ +export type Encoded = T extends Date + ? string | number + : T extends Uint8Array + ? string + : T extends string | number | boolean | null | undefined + ? T + : T extends readonly (infer E)[] + ? Encoded[] + : T extends object + ? { [K in keyof T]: Encoded } + : T; + +export type KeysMap = { readonly [K in keyof T]?: string }; + +export type Entry = ZodMiniType | Schema; + +export type Shape = { [K in keyof T]-?: Entry } & { readonly _keysMap?: KeysMap }; + +/** + * The codec exported beside every model: `decode` from the wire, `encode` back to it. + * + * @remarks + * Both directions are complete on their own β€” one call each, never a pair. `decode` accepts the + * wire form only and `encode` the SDK form only, so passing one direction's value to the other is + * rejected rather than waved through. A failure throws `SchemaError`; through an operation the + * same failure reaches you one level down, on the `cause` of a `DecodeError` or an + * `EncodeError`, which name the call. + * + * Validation is **structural**: types, shape, and required keys. Value constraints such as + * `minLength`, `pattern` or enum membership are the service's to enforce, and the SDK does not + * duplicate them. + */ +export type Schema> = { + readonly decode: (value: unknown) => T; + readonly encode: (value: unknown) => W; +}; + +/** + * The codec of an open enum, which also exposes the known members at run time. + * + * @remarks + * `values` is the set the spec declared. It is **not** enforced β€” an open enum validates the base + * type only, so an unrecognized server value round-trips instead of throwing. Test membership + * yourself against `values` when you need to. + */ +export type EnumSchema = Schema & { + readonly values: readonly T[]; +}; + +type Built = Schema & { + readonly zod: () => ZodMiniType; + readonly tagged: (discriminator: string, value: string) => ZodMiniType; +}; + +export function object(shape: Shape): Schema> { + let memo: ZodMiniType> | undefined; + const zodType = (): ZodMiniType> => (memo ??= buildObject(shape)); + return { + zod: zodType, + tagged: (discriminator, value) => buildObject(shape, [discriminator, value]), + decode: (value) => decodeWith(zodType(), value), + encode: (value) => encodeWith(zodType(), value), + } as Built>; +} + +export function discriminatedUnion( + discriminator: string, + variants: Readonly>>, +): Schema> { + let memo: ZodMiniType> | undefined; + const build = (): ZodMiniType> => { + const members = Object.entries(variants).map(([value, variant]) => + built(variant).tagged(discriminator, value), + ); + return zod.discriminatedUnion(discriminator, members as never); + }; + const zodType = (): ZodMiniType> => (memo ??= build()); + return { + zod: zodType, + tagged: () => zodType(), + decode: (value) => decodeWith(zodType(), value), + encode: (value) => encodeWith(zodType(), value), + } as Built>; +} + +export function enumOf(members: Readonly>): EnumSchema { + const values = Object.values(members); + const base: ZodMiniType = values.every((value) => typeof value === "number") + ? zod.number() + : zod.string(); + return { + ...of(base as ZodMiniType), + values, + }; +} + +export function of>(zodType: ZodMiniType): Schema { + return { + zod: () => zodType, + tagged: () => zodType, + decode: (value) => decodeWith(zodType, value), + encode: (value) => encodeWith(zodType, value), + } as Built; +} + +function buildObject(shape: Shape, tag?: readonly [string, string]): ZodMiniType> { + const keysMap = (shape as { _keysMap?: Record })._keysMap ?? {}; + const wire: Record> = {}; + const renames: [string, string][] = []; + + for (const [key, entry] of Object.entries(shape)) { + if (key === "_keysMap") continue; + const wireKey = keysMap[key] ?? key; + wire[wireKey] = resolve(entry as Entry); + if (wireKey !== key) renames.push([wireKey, key]); + } + if (tag) wire[tag[0]] = zod.literal(tag[1]); + + const object: ZodMiniType, Record> = zod.looseObject(wire); + const renaming: ZodMiniType = + renames.length === 0 + ? object + : zod.codec(object, zod.custom>(), { + decode: (value) => renameKeys(value, renames, "decode"), + encode: (value) => renameKeys(value, renames, "encode"), + }); + return renaming as ZodMiniType>; +} + +function renameKeys( + value: Record, + renames: readonly [string, string][], + direction: "decode" | "encode", +): Record { + const out = { ...value }; + for (const [wireKey, key] of renames) { + const [from, to] = direction === "decode" ? [wireKey, key] : [key, wireKey]; + if (!(from in out)) continue; + out[to] = out[from]; + delete out[from]; + } + return out; +} + +export function resolve(entry: Entry>): ZodMiniType> { + return isSchema(entry) ? built(entry).zod() : entry; +} + +function built(schema: Schema>): Built> { + return schema as Built>; +} + +function isSchema(entry: Entry): entry is Schema { + return typeof (entry as { decode?: unknown }).decode === "function"; +} diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/index.ts b/plugins/cloudinary-analysis/sdk/typescript/src/index.ts new file mode 100644 index 00000000..3ff2a839 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/index.ts @@ -0,0 +1,224 @@ +export { AnalyzeApiBetaClient } from "./client.js"; +export type { ClientOptions } from "./client-options.js"; + +export { + PkceMethod, + type BasicAuthCredentials, + type OAuth2AuthorizationCodeCredentials, + type AuthorizationCodePrompt, +} from "./core/auth/credentials.js"; +export type { + OAuth2RefreshableTokenStrategy, + OAuthToken, + OAuth2CredentialPlacement, + OAuthTokenRefreshable, +} from "./core/auth/oauth2-strategies.js"; + +export { Analysis } from "./resources/analysis.js"; +export { Tasks } from "./resources/tasks.js"; + +export { + aiVisionGeneralAnalysisDataSchema, + type AiVisionGeneralAnalysisData, +} from "./models/ai-vision-general-analysis-data.js"; +export { + aiVisionModerationAnalysisDataSchema, + type AiVisionModerationAnalysisData, +} from "./models/ai-vision-moderation-analysis-data.js"; +export { + aiVisionTaggingAnalysisDataSchema, + type AiVisionTaggingAnalysisData, +} from "./models/ai-vision-tagging-analysis-data.js"; +export { + analysisWebhookPayloadSchema, + type AnalysisWebhookPayload, +} from "./models/analysis-webhook-payload.js"; +export { + analyzeAiVisionGeneralRequestSchema, + type AnalyzeAiVisionGeneralRequest, +} from "./models/analyze-ai-vision-general-request.js"; +export { + analyzeAiVisionGeneralResponseSchema, + type AnalyzeAiVisionGeneralResponse, +} from "./models/analyze-ai-vision-general-response.js"; +export { + analyzeAiVisionModerationRequestSchema, + type AnalyzeAiVisionModerationRequest, +} from "./models/analyze-ai-vision-moderation-request.js"; +export { + analyzeAiVisionModerationResponseSchema, + type AnalyzeAiVisionModerationResponse, +} from "./models/analyze-ai-vision-moderation-response.js"; +export { + analyzeAiVisionTaggingRequestSchema, + type AnalyzeAiVisionTaggingRequest, +} from "./models/analyze-ai-vision-tagging-request.js"; +export { + analyzeAiVisionTaggingResponseSchema, + type AnalyzeAiVisionTaggingResponse, +} from "./models/analyze-ai-vision-tagging-response.js"; +export { + analyzeCaptioningResponseSchema, + type AnalyzeCaptioningResponse, +} from "./models/analyze-captioning-response.js"; +export { + analyzeCldFashionResponseSchema, + type AnalyzeCldFashionResponse, +} from "./models/analyze-cld-fashion-response.js"; +export { + analyzeCldTextResponseSchema, + type AnalyzeCldTextResponse, +} from "./models/analyze-cld-text-response.js"; +export { analyzeCocoResponseSchema, type AnalyzeCocoResponse } from "./models/analyze-coco-response.js"; +export { + analyzeGoogleLogoDetectionResponseSchema, + type AnalyzeGoogleLogoDetectionResponse, +} from "./models/analyze-google-logo-detection-response.js"; +export { + analyzeGoogleTaggingResponseSchema, + type AnalyzeGoogleTaggingResponse, +} from "./models/analyze-google-tagging-response.js"; +export { + analyzeHumanAnatomyResponseSchema, + type AnalyzeHumanAnatomyResponse, +} from "./models/analyze-human-anatomy-response.js"; +export { + analyzeImageQualityResponseSchema, + type AnalyzeImageQualityResponse, +} from "./models/analyze-image-quality-response.js"; +export { analyzeLvisResponseSchema, type AnalyzeLvisResponse } from "./models/analyze-lvis-response.js"; +export { + analyzeShopClassifierResponseSchema, + type AnalyzeShopClassifierResponse, +} from "./models/analyze-shop-classifier-response.js"; +export { analyzeUnidetResponseSchema, type AnalyzeUnidetResponse } from "./models/analyze-unidet-response.js"; +export { + analyzeWatermarkDetectionResponseSchema, + type AnalyzeWatermarkDetectionResponse, +} from "./models/analyze-watermark-detection-response.js"; +export { + asyncOperationAcceptedResponseSchema, + type AsyncOperationAcceptedResponse, +} from "./models/async-operation-accepted-response.js"; +export { baseAnalyzeRequestSchema, type BaseAnalyzeRequest } from "./models/base-analyze-request.js"; +export { baseAnalyzeResponseSchema, type BaseAnalyzeResponse } from "./models/base-analyze-response.js"; +export { baseWebhookPayloadSchema, type BaseWebhookPayload } from "./models/base-webhook-payload.js"; +export { boundingPolySchema, type BoundingPoly } from "./models/bounding-poly.js"; +export { + captioningAnalysisDataSchema, + type CaptioningAnalysisData, +} from "./models/captioning-analysis-data.js"; +export { + cldFashionAnalysisDataSchema, + type CldFashionAnalysisData, +} from "./models/cld-fashion-analysis-data.js"; +export { cldTextAnalysisDataSchema, type CldTextAnalysisData } from "./models/cld-text-analysis-data.js"; +export { cocoAnalysisDataSchema, type CocoAnalysisData } from "./models/coco-analysis-data.js"; +export { dataSchema, type Data } from "./models/data.js"; +export { data1Schema, type Data1 } from "./models/data1.js"; +export { data10Schema, type Data10 } from "./models/data10.js"; +export { data11Schema, type Data11 } from "./models/data11.js"; +export { data12Schema, type Data12 } from "./models/data12.js"; +export { data13Schema, type Data13 } from "./models/data13.js"; +export { data14Schema, type Data14 } from "./models/data14.js"; +export { data15Schema, type Data15 } from "./models/data15.js"; +export { data16Schema, type Data16 } from "./models/data16.js"; +export { data17Schema, type Data17 } from "./models/data17.js"; +export { data2Schema, type Data2 } from "./models/data2.js"; +export { data3Schema, type Data3 } from "./models/data3.js"; +export { data4Schema, type Data4 } from "./models/data4.js"; +export { data5Schema, type Data5 } from "./models/data5.js"; +export { data6Schema, type Data6 } from "./models/data6.js"; +export { data7Schema, type Data7 } from "./models/data7.js"; +export { data8Schema, type Data8 } from "./models/data8.js"; +export { data9Schema, type Data9 } from "./models/data9.js"; +export { detectionSchema, type Detection } from "./models/detection.js"; +export { ErrorCategory, errorCategorySchema } from "./models/error-category.js"; +export { errorObjectSchema, type ErrorObject } from "./models/error-object.js"; +export { errorResponseSchema, type ErrorResponse } from "./models/error-response.js"; +export { errorResponseErrorSchema, type ErrorResponseError } from "./models/error-response-error.js"; +export { Feature, featureSchema } from "./models/feature.js"; +export { + getTaskStatusResponseSchema, + type GetTaskStatusResponse, +} from "./models/get-task-status-response.js"; +export { + googleLogoDetectionAnalysisDataSchema, + type GoogleLogoDetectionAnalysisData, +} from "./models/google-logo-detection-analysis-data.js"; +export { + googleTaggingAnalysisDataSchema, + type GoogleTaggingAnalysisData, +} from "./models/google-tagging-analysis-data.js"; +export { + humanAnatomyAnalysisDataSchema, + type HumanAnatomyAnalysisData, +} from "./models/human-anatomy-analysis-data.js"; +export { + imageQualityAnalysisDataSchema, + type ImageQualityAnalysisData, +} from "./models/image-quality-analysis-data.js"; +export { labelSchema, type Label } from "./models/label.js"; +export { labelAnnotationsSchema, type LabelAnnotations } from "./models/label-annotations.js"; +export { limitsResponseSchema, type LimitsResponse } from "./models/limits-response.js"; +export { logoAnnotationSchema, type LogoAnnotation } from "./models/logo-annotation.js"; +export { lvisAnalysisDataSchema, type LvisAnalysisData } from "./models/lvis-analysis-data.js"; +export { OperationType, operationTypeSchema } from "./models/operation-type.js"; +export { rateLimitedResponseSchema, type RateLimitedResponse } from "./models/rate-limited-response.js"; +export { rateLimitedResponse1Schema, type RateLimitedResponse1 } from "./models/rate-limited-response1.js"; +export { responseSchema, type Response } from "./models/response.js"; +export { response1Schema, type Response1 } from "./models/response1.js"; +export { + shopClassifierAnalysisDataSchema, + type ShopClassifierAnalysisData, +} from "./models/shop-classifier-analysis-data.js"; +export { sourceSchema, type Source } from "./models/source.js"; +export { source1Schema, type Source1 } from "./models/source1.js"; +export { source2Schema, type Source2 } from "./models/unions/source2.js"; +export { tagSchema, type Tag } from "./models/tag.js"; +export { tagDefinitionSchema, type TagDefinition } from "./models/tag-definition.js"; +export { TaskStatus, taskStatusSchema } from "./models/task-status.js"; +export { unidetAnalysisDataSchema, type UnidetAnalysisData } from "./models/unidet-analysis-data.js"; +export { Value, valueSchema } from "./models/value.js"; +export { vertexSchema, type Vertex } from "./models/vertex.js"; +export { + watermarkDetectionAnalysisDataSchema, + type WatermarkDetectionAnalysisData, +} from "./models/watermark-detection-analysis-data.js"; +export { limitSchema, type Limit } from "./models/limit.js"; +export { limitsObjectSchema, type LimitsObject } from "./models/limits-object.js"; + +export { + CoreError as AnalyzeApiBetaError, + ResponseError, + DecodeError, + EncodeError, + ConnectionError, + TimeoutError, + AuthError, + ConfigurationError, +} from "./core/errors.js"; +export { ApiError } from "./core/api-error.js"; +export { SchemaError } from "./core/validation/schema-error.js"; +export { + defaultLoggingOptions, + type LoggingOptions, + type RequestLoggingOptions, + type Logger, + type LogDetails, + type HttpRequestDetails, + type RequestHeadersDetails, + type RequestBodyDetails, + type HttpResponseDetails, + type ResponseHeadersDetails, + type HttpRetryingDetails, + type HttpFailedDetails, + type LogLevel, +} from "./core/logging.js"; +export type { ApiPromise, ApiResult } from "./core/api-promise.js"; +export type { HttpMethod, RequestOptions } from "./core/api-request.js"; +export type { RetryOptions, RequestRetryOptions, RetryAttempt, RetryReason } from "./core/retry.js"; +export type { BinaryContent, BinaryData, BinaryErrorContent, FileData, FileInput } from "./core/binary.js"; +export type { ErrorKind } from "./core/errors.js"; +export type { Declared, Undeclared } from "./core/api-error.js"; +export type { Schema, EnumSchema, Encoded } from "./core/validation/schema.js"; diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-general-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-general-analysis-data.ts new file mode 100644 index 00000000..ceec5344 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-general-analysis-data.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { responseSchema, type Response } from "./response.js"; + +export type AiVisionGeneralAnalysisData = { + responses: Response[]; + modelVersion?: number; +}; + +export const aiVisionGeneralAnalysisDataSchema: Schema = + s.object({ + responses: s.array(s.lazy(() => responseSchema)), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-moderation-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-moderation-analysis-data.ts new file mode 100644 index 00000000..53501e36 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-moderation-analysis-data.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { response1Schema, type Response1 } from "./response1.js"; + +export type AiVisionModerationAnalysisData = { + responses: Response1[]; + modelVersion?: number; +}; + +export const aiVisionModerationAnalysisDataSchema: Schema = + s.object({ + responses: s.array(s.lazy(() => response1Schema)), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-tagging-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-tagging-analysis-data.ts new file mode 100644 index 00000000..ddf4e26e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/ai-vision-tagging-analysis-data.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { tagSchema, type Tag } from "./tag.js"; + +export type AiVisionTaggingAnalysisData = { + tags: Tag[]; + modelVersion?: number; +}; + +export const aiVisionTaggingAnalysisDataSchema: Schema = + s.object({ + tags: s.array(s.lazy(() => tagSchema)), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analysis-webhook-payload.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analysis-webhook-payload.ts new file mode 100644 index 00000000..ee8e245c --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analysis-webhook-payload.ts @@ -0,0 +1,38 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { errorObjectSchema, type ErrorObject } from "./error-object.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; +import { operationTypeSchema, type OperationType } from "./operation-type.js"; + +/** Payload for webhook notifications for analysis operations */ +export type AnalysisWebhookPayload = { + error?: ErrorObject | null; + limits?: LimitsObject | null; + operationName?: string; + operationType?: OperationType; + notificationType?: string; + requestId?: string; + taskId?: string; + httpStatus?: number; + data?: Record; +}; + +export const analysisWebhookPayloadSchema: Schema = s.object({ + error: s.optionalNullable(s.lazy(() => errorObjectSchema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + operationName: s.optional(s.string()), + operationType: s.optional(s.lazy(() => operationTypeSchema)), + notificationType: s.optional(s.string()), + requestId: s.optional(s.string()), + taskId: s.optional(s.string()), + httpStatus: s.optional(s.int()), + data: s.optional(s.record(s.string(), s.unknown())), + _keysMap: { + operationName: "operation_name", + operationType: "operation_type", + notificationType: "notification_type", + requestId: "request_id", + taskId: "task_id", + httpStatus: "http_status", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-request.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-request.ts new file mode 100644 index 00000000..27b8f3b5 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-request.ts @@ -0,0 +1,32 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { source2Schema, type Source2 } from "./unions/source2.js"; + +export type AnalyzeAiVisionGeneralRequest = { + source: Source2; + /** + * Whether to perform the analysis asynchronously. If true, the response is returned immediately + * and analysis is done in the background. Once complete, a webhook notification will be sent to + * the specified URL and/or to the URLs defined in the Webhook Notifications settings of your + * Cloudinary Console. + */ + async?: boolean; + /** + * The webhook URL to notify when the analysis is complete. Only relevant when async is set to + * true. + */ + notificationUrl?: string; + /** One or more prompts of open questions or tasks with an open answer */ + prompts?: string[]; +}; + +export const analyzeAiVisionGeneralRequestSchema: Schema = + s.object({ + source: source2Schema, + async: s.optional(s.boolean()), + notificationUrl: s.optional(s.string()), + prompts: s.optional(s.array(s.string())), + _keysMap: { + notificationUrl: "notification_url", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-response.ts new file mode 100644 index 00000000..9c8161ed --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-general-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { dataSchema, type Data } from "./data.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeAiVisionGeneralResponse = { + data?: Data; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeAiVisionGeneralResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => dataSchema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-request.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-request.ts new file mode 100644 index 00000000..fee78880 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-request.ts @@ -0,0 +1,33 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { source2Schema, type Source2 } from "./unions/source2.js"; + +export type AnalyzeAiVisionModerationRequest = { + source: Source2; + /** + * Whether to perform the analysis asynchronously. If true, the response is returned immediately + * and analysis is done in the background. Once complete, a webhook notification will be sent to + * the specified URL and/or to the URLs defined in the Webhook Notifications settings of your + * Cloudinary Console. + */ + async?: boolean; + /** + * The webhook URL to notify when the analysis is complete. Only relevant when async is set to + * true. + */ + notificationUrl?: string; + /** A list of yes/no questions */ + rejectionQuestions?: string[]; +}; + +export const analyzeAiVisionModerationRequestSchema: Schema = + s.object({ + source: source2Schema, + async: s.optional(s.boolean()), + notificationUrl: s.optional(s.string()), + rejectionQuestions: s.optional(s.array(s.string())), + _keysMap: { + notificationUrl: "notification_url", + rejectionQuestions: "rejection_questions", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-response.ts new file mode 100644 index 00000000..b1f7b8e7 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-moderation-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data1Schema, type Data1 } from "./data1.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeAiVisionModerationResponse = { + data?: Data1; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeAiVisionModerationResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data1Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-request.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-request.ts new file mode 100644 index 00000000..2efd8da6 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-request.ts @@ -0,0 +1,33 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { tagDefinitionSchema, type TagDefinition } from "./tag-definition.js"; +import { source2Schema, type Source2 } from "./unions/source2.js"; + +export type AnalyzeAiVisionTaggingRequest = { + source: Source2; + /** + * Whether to perform the analysis asynchronously. If true, the response is returned immediately + * and analysis is done in the background. Once complete, a webhook notification will be sent to + * the specified URL and/or to the URLs defined in the Webhook Notifications settings of your + * Cloudinary Console. + */ + async?: boolean; + /** + * The webhook URL to notify when the analysis is complete. Only relevant when async is set to + * true. + */ + notificationUrl?: string; + tagDefinitions: TagDefinition[]; +}; + +export const analyzeAiVisionTaggingRequestSchema: Schema = + s.object({ + source: source2Schema, + async: s.optional(s.boolean()), + notificationUrl: s.optional(s.string()), + tagDefinitions: s.array(s.lazy(() => tagDefinitionSchema)), + _keysMap: { + notificationUrl: "notification_url", + tagDefinitions: "tag_definitions", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-response.ts new file mode 100644 index 00000000..45cb1e66 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-ai-vision-tagging-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data2Schema, type Data2 } from "./data2.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeAiVisionTaggingResponse = { + data?: Data2; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeAiVisionTaggingResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data2Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-captioning-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-captioning-response.ts new file mode 100644 index 00000000..7c4daaaf --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-captioning-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data3Schema, type Data3 } from "./data3.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeCaptioningResponse = { + data?: Data3; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeCaptioningResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data3Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-fashion-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-fashion-response.ts new file mode 100644 index 00000000..95894b7f --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-fashion-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data5Schema, type Data5 } from "./data5.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeCldFashionResponse = { + data?: Data5; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeCldFashionResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data5Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-text-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-text-response.ts new file mode 100644 index 00000000..746d0cf9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-cld-text-response.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data6Schema, type Data6 } from "./data6.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeCldTextResponse = { + data?: Data6; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeCldTextResponseSchema: Schema = s.object({ + data: s.optional(s.lazy(() => data6Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-coco-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-coco-response.ts new file mode 100644 index 00000000..b918ff46 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-coco-response.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data7Schema, type Data7 } from "./data7.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeCocoResponse = { + data?: Data7; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeCocoResponseSchema: Schema = s.object({ + data: s.optional(s.lazy(() => data7Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-logo-detection-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-logo-detection-response.ts new file mode 100644 index 00000000..0097928b --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-logo-detection-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data8Schema, type Data8 } from "./data8.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeGoogleLogoDetectionResponse = { + data?: Data8; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeGoogleLogoDetectionResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data8Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-tagging-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-tagging-response.ts new file mode 100644 index 00000000..98050745 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-google-tagging-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data9Schema, type Data9 } from "./data9.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeGoogleTaggingResponse = { + data?: Data9; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeGoogleTaggingResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data9Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-human-anatomy-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-human-anatomy-response.ts new file mode 100644 index 00000000..7f1bee85 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-human-anatomy-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data10Schema, type Data10 } from "./data10.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeHumanAnatomyResponse = { + data?: Data10; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeHumanAnatomyResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data10Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-image-quality-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-image-quality-response.ts new file mode 100644 index 00000000..be458f97 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-image-quality-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data11Schema, type Data11 } from "./data11.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeImageQualityResponse = { + data?: Data11; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeImageQualityResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data11Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-lvis-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-lvis-response.ts new file mode 100644 index 00000000..054fc5de --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-lvis-response.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data12Schema, type Data12 } from "./data12.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeLvisResponse = { + data?: Data12; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeLvisResponseSchema: Schema = s.object({ + data: s.optional(s.lazy(() => data12Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-shop-classifier-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-shop-classifier-response.ts new file mode 100644 index 00000000..15cfb51f --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-shop-classifier-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data13Schema, type Data13 } from "./data13.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeShopClassifierResponse = { + data?: Data13; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeShopClassifierResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data13Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-unidet-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-unidet-response.ts new file mode 100644 index 00000000..a83e7400 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-unidet-response.ts @@ -0,0 +1,19 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data14Schema, type Data14 } from "./data14.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeUnidetResponse = { + data?: Data14; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeUnidetResponseSchema: Schema = s.object({ + data: s.optional(s.lazy(() => data14Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-watermark-detection-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-watermark-detection-response.ts new file mode 100644 index 00000000..9c0068e9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/analyze-watermark-detection-response.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data15Schema, type Data15 } from "./data15.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type AnalyzeWatermarkDetectionResponse = { + data?: Data15; + limits?: LimitsObject | null; + requestId?: string; +}; + +export const analyzeWatermarkDetectionResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data15Schema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/async-operation-accepted-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/async-operation-accepted-response.ts new file mode 100644 index 00000000..90f01f39 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/async-operation-accepted-response.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data16Schema, type Data16 } from "./data16.js"; + +export type AsyncOperationAcceptedResponse = { + data?: Data16; + requestId?: string; +}; + +export const asyncOperationAcceptedResponseSchema: Schema = + s.object({ + data: s.optional(s.lazy(() => data16Schema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-request.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-request.ts new file mode 100644 index 00000000..ff86e83b --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-request.ts @@ -0,0 +1,28 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { source2Schema, type Source2 } from "./unions/source2.js"; + +export type BaseAnalyzeRequest = { + source: Source2; + /** + * Whether to perform the analysis asynchronously. If true, the response is returned immediately + * and analysis is done in the background. Once complete, a webhook notification will be sent to + * the specified URL and/or to the URLs defined in the Webhook Notifications settings of your + * Cloudinary Console. + */ + async?: boolean; + /** + * The webhook URL to notify when the analysis is complete. Only relevant when async is set to + * true. + */ + notificationUrl?: string; +}; + +export const baseAnalyzeRequestSchema: Schema = s.object({ + source: source2Schema, + async: s.optional(s.boolean()), + notificationUrl: s.optional(s.string()), + _keysMap: { + notificationUrl: "notification_url", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-response.ts new file mode 100644 index 00000000..f2c5542f --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-analyze-response.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; + +export type BaseAnalyzeResponse = { + limits?: LimitsObject | null; + requestId?: string; +}; + +export const baseAnalyzeResponseSchema: Schema = s.object({ + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/base-webhook-payload.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-webhook-payload.ts new file mode 100644 index 00000000..f2854273 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/base-webhook-payload.ts @@ -0,0 +1,36 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { errorObjectSchema, type ErrorObject } from "./error-object.js"; +import { limitsObjectSchema, type LimitsObject } from "./limits-object.js"; +import { operationTypeSchema, type OperationType } from "./operation-type.js"; + +/** Base payload for webhook notifications */ +export type BaseWebhookPayload = { + error?: ErrorObject | null; + limits?: LimitsObject | null; + operationName?: string; + operationType?: OperationType; + notificationType?: string; + requestId?: string; + taskId?: string; + httpStatus?: number; +}; + +export const baseWebhookPayloadSchema: Schema = s.object({ + error: s.optionalNullable(s.lazy(() => errorObjectSchema)), + limits: s.optionalNullable(s.lazy(() => limitsObjectSchema)), + operationName: s.optional(s.string()), + operationType: s.optional(s.lazy(() => operationTypeSchema)), + notificationType: s.optional(s.string()), + requestId: s.optional(s.string()), + taskId: s.optional(s.string()), + httpStatus: s.optional(s.int()), + _keysMap: { + operationName: "operation_name", + operationType: "operation_type", + notificationType: "notification_type", + requestId: "request_id", + taskId: "task_id", + httpStatus: "http_status", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/bounding-poly.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/bounding-poly.ts new file mode 100644 index 00000000..5a64b35c --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/bounding-poly.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { vertexSchema, type Vertex } from "./vertex.js"; + +export type BoundingPoly = { + vertices?: Vertex[]; +}; + +export const boundingPolySchema: Schema = s.object({ + vertices: s.optional(s.array(s.lazy(() => vertexSchema))), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/captioning-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/captioning-analysis-data.ts new file mode 100644 index 00000000..abeff656 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/captioning-analysis-data.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data4Schema, type Data4 } from "./data4.js"; + +export type CaptioningAnalysisData = { + data: Data4; + modelVersion?: number; +}; + +export const captioningAnalysisDataSchema: Schema = s.object({ + data: data4Schema, + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-fashion-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-fashion-analysis-data.ts new file mode 100644 index 00000000..e307ca6d --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-fashion-analysis-data.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CldFashionAnalysisData = { + tags: Record; + modelVersion?: number; +}; + +export const cldFashionAnalysisDataSchema: Schema = s.object({ + tags: s.record(s.string(), s.unknown()), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-text-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-text-analysis-data.ts new file mode 100644 index 00000000..73587eb9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/cld-text-analysis-data.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CldTextAnalysisData = { + tags: Record; + modelVersion?: number; +}; + +export const cldTextAnalysisDataSchema: Schema = s.object({ + tags: s.record(s.string(), s.unknown()), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/coco-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/coco-analysis-data.ts new file mode 100644 index 00000000..1c53da4e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/coco-analysis-data.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type CocoAnalysisData = { + tags: Record; + modelVersion?: number; +}; + +export const cocoAnalysisDataSchema: Schema = s.object({ + tags: s.record(s.string(), s.unknown()), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data.ts new file mode 100644 index 00000000..0388ff32 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + aiVisionGeneralAnalysisDataSchema, + type AiVisionGeneralAnalysisData, +} from "./ai-vision-general-analysis-data.js"; + +export type Data = { + entity?: string; + analysis?: AiVisionGeneralAnalysisData; +}; + +export const dataSchema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => aiVisionGeneralAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data1.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data1.ts new file mode 100644 index 00000000..73695b14 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data1.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + aiVisionModerationAnalysisDataSchema, + type AiVisionModerationAnalysisData, +} from "./ai-vision-moderation-analysis-data.js"; + +export type Data1 = { + entity?: string; + analysis?: AiVisionModerationAnalysisData; +}; + +export const data1Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => aiVisionModerationAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data10.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data10.ts new file mode 100644 index 00000000..7a49b0cb --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data10.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + humanAnatomyAnalysisDataSchema, + type HumanAnatomyAnalysisData, +} from "./human-anatomy-analysis-data.js"; + +export type Data10 = { + entity?: string; + analysis?: HumanAnatomyAnalysisData; +}; + +export const data10Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => humanAnatomyAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data11.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data11.ts new file mode 100644 index 00000000..914e65e9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data11.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + imageQualityAnalysisDataSchema, + type ImageQualityAnalysisData, +} from "./image-quality-analysis-data.js"; + +export type Data11 = { + entity?: string; + analysis?: ImageQualityAnalysisData; +}; + +export const data11Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => imageQualityAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data12.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data12.ts new file mode 100644 index 00000000..63ecea93 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data12.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { lvisAnalysisDataSchema, type LvisAnalysisData } from "./lvis-analysis-data.js"; + +export type Data12 = { + entity?: string; + analysis?: LvisAnalysisData; +}; + +export const data12Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => lvisAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data13.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data13.ts new file mode 100644 index 00000000..41af67ca --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data13.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + shopClassifierAnalysisDataSchema, + type ShopClassifierAnalysisData, +} from "./shop-classifier-analysis-data.js"; + +export type Data13 = { + entity?: string; + analysis?: ShopClassifierAnalysisData; +}; + +export const data13Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => shopClassifierAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data14.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data14.ts new file mode 100644 index 00000000..6e0d9c9c --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data14.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { unidetAnalysisDataSchema, type UnidetAnalysisData } from "./unidet-analysis-data.js"; + +export type Data14 = { + entity?: string; + analysis?: UnidetAnalysisData; +}; + +export const data14Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => unidetAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data15.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data15.ts new file mode 100644 index 00000000..d7841068 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data15.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + watermarkDetectionAnalysisDataSchema, + type WatermarkDetectionAnalysisData, +} from "./watermark-detection-analysis-data.js"; + +export type Data15 = { + entity?: string; + analysis?: WatermarkDetectionAnalysisData; +}; + +export const data15Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => watermarkDetectionAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data16.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data16.ts new file mode 100644 index 00000000..38882767 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data16.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { taskStatusSchema, type TaskStatus } from "./task-status.js"; + +export type Data16 = { + taskId: string; + /** The status of an async task */ + status: TaskStatus; +}; + +export const data16Schema: Schema = s.object({ + taskId: s.string(), + status: taskStatusSchema, + _keysMap: { + taskId: "task_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data17.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data17.ts new file mode 100644 index 00000000..96e4ee82 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data17.ts @@ -0,0 +1,17 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { taskStatusSchema, type TaskStatus } from "./task-status.js"; + +export type Data17 = { + taskId?: string; + /** The status of an async task */ + status?: TaskStatus; +}; + +export const data17Schema: Schema = s.object({ + taskId: s.optional(s.string()), + status: s.optional(s.lazy(() => taskStatusSchema)), + _keysMap: { + taskId: "task_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data2.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data2.ts new file mode 100644 index 00000000..172703a9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data2.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + aiVisionTaggingAnalysisDataSchema, + type AiVisionTaggingAnalysisData, +} from "./ai-vision-tagging-analysis-data.js"; + +export type Data2 = { + entity?: string; + analysis?: AiVisionTaggingAnalysisData; +}; + +export const data2Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => aiVisionTaggingAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data3.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data3.ts new file mode 100644 index 00000000..7a69dd44 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data3.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { captioningAnalysisDataSchema, type CaptioningAnalysisData } from "./captioning-analysis-data.js"; + +export type Data3 = { + entity?: string; + analysis?: CaptioningAnalysisData; +}; + +export const data3Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => captioningAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data4.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data4.ts new file mode 100644 index 00000000..0c053ee9 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data4.ts @@ -0,0 +1,10 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Data4 = { + caption: string; +}; + +export const data4Schema: Schema = s.object({ + caption: s.string(), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data5.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data5.ts new file mode 100644 index 00000000..2b395c16 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data5.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cldFashionAnalysisDataSchema, type CldFashionAnalysisData } from "./cld-fashion-analysis-data.js"; + +export type Data5 = { + entity?: string; + analysis?: CldFashionAnalysisData; +}; + +export const data5Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => cldFashionAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data6.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data6.ts new file mode 100644 index 00000000..af20ae80 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data6.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cldTextAnalysisDataSchema, type CldTextAnalysisData } from "./cld-text-analysis-data.js"; + +export type Data6 = { + entity?: string; + analysis?: CldTextAnalysisData; +}; + +export const data6Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => cldTextAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data7.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data7.ts new file mode 100644 index 00000000..29eda88e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data7.ts @@ -0,0 +1,13 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { cocoAnalysisDataSchema, type CocoAnalysisData } from "./coco-analysis-data.js"; + +export type Data7 = { + entity?: string; + analysis?: CocoAnalysisData; +}; + +export const data7Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => cocoAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data8.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data8.ts new file mode 100644 index 00000000..44ffef31 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data8.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + googleLogoDetectionAnalysisDataSchema, + type GoogleLogoDetectionAnalysisData, +} from "./google-logo-detection-analysis-data.js"; + +export type Data8 = { + entity?: string; + analysis?: GoogleLogoDetectionAnalysisData; +}; + +export const data8Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => googleLogoDetectionAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/data9.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/data9.ts new file mode 100644 index 00000000..cb0141c6 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/data9.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { + googleTaggingAnalysisDataSchema, + type GoogleTaggingAnalysisData, +} from "./google-tagging-analysis-data.js"; + +export type Data9 = { + entity?: string; + analysis?: GoogleTaggingAnalysisData; +}; + +export const data9Schema: Schema = s.object({ + entity: s.optional(s.string()), + analysis: s.optional(s.lazy(() => googleTaggingAnalysisDataSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/detection.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/detection.ts new file mode 100644 index 00000000..b7294eb0 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/detection.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Detection = { + name: string; + confidence: number; +}; + +export const detectionSchema: Schema = s.object({ + name: s.string(), + confidence: s.float64(), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/error-category.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-category.ts new file mode 100644 index 00000000..6954ecf2 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-category.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const ErrorCategory = { + UserError: "user_error", + AuthError: "auth_error", + ServerError: "server_error", + RateLimitError: "rate_limit_error", +} as const; +export type ErrorCategory = (typeof ErrorCategory)[keyof typeof ErrorCategory] | (string & {}); + +export const errorCategorySchema: EnumSchema = s.enumOf(ErrorCategory); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/error-object.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-object.ts new file mode 100644 index 00000000..5c51cc9c --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-object.ts @@ -0,0 +1,22 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { errorCategorySchema, type ErrorCategory } from "./error-category.js"; + +export type ErrorObject = { + category?: ErrorCategory; + code?: string; + message?: string; + details?: Record; + requestId?: string; +}; + +export const errorObjectSchema: Schema = s.object({ + category: s.optional(s.lazy(() => errorCategorySchema)), + code: s.optional(s.string()), + message: s.optional(s.string()), + details: s.optional(s.record(s.string(), s.unknown())), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response-error.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response-error.ts new file mode 100644 index 00000000..a81e549e --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response-error.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { errorObjectSchema, type ErrorObject } from "./error-object.js"; + +export type ErrorResponseError = { + error?: ErrorObject | null; +}; + +export const errorResponseErrorSchema: Schema = s.object({ + error: s.optionalNullable(s.lazy(() => errorObjectSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response.ts new file mode 100644 index 00000000..48a564f5 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/error-response.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { errorObjectSchema, type ErrorObject } from "./error-object.js"; + +export type ErrorResponse = { + error?: ErrorObject | null; +}; + +export const errorResponseSchema: Schema = s.object({ + error: s.optionalNullable(s.lazy(() => errorObjectSchema)), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/feature.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/feature.ts new file mode 100644 index 00000000..5fa749cb --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/feature.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { EnumSchema } from "../core/validation/schema.js"; + +export const Feature = { + GoogleTagging: "google_tagging", + ObjectDetection: "object_detection", + AiVision: "ai_vision", +} as const; +export type Feature = (typeof Feature)[keyof typeof Feature] | (string & {}); + +export const featureSchema: EnumSchema = s.enumOf(Feature); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/get-task-status-response.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/get-task-status-response.ts new file mode 100644 index 00000000..8bc1b2ad --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/get-task-status-response.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { data17Schema, type Data17 } from "./data17.js"; + +export type GetTaskStatusResponse = { + data?: Data17; + requestId?: string; +}; + +export const getTaskStatusResponseSchema: Schema = s.object({ + data: s.optional(s.lazy(() => data17Schema)), + requestId: s.optional(s.string()), + _keysMap: { + requestId: "request_id", + }, +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/google-logo-detection-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/google-logo-detection-analysis-data.ts new file mode 100644 index 00000000..ae956561 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/google-logo-detection-analysis-data.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { logoAnnotationSchema, type LogoAnnotation } from "./logo-annotation.js"; + +export type GoogleLogoDetectionAnalysisData = { + logoAnnotations: LogoAnnotation[]; +}; + +export const googleLogoDetectionAnalysisDataSchema: Schema = + s.object({ + logoAnnotations: s.array(s.lazy(() => logoAnnotationSchema)), + _keysMap: { + logoAnnotations: "logo_annotations", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/google-tagging-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/google-tagging-analysis-data.ts new file mode 100644 index 00000000..a142f89d --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/google-tagging-analysis-data.ts @@ -0,0 +1,15 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { labelAnnotationsSchema, type LabelAnnotations } from "./label-annotations.js"; + +export type GoogleTaggingAnalysisData = { + labelAnnotations: LabelAnnotations; +}; + +export const googleTaggingAnalysisDataSchema: Schema = + s.object({ + labelAnnotations: labelAnnotationsSchema, + _keysMap: { + labelAnnotations: "label_annotations", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/human-anatomy-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/human-anatomy-analysis-data.ts new file mode 100644 index 00000000..a6f93f68 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/human-anatomy-analysis-data.ts @@ -0,0 +1,16 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type HumanAnatomyAnalysisData = { + tags: Record; + modelVersion?: number; +}; + +export const humanAnatomyAnalysisDataSchema: Schema = + s.object({ + tags: s.record(s.string(), s.unknown()), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/image-quality-analysis-data.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/image-quality-analysis-data.ts new file mode 100644 index 00000000..e6359b1b --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/image-quality-analysis-data.ts @@ -0,0 +1,20 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type ImageQualityAnalysisData = { + quality?: string; + score?: number; + confidence?: number; + modelVersion?: number; +}; + +export const imageQualityAnalysisDataSchema: Schema = + s.object({ + quality: s.optional(s.string()), + score: s.optional(s.float64()), + confidence: s.optional(s.float64()), + modelVersion: s.optional(s.int()), + _keysMap: { + modelVersion: "model_version", + }, + }); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/label-annotations.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/label-annotations.ts new file mode 100644 index 00000000..314cafa3 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/label-annotations.ts @@ -0,0 +1,11 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; +import { labelSchema, type Label } from "./label.js"; + +export type LabelAnnotations = { + labels?: Label[]; +}; + +export const labelAnnotationsSchema: Schema = s.object({ + labels: s.optional(s.array(s.lazy(() => labelSchema))), +}); diff --git a/plugins/cloudinary-analysis/sdk/typescript/src/models/label.ts b/plugins/cloudinary-analysis/sdk/typescript/src/models/label.ts new file mode 100644 index 00000000..4af273d4 --- /dev/null +++ b/plugins/cloudinary-analysis/sdk/typescript/src/models/label.ts @@ -0,0 +1,12 @@ +import * as s from "../core/validation/index.js"; +import type { Schema } from "../core/validation/schema.js"; + +export type Label = { + label: string; + score: number; +}; + +export const labelSchema: Schema