Skip to content

Add amr claim to Token model#57

Merged
AaronAtDuo merged 3 commits into
duosecurity:mainfrom
brandonwongduo:add-amr-token-field
Jul 10, 2026
Merged

Add amr claim to Token model#57
AaronAtDuo merged 3 commits into
duosecurity:mainfrom
brandonwongduo:add-amr-token-field

Conversation

@brandonwongduo

@brandonwongduo brandonwongduo commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add an optional amr field (List<String>) to the Token model to surface the Authentication Methods References claim added to the /token endpoint response (Duo OAuth API docs).
  • Populate the field in Utils.transformDecodedJwtToToken() via decodedJwt.getClaim("amr").asList(String.class). The claim is optional, so absent claims leave the field null (java-jwt's NullClaim.asList() returns null) — the same pattern already used for other optional claims in this method.
  • Update equals/hashCode/toString on Token accordingly. The existing all-args constructor is intentionally left unchanged to preserve the public API; callers use the no-arg constructor with setters.

Note for reviewers

The amr claim has three possible states from Duo: absent, empty list, or a list of strings. This PR preserves that distinction — a missing claim leaves getAmr() returning null, and an empty list returns Collections.emptyList(). This is consistent with the other optional fields on Token (auth_result, auth_context, etc.), which also use null to signal "not provided." If reviewers would prefer collapsing absent → empty list so callers only need to handle one shape, happy to change it.

Test plan

  • transformDecodedJwtToToken — asserts getAmr() is null when the claim is missing
  • transformDecodedJwtToTokenWithAmr — asserts a populated list round-trips
  • transformDecodedJwtToTokenWithEmptyAmr — asserts an empty list round-trips
  • mvn -pl duo-universal-sdk test (CI)

🤖 Generated with Claude Code

brandonwongduo and others added 2 commits July 10, 2026 13:04
Populate the optional amr (Authentication Methods References) claim
from the /token endpoint id token when present, leaving it null when
absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap the amr asList() call in a JWTDecodeException guard so a
non-string array element (RFC 8176 violation from upstream) yields
null instead of leaking a RuntimeException past the SDK's declared
DuoException contract.

Also correct the 9-arg Token constructor javadoc (it is no longer
"all properties"), mirror amr in toString per the existing pattern,
and add tests for the null / non-array / non-string-element wire
shapes plus an equals/hashCode contract test for the new field.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@brandonwongduo
brandonwongduo marked this pull request as ready for review July 10, 2026 19:25
The prior test asserted that a JSON array of integers would be
rejected by asList(String.class), but Jackson silently coerces
numeric nodes to their string form when the target type is String,
so no JWTDecodeException is thrown. Update the test to reflect the
actual library behavior — the try/catch in extractAmr remains as
defense-in-depth for element types Jackson cannot coerce.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AaronAtDuo
AaronAtDuo merged commit 5a64d5e into duosecurity:main Jul 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants