Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f81b874
feat(auth0-api-js): organizationPolicy enforcement (SDK-11295)
tusharpandey13 Sep 29, 2026
b2543ed
feat(api-js): add getClientCredentialsToken (M2M) [SDK-11361]
tusharpandey13 Sep 29, 2026
da0bd58
test(api-js,auth-js): cover getClientCredentialsToken + scope plumbin…
tusharpandey13 Sep 29, 2026
ddab10c
docs(api-js): document getClientCredentialsToken (M2M) [SDK-11361]
tusharpandey13 Sep 29, 2026
61faf53
feat(auth0-api-js): add OBO TokenStore contracts and cache-aside to g…
tusharpandey13 Sep 29, 2026
87294c1
docs(auth0-api-js): document OBO TokenStore caching and DownscopedTok…
tusharpandey13 Sep 29, 2026
f56bc70
fix(auth0-api-js): injective OBO cache key + preserve token type on c…
tusharpandey13 Sep 29, 2026
1b52828
security(obo-cache): fix SR-2 key isolation and SR-3 unconditional ve…
tusharpandey13 Sep 29, 2026
46d1826
fix(auth0-api-js): surface granted scope/tokenType from M2M grant; re…
tusharpandey13 Sep 30, 2026
3216d64
fix(auth0-api-js): add clock-skew leeway to OBO cache-hit; harden iso…
tusharpandey13 Sep 30, 2026
e1cb99c
test(auth0-api-js): add MSW end-to-end tests for OBO cache-aside
tusharpandey13 Sep 30, 2026
6074ef9
test(auth0-api-js): expand MSW e2e coverage for organization policy
tusharpandey13 Sep 30, 2026
e5ad603
test(auth0-api-js): add MSW e2e tests for M2M client-credentials gran…
tusharpandey13 Sep 30, 2026
9158f30
fix(auth0-api-js): validate organization config and fix WWW-Authentic…
tusharpandey13 Oct 11, 2026
b7c4427
fix(auth0-api-js): isolate organization config from later mutation
tusharpandey13 Oct 15, 2026
a96f75a
docs(auth0-api-js): name the organization error codes in their JSDoc
tusharpandey13 Oct 15, 2026
c8faca6
feat(auth0-auth-js): forward scope on the client credentials grant
tusharpandey13 Oct 15, 2026
805a168
Merge remote-tracking branch 'origin/main' into feat/mcp-p1-org
tusharpandey13 Oct 15, 2026
90b8789
fix(auth0-api-js): address review feedback on the client credentials …
tusharpandey13 Oct 15, 2026
9911c62
feat(auth0-api-js): cache on-behalf-of tokens behind a pluggable toke…
tusharpandey13 Oct 15, 2026
4c3f4da
docs(auth0-api-js): document on-behalf-of token caching
tusharpandey13 Oct 15, 2026
234cc20
Merge remote-tracking branch 'origin/main' into feat/mcp-p1-org
tusharpandey13 Oct 16, 2026
e58871e
Merge remote-tracking branch 'origin/main' into feat/auth-js-client-c…
tusharpandey13 Oct 16, 2026
738f345
Merge origin/main into feat/mcp-p1-m2m
tusharpandey13 Oct 19, 2026
58a42e7
Merge remote-tracking branch 'origin/main' into feat/auth-js-client-c…
tusharpandey13 Oct 19, 2026
b9da34f
Merge remote-tracking branch 'origin/main' into feat/mcp-p1-org
tusharpandey13 Oct 19, 2026
6b77ef3
Merge origin/main into feat/mcp-p1-obo-cache
tusharpandey13 Oct 19, 2026
c5543be
test(auth0-api-js): assert scope reaches the client credentials token…
tusharpandey13 Oct 19, 2026
32ebde9
docs(auth0-api-js): document absent and empty scope on the client cre…
tusharpandey13 Oct 19, 2026
b2e6e53
docs(auth0-api-js): keep the token type casing note on TokenSet
tusharpandey13 Oct 19, 2026
d303efd
docs(auth0-auth-js): document the scope option on client credentials
tusharpandey13 Oct 19, 2026
c668558
docs(auth0-api-js): state the absent and empty scope rule on OBO results
tusharpandey13 Oct 19, 2026
3f99137
Merge remote-tracking branch 'origin/feat/mcp-p1-org' into prep/api-j…
tusharpandey13 Oct 19, 2026
25768c4
Merge remote-tracking branch 'origin/feat/auth-js-client-credentials-…
tusharpandey13 Oct 19, 2026
8111d3b
Merge remote-tracking branch 'origin/feat/mcp-p1-m2m' into prep/api-j…
tusharpandey13 Oct 19, 2026
a6edc0e
fix(auth0-api-js): enforce organization policy on pre-verified On Beh…
tusharpandey13 Oct 19, 2026
393ea4b
Release auth0-api-js v1.7.0-beta.0
tusharpandey13 Oct 19, 2026
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
2 changes: 1 addition & 1 deletion packages/auth0-api-js/.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.6.1
v1.7.0-beta.0
8 changes: 8 additions & 0 deletions packages/auth0-api-js/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Change Log

## [auth0-api-js-v1.7.0-beta.0](https://github.com/auth0/auth0-auth-js/tree/auth0-api-js-v1.7.0-beta.0) (2026-10-19)
[Full Changelog](https://github.com/auth0/auth0-auth-js/compare/auth0-api-js-v1.6.1...auth0-api-js-v1.7.0-beta.0)

**Added**
- feat(auth0-api-js): organizationPolicy enforcement [\#291](https://github.com/auth0/auth0-auth-js/pull/291) ([@tusharpandey13](https://github.com/tusharpandey13))
- feat(auth0-api-js): OBO token-store seam and cache-aside path [\#290](https://github.com/auth0/auth0-auth-js/pull/290) ([@tusharpandey13](https://github.com/tusharpandey13))
- feat(auth0-api-js): getClientCredentialsToken (M2M) [\#289](https://github.com/auth0/auth0-auth-js/pull/289) ([@tusharpandey13](https://github.com/tusharpandey13))

## [auth0-api-js-v1.6.1](https://github.com/auth0/auth0-auth-js/tree/auth0-api-js-v1.6.1) (2026-05-18)
[Full Changelog](https://github.com/auth0/auth0-auth-js/compare/auth0-api-js-v1.6.0...auth0-api-js-v1.6.1)

Expand Down
480 changes: 477 additions & 3 deletions packages/auth0-api-js/EXAMPLES.md

Large diffs are not rendered by default.

132 changes: 130 additions & 2 deletions packages/auth0-api-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,139 @@ export async function handleCalendarRequest(request: Request) {
}
```

In the current implementation, `getTokenOnBehalfOf()` forwards the incoming access token as the
[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693#section-2.1) `subject_token` and relies on `Auth0` to handle any DPoP-specific behavior for that token.
`getTokenOnBehalfOf()` forwards the incoming access token as the
[RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693#section-2.1) `subject_token` and applies no
DPoP-specific handling of its own during the exchange. When you supply a token store (see below), the SDK
also verifies the subject token before the exchange.
The `OBO` result only includes access-token-oriented fields. It does not expose `id_token` or
`refresh_token`.

#### Caching OBO tokens

Pass a context object with a `store` as the third argument to avoid calling Auth0 on every request.
The SDK checks the store before the exchange and writes the result back on success. No built-in
store is provided; you supply the implementation of the `TokenStore` interface.

```ts
const obo = await apiClient.getTokenOnBehalfOf(
incomingAccessToken,
{ audience: 'https://calendar-api.example.com', scope: 'calendar:read calendar:write' },
{ store }
);
```

A few behaviors to know before you rely on it:

- **Verification depends on the store.** The subject token is verified if and only if a store is
supplied, because caching needs its claims to build a cache key. With no store, the token is
forwarded untouched. With a store and no `verified` member in the context, the SDK verifies the
token as a Bearer token with no request context, which cannot satisfy `dpop: { mode: 'required' }`,
cannot accept a DPoP-bound token, and gives a `domains` resolver no URL and no headers. In those
cases, verify the token once at your ingress and pass the result as
`{ store, verified: { accessToken, claims } }`.
- **`verified.claims` must be the object `verifyAccessToken` returned on the same `ApiClient`.** The
SDK checks object identity against a private registry, so claims that were hand-built, cloned,
spread, round-tripped through JSON, returned by a different `ApiClient` instance or sent across a
process or worker boundary are rejected with an `InvalidConfigurationError`. The `accessToken`
must equal the token being exchanged, and the claims must not be expired: an `exp` at or within a
few seconds of the current time is rejected. The restriction exists because the cache key is
derived from these claims. If unverified claims were accepted, a caller could read or overwrite
another principal's cached delegated token. When ingress and the exchange run in different
processes, call `verifyAccessToken` again on the exchanging side. A local verification is cheap
because the JWKS is cached.
- **Scope matching.** The `scopeMatching` option on `ApiClient` is `'strict'` (default) or
`'non_strict'`. Both compare the scopes an exchange was granted. `'strict'` reuses a token only
when its granted scopes equal the requested set. `'non_strict'` also reuses a token whose granted
scopes cover the request, and requires an `IndexedTokenStore`; otherwise the call throws an
`InvalidConfigurationError`.
- **Narrowed grants are not rejected here.** If Auth0 grants fewer scopes than requested, the token
is cached under the scopes it was granted and returned, with `scope` set to what the authorization
server sent. This package does not decide whether that is acceptable. The code that serves the
request does, because it has the context to turn a narrowed grant into the right response. Check
`grantedScopes` on the result if you need every requested scope. It is always present, on a fresh
exchange and on a cache hit, and it is normalized, deduplicated and sorted. Unlike `scope`, it
reports the requested set when the server omits `scope`, and the broader scopes the cached token
carries on a non-strict hit for a narrower request. Do not test `scope` for truthiness: an empty
string means nothing was granted and is distinct from the field being absent, which means the
scopes were granted as requested (RFC 6749 section 5.1), so `if (result.scope)` reads an empty
grant as a full grant.
- **A token with mistyped key claims is not cached.** If `iss`, `client_id`, `azp`, `sub`, `org_id`,
`sid` or `jti` is present with a non-string value, the call proceeds as an ordinary uncached
exchange. A conforming token never triggers this.
- **Store contract.** A returned entry must expose its fields as its own properties, because each
field is read through `Object.hasOwn`. Plain objects and `JSON.parse` output work; a class
instance with prototype getters reads as empty and is a miss. A record or index member with more
than 1000 scopes is rejected as malformed, and non-strict matching reads at most 20 candidate
entries per lookup. The index is a hint: a member that points at a key that does not match its own
recorded scopes is skipped. This package never deletes or prunes entries or index members, so
eviction belongs to your implementation.
- **Revocation window.** The session component of the cache key outlives any single access token, so
a delegated token minted earlier in a session is reused for later tokens from the same session. A
session revoked at the authorization server does not invalidate an already cached delegated token
before that token's own expiry. This is inherent to caching delegated tokens.
- **A store failure never fails a call.** A read that throws, a malformed entry and an entry close
to expiry are all cache misses, and a write that throws still returns the new token.
- **Protect tokens at rest.** Cache keys are opaque and contain no claim values in the clear, but
this package applies no encryption. A store backed by shared or persistent storage is responsible
for protecting the tokens it holds.

See [Caching OBO tokens with a TokenStore](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-api-js/EXAMPLES.md#caching-obo-tokens-with-a-tokenstore) in [EXAMPLES.md](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-api-js/EXAMPLES.md) for worked examples, including DPoP with caching, an indexed store, the `CachedToken` shape, and responsibility boundaries.

### 7. Client Credentials (Machine-to-Machine)

The SDK supports the OAuth 2.0 client credentials grant, which allows a confidential client (a server-side application with a client secret or a private key JWT) to obtain an access token without any user involvement. This is the standard pattern for machine-to-machine (M2M) communication, such as a background job or a microservice calling a downstream API.

To use this flow, you must configure the `ApiClient` with client credentials:

```ts
import { ApiClient } from '@auth0/auth0-api-js';

const apiClient = new ApiClient({
domain: '<AUTH0_DOMAIN>',
audience: '<AUTH0_AUDIENCE>',
clientId: '<AUTH0_CLIENT_ID>',
clientSecret: '<AUTH0_CLIENT_SECRET>',
});

// Request a token using the audience configured at construction time.
const tokenSet = await apiClient.getClientCredentialsToken({});
```

You can also override the audience or request specific scopes for a given call:

```ts
// Override audience and request specific scopes.
const tokenSet = await apiClient.getClientCredentialsToken({
audience: 'https://api.backend.com',
scope: 'read:data write:data',
});

// Use the access token to call a downstream API.
const response = await fetch('https://api.backend.com/data', {
headers: {
authorization: `Bearer ${tokenSet.accessToken}`,
},
});
```

The method returns a `TokenSet` object with the following properties:

- `accessToken`: The access token issued by Auth0.
- `expiresAt`: The token expiration time as seconds since the Unix epoch.
- `scope`: The scope the authorization server granted, as a space-delimited string returned exactly as given. An absent `scope` means the scopes were granted as requested (RFC 6749 section 5.1). An empty string means nothing was granted. To tell the two apart, test for `undefined` rather than for falsiness.
- `tokenType`: The token type, normalized to lower case by the underlying OAuth client, so a bearer token arrives as `bearer`.

To check whether a token has expired before using it, compare `expiresAt` to the current time:

```ts
// expiresAt is in epoch seconds; Date.now() is in milliseconds.
const isExpired = Math.floor(Date.now() / 1000) >= tokenSet.expiresAt;
```

> **Requirements**: The `ApiClient` must be constructed with either `clientSecret` or `clientAssertionSigningKey`. Calling `getClientCredentialsToken()` without client authentication configured throws a `MissingClientAuthError`.

See the [Client Credentials](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-api-js/EXAMPLES.md#client-credentials-machine-to-machine) example in [EXAMPLES.md](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-api-js/EXAMPLES.md) for more details.

## Feedback

### Contributing
Expand Down
4 changes: 2 additions & 2 deletions packages/auth0-api-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@auth0/auth0-api-js",
"version": "1.6.1",
"version": "1.7.0-beta.0",
"description": "Auth0 Authentication SDK for API's on JavaScript runtimes",
"author": "Auth0",
"license": "MIT",
Expand All @@ -27,7 +27,7 @@
}
},
"dependencies": {
"@auth0/auth0-auth-js": "^1.3.0",
"@auth0/auth0-auth-js": "^1.18.0",
"jose": "^6.0.8",
"oauth4webapi": "^3.3.0"
},
Expand Down
Loading
Loading