Skip to content

[BREAKING] feat!: remove Authentication layer - #1390

Open
tusharpandey13 wants to merge 10 commits into
masterfrom
feat/auth-separation-v6
Open

[BREAKING] feat!: remove Authentication layer#1390
tusharpandey13 wants to merge 10 commits into
masterfrom
feat/auth-separation-v6

Conversation

@tusharpandey13

@tusharpandey13 tusharpandey13 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

WIP / Draft — opened for approach review before finalizing. Depends on auth-separation program decisions (deprecation window, release sequencing). Version bump and CHANGELOG will be in a separate release PR.

What this does

Removes the Authentication layer from node-auth0, making it a Management-API-only SDK. ManagementClient continues to work — internal token acquisition is handled directly via the client credentials grant.

Changes

Area Change
Removed src/auth/ (9 files), src/userinfo/AuthenticationClient + UserInfoClient gone from main entrypoint
TokenProvider Inlined client credentials grant (raw fetch + jose). Preserves 10s leeway refresh, in-flight de-dup, client-secret + client-assertion modes
Dead code Removed src/lib/runtime.ts, auth-only src/utils.ts helpers (mtlsPrefix, resolveValueToPromise). Kept generateClientInfo for telemetry
Dependencies Removed uuid
Docs README updated; migration guide added with v6→v7 method-mapping table

Breaking changes

  • AuthenticationClient and UserInfoClient removed from auth0 main entrypoint. The auth0/legacy entrypoint (auth0-legacy v4) still ships them.
  • mTLS: Management clients with useMTLS: true must supply an explicit fetch option. Throws at construction if absent — prevents silent 401s at request time.
  • mTLS + client-assertion: mutually exclusive — throws at construction if both are provided (incompatible token endpoint auth methods).
  • Domain validation: domain must be a bare hostname. Slashes or query strings throw at construction.

Implementation notes

  • TokenProvider POSTs to https://{domain}/oauth/token with application/x-www-form-urlencoded. expires_in (seconds) → Date.now() + expires_in * 1000 for cache expiry. Getting this wrong causes refresh storms or stale-token 401s — covered by tests.
  • Client-assertion path uses jose (importPKCS8 + SignJWT), already a project dependency.
  • Internal token request advertises node-auth0 identity in the Auth0-Client header — the Management token acquisition is an internal concern; tenant analytics should see node-auth0.
  • Telemetry env field (runtime fingerprint) is intentionally absent. The Auth0-Client header only encodes name + version — the runtime fingerprint carried in pre-v7 headers is not forwarded. Documented in code.

Tests

  • token-provider.test.ts: covers credential modes, cache hit, leeway refresh, in-flight de-dup, error propagation, error-not-cached retry, expiry, mTLS customFetch forwarding, mTLS throw-on-no-fetch, domain validation, mTLS+assertion guard (TC-2.1–2.12).
  • export-surface.test.ts: asserts AuthenticationClient/UserInfoClient absent from main entrypoint.
  • Deleted tests/auth/**, tests/userinfo/**, tests/lib/runtime.test.ts.

Validation

  • Build (CJS + ESM)
  • Lint
  • Unit tests — 545/545

🤖 Generated with Claude Code

tusharpandey13 and others added 4 commits August 17, 2026 13:12
…uth-js

BREAKING CHANGE: removes AuthenticationClient and UserInfoClient from the
auth0 package. Management API token acquisition now delegates to
@auth0/auth0-auth-js AuthClient.getTokenByClientCredentials. mTLS now
requires an explicit fetch option.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p any cast

Match published TelemetryConfig ({enabled:false} | {enabled?:true,name,version});
drop unsupported env field; type options as AuthClientOptions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent token tests

- Delete obsolete tests/auth/**, tests/userinfo/**, tests/lib/runtime.test.ts
- Rewrite token-provider test to mock @auth0/auth0-auth-js AuthClient (8 cases:
  both credential modes, cache hit, leeway refresh with expiresAt*1000 boundary,
  in-flight dedup, error propagation, error-not-cached, expiry)
- Add export-surface test asserting AuthenticationClient/UserInfoClient removed
- jest: map @auth0/auth0-auth-js to CJS stub for unit/wire (avoids ESM
  openid-client under Jest CJS runtime); allow openid-client/oauth4webapi
  transform in root-tests ESM project

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- README: replace AuthenticationClient/UserInfoClient sections with pointers
  to @auth0/auth0-auth-js; add 'Migrating from v6 to v7' with method-mapping
  table and mTLS note; preserve auth0/legacy docs
- CHANGELOG: v7.0.0 breaking-change entry
- token-provider: doc comment on @auth0/auth0-auth-js delegation + expiresAt
  seconds-to-ms conversion

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tusharpandey13 tusharpandey13 changed the title feat!: remove Authentication layer (v7.0.0), delegate Management token to @auth0/auth0-auth-js [DO NOT MERGE] feat!: remove Authentication layer (v7.0.0), delegate Management token to @auth0/auth0-auth-js Aug 17, 2026
tusharpandey13 and others added 2 commits August 24, 2026 21:10
…dd TC-2.9/2.10

Throw at construction when useMTLS=true and no fetch is provided, preventing
silent 401s at request time. Replace (options as any).fetch with a typed
intersection narrowing. Add comments documenting the telemetry env-field delta
and node-auth0 identity intent. Add TC-2.9 and TC-2.10 covering both paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tusharpandey13 tusharpandey13 changed the title [DO NOT MERGE] feat!: remove Authentication layer (v7.0.0), delegate Management token to @auth0/auth0-auth-js [DO NOT MERGE] feat!: remove Authentication layer , delegate Management token to @auth0/auth0-auth-js Aug 25, 2026
@tusharpandey13 tusharpandey13 changed the title [DO NOT MERGE] feat!: remove Authentication layer , delegate Management token to @auth0/auth0-auth-js [DO NOT MERGE] feat!: remove Authentication layer Aug 25, 2026
tusharpandey13 and others added 2 commits August 25, 2026 10:44
…t credentials grant

Replace the auth0-auth-js AuthClient delegation with a self-contained raw
fetch + jose implementation. The dep added openid-client and oauth4webapi as
transitive dependencies for what amounts to a single POST to /oauth/token.

Key changes:
- Inline fetchToken(): URLSearchParams body, Content-Type header, response parsing
- Client-assertion path: importPKCS8 + SignJWT via jose (already a dep)
- mTLS: forward caller-supplied fetch; guard against useMTLS + clientAssertion
  combination (mutually exclusive auth methods)
- Domain validation: reject domains containing slashes or query strings
- Telemetry header: use jose base64url.encode instead of Buffer (portability)
- expiresAt computed as Date.now() + expires_in * 1000 (relative, same as pre-v7)
- Add TC-2.11 (domain validation) and TC-2.12 (mTLS+assertion guard); 13/13 pass

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete tests/lib/utils.test.ts — tests resolveValueToPromise which was
  removed from src/utils.ts as auth-only dead code
- Add fetch mock to mTLS test in management-client-custom-domain.test.ts —
  TokenProvider now throws at construction when useMTLS=true and no fetch
  is provided (fail-fast guard added in previous commit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tusharpandey13
tusharpandey13 marked this pull request as ready for review August 25, 2026 05:28
@tusharpandey13
tusharpandey13 requested a review from a team as a code owner August 25, 2026 05:28
@tusharpandey13 tusharpandey13 changed the title [DO NOT MERGE] feat!: remove Authentication layer [BREAKING] feat!: remove Authentication layer Aug 25, 2026
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.55172% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.40%. Comparing base (f9d6413) to head (64e8eac).

Files with missing lines Patch % Lines
src/management/wrapper/token-provider.ts 96.55% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1390      +/-   ##
==========================================
- Coverage   89.73%   89.40%   -0.33%     
==========================================
  Files         441      429      -12     
  Lines       20799    20374     -425     
  Branches    10146     9720     -426     
==========================================
- Hits        18663    18216     -447     
- Misses       2136     2158      +22     
Flag Coverage Δ
alltests 89.40% <96.55%> (-0.33%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/index.ts 100.00% <ø> (ø)
src/lib/middleware/auth0-client-telemetry.ts 57.14% <ø> (-35.72%) ⬇️
src/management/wrapper/ManagementClient.ts 100.00% <ø> (ø)
src/utils.ts 100.00% <ø> (ø)
src/management/wrapper/token-provider.ts 96.92% <96.55%> (-3.08%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…erated

mTLS token acquisition reads options.fetch which is defined on
BaseClientOptions in the Fern-generated BaseClient.ts. Add a comment
at the usage site so the dependency survives future regenerations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread src/management/tests/__mocks__/auth0-auth-js.cjs Outdated
Comment thread src/management/wrapper/token-provider.ts Outdated
…th-js mock artifacts

- Forward plain-string options.headers to POST /oauth/token, matching the
  behavior fixed in v6 via PR #1392. Supplier-function headers are skipped
  (require async resolution, not supported on the token endpoint path).
  SDK-controlled headers (Content-Type, Auth0-Client) always take precedence.
- Delete src/management/tests/__mocks__/auth0-auth-js.cjs — leftover CJS stub
  from when TokenProvider delegated to @auth0/auth0-auth-js; no longer needed.
- Remove three moduleNameMapper entries for @auth0/auth0-auth-js from jest.config.mjs.
- Add TC-2.13 covering plain-string forwarding, supplier filtering, and override precedence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

private async fetchToken(): Promise<TokenResult> {
const { domain, clientId, audience } = this.options;
const tokenUrl = `https://${domain}/oauth/token`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lost the mtls. endpoint alias here. The old code in src/auth/oauth.ts built the host as ${mtlsPrefix}.${options.domain} when useMTLS was set, and mtlsPrefix is gone from utils.ts now. So with useMTLS: true the token request goes to https://{domain}/oauth/token instead of https://mtls.{domain}/oauth/token.

Two ways this goes wrong:

  1. The client is configured for tls_client_auth only, so the request comes back 401.
  2. The client also has a secret, so the call quietly succeeds over client_secret_post and the token comes back with no cnf claim. We lose the certificate binding without any error, which is the whole point of mTLS.

For reference, auth0-auth-js handles this by sending use_mtls_endpoint_aliases: true and reading mtls_endpoint_aliases.token_endpoint from discovery.

Can we prefix the host when useMTLS is true? Also worth deciding whether the Management API baseUrl needs the alias as well once the token is certificate bound.

// mTLS uses TLS client certificate as the auth method (tls_client_auth).
// Combining useMTLS with clientAssertionSigningKey is a misconfiguration —
// the two auth methods are mutually exclusive on Auth0's token endpoint.
if ("clientAssertionSigningKey" in options) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here.

useMTLS?: boolean is still declared on ManagementClientOptionsWithClientAssertion, so the type advertises a combination that now always throws at construction. If we remove it from that interface, TypeScript catches the mistake at compile time instead of at runtime.

The guard is also one sided. useMTLS together with clientSecret is allowed and still puts client_secret in the body, which does not match the "mutually exclusive auth methods" reasoning in the comment above. Related to the endpoint question on line 63.

) {
this.authenticationClient = new AuthenticationClient({ ...options, headers: undefined });
// Validate domain: must be a bare hostname, no slashes or query strings.
if (/[/?#]/.test(options.domain)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check only runs on the client credentials path, since TokenProvider is only constructed from createTokenSupplier. With a static token, new ManagementClient({ domain: "tenant.auth0.com/x", token }) still builds https://tenant.auth0.com/x/api/v2 and nobody complains. An empty string also passes the regex.

Since the PR lists domain validation as a breaking change, it would be better to do this in the ManagementClient constructor (or in buildManagementBaseUrl) so it covers both auth modes, and to reject empty or whitespace only domains too.

const userHeaders = this.options.headers ?? {};
const headers: Record<string, string> = {};
for (const [key, value] of Object.entries(userHeaders)) {
if (typeof value === "string") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can actually resolve these. fetchToken is already async and Fern gives us core.Supplier.get (it is used in makePassthroughRequest.ts), so await core.Supplier.get(value) would work here.

As written, a dynamic header like a per request trace id shows up on Management API calls but not on the token call, which is an awkward gap to debug.

Side note on the commit message: it says this matches "the behavior fixed in v6 via PR #1392", but #1392 is still open, so this PR is currently the only place the change exists. Worth sequencing the two so they do not conflict.

headers[key] = value;
}
}
headers["Content-Type"] = "application/x-www-form-urlencoded";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above says SDK headers always take precedence, but that only holds when the caller uses the exact same casing. headers is a plain object, so if someone passes content-type in lower case we end up with both keys and fetch joins them instead of replacing.

I reproduced it on Node 22: the request goes out with content-type: application/json, application/x-www-form-urlencoded, and the token endpoint rejects that with a 400, so the client never gets a token.

TC-2.13 only covers the exact case Content-Type, which is why it passes while the claim is not true. Suggest normalising keys to lower case before merging, or building a Headers object and using .set().

Comment thread README.md
import { AuthClient } from "@auth0/auth0-auth-js";
```

### Method mapping

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table covers 9 of the roughly 19 public methods this PR removes. The ones missing that do have equivalents in auth0-auth-js:

  • oauth.authorizationCodeGrantWithPKCE
  • oauth.pushedAuthorization
  • oauth.tokenForConnection maps to getTokenForConnection
  • backchannel.authorize and backchannel.backchannelGrant map to initiateBackchannelAuthentication and backchannelAuthenticationGrant
  • tokenExchange.exchangeToken maps to exchangeToken
  • the four passwordless.* methods, collapsed into one row today

IDTokenValidator.validate has no public equivalent that I can find, so it is better to say that outright than to leave it out.

Also, the repo convention is a top level migration file. v5_MIGRATION_GUIDE.md exists and the Documentation list links a v6_MIGRATION_GUIDE.md, but this whole section lives only in the README and is not linked from that list.

Comment thread README.md
Comment on lines +384 to +387
| `authenticationClient.database.signUp(...)` | `authClient.signUp(...)` |
| `authenticationClient.database.changePassword(...)` | `authClient.changePassword(...)` |
| `authenticationClient.passwordless.*` | `authClient.passwordless.*` (sub-client) |
| `userInfoClient.getUserInfo(accessToken)` | `authClient.getUserInfo(accessToken)` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two rows here do not match the real API. In auth0-auth-js these live on a sub client: AuthClient.database is a DatabaseClient, so it should be authClient.database.signUp(...) and authClient.database.changePassword(...). authClient.signUp does not exist.

The getUserInfo row has the same problem as the section above, that method is not there at all.

Worth a footnote that getTokenByCode takes a URL and not a code, so it is not a drop in replacement for authorizationCodeGrant. The other five rows check out.

Comment thread README.md
Comment on lines +389 to +391
### mTLS configuration

If you use mTLS, you must now provide an explicit `customFetch` option:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section documents useMtls and customFetch on AuthClient in auth0-auth-js, but the mTLS breaking changes in this PR are on ManagementClient and the option names are different.

Nothing here tells a ManagementClient({ useMTLS: true }) user that they now have to pass a fetch option, or that useMTLS together with clientAssertionSigningKey throws at construction. Those are the customers most likely to break, and right now the thrown error message is the only clue they get.

Can we add a short "ManagementClient breaking changes" block with a working ManagementClient mTLS snippet?

Comment thread .gitignore
/docs
/coverage
*.lcov No newline at end of file
*.lcov.forge/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This merged two patterns into one line. *.lcov had no trailing newline, so the edit produced *.lcov.forge/ and now neither *.lcov nor .forge/ is ignored, which means coverage output can get committed by accident.

Should be *.lcov on its own line with a newline, and .forge/ on its own line if we want it ignored. It is also unrelated to the auth separation, so it may be cleaner as a separate commit.

Comment thread yarn.lock

"@auth0/auth0-auth-js@^1.12.1":
version "1.12.1"
resolved "https://a0us.jfrog.io/artifactory/api/npm/npm/@auth0/auth0-auth-js/-/auth0-auth-js-1.12.1.tgz#cb0e644c31dfdfe1e6707ae9d59a33dbe4a5c4f2"

@nandan-bhat nandan-bhat Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These four added entries resolve to a0us.jfrog.io, which is our internal Artifactory and returns 401 without credentials. This is a public repo.

They are also stale. @auth0/auth0-auth-js was reverted out of package.json in 53d0879, so nothing references them. Installs still work today (I checked, yarn install --frozen-lockfile passes because yarn ignores unreferenced entries), but any future jose@^6 resolution would point external contributors at a host they cannot reach. The base lockfile has zero jfrog URLs.

Can we regenerate the lockfile against the public registry and drop these?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants