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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ existing `0.1.0` release; earlier development prereleases are not listed.

## [Unreleased]

### Added

- Forward admin and Identity SAT issuance and Forward SAT revocation, using explicit per-call SA Key authentication, typed issuance/revocation metadata, and no automatic retries. Existing platform credentials and PAT/SAT business authentication remain unchanged.

## [0.4.0] - 2026-10-12

### Added
Expand Down
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,63 @@ cleanup verification, run `npm run test:live:sa:execution` with
SA credentials; `QODER_AUTH_MODE` switches examples, not the PAT test suites. See the
[official authentication contract](https://docs.qoder.com/cloud-agents/api/conventions/authentication).

### Forward SAT management

Forward exposes explicit admin/Identity SAT issuance and revocation in addition
to automatic platform exchange. These three endpoints authenticate with an
**SA Key**, supplied per method call; ordinary business requests still use PAT
or SAT. The Key overrides client/request Authorization only for that call, is
never included in the JSON body, and is not saved on the client.

`ttl_seconds` is optional (1-43200); omission uses the issuer's default. Issuance
and revocation are not automatically retried, even when the client or request
allows retries. Do not blindly repeat an issuance after an uncertain network
failure: it may already have issued a token. Metadata is for audit information,
not secrets. Keep the issuance response's Token ID, Credential ID and expiry for
precise revocation. A returned static SAT does not renew automatically, and
revocation affects its Forward binding; it does not cancel an already-running
Session or revoke unrelated Managed/platform access. Never log the SA Key, SAT,
serialized token response or Authorization headers.

Run `npm run test:live:sa:tokens` with `QODER_SA_CN_KEY`,
`QODER_SA_GLOBAL_KEY` and `QODER_SA_LIVE_ALLOW_WRITE=true` to verify both regions.
This separate suite issues admin and Identity SATs, checks business access and
cross-Identity refusal, then revokes the tokens, verifies access is refused and
deletes its temporary Identities. It does not execute a model or require
`QODER_SA_LIVE_ALLOW_EXECUTION=true`.

```ts
import { ForwardClient } from 'qca-sdk';

const forwardBaseURL = process.env.QODER_FORWARD_BASE_URL || 'https://api.qoder.com/api/v1/forward';
const saKey = process.env.QODER_SA_KEY!;
const identityID = process.env.QODER_IDENTITY_ID!;
const issuer = new ForwardClient({ baseURL: forwardBaseURL });
// Admin: issuer.serviceAccountTokens.create({ saKey, ttl_seconds: 600 }).
const token = await issuer.identities.serviceAccountTokens.create(identityID, {
saKey, ttl_seconds: 600,
});
try {
const runtime = new ForwardClient({
baseURL: forwardBaseURL, authMode: 'sa', accessToken: token.access_token,
});
const page = await runtime.sessions.list({ identity_ids: [identityID], limit: 1 });
} finally {
await issuer.serviceAccountTokens.revoke(token.auth_token_id, {
saKey, target_credential_id: token.credential_id,
target_expires_at: token.expires_at, reason: 'runtime complete',
});
}
```

After building, run `node examples/auth/forward-tokens.mjs`. It revokes its token
even if the business call fails. Set `QODER_SA_KEY` and `QODER_SA_REGION=cn|global`;
set `QODER_IDENTITY_ID` for Identity mode, or leave it unset for admin mode.

See the official [admin issuance](https://docs.qoder.com/cloud-agents/api/forward/service-account-tokens/create),
[Identity issuance](https://docs.qoder.com/cloud-agents/api/forward/service-account-tokens/create-identity)
and [revocation](https://docs.qoder.com/cloud-agents/api/forward/service-account-tokens/revoke) contracts.

### Authentication examples

The two read-only examples under `examples/auth/` show client construction directly:
Expand Down
6 changes: 6 additions & 0 deletions docs/api/forward/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
- [Identities](classes/Identities.md)
- [IdentityConfigs](classes/IdentityConfigs.md)
- [IdentityMemoryStores](classes/IdentityMemoryStores.md)
- [IdentityServiceAccountTokens](classes/IdentityServiceAccountTokens.md)
- [MemoryStoreMemories](classes/MemoryStoreMemories.md)
- [MemoryStoreMemoryVersions](classes/MemoryStoreMemoryVersions.md)
- [MemoryStores](classes/MemoryStores.md)
- [Models](classes/Models.md)
- [ScheduleRuns](classes/ScheduleRuns.md)
- [Schedules](classes/Schedules.md)
- [ServiceAccountTokens](classes/ServiceAccountTokens.md)
- [SessionEvents](classes/SessionEvents.md)
- [SessionResources](classes/SessionResources.md)
- [Sessions](classes/Sessions.md)
Expand Down Expand Up @@ -156,6 +158,10 @@
- [ScheduleTriggerPolicy](interfaces/ScheduleTriggerPolicy.md)
- [ScheduleUnpauseParams](interfaces/ScheduleUnpauseParams.md)
- [ScheduleUpdateParams](interfaces/ScheduleUpdateParams.md)
- [ServiceAccountToken](interfaces/ServiceAccountToken.md)
- [ServiceAccountTokenCreateParams](interfaces/ServiceAccountTokenCreateParams.md)
- [ServiceAccountTokenRevokeParams](interfaces/ServiceAccountTokenRevokeParams.md)
- [ServiceAccountTokenRevokeResponse](interfaces/ServiceAccountTokenRevokeResponse.md)
- [Session](interfaces/Session.md)
- [SessionArchiveParams](interfaces/SessionArchiveParams.md)
- [SessionCancelParams](interfaces/SessionCancelParams.md)
Expand Down
6 changes: 6 additions & 0 deletions docs/api/forward/classes/Identities.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@

> `readonly` **memoryStores**: [`IdentityMemoryStores`](IdentityMemoryStores.md)

***

### serviceAccountTokens

> `readonly` **serviceAccountTokens**: [`IdentityServiceAccountTokens`](IdentityServiceAccountTokens.md)

## Methods

### clear()
Expand Down
69 changes: 69 additions & 0 deletions docs/api/forward/classes/IdentityServiceAccountTokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / IdentityServiceAccountTokens

# Class: IdentityServiceAccountTokens

Issues SATs bound to a Forward Identity.

## Extends

- `APIResource`

## Constructors

### Constructor

> **new IdentityServiceAccountTokens**(`_client`): `IdentityServiceAccountTokens`

#### Parameters

##### \_client

[`APIClient`](../../index/classes/APIClient.md)

#### Returns

`IdentityServiceAccountTokens`

#### Inherited from

`APIResource.constructor`

## Properties

### \_client

> `protected` `readonly` **\_client**: [`APIClient`](../../index/classes/APIClient.md)

#### Inherited from

`APIResource._client`

## Methods

### create()

> **create**(`identityID`, `params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountToken`](../interfaces/ServiceAccountToken.md)\>

Issue an Identity SAT using the explicit SA Key. Automatic retries are disabled.

#### Parameters

##### identityID

`string`

##### params

[`ServiceAccountTokenCreateParams`](../interfaces/ServiceAccountTokenCreateParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountToken`](../interfaces/ServiceAccountToken.md)\>
91 changes: 91 additions & 0 deletions docs/api/forward/classes/ServiceAccountTokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / ServiceAccountTokens

# Class: ServiceAccountTokens

Forward SAT issuance and revocation require an explicit SA Key per call.

## Extends

- `APIResource`

## Constructors

### Constructor

> **new ServiceAccountTokens**(`_client`): `ServiceAccountTokens`

#### Parameters

##### \_client

[`APIClient`](../../index/classes/APIClient.md)

#### Returns

`ServiceAccountTokens`

#### Inherited from

`APIResource.constructor`

## Properties

### \_client

> `protected` `readonly` **\_client**: [`APIClient`](../../index/classes/APIClient.md)

#### Inherited from

`APIResource._client`

## Methods

### create()

> **create**(`params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountToken`](../interfaces/ServiceAccountToken.md)\>

Issue an admin SAT. Automatic retries are disabled to avoid duplicate issuance.

#### Parameters

##### params

[`ServiceAccountTokenCreateParams`](../interfaces/ServiceAccountTokenCreateParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountToken`](../interfaces/ServiceAccountToken.md)\>

***

### revoke()

> **revoke**(`authTokenID`, `params`, `options?`): [`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountTokenRevokeResponse`](../interfaces/ServiceAccountTokenRevokeResponse.md)\>

Revoke a Forward SAT binding from the same SA. Creation metadata disambiguates the target.

#### Parameters

##### authTokenID

`string`

##### params

[`ServiceAccountTokenRevokeParams`](../interfaces/ServiceAccountTokenRevokeParams.md)

##### options?

[`RequestOptions`](../../index/interfaces/RequestOptions.md)

#### Returns

[`APIPromise`](../../index/classes/APIPromise.md)\<[`ServiceAccountTokenRevokeResponse`](../interfaces/ServiceAccountTokenRevokeResponse.md)\>
75 changes: 75 additions & 0 deletions docs/api/forward/interfaces/ServiceAccountToken.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / ServiceAccountToken

# Interface: ServiceAccountToken

A newly issued Forward SAT. Treat access_token and serialized responses as secrets.

## Indexable

> \[`key`: `string`\]: `unknown`

## Properties

### access\_token

> **access\_token**: `string`

***

### auth\_token\_id

> **auth\_token\_id**: `string`

***

### credential\_id

> **credential\_id**: `string`

***

### expires\_at

> **expires\_at**: `string`

***

### expires\_in

> **expires\_in**: `number`

***

### identity\_id?

> `optional` **identity\_id?**: `string`

Absent for admin tokens.

***

### service\_account\_id

> **service\_account\_id**: `string`

***

### subject\_type

> **subject\_type**: `"admin"` \| `"identity"`

***

### token\_type

> **token\_type**: `"Bearer"`

***

### type

> **type**: `"service_account_token"`
31 changes: 31 additions & 0 deletions docs/api/forward/interfaces/ServiceAccountTokenCreateParams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[**qca-sdk**](../../README.md)

***

[qca-sdk](../../README.md) / [forward](../README.md) / ServiceAccountTokenCreateParams

# Interface: ServiceAccountTokenCreateParams

## Properties

### metadata?

> `optional` **metadata?**: `Record`\<`string`, `unknown`\>

Business audit metadata; do not include credentials or other secrets.

***

### saKey

> **saKey**: `string`

SA Key sent only in this request's Authorization header; not part of the JSON body.

***

### ttl\_seconds?

> `optional` **ttl\_seconds?**: `number`

Lifetime in seconds, 1-43200. Omit to use the issuer's default.
Loading
Loading