Release 4.0.0 — v4 major version#1011
Conversation
Adds a "Skill for Coding Agents" section at the top of V4_MIGRATION_GUIDE.md and a one-liner in README.md linking to the migration guide and skill.
The skill reference belongs in the migration guide, not the main README.
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: utkrishtS <utkrisht.sahu@okta.com> Co-authored-by: sanchitmehta94 <sanchit.mehta@okta.com> Co-authored-by: Utkrisht Sahu <utkrishtsahu@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Subhankar Maiti <subhankar.maiti@okta.com> Co-authored-by: Subhankar Maiti <35273200+subhankarmaiti@users.noreply.github.com> Co-authored-by: José Carlos Chávez <jcchavezs@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: utkrishtS <utkrisht.sahu@okta.com> Co-authored-by: sanchitmehta94 <sanchit.mehta@okta.com> Co-authored-by: Utkrisht Sahu <utkrishtsahu@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Subhankar Maiti <subhankar.maiti@okta.com> Co-authored-by: Subhankar Maiti <35273200+subhankarmaiti@users.noreply.github.com> Co-authored-by: José Carlos Chávez <jcchavezs@gmail.com>
📝 WalkthroughWalkthroughThis PR ships Auth0.Android v4: removal of deprecated Passkeys, Management API, and MFA surfaces; a credentials manager/storage overhaul (clearAll, default min TTL, DPoP cleanup); a new Auth Tab/Ephemeral Browsing feature; SSO credentials moving to Date-based expiry; a DefaultClient.Builder refactor; updated CI/build toolchain (Java 17, AGP, Gradle); extensive Mockito test modernization; and documentation/sample app updates. ChangesCI/CD and Build Infrastructure
v4 Breaking Changes and Credentials/Storage Overhaul
Auth Tab and Ephemeral Browsing Feature
Documentation Updates
Sample App Updates
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant AuthTabLauncher
participant AuthenticationActivity
participant AuthTabResultHandler
Browser->>AuthTabLauncher: Auth Tab result (resultCode, uri)
AuthTabLauncher->>AuthTabResultHandler: handle(resultCode, resultUri)
AuthTabResultHandler->>AuthenticationActivity: onSuccess/onFailure/onCancel
AuthenticationActivity->>AuthenticationActivity: deliver Intent result, finish()
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Tools execution failed with the following error: Failed to run tools: Stream initialization permanently failed: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| if (willAccessTokenExpire) { | ||
| val tokenLifetime = | ||
| (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 | ||
| val tokenLifetime = (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 |
| if (willAccessTokenExpire) { | ||
| val tokenLifetime = | ||
| (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 | ||
| val tokenLifetime = (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 |
| if (willAccessTokenExpire) { | ||
| val tokenLifetime = | ||
| (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 | ||
| val tokenLifetime = (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 |
| if (willAccessTokenExpire) { | ||
| val tokenLifetime = | ||
| (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 | ||
| val tokenLifetime = (expiresAt - currentTimeInMillis - minTtl * 1000) / -1000 |
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt (2)
123-127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssert the legacy JSON actually omits
dPoPEnabled.Line 125 can become a no-op if serialization formatting/order changes, letting the test pass without exercising legacy deserialization.
Proposed fix
val json = state.serializeToJson() // Remove the dPoPEnabled field to simulate legacy JSON val legacyJson = json.replace(",\"dPoPEnabled\":false", "") + Assert.assertFalse(legacyJson.contains("\"dPoPEnabled\"")) val deserializedState = OAuthManagerState.deserializeState(legacyJson)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt` around lines 123 - 127, The legacy JSON setup in OAuthManagerStateTest should explicitly verify that dPoPEnabled is removed, since the current string replacement can silently do nothing if serialization output changes. Update the test around serializeToJson and deserializeState to assert the legacyJson no longer contains dPoPEnabled before deserializing, so the legacy-path behavior is actually exercised.
104-131: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAdd a DPoP restore-path test.
OAuthManagerStateTestcovers serialization/defaulting, andWebAuthProviderTestalready covers a normal DPoP resume flow, but nothing exercisesOAuthManager.fromState(..., context)after process restore. Add aWebAuthProviderTestthat restores anOAuthManagerStatewithdPoPEnabled = trueand checks the resumed exchange still uses DPoP.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt` around lines 104 - 131, The current tests cover JSON defaulting and a normal DPoP flow, but not the restore path through OAuthManager.fromState(..., context). Add a new test in WebAuthProviderTest that builds or deserializes an OAuthManagerState with dPoPEnabled set to true, restores it via OAuthManager.fromState, and verifies the resumed authorization/exchange path still runs with DPoP enabled. Use the existing OAuthManagerState and WebAuthProviderTest fixtures to locate the restore flow and assert the DPoP-specific behavior after process restore.Source: Path instructions
auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt (1)
863-893: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUse the effective Custom Tabs options for login compatibility checks.
Line 864 checks
ctOptionsbefore Lines 888-890 apply ephemeral/Auth Tab options, unlike logout which checkseffectiveCtOptions. This can reject or allow the wrong browser path for the new login options.Proposed fix
- if (!ctOptions.hasCompatibleBrowser(context.packageManager)) { + var effectiveCtOptions = ctOptions + if (ephemeralBrowsing) effectiveCtOptions = effectiveCtOptions.copyWithEphemeralBrowsing() + if (authTab) effectiveCtOptions = effectiveCtOptions.copyWithAuthTab() + + if (!effectiveCtOptions.hasCompatibleBrowser(context.packageManager)) { val ex = AuthenticationException( "a0.browser_not_available", "No compatible Browser application is installed." ) callback.onFailure(ex) return } @@ - var effectiveCtOptions = ctOptions - if (ephemeralBrowsing) effectiveCtOptions = effectiveCtOptions.copyWithEphemeralBrowsing() - if (authTab) effectiveCtOptions = effectiveCtOptions.copyWithAuthTab() - val manager = OAuthManager(As per path instructions,
auth0/src/main/java/com/auth0/android/provider/**/*.kt: Implement new Web Auth builder options inWebAuthProvider.kt, and updateOAuthManager.ktif flow internals change.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt` around lines 863 - 893, The login path in WebAuthProvider is checking browser compatibility against ctOptions before applying ephemeral browsing and Auth Tab tweaks, which can make the compatibility decision use the wrong configuration. Move the compatibility check to use the final effectiveCtOptions after the copyWithEphemeralBrowsing and copyWithAuthTab adjustments, matching the logout flow and keeping the browser selection aligned with the actual login options. Use the existing resetManagerInstance, hasCompatibleBrowser, and effectiveCtOptions logic in WebAuthProvider to locate the change.Source: Path instructions
EXAMPLES.md (1)
468-477: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUpdate the DPoP and recovery examples
EXAMPLES.md:468-477, 505-537
The DPoP block still usesWebAuthProvider.useDPoP(), and the configuration-change sample referencesloginCallback/logoutCallbackwithout declaring them. Both examples should be rewritten so copied code matches the current API and compiles as written.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@EXAMPLES.md` around lines 468 - 477, The DPoP and recovery examples are out of sync with the current API and won’t compile as pasted. Update the example blocks around WebAuthProvider and the recovery sample so they use the current DPoP setup on AuthenticationAPIClient and remove the obsolete WebAuthProvider.useDPoP() usage, then declare or inline the loginCallback and logoutCallback values before they are referenced in the recovery example. Make sure the examples under the relevant EXAMPLES.md sections are self-contained and copy-paste ready.sample/src/main/java/com/auth0/sample/DatabaseLoginFragment.kt (1)
104-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRecovery callback diverges from
webAuth()callback behavior.The
callbackregistered viaWebAuthProvider.addCallback()inonStart()only saves credentials tocredentialsManager, while the inline callback inwebAuth()(line 248) saves to bothcredentialsManagerandsecureCredentialsManager. After process-death or config-change recovery, credentials would be missing fromsecureCredentialsManager, causinggetCredsSecure()to fail. The recovery callback also doesn't handleerror.isCanceled(thewebAuth()callback shows "Browser was closed" for cancellations).🔧 Proposed fix to align recovery callback with webAuth() behavior
private val callback = object : Callback<Credentials, AuthenticationException> { override fun onSuccess(result: Credentials) { credentialsManager.saveCredentials(result) + secureCredentialsManager.saveCredentials(result) Snackbar.make( requireView(), "Hello ${result.user.name}", Snackbar.LENGTH_LONG ).show() } override fun onFailure(error: AuthenticationException) { - Snackbar.make(requireView(), error.getDescription(), Snackbar.LENGTH_LONG) - .show() + val message = + if (error.isCanceled) + "Browser was closed" + else + error.getDescription() + Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG) + .show() } }Also applies to: 190-197
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@sample/src/main/java/com/auth0/sample/DatabaseLoginFragment.kt` around lines 104 - 118, The recovery callback in DatabaseLoginFragment should match the behavior of the webAuth() callback: in the callback object used by WebAuthProvider.addCallback(), save successful Credentials to both credentialsManager and secureCredentialsManager, and mirror the same cancellation handling by checking AuthenticationException.isCanceled in onFailure so the UI shows the browser-closed message instead of a generic error. Update the callback’s onSuccess and onFailure logic to keep recovery and normal login flows consistent.
🧹 Nitpick comments (2)
.github/actions/setup/action.yml (1)
14-25: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRemove
cache: 'gradle'fromsetup-javato avoid duplicate caching withsetup-gradle.Same issue as in
maven-publish/action.yml: thegradle/actions/setup-gradleaction handles Gradle caching by default. Thecache: 'gradle'onsetup-javais redundant and can cause cache conflicts. Thesetup-gradleaction'scache-cleanup: on-successfurther confirms it manages the cache lifecycle.♻️ Proposed fix
- name: Set up Java uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # pin@v4 with: distribution: 'temurin' java-version: ${{ inputs.java }} - cache: 'gradle'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/setup/action.yml around lines 14 - 25, Remove the redundant Gradle cache configuration from the Set up Java step in the setup/action workflow. The issue is that setup-java is currently enabling cache: 'gradle' while setup-gradle already manages Gradle caching and cleanup, so update the setup-java invocation to only handle Java setup and leave caching to gradle/actions/setup-gradle..github/actions/maven-publish/action.yml (1)
24-31: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winDrop
setup-javaGradle caching heregradle/actions/setup-gradlealready handles Gradle caching, socache: 'gradle'onactions/setup-javais redundant and can create duplicate cache entries. Remove it here; the same pattern also appears in.github/actions/setup/action.yml.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/maven-publish/action.yml around lines 24 - 31, The Gradle cache configuration in the actions setup is duplicated because `gradle/actions/setup-gradle` already manages caching. Remove the `cache: 'gradle'` setting from the `actions/setup-java` step in `maven-publish/action.yml`, and apply the same cleanup in the similar `setup-java` configuration inside `setup/action.yml` so both workflows rely on `setup-gradle` for caching only.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/codeql.yml:
- Around line 44-45: The Checkout step in the CodeQL workflow is leaving Git
credentials persisted by default, which can expose the GITHUB_TOKEN
unnecessarily. Update the actions/checkout usage in the workflow to explicitly
disable credential persistence, since this job does not need Git auth after
checkout. Use the Checkout step in the codeql workflow as the target and set
persist-credentials to false there.
- Around line 48-57: Pin the CodeQL workflow steps using the same commit SHA
style already used elsewhere in this workflow instead of the moving
github/codeql-action/init@v4, autobuild@v4, and analyze@v4 tags. Update the
action references in the CodeQL job so the init, Autobuild, and Perform CodeQL
Analysis steps all point to specific commit SHAs for reproducibility and
auditability.
In @.github/workflows/release.yml:
- Line 38: The RL Scanner action reference is using a moving branch target,
which should be pinned to a fixed version for reproducibility and supply-chain
safety. Update the action reference in the release workflow to use a stable
pinned commit or release tag, matching the approach used by other pinned actions
like actions/checkout, so the workflow always runs the same vetted code.
In `@AGENTS.md`:
- Around line 17-35: The repository tree in AGENTS.md is wrapped in a bare
fenced block, which triggers markdownlint; update that directory listing to use
a language-tagged fence such as text (or convert it to a list) so the
documentation stays lint-clean. Make the change in the tree section near the
Auth0.Android layout block and keep the rest of the structure unchanged.
In `@auth0/src/main/java/com/auth0/android/authentication/storage/Storage.kt`:
- Around line 78-82: Storage.removeAll() is currently a no-op default, so logout
flows that rely on it can silently fail to clear data. Update the Storage
contract by making removeAll() abstract or changing the default implementation
to throw, and ensure clearCredentials() and clearAll() continue to call it so
every custom Storage implementation must explicitly handle full clearing.
In `@auth0/src/main/java/com/auth0/android/provider/CustomTabsController.java`:
- Around line 160-213: `launchAsAuthTab()` is calling
`authTabIntent.launch(authTabLauncher, uri, scheme)` from a background path, but
`ActivityResultLauncher.launch()` must run on the main thread. Update
`CustomTabsController.launchAsAuthTab()` to dispatch the final
`authTabIntent.launch(...)` call through `threadSwitcher.mainThread(...)` while
keeping the existing session/setup logic intact, so the launch happens on the UI
thread and avoids a runtime crash.
In
`@auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt`:
- Around line 4435-4474: The test is failing during the initial credentials save
instead of the renewed API credentials persistence path. In
SecureCredentialsManagerTest.shouldFailWithStoreFailedWhenSavingRenewedApiCredentialsThrows,
adjust the crypto.encrypt stubbing so the first encryption used by
saveCredentials(...) succeeds and only the later encryption used by
saveApiCredentials(...) throws CryptoException. Keep the verification on
continueGetApiCredentials and the failure message assertions so the test
exercises the intended renewal-persist flow.
- Around line 2378-2384: The clearAll() test in SecureCredentialsManagerTest
should also cover the new DPoP cleanup behavior. Update
shouldClearAllCredentialsKeyPairsAndBiometricSession to verify
mockDPoPKeyStore.deleteKeyPair() is called alongside storage.removeAll(),
crypto.deleteAllKeys(), and biometric invalidation. Add a separate failure-path
test around SecureCredentialsManager.clearAll() where deleteKeyPair() throws,
and assert storage/crypto cleanup still completes and the biometric session
state is handled consistently; use the existing manager, mockDPoPKeyStore,
crypto, and storage setup in this test class.
- Around line 2125-2356: The concurrency tests in SecureCredentialsManagerTest
create Executors that are never shut down, which can leave non-daemon threads
running and make the suite hang or flake. Update each affected test method that
uses sharedExecutor, singleThreadExecutor, or callerPool to wrap the test body
in try/finally and call shutdownNow() on every executor in the finally block.
Keep the existing test logic intact and make sure this cleanup is applied
consistently across the new getCredentials concurrency cases.
In `@auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java`:
- Around line 714-721: The test should verify that copyWithAuthTab() preserves
ephemeralBrowsing, not just auth-tab state. Update
shouldCopyWithAuthTabPreservesEphemeralBrowsing in CustomTabsOptionsTest to also
assert the copied CustomTabsOptions still reports ephemeral browsing, either by
checking an isEphemeralBrowsing() accessor on CustomTabsOptions or by validating
equivalent behavior through toIntent(). This will ensure the test covers the
intended regression in copyWithAuthTab()/toBuilder().
In
`@auth0/src/test/java/com/auth0/android/request/internal/SSOCredentialsDeserializerTest.kt`:
- Around line 24-89: The SSOCredentialsDeserializerTest currently covers only
happy paths; add error-path tests for the deserializer’s explicit failure
handling. Extend the existing SSOCredentialsDeserializerTest class to verify
that gson.getAdapter(SSOCredentials::class.java).fromJson(...) throws or returns
the expected failure for invalid inputs such as non-object JSON, null, empty
payloads, and missing expires_in. Keep the new tests aligned with the existing
generateSSOCredentialsJSON helpers and use
SSOCredentialsDeserializer/SSOCredentials as the primary symbols to locate the
code.
In `@EXAMPLES.md`:
- Around line 505-537: The sample reuses loginCallback and logoutCallback in
onLoginClick/onLogoutClick even though they are only created as inline arguments
inside WebAuthProvider.registerCallbacks, so the later start() calls cannot see
them. Hoist those Callback<Credentials, AuthenticationException> and
Callback<Void?, AuthenticationException> instances into named variables in the
surrounding scope (or inline equivalent callbacks directly in start()), then
pass the same values to both registerCallbacks() and the start() calls.
In `@README.md`:
- Around line 119-124: The Android sample in the README still uses a lower
minSdkVersion than this release supports; update the sample configuration shown
in the Android block so it uses 26 instead of 24. Make the change in the
Universal Login example snippet only, keeping the rest of the Gradle settings
the same, so the documented sample matches the library floor.
- Around line 25-31: The Getting Started requirements text is using the wrong
Android floor and conflicts with the rest of the PR. Update the requirements
block in README so it matches the SDK’s actual baseline (minSdk 26) instead of
saying Android API 31+ and warning off targetSdkVersion 30. Keep the quickstart
aligned with the documented baseline and adjust the version guidance
accordingly.
In `@V4_MIGRATION_GUIDE.md`:
- Around line 33-39: The table of contents has two entries named New APIs that
currently generate the same anchor, so the later configuration-recovery section
should be renamed to a distinct heading. Update the section title associated
with Handling Configuration Changes During Authentication so its slug is unique,
and make sure the corresponding TOC link in V4_MIGRATION_GUIDE matches that new
heading; use the New APIs and Handling Configuration Changes During
Authentication section headings to locate the change.
- Line 31: The TOC entry for clearCredentials uses a camel-cased fragment that
won’t match GitHub’s lowercase slug, so update the link target to the lowercase
anchor. Fix the markdown table of contents entry that references
clearCredentials and ensure it points to the slug generated from the matching
heading in V4_MIGRATION_GUIDE.md.
---
Outside diff comments:
In `@auth0/src/main/java/com/auth0/android/provider/WebAuthProvider.kt`:
- Around line 863-893: The login path in WebAuthProvider is checking browser
compatibility against ctOptions before applying ephemeral browsing and Auth Tab
tweaks, which can make the compatibility decision use the wrong configuration.
Move the compatibility check to use the final effectiveCtOptions after the
copyWithEphemeralBrowsing and copyWithAuthTab adjustments, matching the logout
flow and keeping the browser selection aligned with the actual login options.
Use the existing resetManagerInstance, hasCompatibleBrowser, and
effectiveCtOptions logic in WebAuthProvider to locate the change.
In `@auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt`:
- Around line 123-127: The legacy JSON setup in OAuthManagerStateTest should
explicitly verify that dPoPEnabled is removed, since the current string
replacement can silently do nothing if serialization output changes. Update the
test around serializeToJson and deserializeState to assert the legacyJson no
longer contains dPoPEnabled before deserializing, so the legacy-path behavior is
actually exercised.
- Around line 104-131: The current tests cover JSON defaulting and a normal DPoP
flow, but not the restore path through OAuthManager.fromState(..., context). Add
a new test in WebAuthProviderTest that builds or deserializes an
OAuthManagerState with dPoPEnabled set to true, restores it via
OAuthManager.fromState, and verifies the resumed authorization/exchange path
still runs with DPoP enabled. Use the existing OAuthManagerState and
WebAuthProviderTest fixtures to locate the restore flow and assert the
DPoP-specific behavior after process restore.
In `@EXAMPLES.md`:
- Around line 468-477: The DPoP and recovery examples are out of sync with the
current API and won’t compile as pasted. Update the example blocks around
WebAuthProvider and the recovery sample so they use the current DPoP setup on
AuthenticationAPIClient and remove the obsolete WebAuthProvider.useDPoP() usage,
then declare or inline the loginCallback and logoutCallback values before they
are referenced in the recovery example. Make sure the examples under the
relevant EXAMPLES.md sections are self-contained and copy-paste ready.
In `@sample/src/main/java/com/auth0/sample/DatabaseLoginFragment.kt`:
- Around line 104-118: The recovery callback in DatabaseLoginFragment should
match the behavior of the webAuth() callback: in the callback object used by
WebAuthProvider.addCallback(), save successful Credentials to both
credentialsManager and secureCredentialsManager, and mirror the same
cancellation handling by checking AuthenticationException.isCanceled in
onFailure so the UI shows the browser-closed message instead of a generic error.
Update the callback’s onSuccess and onFailure logic to keep recovery and normal
login flows consistent.
---
Nitpick comments:
In @.github/actions/maven-publish/action.yml:
- Around line 24-31: The Gradle cache configuration in the actions setup is
duplicated because `gradle/actions/setup-gradle` already manages caching. Remove
the `cache: 'gradle'` setting from the `actions/setup-java` step in
`maven-publish/action.yml`, and apply the same cleanup in the similar
`setup-java` configuration inside `setup/action.yml` so both workflows rely on
`setup-gradle` for caching only.
In @.github/actions/setup/action.yml:
- Around line 14-25: Remove the redundant Gradle cache configuration from the
Set up Java step in the setup/action workflow. The issue is that setup-java is
currently enabling cache: 'gradle' while setup-gradle already manages Gradle
caching and cleanup, so update the setup-java invocation to only handle Java
setup and leave caching to gradle/actions/setup-gradle.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 47c5f386-067b-427f-827d-a942174684b7
📒 Files selected for processing (104)
.circleci/config.yml.github/actions/maven-publish/action.yml.github/actions/setup/action.yml.github/workflows/codeql.yml.github/workflows/java-release.yml.github/workflows/release.yml.github/workflows/rl-scanner.yml.github/workflows/test.yml.gitignore.versionAGENTS.mdCHANGELOG.mdEXAMPLES.mdREADME.mdV4_MIGRATION_GUIDE.mdauth0/build.gradleauth0/src/main/AndroidManifest.xmlauth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.ktauth0/src/main/java/com/auth0/android/authentication/storage/BaseCredentialsManager.ktauth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.ktauth0/src/main/java/com/auth0/android/authentication/storage/CryptoUtil.javaauth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.ktauth0/src/main/java/com/auth0/android/authentication/storage/SharedPreferencesStorage.ktauth0/src/main/java/com/auth0/android/authentication/storage/Storage.ktauth0/src/main/java/com/auth0/android/callback/ManagementCallback.ktauth0/src/main/java/com/auth0/android/dpop/DPoPException.ktauth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.ktauth0/src/main/java/com/auth0/android/management/ManagementException.ktauth0/src/main/java/com/auth0/android/management/UsersAPIClient.ktauth0/src/main/java/com/auth0/android/myaccount/AuthenticationMethodType.ktauth0/src/main/java/com/auth0/android/myaccount/MyAccountAPIClient.ktauth0/src/main/java/com/auth0/android/provider/AuthTabResultHandler.ktauth0/src/main/java/com/auth0/android/provider/AuthenticationActivity.ktauth0/src/main/java/com/auth0/android/provider/BrowserPicker.javaauth0/src/main/java/com/auth0/android/provider/CustomTabsController.javaauth0/src/main/java/com/auth0/android/provider/CustomTabsOptions.javaauth0/src/main/java/com/auth0/android/provider/OAuthManager.ktauth0/src/main/java/com/auth0/android/provider/OAuthManagerState.ktauth0/src/main/java/com/auth0/android/provider/PasskeyAuthProvider.ktauth0/src/main/java/com/auth0/android/provider/PasskeyManager.ktauth0/src/main/java/com/auth0/android/provider/SignatureVerifier.javaauth0/src/main/java/com/auth0/android/provider/WebAuthProvider.ktauth0/src/main/java/com/auth0/android/request/DefaultClient.ktauth0/src/main/java/com/auth0/android/request/UserData.ktauth0/src/main/java/com/auth0/android/request/internal/GsonProvider.ktauth0/src/main/java/com/auth0/android/request/internal/SSOCredentialsDeserializer.ktauth0/src/main/java/com/auth0/android/request/internal/UserProfileDeserializer.javaauth0/src/main/java/com/auth0/android/result/Challenge.ktauth0/src/main/java/com/auth0/android/result/SSOCredentials.ktauth0/src/main/java/com/auth0/android/result/UserProfile.ktauth0/src/test/java/com/auth0/android/Auth0Test.javaauth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.ktauth0/src/test/java/com/auth0/android/authentication/MfaApiClientTest.ktauth0/src/test/java/com/auth0/android/authentication/PasswordlessClientTest.ktauth0/src/test/java/com/auth0/android/authentication/request/ProfileRequestTest.javaauth0/src/test/java/com/auth0/android/authentication/request/SignUpRequestTest.javaauth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.ktauth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.javaauth0/src/test/java/com/auth0/android/authentication/storage/LocalAuthenticationManagerTest.ktauth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerBiometricPolicyTest.ktauth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.ktauth0/src/test/java/com/auth0/android/authentication/storage/SharedPreferencesStorageTest.javaauth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.ktauth0/src/test/java/com/auth0/android/dpop/DPoPTest.ktauth0/src/test/java/com/auth0/android/dpop/DPoPUtilTest.ktauth0/src/test/java/com/auth0/android/management/ManagementExceptionTest.ktauth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.ktauth0/src/test/java/com/auth0/android/myaccount/MyAccountAPIClientTest.ktauth0/src/test/java/com/auth0/android/provider/AuthTabResultHandlerTest.ktauth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.ktauth0/src/test/java/com/auth0/android/provider/BrowserPickerTest.javaauth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.javaauth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.javaauth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.ktauth0/src/test/java/com/auth0/android/provider/PARCodeManagerTest.ktauth0/src/test/java/com/auth0/android/provider/PKCETest.javaauth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.ktauth0/src/test/java/com/auth0/android/provider/PermissionHandlerTest.javaauth0/src/test/java/com/auth0/android/provider/WebAuthProviderTest.ktauth0/src/test/java/com/auth0/android/request/DefaultClientTest.ktauth0/src/test/java/com/auth0/android/request/RetryInterceptorTest.ktauth0/src/test/java/com/auth0/android/request/internal/BaseAuthenticationRequestTest.ktauth0/src/test/java/com/auth0/android/request/internal/BaseRequestTest.ktauth0/src/test/java/com/auth0/android/request/internal/CommonThreadSwitcherDelegateTest.ktauth0/src/test/java/com/auth0/android/request/internal/SSOCredentialsDeserializerMock.ktauth0/src/test/java/com/auth0/android/request/internal/SSOCredentialsDeserializerTest.ktauth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.javaauth0/src/test/java/com/auth0/android/request/internal/ThreadSwitcherShadow.javaauth0/src/test/java/com/auth0/android/request/internal/UserProfileGsonTest.ktauth0/src/test/java/com/auth0/android/result/SSOCredentialsMock.ktauth0/src/test/java/com/auth0/android/util/Auth0UserAgentTest.javaauth0/src/test/java/com/auth0/android/util/AuthenticationAPIMockServer.ktauth0/src/test/java/com/auth0/android/util/ManagementCallbackMatcher.javaauth0/src/test/java/com/auth0/android/util/MockManagementCallback.javaauth0/src/test/java/com/auth0/android/util/SSLTestUtils.ktbuild.gradlegradle.propertiesgradle/jacoco.gradlegradle/wrapper/gradle-wrapper.propertiesproguard/proguard-jetpack.prosample/build.gradlesample/src/main/AndroidManifest.xmlsample/src/main/java/com/auth0/sample/DatabaseLoginFragment.ktsample/src/main/res/layout/fragment_database_login.xml
💤 Files with no reviewable changes (15)
- auth0/src/main/java/com/auth0/android/callback/ManagementCallback.kt
- auth0/src/test/java/com/auth0/android/management/ManagementExceptionTest.kt
- proguard/proguard-jetpack.pro
- auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.kt
- .circleci/config.yml
- auth0/src/main/java/com/auth0/android/management/ManagementException.kt
- auth0/src/test/java/com/auth0/android/util/MockManagementCallback.java
- auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt
- auth0/src/main/java/com/auth0/android/provider/PasskeyAuthProvider.kt
- auth0/src/test/java/com/auth0/android/util/ManagementCallbackMatcher.java
- auth0/src/main/java/com/auth0/android/management/UsersAPIClient.kt
- auth0/src/main/java/com/auth0/android/provider/PasskeyManager.kt
- auth0/src/main/java/com/auth0/android/dpop/DPoPException.kt
- sample/src/main/res/layout/fragment_database_login.xml
- auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Set persist-credentials: false on the checkout step to prevent GITHUB_TOKEN leakage.
The actions/checkout action defaults to persist-credentials: true, which stores the GITHUB_TOKEN in .git/config. In a CodeQL analysis workflow, this token could be exfiltrated through generated artifacts or SARIF files. No subsequent step in this job requires Git credentials, so setting persist-credentials: false is safe and eliminates the risk.
🔒 Proposed fix
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ with:
+ persist-credentials: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Checkout | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false |
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 44-45: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/codeql.yml around lines 44 - 45, The Checkout step in the
CodeQL workflow is leaving Git credentials persisted by default, which can
expose the GITHUB_TOKEN unnecessarily. Update the actions/checkout usage in the
workflow to explicitly disable credential persistence, since this job does not
need Git auth after checkout. Use the Checkout step in the codeql workflow as
the target and set persist-credentials to false there.
Source: Linters/SAST tools
|
|
||
| - name: Run RL Scanner | ||
| uses: auth0/devsecops-tooling/.github/actions/rl-scan@e29f26478db18ff0bcbe4bc447a8fbd54fbeec9e # main on 2026-06-09, TODO: use a release instead | ||
| uses: auth0/devsecops-tooling/.github/actions/rl-scan@main |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check all third-party action references for unpinned tags (`@main`, `@latest`, etc.).
rg -n "uses:.*`@main`\b" .github/ --type yamlRepository: auth0/Auth0.Android
Length of output: 360
🏁 Script executed:
#!/bin/bash
set -euo pipefail
cat -n .github/workflows/release.yml | sed -n '1,120p'
printf '\n---\n'
cat -n .github/workflows/sca_scan.yml | sed -n '1,80p'Repository: auth0/Auth0.Android
Length of output: 3389
Pin the RL Scanner action
auth0/devsecops-tooling/.github/actions/rl-scan@main pulls arbitrary future changes from main, which hurts reproducibility and expands supply-chain risk. Keep this reference pinned like actions/checkout.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml at line 38, The RL Scanner action reference is
using a moving branch target, which should be pinned to a fixed version for
reproducibility and supply-chain safety. Update the action reference in the
release workflow to use a stable pinned commit or release tag, matching the
approach used by other pinned actions like actions/checkout, so the workflow
always runs the same vetted code.
| ``` | ||
| Auth0.Android/ | ||
| ├── auth0/ # Main SDK library module | ||
| ├── auth0/ # Main SDK library module | ||
| │ ├── src/main/java/com/auth0/android/ | ||
| │ │ ├── provider/ # Browser-based auth providers | ||
| │ │ ├── authentication/ # Direct API authentication | ||
| │ │ ├── management/ # Management API client | ||
| │ │ ├── myaccount/ # My Account API client | ||
| │ │ ├── request/ # Network request abstractions | ||
| │ │ ├── result/ # Response/error handling | ||
| │ │ └── Auth0.kt # Main configuration class | ||
| │ └── src/test/ # Unit tests | ||
| ├── sample/ # Demo application | ||
| ├── .github/ # CI/CD workflows | ||
| ├── gradle/ # Build configuration | ||
| │ ├── versioning.gradle # Version management | ||
| │ └── maven-publish.gradle # Publishing setup | ||
| └── .version # Current SDK version | ||
| │ │ ├── authentication/ # Authentication API client, grants, MFA, and storage | ||
| │ │ ├── callback/ # Callback interfaces | ||
| │ │ ├── dpop/ # DPoP proof-of-possession support | ||
| │ │ ├── myaccount/ # My Account API client | ||
| │ │ ├── provider/ # Browser authentication providers | ||
| │ │ ├── request/ # Request abstractions | ||
| │ │ ├── result/ # Response and error models | ||
| │ │ └── Auth0.kt # Main account/config entry point | ||
| │ └── src/test/ # Unit tests (Robolectric/JUnit) | ||
| ├── sample/ # Demo app module | ||
| ├── .github/workflows/ # CI and release workflows | ||
| ├── gradle/versioning.gradle # Version resolution from .version | ||
| ├── gradle/maven-publish.gradle # Maven publishing config | ||
| └── .version # SDK version source of truth | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a language tag to the tree fence.
This block is just a directory listing, so the bare fence trips markdownlint. Use text (or convert it to a list) to keep the doc lint-clean.
Suggested fix
-```
+```text📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| Auth0.Android/ | |
| ├── auth0/ # Main SDK library module | |
| ├── auth0/ # Main SDK library module | |
| │ ├── src/main/java/com/auth0/android/ | |
| │ │ ├── provider/ # Browser-based auth providers | |
| │ │ ├── authentication/ # Direct API authentication | |
| │ │ ├── management/ # Management API client | |
| │ │ ├── myaccount/ # My Account API client | |
| │ │ ├── request/ # Network request abstractions | |
| │ │ ├── result/ # Response/error handling | |
| │ │ └── Auth0.kt # Main configuration class | |
| │ └── src/test/ # Unit tests | |
| ├── sample/ # Demo application | |
| ├── .github/ # CI/CD workflows | |
| ├── gradle/ # Build configuration | |
| │ ├── versioning.gradle # Version management | |
| │ └── maven-publish.gradle # Publishing setup | |
| └── .version # Current SDK version | |
| │ │ ├── authentication/ # Authentication API client, grants, MFA, and storage | |
| │ │ ├── callback/ # Callback interfaces | |
| │ │ ├── dpop/ # DPoP proof-of-possession support | |
| │ │ ├── myaccount/ # My Account API client | |
| │ │ ├── provider/ # Browser authentication providers | |
| │ │ ├── request/ # Request abstractions | |
| │ │ ├── result/ # Response and error models | |
| │ │ └── Auth0.kt # Main account/config entry point | |
| │ └── src/test/ # Unit tests (Robolectric/JUnit) | |
| ├── sample/ # Demo app module | |
| ├── .github/workflows/ # CI and release workflows | |
| ├── gradle/versioning.gradle # Version resolution from .version | |
| ├── gradle/maven-publish.gradle # Maven publishing config | |
| └── .version # SDK version source of truth | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 17-17: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@AGENTS.md` around lines 17 - 35, The repository tree in AGENTS.md is wrapped
in a bare fenced block, which triggers markdownlint; update that directory
listing to use a language-tagged fence such as text (or convert it to a list) so
the documentation stays lint-clean. Make the change in the tree section near the
Auth0.Android layout block and keep the rest of the structure unchanged.
Source: Linters/SAST tools
|
|
||
| /** | ||
| * Removes all values from the storage. | ||
| */ | ||
| public fun removeAll() {} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- Storage.kt ---\n'
sed -n '1,180p' auth0/src/main/java/com/auth0/android/authentication/storage/Storage.kt
printf '\n--- CredentialsManager clear methods ---\n'
rg -n "clearCredentials|clearAll|removeAll\\(" auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt
sed -n '720,790p' auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt
printf '\n--- Storage implementations ---\n'
rg -n "class .*Storage|object .*Storage|: Storage" auth0/src/main/java auth0/src/test auth0/src/androidTest
printf '\n--- Migration guide mentions ---\n'
rg -n "removeAll|Storage|clearCredentials|clearAll" V4_MIGRATION_GUIDE.md auth0/src/main/java/com/auth0/android/authentication/storageRepository: auth0/Auth0.Android
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- Storage.kt ---'
sed -n '1,180p' auth0/src/main/java/com/auth0/android/authentication/storage/Storage.kt
echo
echo '--- CredentialsManager clear methods ---'
rg -n "clearCredentials|clearAll|removeAll\\(" auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt
sed -n '720,790p' auth0/src/main/java/com/auth0/android/authentication/storage/CredentialsManager.kt
echo
echo '--- Storage implementations ---'
rg -n "class .*Storage|object .*Storage|: Storage" auth0/src/main/java auth0/src/test auth0/src/androidTest
echo
echo '--- Migration guide mentions ---'
rg -n "removeAll|Storage|clearCredentials|clearAll" V4_MIGRATION_GUIDE.md auth0/src/main/java/com/auth0/android/authentication/storageRepository: auth0/Auth0.Android
Length of output: 7618
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- SharedPreferencesStorage.kt ---'
sed -n '1,220p' auth0/src/main/java/com/auth0/android/authentication/storage/SharedPreferencesStorage.kt
echo
echo '--- Storage tests for removeAll ---'
rg -n "removeAll|clearCredentials|clearAll" auth0/src/test/java/com/auth0/android/authentication/storage
echo
echo '--- Migration guide / docs mentions ---'
rg -n "removeAll|Storage|clearCredentials|clearAll" . -g '!*build*' -g '!*dist*'Repository: auth0/Auth0.Android
Length of output: 35277
Make Storage.removeAll() a real contract
clearCredentials() and clearAll() now depend entirely on removeAll(), so a custom Storage that leaves the default no-op will silently keep tokens on logout. Make the method abstract or have the default implementation throw so implementations can’t skip clearing by accident.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@auth0/src/main/java/com/auth0/android/authentication/storage/Storage.kt`
around lines 78 - 82, Storage.removeAll() is currently a no-op default, so
logout flows that rely on it can silently fail to clear data. Update the Storage
contract by making removeAll() abstract or changing the default implementation
to throw, and ensure clearCredentials() and clearAll() continue to call it so
every custom Storage implementation must explicitly handle full clearing.
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
| WebAuthProvider.registerCallbacks( | ||
| lifecycleOwner = this, | ||
| loginCallback = object : Callback<Credentials, AuthenticationException> { | ||
| override fun onSuccess(result: Credentials) { | ||
| // Handle successful login | ||
| } | ||
| override fun onFailure(error: AuthenticationException) { | ||
| // Handle error | ||
| } | ||
| }, | ||
| logoutCallback = object : Callback<Void?, AuthenticationException> { | ||
| override fun onSuccess(result: Void?) { | ||
| // Handle successful logout | ||
| } | ||
| override fun onFailure(error: AuthenticationException) { | ||
| // Handle error | ||
| } | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| fun onLoginClick() { | ||
| WebAuthProvider.login(account) | ||
| .withScheme("demo") | ||
| .start(this, loginCallback) | ||
| } | ||
|
|
||
| fun onLogoutClick() { | ||
| WebAuthProvider.logout(account) | ||
| .withScheme("demo") | ||
| .start(this, logoutCallback) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Hoist the callbacks before reusing them.
loginCallback and logoutCallback are only named arguments in registerCallbacks(), but the later start() calls treat them like in-scope variables. Declare them once and reuse those values, or inline the callbacks in start().
Suggested fix
WebAuthProvider.registerCallbacks(
lifecycleOwner = this,
- loginCallback = object : Callback<Credentials, AuthenticationException> {
- override fun onSuccess(result: Credentials) { /* handle credentials */ }
- override fun onFailure(error: AuthenticationException) { /* handle error */ }
- },
- logoutCallback = object : Callback<Void?, AuthenticationException> {
- override fun onSuccess(result: Void?) { /* handle logout */ }
- override fun onFailure(error: AuthenticationException) { /* handle error */ }
- }
+ loginCallback = loginCallback,
+ logoutCallback = logoutCallback
)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@EXAMPLES.md` around lines 505 - 537, The sample reuses loginCallback and
logoutCallback in onLoginClick/onLogoutClick even though they are only created
as inline arguments inside WebAuthProvider.registerCallbacks, so the later
start() calls cannot see them. Hoist those Callback<Credentials,
AuthenticationException> and Callback<Void?, AuthenticationException> instances
into named variables in the surrounding scope (or inline equivalent callbacks
directly in start()), then pass the same values to both registerCallbacks() and
the start() calls.
| ## Getting Started | ||
|
|
||
| ### Requirements | ||
|
|
||
| Android API version 31 or later and Java 8+. | ||
| Android API version 31 or later and Java 17+. | ||
|
|
||
| > :warning: Applications targeting Android SDK version 30 (`targetSdkVersion = 30`) and below should use version 2.9.0. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Correct the platform floor in the requirements block.
v4 is documented elsewhere in this PR as minSdk 26, so saying API 31 or later (and warning off apps targeting 30) conflicts with the SDK's actual baseline. Update this section so the quickstart doesn't tell users to raise their floor unnecessarily.
Suggested fix
-Android API version 31 or later and Java 17+.
+Android API version 26 or later and Java 17+.
@@
-> :warning: Applications targeting Android SDK version 30 (`targetSdkVersion = 30`) and below should use version 2.9.0.
+> :warning: Applications with `minSdkVersion < 26` should continue using v3.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## Getting Started | |
| ### Requirements | |
| Android API version 31 or later and Java 8+. | |
| Android API version 31 or later and Java 17+. | |
| > :warning: Applications targeting Android SDK version 30 (`targetSdkVersion = 30`) and below should use version 2.9.0. | |
| ## Getting Started | |
| ### Requirements | |
| Android API version 26 or later and Java 17+. | |
| > :warning: Applications with `minSdkVersion < 26` should continue using v3. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 25 - 31, The Getting Started requirements text is
using the wrong Android floor and conflicts with the rest of the PR. Update the
requirements block in README so it matches the SDK’s actual baseline (minSdk 26)
instead of saying Android API 31+ and warning off targetSdkVersion 30. Keep the
quickstart aligned with the documented baseline and adjust the version guidance
accordingly.
| android { | ||
| compileSdkVersion 30 | ||
| compileSdkVersion 36 | ||
| defaultConfig { | ||
| applicationId "com.auth0.samples" | ||
| minSdkVersion 21 | ||
| targetSdkVersion 30 | ||
| minSdkVersion 24 | ||
| targetSdkVersion 36 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Raise the sample minSdkVersion to 26.
The Universal Login example still shows minSdkVersion 24, which is below the library floor documented in this release. Bump it to 26 so the sample can actually consume v4.
Suggested fix
- minSdkVersion 24
+ minSdkVersion 26📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| android { | |
| compileSdkVersion 30 | |
| compileSdkVersion 36 | |
| defaultConfig { | |
| applicationId "com.auth0.samples" | |
| minSdkVersion 21 | |
| targetSdkVersion 30 | |
| minSdkVersion 24 | |
| targetSdkVersion 36 | |
| android { | |
| compileSdkVersion 36 | |
| defaultConfig { | |
| applicationId "com.auth0.samples" | |
| minSdkVersion 26 | |
| targetSdkVersion 36 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 119 - 124, The Android sample in the README still
uses a lower minSdkVersion than this release supports; update the sample
configuration shown in the Android block so it uses 26 instead of 24. Make the
change in the Universal Login example snippet only, keeping the rest of the
Gradle settings the same, so the documented sample matches the library floor.
| + [Credentials Manager minTTL](#credentials-manager-minttl) | ||
| - [**Behavior Changes**](#behavior-changes) | ||
| + [CredentialsManager Now Uses the Global Executor](#credentialsmanager-now-uses-the-global-executor) | ||
| + [clearCredentials() Now Clears All Storage](#clearCredentials-now-clears-all-storage) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Lowercase the clearCredentials TOC anchor.
GitHub slugifies this heading to lowercase, so the camel-cased fragment won't resolve. Update the anchor so the TOC link lands on the section.
Suggested fix
- + [clearCredentials() Now Clears All Storage](`#clearCredentials-now-clears-all-storage`)
+ + [clearCredentials() Now Clears All Storage](`#clearcredentials-now-clears-all-storage`)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| + [clearCredentials() Now Clears All Storage](#clearCredentials-now-clears-all-storage) | |
| [clearCredentials() Now Clears All Storage](`#clearcredentials-now-clears-all-storage`) |
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 31-31: Link fragments should be valid
Expected: #clearcredentials-now-clears-all-storage; Actual: #clearCredentials-now-clears-all-storage
(MD051, link-fragments)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@V4_MIGRATION_GUIDE.md` at line 31, The TOC entry for clearCredentials uses a
camel-cased fragment that won’t match GitHub’s lowercase slug, so update the
link target to the lowercase anchor. Fix the markdown table of contents entry
that references clearCredentials and ensure it points to the slug generated from
the matching heading in V4_MIGRATION_GUIDE.md.
Source: Linters/SAST tools
| - [**New APIs**](#new-apis) | ||
| + [clearAll() — Full Credential and Key Cleanup](#clearall--full-credential-and-key-cleanup) | ||
| - [**Dependency Changes**](#dependency-changes) | ||
| + [Gson 2.8.9 → 2.11.0](#️-gson-289--2110-transitive-dependency) | ||
| + [DefaultClient.Builder](#defaultclientbuilder) | ||
| - [**New APIs**](#new-apis) | ||
| + [Handling Configuration Changes During Authentication](#handling-configuration-changes-during-authentication) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Give the later New APIs section a distinct heading.
The TOC now has two New APIs entries, and both collapse to the same slug. Rename the configuration-recovery section so navigation points to the right place.
Suggested fix
- + [**New APIs**](`#new-apis`)
+ + [**Handling Configuration Changes During Authentication**](`#handling-configuration-changes-during-authentication`)
...
-## New APIs
+## Handling Configuration Changes During AuthenticationAlso applies to: 433-435
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@V4_MIGRATION_GUIDE.md` around lines 33 - 39, The table of contents has two
entries named New APIs that currently generate the same anchor, so the later
configuration-recovery section should be renamed to a distinct heading. Update
the section title associated with Handling Configuration Changes During
Authentication so its slug is unique, and make sure the corresponding TOC link
in V4_MIGRATION_GUIDE matches that new heading; use the New APIs and Handling
Configuration Changes During Authentication section headings to locate the
change.
Source: Linters/SAST tools
There was a problem hiding this comment.
♻️ Duplicate comments (1)
.github/workflows/codeql.yml (1)
44-45: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winCheckout still lacks
persist-credentials: false.This was previously flagged and remains unresolved. The
actions/checkoutstep defaults topersist-credentials: true, which storesGITHUB_TOKENin.git/config. No subsequent step in this job requires Git credentials.🔒 Proposed fix
- name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/codeql.yml around lines 44 - 45, The Checkout step in the CodeQL workflow still persists GitHub credentials by default, leaving GITHUB_TOKEN in git config unnecessarily. Update the existing actions/checkout usage in the workflow to set persist-credentials to false so the checkout does not store credentials, and keep the change localized to the Checkout step.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In @.github/workflows/codeql.yml:
- Around line 44-45: The Checkout step in the CodeQL workflow still persists
GitHub credentials by default, leaving GITHUB_TOKEN in git config unnecessarily.
Update the existing actions/checkout usage in the workflow to set
persist-credentials to false so the checkout does not store credentials, and
keep the change localized to the Checkout step.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f33769a2-5be0-49ba-ac26-2c8efd033449
📒 Files selected for processing (2)
.github/workflows/codeql.yml.github/workflows/release.yml
💤 Files with no reviewable changes (1)
- .github/workflows/release.yml
| pull_request: | ||
| branches: | ||
| - main | ||
| - v4_development |
There was a problem hiding this comment.
This will be removed in a separate PR post merge
| * | ||
| * ## Scopes Required | ||
| * | ||
| * `read:me:authentication_methods` |
There was a problem hiding this comment.
This shouldn't be removed. Will be done in another follow up PR
| @@ -1 +1 @@ | |||
| 3.21.0 No newline at end of file | |||
| 4.0.0-beta.1 No newline at end of file | |||
There was a problem hiding this comment.
This will be updated once we raise the release PR on main
Summary
Promotes
v4_developmenttomainas the 4.0.0 major release of the Auth0.Android SDK. Contains the full v4 restructuring (two betas released) plus every feature carried forward from the 3.x line, up to and including 3.21.0.mainis now an ancestor ofv4_development(via the reconciliation merge #1010), so this PR is conflict-free.v4 architecture changes
See
V4_MIGRATION_GUIDE.mdfor the complete list. Highlights:pendingLoginResult/PendingResult/LifecycleAwareCallback/registerCallbacks()CredentialsManagerglobal executor +runCatchingOnExecutorguard (with fatal-error rethrow forVirtualMachineError/ThreadDeath/LinkageError)clearCredentials()clears all viaStorage.removeAll(); newclearAll()AuthenticationActivityonComponentActivity+ Auth Tab supportIntentional breaking removals (documented in the migration guide)
UsersAPIClient,ManagementException,ManagementCallback)PasskeyAuthProvider/PasskeyManager(replaced byAuthenticationAPIClientpasskey APIs)loginWithOTP/loginWithOOB/loginWithRecoveryCode/multifactorChallenge)SecureCredentialsManagerAuth0-based constructorsDPoPException.UNSUPPORTED_ERRORSSOCredentials.expiresIn→expiresAt3.x features ported forward into v4
additionalProperties(feat : Added additional properties support to passkeys signup API #969)typeparameter (feat : Add support to pass type parameter to the MyAccount list authentication method API #974)session_expiryceiling (feat: enforce IPSIE session_expiry ceiling in credentials managers #983)getAuthenticatorstype-filtering + TOTP field mapping (fix: MFAClient getAuthenticators filtering based on Authenticator.type field #998)Summary by CodeRabbit
New Features
Breaking Changes
Bug Fixes