Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/26710-signed-connector-auth-alipay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@objectstack/spec': minor
'@objectstack/service-automation': minor
---

feat(spec): `alipay-v3` joins the `signed` connector-instance auth schemes, with response verification (ADR-0143 D2.3, #26710)

Clause-②: yes (widening)
Metadata-protocol: added

<!-- adr-0087: not-required (no-migration-prescription) One member is added to the closed signing-scheme list and one shape to the `signed` union. The signer registry's third parameter changes from the key string to an object carrying the key and an optional vendor public key; that function is internal to the automation service and in no release yet (it ships with the pending `26710-signed-connector-auth-aliyun` changeset). Nothing an author wrote before is refused or re-shaped, so there is nothing for a migration to rewrite. -->

A declarative connector instance can now declare Alipay's OpenAPI v3 signature as its signing scheme. Its responses are verified too. It joins the Alibaba Cloud and Tencent Cloud schemes.

- **`@objectstack/spec`**
- `ConnectorSigningSchemeSchema` gains `alipay-v3`.
- The `signed` union gains its closed shape: `{ type: 'signed', scheme: 'alipay-v3', keyId, credentialRef, vendorPublicKeyRef }`.
- `keyId` is the app id.
- `credentialRef` names the application private key.
- `vendorPublicKeyRef` names the Alipay public key that every response is verified with. It is required.
- The same publish refusals apply as for the other schemes: inline key spellings by name, an authored `signature`, and `signed` on `provider: 'mcp'`. Also refused:
- `appPrivateKey`, by name;
- `alipayPublicKey`, which is answered with `vendorPublicKeyRef`.

`appId` is answered with `keyId`. An unknown scheme's refusal now lists all three schemes.
- **`@objectstack/service-automation`**
- Every request is signed `ALIPAY-SHA256withRSA` with a fresh nonce and timestamp.
- Every response below status 400 must carry a valid Alipay signature, an empty body included. A signed error response is verified too. A response that fails either check fails the dispatch, and its body is never returned.
- The vendor public key resolves through the same chain as `credentialRef`. An instance whose public key is missing is degraded with that reference named. The credential door lists both references against the instance.
- A key that is not an RSA key fails boot, naming which reference, never the value.
- The keys never leave the host.
46 changes: 37 additions & 9 deletions content/docs/automation/connectors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,9 @@ Some upstreams take no token at all: every request carries a signature over its
own method, path, query, headers and body, computed with a key the caller never
sends. The `signed` kind declares that (ADR-0143 D2.3). The metadata names the
**scheme** and the public **key id**; the signing key itself is a
`credentialRef`, resolved through the same chain as every other kind.
`credentialRef`, resolved through the same chain as every other kind. A scheme
whose vendor signs its responses also names the vendor's public key, as a
second reference.

```ts
// Alibaba Cloud OpenAPI, V3 signature (ACS3-HMAC-SHA256):
Expand All @@ -635,8 +637,22 @@ auth: {
keyId: 'YourSecretId', // the SecretId: public, stays in metadata
credentialRef: 'TENCENT_SECRET_KEY',
}

// Alipay OpenAPI v3 (ALIPAY-SHA256withRSA), responses verified:
auth: {
type: 'signed',
scheme: 'alipay-v3',
keyId: '2021000000000000', // the app id: public, stays in metadata
credentialRef: 'ALIPAY_APP_PRIVATE_KEY', // the application private key (RSA)
vendorPublicKeyRef: 'ALIPAY_PUBLIC_KEY', // the Alipay public key responses are verified with
}
```

Both of an `alipay-v3` instance's references are listed against it by the
credential door, and setting or replacing either one re-materializes it. The
keys are accepted as Alipay's console issues them (the bare base64 body) or as
PEM.

What the platform does with it:

- **Every request is signed, each attempt fresh.** A retry is signed again with
Expand All @@ -648,7 +664,8 @@ What the platform does with it:
(`external.connector` is refused with `422`): a driver cannot sign per
request.
- **Schemes that sign their responses are verified** before a result is
returned; a response that does not verify fails the dispatch.
returned; a response that does not verify fails the dispatch, and its body
never reaches the flow.
- **`aliyun-acs3-hmac-sha256`** signs `host`, `content-type` and every
`x-acs-*` header, and sets `x-acs-date`, `x-acs-signature-nonce`,
`x-acs-content-sha256` and `Authorization`. The API's own action and version
Expand All @@ -659,9 +676,16 @@ What the platform does with it:
the first label of the endpoint host (`cvm.tencentcloudapi.com`, or a
regional `cvm.ap-guangzhou.tencentcloudapi.com`, is `cvm`), so point
`baseUrl` at the product's own endpoint. `X-TC-Action`, `X-TC-Version` and
`X-TC-Region` are yours to send and are not signed. A request without a body
is sent with `Content-Type: application/x-www-form-urlencoded`, the type it
is signed with.
`X-TC-Region` are yours to send and are not signed. A request without a
content type is sent with `Content-Type: application/x-www-form-urlencoded`,
the type it is signed with.
- **`alipay-v3`** signs the method, the path and query as sent and the body,
with a fresh nonce and millisecond timestamp, and sets `Authorization:
ALIPAY-SHA256withRSA app_id=…,nonce=…,timestamp=…,sign=…`. Every response
below status 400 must carry a valid `alipay-signature` (an empty body too),
checked against the key `vendorPublicKeyRef` names; an error response is
verified when it is signed, and an unsigned one fails as the error it is.
Public-key mode only: Alipay's certificate mode is not supported.

What the publish door refuses:

Expand All @@ -670,9 +694,11 @@ What the publish door refuses:
signer and its test vectors, so a scheme the server cannot run is never
accepted.
- **An inline key**: `secret`, `privateKey`, `secretKey` or `accessKeySecret`
is refused by name, pointing at `credentialRef`. The vendors' words for the
key id (`accessKeyId`, `secretId`) are answered with `keyId`. An authored `signature` is
refused too: it is computed for every request.
is refused by name, pointing at `credentialRef` (on `alipay-v3`,
`appPrivateKey` too, and `alipayPublicKey` points at `vendorPublicKeyRef`).
The vendors' words for the key id (`accessKeyId`, `secretId`, `appId`) are
answered with `keyId`. An authored `signature` is refused too: it is computed
for every request.
- **`provider: 'mcp'`**, at `auth.type`: an MCP server is not signed per
request. Declare `provider: 'rest'` or `provider: 'openapi'`.

Expand Down Expand Up @@ -736,7 +762,9 @@ retry):
| An `authorization_code` instance nobody connected, or whose grant the vendor refused | **Not fatal**: the instance registers **degraded**, "not connected" or "reconnect required" — connect it (`POST …/connectors/:name/connection`), and it materializes without a restart |
| The token endpoint is unreachable or answers `5xx` / `429` | **Not fatal**: the dispatch fails `CONNECTOR_UPSTREAM_UNAVAILABLE` and is retried on the existing backoff; the connection stays connected |
| `auth` is `signed` on a provider that does not say it applies the request signer | **Hard boot error** naming the instance, the scheme and the provider: its calls would go out unsigned — declare it through `rest` or `openapi` |
| `credentialRef` resolves to nothing | **Not fatal**: the instance registers **degraded**, naming the reference — set it (`PUT …/credential-refs/:ref`), and it materializes without a restart |
| `credentialRef` (or a `signed` instance's `vendorPublicKeyRef`) resolves to nothing | **Not fatal**: the instance registers **degraded**, naming the reference — set it (`PUT …/credential-refs/:ref`), and it materializes without a restart |
| A `signed` reference resolves to something that is not a key of the scheme's kind (an `alipay-v3` key that is not RSA) | **Hard boot error** naming the instance and which reference cannot sign or verify, never the value |
| A response to an `alipay-v3` call is unsigned (below status 400) or its signature does not verify | The dispatch **fails** naming the instance and the status; the response's body is never returned |
| The upstream refuses the credential (`401` / `403`) | **Not fatal**: the instance registers **degraded**. A stored credential is marked `rejected` and waits until it is set again (or a restart); an environment or vault credential is retried on the backoff |
| Instance `name` collides with a plugin-registered connector | **Hard boot error** — there is no silent precedence between the two worlds |
| stdio transport without host opt-in | **Hard boot error** with the `declarativeStdio` fix inline — a security rejection is never retried |
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Protocol reference — every schema by module
navTitle: Protocol Reference
description: Every schema published by @objectstack/spec — 1576 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1577 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand All @@ -25,7 +25,7 @@ counts are sums of the rows they head. Regenerate with
| [Automation Protocol](/docs/references/automation) | 15 | 77 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
| [Data Protocol](/docs/references/data) | 31 | 174 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
| [Integration Protocol](/docs/references/integration) | 1 | 22 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
| [Integration Protocol](/docs/references/integration) | 1 | 23 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
| [Kernel Protocol](/docs/references/kernel) | 29 | 151 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
| [Marketplace Protocol](/docs/references/marketplace) | 4 | 30 | The package & marketplace format — package identity and versions, listing, publish, review, search, install, template manifests. |
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
Expand All @@ -34,7 +34,7 @@ counts are sums of the rows they head. Regenerate with
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 37 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 175 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **204** | **1576** | 14 protocol modules |
| **Total** | **204** | **1577** | 14 protocol modules |

---

Expand Down Expand Up @@ -192,13 +192,13 @@ Users and accounts, organizations, positions, SCIM provisioning.

## Integration Protocol

**Source:** `packages/spec/src/integration/` · **Import:** `@objectstack/spec/integration` · **1 page, 22 schemas**
**Source:** `packages/spec/src/integration/` · **Import:** `@objectstack/spec/integration` · **1 page, 23 schemas**

The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances.

| File | Schemas |
| :--- | :--- |
| [`connector.zod.ts`](/docs/references/integration/connector) | `Connector`, `ConnectorAction`, `ConnectorActionEffect`, `ConnectorAppCredentialVendor`, `ConnectorInstanceAPIKeyAuth`, `ConnectorInstanceAppCredentialAuth`, `ConnectorInstanceAuth`, `ConnectorInstanceBasicAuth`, `ConnectorInstanceBearerAuth`, `ConnectorInstanceNoAuth`, `ConnectorInstanceOAuth2Auth`, `ConnectorInstanceOAuth2AuthorizationCodeAuth`, `ConnectorInstanceOAuth2ClientCredentialsAuth`, `ConnectorInstanceSignedAliyunAcs3Auth`, `ConnectorInstanceSignedAuth`, `ConnectorInstanceSignedTencentCloudTc3Auth`, `ConnectorOAuth2Grant`, `ConnectorRetryStrategy`, `ConnectorSigningScheme`, `ConnectorType`, `DeclarativeConnectorEntry`, `RetryConfig` |
| [`connector.zod.ts`](/docs/references/integration/connector) | `Connector`, `ConnectorAction`, `ConnectorActionEffect`, `ConnectorAppCredentialVendor`, `ConnectorInstanceAPIKeyAuth`, `ConnectorInstanceAppCredentialAuth`, `ConnectorInstanceAuth`, `ConnectorInstanceBasicAuth`, `ConnectorInstanceBearerAuth`, `ConnectorInstanceNoAuth`, `ConnectorInstanceOAuth2Auth`, `ConnectorInstanceOAuth2AuthorizationCodeAuth`, `ConnectorInstanceOAuth2ClientCredentialsAuth`, `ConnectorInstanceSignedAlipayV3Auth`, `ConnectorInstanceSignedAliyunAcs3Auth`, `ConnectorInstanceSignedAuth`, `ConnectorInstanceSignedTencentCloudTc3Auth`, `ConnectorOAuth2Grant`, `ConnectorRetryStrategy`, `ConnectorSigningScheme`, `ConnectorType`, `DeclarativeConnectorEntry`, `RetryConfig` |

---

Expand Down
Loading
Loading