From b4e2a2ffa7aafc47488681aeb76e9363fa288361 Mon Sep 17 00:00:00 2001 From: tanya732 Date: Wed, 19 Aug 2026 17:55:03 +0530 Subject: [PATCH 1/2] add Migration Guide V5 --- .fernignore | 2 +- CLAUDE.md | 2 +- README.md | 8 +- v3_MIGRATION_GUIDE.md | 369 ------------------------------------------ v5_MIGRATION_GUIDE.md | 113 +++++++++++++ 5 files changed, 119 insertions(+), 375 deletions(-) delete mode 100644 v3_MIGRATION_GUIDE.md create mode 100644 v5_MIGRATION_GUIDE.md diff --git a/.fernignore b/.fernignore index 02581ed8b..3b5964c30 100644 --- a/.fernignore +++ b/.fernignore @@ -10,8 +10,8 @@ references/ # Examples and Migration Guide from auth0-real EXAMPLES.md -v3_MIGRATION_GUIDE.md v4_MIGRATION_GUIDE.md +v5_MIGRATION_GUIDE.md LICENSE CHANGELOG.md diff --git a/CLAUDE.md b/CLAUDE.md index 405e351b1..2d7175153 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -182,7 +182,7 @@ See [references/pitfalls.md](references/pitfalls.md) for the full list. Highligh | `README.md` | Overview, install, getting started (Auth + Management) | yes (hand-maintained) | | `EXAMPLES.md` | Scenario code samples | yes (hand-maintained) | | `reference.md` | Management API code samples | no — **generated**, do not hand-edit | -| `v4_MIGRATION_GUIDE.md` / `v3_MIGRATION_GUIDE.md` | Major-version migration | yes (hand-maintained) | +| `v5_MIGRATION_GUIDE.md` / `v4_MIGRATION_GUIDE.md` | Major-version migration | yes (hand-maintained) | | `CHANGELOG.md` | Release history | yes — release-flow artifact, not edited per-PR | See [references/docs-update.md](references/docs-update.md) for the full code-to-docs mapping. diff --git a/README.md b/README.md index c18dc59ef..7bbf64923 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > > While this change won't affect most developers, if you have implemented a dependency signature validation step in your build process, you may notice a warning that past releases can't be verified. This is expected, and a result of the key rotation process. Updating to the latest version will resolve this for you. > -> We are improving our API specs which introduces minor breaking changes. +> We are improving our API specs which introduces minor breaking changes. Expect major releases more frequently than usual while this work continues. Each one ships with a migration guide, and breaking changes are kept small and mechanical. ![A Java client library for the Auth0 Authentication and Management APIs.](https://cdn.auth0.com/website/sdks/banners/auth0-java-banner.png) @@ -20,8 +20,8 @@ ## Documentation - [Reference](./reference.md) - code samples for Management APIs. - [Examples](./EXAMPLES.md) - code samples for common auth0-java scenarios. +- [v5 Migration Guide](./v5_MIGRATION_GUIDE.md) - guidance for updating your application from version 4 to version 5 of auth0-java. - [v4 Migration Guide](./v4_MIGRATION_GUIDE.md) - guidance for updating your application from version 3 to version 4 of auth0-java. -- [v3 Migration Guide](./v3_MIGRATION_GUIDE.md) - guidance for updating your application from version 2 to version 3 of auth0-java. - [Docs site](https://www.auth0.com/docs) - explore our docs site and learn more about Auth0. ## Getting Started @@ -40,14 +40,14 @@ Add the dependency via Maven: com.auth0 auth0 - 4.2.0 + 5.0.0 ``` or Gradle: ```gradle -implementation 'com.auth0:auth0:4.2.0' +implementation 'com.auth0:auth0:5.0.0' ``` ### Configure the SDK diff --git a/v3_MIGRATION_GUIDE.md b/v3_MIGRATION_GUIDE.md deleted file mode 100644 index 9d98602c3..000000000 --- a/v3_MIGRATION_GUIDE.md +++ /dev/null @@ -1,369 +0,0 @@ -# V3 Migration Guide - -A guide to migrating the Auth0 Java SDK from `v2` to `v3`. - -- [Overall changes](#overall-changes) - - [Java versions](#java-versions) - - [Authentication API](#authentication-api) - - [Management API](#management-api) -- [Specific changes to the Management API](#specific-changes-to-the-management-api) - - [Client initialization](#client-initialization) - - [Sub-client organization](#sub-client-organization) - - [Request and response patterns](#request-and-response-patterns) - - [Pagination](#pagination) - - [Exception handling](#exception-handling) - - [Accessing raw HTTP responses](#accessing-raw-http-responses) - - [Request-level configuration](#request-level-configuration) - - [Type changes](#type-changes) - -## Overall changes - -### Java versions - -Both v2 and v3 require Java 8 or above. - -### Authentication API - -This major version change does not affect the Authentication API. The `AuthAPI` class has been ported directly from v2 to v3. Any code written for the Authentication API in the v2 version should work in the v3 version. - -```java -// Works in both v2 and v3 -AuthAPI auth = AuthAPI.newBuilder("{YOUR_DOMAIN}", "{YOUR_CLIENT_ID}", "{YOUR_CLIENT_SECRET}").build(); -``` - -### Management API - -V3 introduces significant improvements to the Management API SDK by migrating to [Fern](https://github.com/fern-api/fern) as the code generation tool. This provides: - -- Better resource grouping with sub-client organization -- Type-safe request and response objects using builder patterns -- Automatic pagination with `SyncPagingIterable` -- Simplified access to HTTP response metadata via `withRawResponse()` -- Consistent method naming (`list`, `create`, `get`, `update`, `delete`) - -## Specific changes to the Management API - -### Client initialization - -The Management API client initialization has changed from `ManagementAPI` to `ManagementApi`, and uses a different builder pattern. - -**v2:** -```java -import com.auth0.client.mgmt.ManagementAPI; - -// Using domain and token -ManagementAPI mgmt = ManagementAPI.newBuilder("{YOUR_DOMAIN}", "{YOUR_API_TOKEN}").build(); - -// Using TokenProvider -TokenProvider tokenProvider = SimpleTokenProvider.create("{YOUR_API_TOKEN}"); -ManagementAPI mgmt = ManagementAPI.newBuilder("{YOUR_DOMAIN}", tokenProvider).build(); -``` - -**v3:** -1st Approach : Standard Token-Based -```java -import com.auth0.client.mgmt.ManagementApi; - -ManagementApi client = ManagementApi - .builder() - .url("https://{YOUR_DOMAIN}/api/v2") - .token("{YOUR_API_TOKEN}") - .build(); -``` - -or - -2nd Approach : OAuth client credentials flow - -```java -OAuthTokenSupplier tokenSupplier = new OAuthTokenSupplier( -"{CLIENT_ID}", -"{CLIENT_SECRET}", -"https://{YOUR_DOMAIN}", -"{YOUR_AUDIENCE}" -); - -ClientOptions clientOptions = ClientOptions.builder() -.environment(Environment.custom("https://{YOUR_AUDIENCE}")) -.addHeader("Authorization", () -> "Bearer " + tokenSupplier.get()) -.build(); - -ManagementApi client = new ManagementApi(clientOptions); - -``` - -#### Builder options comparison - -| Option | v2 | v3 | -|--------|----|----| -| Domain/URL | `newBuilder(domain, token)` | `.url("https://domain/api/v2")` | -| Token | Constructor parameter | `.token(token)` | -| Timeout | Via `HttpOptions` | `.timeout(seconds)` | -| Max retries | Via `HttpOptions` | `.maxRetries(count)` | -| Custom HTTP client | `.withHttpClient(Auth0HttpClient)` | `.httpClient(OkHttpClient)` | -| Custom headers | Not directly supported | `.addHeader(name, value)` | - -### Sub-client organization - -V3 introduces a hierarchical sub-client structure. Operations on related resources are now accessed through nested clients instead of methods on a flat entity class. - -**v2:** -```java -// All user operations on UsersEntity -Request userRequest = mgmt.users().get("user_id", new UserFilter()); -Request> permissionsRequest = mgmt.users().getPermissions("user_id", new PermissionsFilter()); -Request> rolesRequest = mgmt.users().getRoles("user_id", new RolesFilter()); -Request logsRequest = mgmt.users().getLogEvents("user_id", new LogEventFilter()); -``` - -**v3:** -```java -// Operations organized into sub-clients -GetUserResponseContent user = client.users().get("user_id"); -SyncPagingIterable permissions = client.users().permissions().list("user_id"); -SyncPagingIterable roles = client.users().roles().list("user_id"); -SyncPagingIterable logs = client.users().logs().list("user_id"); -``` - -#### Common sub-client mappings - -| v2 Method | v3 Sub-client | -|-----------|---------------| -| `mgmt.users().getPermissions()` | `client.users().permissions().list()` | -| `mgmt.users().getRoles()` | `client.users().roles().list()` | -| `mgmt.users().getLogEvents()` | `client.users().logs().list()` | -| `mgmt.users().getOrganizations()` | `client.users().organizations().list()` | -| `mgmt.users().link()` | `client.users().identities().link()` | -| `mgmt.users().unlink()` | `client.users().identities().delete()` | -| `mgmt.users().deleteMultifactorProvider()` | `client.users().multifactor().deleteProvider()` | -| `mgmt.organizations().getMembers()` | `client.organizations().members().list()` | -| `mgmt.organizations().getInvitations()` | `client.organizations().invitations().list()` | -| `mgmt.organizations().getEnabledConnections()` | `client.organizations().enabledConnections().list()` | -| `mgmt.actions().getVersions()` | `client.actions().versions().list()` | -| `mgmt.actions().getTriggerBindings()` | `client.actions().triggers().bindings().list()` | -| `mgmt.guardian().getFactors()` | `client.guardian().factors().list()` | -| `mgmt.branding().getUniversalLoginTemplate()` | `client.branding().templates().getUniversalLogin()` | -| `mgmt.connections().getScimConfiguration()` | `client.connections().scimConfiguration().get()` | - -### Request and response patterns - -V3 uses type-safe request content objects with builders instead of domain objects or filter parameters. - -**v2:** -```java -import com.auth0.json.mgmt.users.User; -import com.auth0.net.Request; - -// Creating a user -User user = new User("Username-Password-Authentication"); -user.setEmail("test@example.com"); -user.setPassword("password123".toCharArray()); - -Request request = mgmt.users().create(user); -User createdUser = request.execute().getBody(); -``` - -**v3:** -```java -import com.auth0.client.mgmt.types.CreateUserRequestContent; -import com.auth0.client.mgmt.types.CreateUserResponseContent; - -// Creating a user -CreateUserResponseContent user = client.users().create( - CreateUserRequestContent - .builder() - .connection("Username-Password-Authentication") - .email("test@example.com") - .password("password123") - .build() -); -``` - -#### Key differences - -| Aspect | v2 | v3 | -|--------|----|----| -| Request building | Domain objects with setters | Builder pattern with `*RequestContent` types | -| Response type | `Request` requiring `.execute().getBody()` | Direct return of response object | -| Filtering | Filter classes (e.g., `UserFilter`) | `*RequestParameters` builder classes | -| Execution | Explicit `.execute()` call | Implicit execution on method call | - -### Pagination - -V3 introduces `SyncPagingIterable` for automatic pagination, replacing the manual `Request` pattern. - -**v2:** -```java -import com.auth0.json.mgmt.users.UsersPage; -import com.auth0.client.mgmt.filter.UserFilter; - -Request request = mgmt.users().list(new UserFilter().withPage(0, 50)); -UsersPage page = request.execute().getBody(); - -for (User user : page.getItems()) { - System.out.println(user.getEmail()); -} - -// Manual pagination -while (page.getNext() != null) { - request = mgmt.users().list(new UserFilter().withPage(page.getNext(), 50)); - page = request.execute().getBody(); - for (User user : page.getItems()) { - System.out.println(user.getEmail()); - } -} -``` - -**v3:** -```java -import com.auth0.client.mgmt.core.SyncPagingIterable; -import com.auth0.client.mgmt.types.UserResponseSchema; -import com.auth0.client.mgmt.types.ListUsersRequestParameters; - -// Automatic iteration through all pages -SyncPagingIterable users = client.users().list( - ListUsersRequestParameters - .builder() - .perPage(50) - .build() -); - -for (UserResponseSchema user : users) { - System.out.println(user.getEmail()); -} - -// Or manual page control -List pageItems = users.getItems(); -while (users.hasNext()) { - pageItems = users.nextPage().getItems(); - // process page -} -``` - -### Exception handling - -V3 uses a unified `ManagementApiException` class instead of the v2 exception hierarchy. - -**v2:** -```java -import com.auth0.exception.Auth0Exception; -import com.auth0.exception.APIException; -import com.auth0.exception.RateLimitException; - -try { - User user = mgmt.users().get("user_id", null).execute().getBody(); -} catch (RateLimitException e) { - // Rate limited - long retryAfter = e.getLimit(); -} catch (APIException e) { - int statusCode = e.getStatusCode(); - String error = e.getError(); - String description = e.getDescription(); -} catch (Auth0Exception e) { - // Network or other errors -} -``` - -**v3:** -```java -import com.auth0.client.mgmt.core.ManagementApiException; - -try { - GetUserResponseContent user = client.users().get("user_id"); -} catch (ManagementApiException e) { - int statusCode = e.statusCode(); - Object body = e.body(); - Map> headers = e.headers(); - String message = e.getMessage(); -} -``` - -### Accessing raw HTTP responses - -V3 provides access to full HTTP response metadata via `withRawResponse()`. - -**v2:** -```java -// Response wrapper provided status code -Response response = mgmt.users().get("user_id", null).execute(); -int statusCode = response.getStatusCode(); -User user = response.getBody(); -``` - -**v3:** -```java -import com.auth0.client.mgmt.core.ManagementApiHttpResponse; - -// Use withRawResponse() to access headers and metadata -ManagementApiHttpResponse response = client.users() - .withRawResponse() - .get("user_id"); - -GetUserResponseContent user = response.body(); -Map> headers = response.headers(); -``` - -### Request-level configuration - -V3 allows per-request configuration through `RequestOptions`. - -**v2:** -```java -// Most configuration was at client level only -// Request-level headers required creating a new request manually -Request request = mgmt.users().get("user_id", null); -request.addHeader("X-Custom-Header", "value"); -User user = request.execute().getBody(); -``` - -**v3:** -```java -import com.auth0.client.mgmt.core.RequestOptions; - -GetUserResponseContent user = client.users().get( - "user_id", - GetUserRequestParameters.builder().build(), - RequestOptions.builder() - .timeout(10) - .maxRetries(1) - .addHeader("X-Custom-Header", "value") - .build() -); -``` - -### Type changes - -V3 uses generated type classes located in `com.auth0.client.mgmt.types` instead of the hand-written POJOs in `com.auth0.json.mgmt`. - -**v2:** -```java -import com.auth0.json.mgmt.users.User; -import com.auth0.json.mgmt.roles.Role; -import com.auth0.json.mgmt.organizations.Organization; -``` - -**v3:** -```java -import com.auth0.client.mgmt.types.UserResponseSchema; -import com.auth0.client.mgmt.types.CreateUserRequestContent; -import com.auth0.client.mgmt.types.CreateUserResponseContent; -import com.auth0.client.mgmt.types.Role; -import com.auth0.client.mgmt.types.Organization; -``` - -Type naming conventions in v3: -- Request body types: `*RequestContent` (e.g., `CreateUserRequestContent`) -- Response types: `*ResponseContent` or `*ResponseSchema` (e.g., `GetUserResponseContent`, `UserResponseSchema`) -- Query parameters: `*RequestParameters` (e.g., `ListUsersRequestParameters`) - -All types use immutable builders: - -```java -// v3 type construction -CreateUserRequestContent request = CreateUserRequestContent - .builder() - .connection("Username-Password-Authentication") - .email("test@example.com") - .password("secure-password") - .build(); -``` diff --git a/v5_MIGRATION_GUIDE.md b/v5_MIGRATION_GUIDE.md new file mode 100644 index 000000000..571f432a2 --- /dev/null +++ b/v5_MIGRATION_GUIDE.md @@ -0,0 +1,113 @@ +# Migrating from v4 to v5 + +`v5` is a compatible evolution of `v4`. The Authentication API is unchanged, and the Management API keeps the same client structure, builder patterns, and pagination. There is a single breaking change: response fields that the API declares as nullable now use `OptionalNullable` instead of `Optional`, so the SDK reflects the actual API contract. + +- [Overview](#overview) +- [Breaking changes](#breaking-changes) + - [1. Nullable response fields use `OptionalNullable`](#1-nullable-response-fields-use-optionalnullablet) +- [Migration steps](#migration-steps) + +For everything else added in `v5` — the Keys Network ACLs client, third-party client access, and Cross-App Access connection profiles — see the [changelog](CHANGELOG.md). Those changes are additive and require no action. + +## Overview + +Most `v4` code compiles and runs unchanged on `v5`. You only need to act if your code reads one of the affected date/nullable fields: + +| Area | What changed | Impact | +|------|--------------|--------| +| Session response types | Timestamp getters return `OptionalNullable` instead of `Optional` | Callers reading `created_at`, `updated_at`, `authenticated_at`, `idle_expires_at`, `expires_at`, `last_interacted_at` | +| Refresh token response types | Timestamp getters return `OptionalNullable` instead of `Optional` | Callers reading `created_at`, `idle_expires_at`, `expires_at`, `last_exchanged_at` | +| `SessionAuthenticationSignal` | `getTimestamp()` returns `OptionalNullable` | Callers reading `timestamp` | +| `FlowActionFlowMapValueParams` | `getFallback()` returns `OptionalNullable` | Callers reading `fallback` | + +Everything else in `v5` is additive or internal. Builder setters keep their `Optional` and raw-value overloads, so code that *writes* these types is unaffected — only read paths change. + +## Breaking changes + +### 1. Nullable response fields use `OptionalNullable` + +These fields are declared nullable in the Auth0 API definition. Earlier SDK versions dropped that nullability and generated plain `Optional`; `v5` preserves it, which allows the SDK to represent a field that the API returned as an explicit `null` distinctly from one it omitted entirely. + +The following 32 getters are affected: + +| Type | Getters | +|------|---------| +| `GetSessionResponseContent`, `SessionResponseContent`, `UpdateSessionResponseContent` | `getCreatedAt()`, `getUpdatedAt()`, `getAuthenticatedAt()`, `getIdleExpiresAt()`, `getExpiresAt()`, `getLastInteractedAt()` | +| `GetRefreshTokenResponseContent`, `RefreshTokenResponseContent`, `UpdateRefreshTokenResponseContent` | `getCreatedAt()`, `getIdleExpiresAt()`, `getExpiresAt()`, `getLastExchangedAt()` | +| `SessionAuthenticationSignal` | `getTimestamp()` | +| `FlowActionFlowMapValueParams` | `getFallback()` | + +There are three separate things to check. Only the first is caught by the compiler. + +#### a. Signature change + +`OptionalNullable` is not a drop-in replacement for `Optional`. It provides `isPresent()`, `isAbsent()`, `isNull()`, `wasSpecified()`, `get()`, `getValueOrNull()`, `orElse()`, `map()`, and `toOptional()` — but **not** `ifPresent()`, `orElseThrow()`, `filter()`, or `stream()`. + +The simplest migration is `.toOptional()`, which returns an empty `Optional` for both the absent and explicit-null cases: + +```java +// v4 +Optional created = session.getCreatedAt(); +session.getCreatedAt().ifPresent(this::audit); + +// v5 +Optional created = session.getCreatedAt().toOptional(); +session.getCreatedAt().toOptional().ifPresent(this::audit); +``` + +If you want to distinguish the two states, use the richer API directly: + +```java +// v5 +OptionalNullable expires = session.getExpiresAt(); +if (expires.isPresent()) { + handle(expires.get()); +} else if (expires.isNull()) { + // the API explicitly returned null for this field +} else { + // the API omitted the field +} +``` + +#### b. `get()` throws a different exception + +`Optional.get()` throws `NoSuchElementException`. `OptionalNullable.get()` throws `IllegalStateException`, and does so for both the absent and explicit-null states. + +```java +// v4 — catches successfully +try { + SessionDate created = session.getCreatedAt().get(); +} catch (NoSuchElementException e) { ... } + +// v5 — the catch no longer matches; the exception propagates +try { + SessionDate created = session.getCreatedAt().get(); +} catch (IllegalStateException e) { ... } +``` + +This compiles unchanged in `v5`, so the compiler will not flag it. Search for `NoSuchElementException` around these getters. + +#### c. `orElse()` can return `null` + +`Optional.orElse(fallback)` never returns `null`. `OptionalNullable.orElse(fallback)` returns `null` when the field was explicitly null, and returns `fallback` only when the field was absent: + +```java +SessionDate created = session.getCreatedAt().orElse(DEFAULT); +// v5: returns DEFAULT if absent, but null if the API returned an explicit null +``` + +If you rely on a non-null result, use `.toOptional().orElse(fallback)` instead, which treats both states as empty: + +```java +SessionDate created = session.getCreatedAt().toOptional().orElse(DEFAULT); +``` + +Like (b), this compiles unchanged, so it is worth an explicit grep. + +## Migration steps + +1. Update the dependency to the `v5` release. +2. Compile. Any getter listed above that you assign to an `Optional`, or call `ifPresent()` / `orElseThrow()` / `filter()` / `stream()` on, will fail — append `.toOptional()`. +3. Grep for `NoSuchElementException` near those getters and change it to `IllegalStateException`, or switch to `.toOptional().orElseThrow(...)`. +4. Grep for `.orElse(` on those getters. If a non-null result is required, use `.toOptional().orElse(...)`. +5. Run your build and test suite. From 76c7090cdbee04bb58a9664441f7102102a75754 Mon Sep 17 00:00:00 2001 From: tanya732 Date: Wed, 19 Aug 2026 17:57:16 +0530 Subject: [PATCH 2/2] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7bbf64923..db7600750 100644 --- a/README.md +++ b/README.md @@ -40,14 +40,14 @@ Add the dependency via Maven: com.auth0 auth0 - 5.0.0 + 4.2.0 ``` or Gradle: ```gradle -implementation 'com.auth0:auth0:5.0.0' +implementation 'com.auth0:auth0:4.2.0' ``` ### Configure the SDK