Skip to content

Make the user object a dynamic claim set - #25

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:dynamic-user-claims
Aug 24, 2026
Merged

Make the user object a dynamic claim set#25
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:dynamic-user-claims

Conversation

@brionmario

@brionmario brionmario commented Aug 20, 2026

Copy link
Copy Markdown
Member

Purpose

The profile screen in the Quickstart sample showed only the user ID and username, even when the signed in user had a first name, last name, mobile number and profile picture on their token.

The cause was in the SDK rather than the sample: User declared a fixed list of fields and the client hand picked claims into them, so any attribute a deployment configures beyond that list was dropped before the app could see it. On Android the claim map was never populated at all, which also left the avatar initials with nothing to read.

The avatar never showed a profile picture either. It resolved the picture claim, discarded it, and always drew initials on a gradient, because the SDK had no image loading dependency.

Approach

User is now the claim set itself. It holds the claims exactly as the server sent them, get(claim) reads any of them, and sub, username, email, displayName, givenName and familyName are properties that read the claim of the same name and nothing else. There are no fallback chains and no composed values, which mirrors KnownUser in the JavaScript SDK.

profileClaims returns the claims with RESERVED_CLAIMS removed. That set holds only protocol claims, the ones describing the token rather than the user: sub, iss, aud, exp, iat, nbf, jti, azp, nonce, typ, at_hash, c_hash, sid, scope, client_id, acr, amr and auth_time. Everything else reaches the app.

ThunderIDClient maps nothing now. The sign in path builds the user straight from the decoded token, and getUser() decodes the stored access token before falling back to the userinfo endpoint, so a session restored after a relaunch has claims too. Gson cannot fill a claim map from a flat JSON body, so the userinfo and profile update responses go through one conversion that reads the response as a map without interpreting any keys.

Name and avatar precedence stays in the presentation layer where it already lived, so UserAvatar keeps deciding how to seed initials and which claim to use for a picture. It now also loads that picture: Coil renders it circle clipped, with SVG decoding added on top of the raster defaults, and the initials gradient serves as both the loading and the error state so an undecodable picture degrades to what the component drew before. One process wide ImageLoader is shared by every avatar rather than one per instance, so they share a memory and disk cache.

Note for reviewers: this adds io.coil-kt:coil-compose and io.coil-kt:coil-svg to the library, which consumers inherit transitively. dev.thunderid.compose had no image loading dependency before. The alternative was a slot parameter letting the app supply the image composable, keeping the SDK dependency free at the cost of making pictures opt in.

Known limitation: AndroidSVG, the rasterizer behind Coil's SVG decoder, does not implement dominant-baseline, so an SVG that centres its text with that attribute renders the text about half a cap height high. Horizontal centring, raster formats and the fallback path are unaffected.

The sample's profile screen iterates profileClaims, humanises each key for its label (given_name becomes "Given Name") and formats values by type, so a newly configured attribute shows up without a code change.

Screenshot 2026-08-20 at 13 00 26

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified. Profile screen and avatar checked on an emulator against a live session, with raster and SVG picture claims.
  • Documentation provided. (Add links if there are any)
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7182b719-8282-4b35-88bf-2281d3a945ac


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Claims are configured per deployment, so the SDK no longer maps a fixed list of
fields off the token. User now carries the claims as sent, exposes them by key and
keeps the well known ones as accessors, matching the JavaScript SDK's KnownUser.

The avatar also resolved the picture claim and then threw it away, because the SDK
had no image loading dependency, so it always drew initials on a gradient. Coil now
loads the picture, with SVG decoding on top of its raster defaults and one process
wide loader so every avatar shares a cache. The gradient stays as the loading and
error state, which covers pictures that cannot be decoded.

The Quickstart profile screen lists every claim except the protocol ones instead of
just the user ID and username.

Signed-off-by: Brion <info@brionmario.com>
@brionmario
brionmario force-pushed the dynamic-user-claims branch from 0c5223d to d8a081d Compare August 20, 2026 09:38
@brionmario brionmario changed the title Make the user object a dynamic claim set Surface every user claim and render the profile picture Aug 20, 2026
@brionmario brionmario changed the title Surface every user claim and render the profile picture Make the user object a dynamic claim set Aug 20, 2026
@brionmario
brionmario merged commit a338a35 into thunder-id:main Aug 24, 2026
7 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