Skip to content

SDKS-5120: Add authenticationValidityDuration support to device binding and signing#520

Merged
spetrov merged 2 commits into
developfrom
SDKS-5120
Jun 12, 2026
Merged

SDKS-5120: Add authenticationValidityDuration support to device binding and signing#520
spetrov merged 2 commits into
developfrom
SDKS-5120

Conversation

@vibhorgoswami

Copy link
Copy Markdown
Contributor

JIRA Ticket

SDKS-5120

Description

  • Adds authenticationValidityDuration field (default: 5 seconds) to DeviceBindingCallback and DeviceSigningVerifierCallback, giving callers control over how long the generated key remains valid for biometric/device
    authentication.
  • Threads the value through to CryptoKey(userId, authenticationValidityDuration) via the DeviceAuthenticator.initialize extension, so the Android Keystore key is generated with the correct user-authentication validity window.
  • Adds unit tests to DeviceBindingCallbackTest and DeviceSigningVerifierCallbackTest covering the default value and verifying the custom value propagates to CryptoKey.timeout.

Definition of Done Checklist:

  • Coded to standards.
  • Ensure backward compatibility.
  • API reference docs is created or updated, if applicable.
  • Unit tests are written or updated.
  • Integration tests are written, if applicable.

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.84%. Comparing base (da60801) to head (4190775).

Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #520      +/-   ##
=============================================
+ Coverage      65.77%   65.84%   +0.06%     
- Complexity      1762     1768       +6     
=============================================
  Files            264      264              
  Lines           8658     8666       +8     
  Branches         969      969              
=============================================
+ Hits            5695     5706      +11     
+ Misses          2532     2530       -2     
+ Partials         431      430       -1     
Flag Coverage Δ
unit-tests 65.84% <100.00%> (+0.06%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

Great work on this — the core implementation is clean and the propagation through initialize()CryptoKey is well structured. A couple of small things worth addressing before merging:

DeviceSigningVerifierCallbackTest.kt — missing @Test annotations: Two pre-existing test methods in this file — testSignForForValidClaims() and testSignForForInvalidClaims() — are missing @Test annotations, so JUnit silently skips them entirely. Since this PR is already touching the file, it would be a great time to fix those here. The testSignForForInvalidClaims case is especially worth addressing as it covers the InvalidCustomClaims error path.

…eBindingCallback and DeviceSigningVerifierCallback
witrisna
witrisna previously approved these changes Jun 11, 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.

Re-review (latest commit: guard authentication validity duration)

The setter guard + tests are a solid addition. The core threading (callback → initialize(…, duration, …) → CryptoKey(userId, timeout)) is correct, the guard correctly rejects the dangerous 0/-1 Keystore special values, and timeout (server-sent, used for withTimeout/JWT exp) is kept properly distinct from authenticationValidityDuration. A few items worth a look:

1. Binary-incompatible change to a published class — contradicts the "backward compatibility" DoD

forgerock-core/.../CryptoKey.ktCryptoKey is a public class in the published forgerock-core artifact. Changing the primary constructor from CryptoKey(keyId: String) to CryptoKey(keyId: String, timeout: Int = 5) changes its JVM signature from <init>(String) to <init>(String, int). Kotlin default parameters do not generate a synthetic overload — there's no @JvmOverloads, and there's no binary-compatibility validator in the build. Any pre-compiled third-party code calling new CryptoKey(userId) would hit NoSuchMethodError at runtime until recompiled. Since this PR ticks "Ensure backward compatibility," could we make a conscious call here — add @JvmOverloads, or confirm CryptoKey is not treated as consumer-facing API? (Intra-SDK callers recompile, so no internal break.)

2. authenticationValidityDuration is a no-op for NONE and APPLICATION_PIN, and the test masks it

Only the biometric authenticators consume cryptoKey.timeout (via setUserAuthenticationParameters / setUserAuthenticationValidityDurationSeconds in BiometricOnly / BiometricAndDeviceCredential). None and ApplicationPinDeviceAuthenticator never read it. That's defensible, but testAuthenticationValidityDurationCustomValuePassedToAuthenticator asserts the value on a mock<None>() — i.e. it verifies plumbing into CryptoKey while using the one authenticator type that ignores the value. Suggest documenting in the public KDoc that the setting only affects biometric-backed keys, and/or covering a biometric type in the test.

3. Property + validation + KDoc duplicated verbatim across both callbacks

DeviceBindingCallback.kt and DeviceSigningVerifierCallback.kt — the var authenticationValidityDuration + require(value > 0) setter + KDoc are copy-pasted into both classes, which both implement Binding. A Kotlin interface can't hold a backing field, but the validation could live in a shared helper (e.g. Binding.validateValidityDuration(Int)) to keep the two copies from drifting (e.g. one becoming >= 0).

4. Default 5 duplicated in three places

The magic default appears in CryptoKey(timeout: Int = 5), initialize(userId, authenticationValidityDuration: Int = 5), and both callbacks' = 5. A single shared constant would keep them from diverging.


No blocking correctness bug — #1 (binary compatibility) is the one I'd want an explicit decision on before merge given the DoD claim; the rest are advisory. (Nice catch incidentally adding @Test to the two previously-unannotated testSignFor* methods so they actually run now.)

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.

Re-review of the latest changes — the setter guard + tests are a solid addition and the core threading is correct. Four inline notes below; #1 (binary compatibility) is the only one I'd want an explicit decision on before merge given the DoD claim.

Comment thread forgerock-core/src/main/java/org/forgerock/android/auth/CryptoKey.kt Outdated
Comment thread forgerock-core/src/main/java/org/forgerock/android/auth/CryptoKey.kt Outdated

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.

LGTM

@spetrov spetrov left a comment

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.

Changes look good to me! 👍🏻

@spetrov spetrov merged commit 693aae3 into develop Jun 12, 2026
9 of 10 checks passed
@spetrov spetrov deleted the SDKS-5120 branch June 12, 2026 18:30
spetrov added a commit that referenced this pull request Jun 16, 2026
* SDKS-4658: Set code verifier to null for Apple Sign-In

The `codeVerifier` is explicitly set to `null` in the `AuthorizationRequest.Builder` for the Apple Sign-In handler.

* SDKS-4714: Use empty request body in self-service session endpoint (#510)

* SDKS-4714: Use empty request body in self-service session endpoint

Remove the `EMPTY` constant from OkHttp's `RequestBody` and has been replaced with `"".toRequestBody()`.

* Update Session.kt

Signed-off-by: Andy Witrisna <andy.witrisna@forgerock.com>

---------

Signed-off-by: Andy Witrisna <andy.witrisna@forgerock.com>

* Add migration support for Ping SDK with DefaultStorageClient (#511)

* Increase sleep time in FRUserMockTest to ensure token expiration (#513)

The `Thread.sleep` duration in `FRUserMockTest.kt` is increased from 1000ms to 2000ms to allow sufficient time for tokens to expire before testing the refresh token flow.

* ForgeRock Android SDK 4.8.4 Release preparation (#512)

* Updated  for the ForgeRock 4.8.4 release
* Fixed various e2e test cases.

* SDKS-4947 Updating README with maintenance information (#515)

* Updating README with maintenance information

* Rename contribution file name

* SDKS-5037 - Upgrade bcpkix-jdk18on from 1.81 to 1.84 to address CVE-2026-5588 (#516)

* ForgeRock Android SDK 4.8.5 Release preparation (#517)

* Removed Sonatype OSS Index Scan from the CI (this service has been discontinued)

* Updated version number for the ForgeRock 4.8.5 release

* Fix mend tasks to fail the pipeline if critical or high vulnerabilities are found

* SDKS-5096: Enhance WebAuthn registration by adding optional device name (#519)

* SDKS-5096: Enhance WebAuthn registration by adding optional device name parameter

* SDKS-5120: Add authenticationValidityDuration support to device binding and signing (#520)

* SDKS-5120: Allow configurable authenticationValidityDuration in DeviceBindingCallback and DeviceSigningVerifierCallback

* PR review - guard authentication validity duration.

* [fix] SDKS-5114 Handle AM 400 for Push Number Challenge with distinct exception (#521)

* [fix] SDKS-5114 Handle AM 400 for Push Number Challenge with distinct exception

AM 8.1.0 (OPENAM-24154) returns HTTP 400 when a user selects the wrong number
in a Push Number Challenge. Previously the SDK surfaced a generic
PushMechanismException with no semantic meaning. This fix introduces
PushNumberChallengeException (extends PushMechanismException) so callers can
distinguish a wrong-number rejection from other failures, and leaves the
PushNotification in its pending state so the app can offer a retry.

Phases:
- phase 1: Create PushNumberChallengeException (8804d77c)
- phase 2: Handle 400 for Push Number Challenge in PushResponder (fe17bf07)
- phase 3: Unit tests for 400 number-challenge path (4d57f2ca)
- code review: Remove body logging, fix test builder placement (cc910478)

Refs: SDKS-5114

* Addressing comment from Stoyan

* Updated version number for the ForgeRock 4.8.6 release (#522)

---------

Signed-off-by: Andy Witrisna <andy.witrisna@forgerock.com>
Co-authored-by: Andy Witrisna <witrisna@gmail.com>
Co-authored-by: Andy Witrisna <andy.witrisna@forgerock.com>
Co-authored-by: Vibhor Goswami <vibhor.goswami@gmail.com>
Co-authored-by: Rodrigo Reis <rodrigo.reis@forgerock.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants