Enable self-service profile updates in Flutter SDKs - #34
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: 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 |
bdb6b60 to
001b9b2
Compare
There was a problem hiding this comment.
🟡 Changes recommended
There are correctness and accessibility issues in the new profile editing flow (read-only enforcement, boolean payload typing, missing semantics) that should be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the Flutter ThunderID SDK to support self-service user profile editing by fetching the real profile (GET /users/me) and schema (GET /users/me/meta) via the platform channel, then validating and saving edits (PUT /users/me). It also keeps the provider’s cached user in sync with /users/me so UI consuming ThunderIDProvider.user updates immediately after saves/refresh.
Changes:
- Added schema-driven profile field rendering + per-field edit/save/cancel behavior, with validation and merge-before-save payload building.
- Extended the method-channel surface to support real profile/schema operations and native cached-user write-through (
setCachedUser). - Added config flags for profile-fetch behavior and Android debug-only insecure TLS for local development, plus unit tests covering the new behavior.
File summaries
| File | Description |
|---|---|
| test/user_profile_fields_test.dart | Adds unit tests for schema field building, validation, claim formatting, payload building, deep merge, and envelope decoding. |
| test/thunderid_client_test.dart | Extends client mock + tests for getUserProfile/getUserSchema/updateUserProfile/setCachedUser and config defaults. |
| samples/quickstart/lib/screens/home_screen.dart | Updates the sample profile screen to use BaseUserProfile and render inline editable fields. |
| samples/quickstart/lib/main.dart | Enables debug-only allowInsecureConnections for local Android instances with self-signed certs. |
| lib/src/widgets/user_profile.dart | Replaces token-decoding profile UI with schema-driven /users/me profile view + field edit/save flow and token-only fallback mode. |
| lib/src/widgets/thunderid_provider.dart | Adds background /users/me sync and merges saved profile attributes into user, plus identity-based notify behavior. |
| lib/src/thunderid_client.dart | Adds setCachedUser, getUserSchema, and updates profile APIs to return UserProfile envelopes. |
| lib/src/models/user_profile.dart | Introduces /users/me UserProfile model + AttributeSchema model and deep normalization of nested channel maps. |
| lib/src/models/thunderid_config.dart | Adds fetchUserProfile and allowInsecureConnections config flags and forwards them through toMap(). |
| lib/src/i18n/default_strings.dart | Adds default strings for edit/cancel and validation errors. |
| ios/thunderid_flutter/Package.swift | Bumps iOS SDK dependency to 1.1.0. |
| ios/thunderid_flutter.podspec | Bumps CocoaPods dependency to ThunderID >= 1.1.0. |
| ios/Classes/ThunderIDMethodHandler.swift | Implements real getUserProfile, getUserSchema, updateUserProfile, and setCachedUser channel handlers + encoders. |
| android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt | Implements real profile/schema/update/cache methods; unwraps JSON containers for codec compatibility; bridges allowInsecureConnections. |
| android/build.gradle | Bumps Android SDK dependency to v1.1.0. |
Review details
Suppressed comments (1)
android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt:137
updateUserProfilecastspayloadtoMap<String, Any>, but a profile update payload can legitimately include nulls (and nested values decoded as platform collections). UsingAny?prevents accidental type assumptions in this boundary layer.
"updateUserProfile" -> {
@Suppress("UNCHECKED_CAST")
val payload = args["payload"] as? Map<String, Any> ?: emptyMap()
result.success(encodeUserProfile(client.updateUserProfile(payload)))
- Files reviewed: 15/15 changed files
- Comments generated: 7
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Signed-off-by: janithjay <janithjayashan018@gmail.com>
001b9b2 to
1af21bc
Compare
Purpose
Currently, the Flutter SDK's user profile view decodes the sign-in token in Dart and renders its claims read-only, and its save button is a no-op that only fires the
onSavedcallback. ThegetUserProfile/updateUserProfilechannel methods it should have used returned a hardcoded empty claim map from both native handlers.This PR makes the profile view fetch from
GET /users/meby default, matching the JavaScript, iOS, and Android SDKs, and adds,GET /users/me/meta(PUT /users/me).ThunderIDProvider'suseracross sign-in/refresh.ThunderIDConfig.fetchUserProfile(defaulttrue), for apps that want the previous token-claims-only, read-only behavior instead.Approach
This package performs no protocol work of its own, so the feature is bridged rather than reimplemented. This PR exposes it over the method channel and builds the Dart UI on top.
getUserSchemaandsetCachedUser, and repointedgetUserProfile/updateUserProfileat the real native methods, which previously returned a hardcoded empty claim map. The channel surface is now identical across the Kotlin handler, the Swift handler, and the Dart client at 21 methods.BaseUserProfilebase64-decoded the access token to build its claim list, the only protocol work inlib/outside the channel, while the already-bridgeddecodeJwtTokensat unused.setCachedUser: both native clients short-circuitgetUser()on their cached user, so a/users/memerge must be written back through the channel or the nextrefresh()resurrects the pre-merge claims.user_profile.dart: schema-driven field building, validation (required + regex, invalid regex ignored rather than blocking save), and a merge-before-save step since the backend rejects a save missing any required attribute, even for a single-field edit.fetchUserProfile = false) mirrors the Quickstart's pre-existing claim-formatting verbatim, hoisted from the sample into the reusable SDK component.ThunderIDProvider: the sync is fire-and-forget with failures swallowed, matching the JavaScript provider.updateShouldNotifynow comparesuserby identity, since a merge swaps in a newUserwhileisSignedInstaystrue.Data-contract pieces match the JavaScript SDK exactly:
readonlyFields, thedefaultAttributeMappingsfallback order, the readonly derivation, the required-then-regex validation order, and the{attributes: ...}PUTpayload.Pre-existing defects this depended on
Three unrelated bugs blocked Android:
allowInsecureConnectionswas never bridged - the Android SDK exposes it and its own Quickstart sets it fromBuildConfig.DEBUG, but it was absent fromThunderIDConfig, so a Flutter app could not reach a locally-served instance. Now bridged, defaulting tofalse, sample-gated onkDebugMode. No effect on iOS.Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks