Skip to content

Implement federated sign-in buttons and associated logic for Google and GitHub#3

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:feat-federated
Jul 16, 2026
Merged

Implement federated sign-in buttons and associated logic for Google and GitHub#3
brionmario merged 1 commit into
thunder-id:mainfrom
brionmario:feat-federated

Conversation

@brionmario

@brionmario brionmario commented Jul 7, 2026

Copy link
Copy Markdown
Member

Purpose

Implement Google & GitHub buttons.

Screenshot 2026-07-16 at 18 06 37

Approach

N/A

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • 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 in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added federated/social sign-in continuation after server redirects, including a new public client API.
    • Added “Continue with” buttons for Google, GitHub, Facebook, Microsoft, and LinkedIn (plus shared outlined trigger styling).
    • Enhanced FlowForm with labeled dividers, rich-text links, and resolved placeholder hints.
  • Bug Fixes

    • Improved handling of canceled federated auth with a dedicated error code and smoother flow resumption.
  • Tests

    • Added/expanded widget and client tests covering the new buttons and redirect continuation behavior.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds redirect-based federated authentication continuation on Android, iOS, and Dart; introduces typed cancellation handling; extends flow response encoding; adds branded trigger buttons and SVG rendering; and updates FlowForm for rich text, dividers, placeholders, and trigger actions.

Changes

Federated auth and flow rendering

Layer / File(s) Summary
Native federated-auth continuation
android/..., ios/..., android/build.gradle, samples/quickstart/...
Native handlers launch federated authentication, process redirect callbacks, resume sign-in with authorization codes, handle cancellation, and encode redirect-related flow data.
Dart continuation API and sign-in handling
lib/src/thunderid_client.dart, lib/src/models/thunderid_error.dart, lib/src/widgets/sign_in.dart, test/thunderid_client_test.dart
The Dart client exposes continueFederatedAuth, maps cancellation to a typed error, and resumes REDIRECTION sign-in responses with native-channel tests.
Trigger button and SVG rendering primitives
lib/src/widgets/adapters/*, test/adapters_test.dart
Adds reusable outlined trigger buttons, provider-specific buttons, multi-path SVG parsing, and rendering tests.
Dynamic flow components and trigger integration
lib/src/widgets/flow_form.dart, pubspec.yaml, test/flow_form_test.dart
FlowForm renders dividers, rich text, tappable links, translated placeholders, and specialized trigger actions with widget coverage.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SignIn
  participant ThunderIDClient
  participant NativeHandler
  participant FederatedAuthSession
  participant FlowServer
  SignIn->>ThunderIDClient: continueFederatedAuth redirectURL
  ThunderIDClient->>NativeHandler: invoke native continuation
  NativeHandler->>FederatedAuthSession: authenticate callback
  FederatedAuthSession-->>NativeHandler: authorization code or cancellation
  NativeHandler->>FlowServer: signIn with embedded code payload
  FlowServer-->>ThunderIDClient: encoded completed flow response
  ThunderIDClient-->>SignIn: EmbeddedFlowResponse or typed error
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the main change: adding federated sign-in buttons and related logic for Google and GitHub.
Description check ✅ Passed The description follows the template with Purpose, Approach, Related Issues, Related PRs, Checklist, and Security checks, and is mostly complete.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (2)
test/adapters_test.dart (1)

26-50: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add a visual assertion for the branded glyphs.

A golden test would catch both malformed SVG geometry and an invisible white-on-light GitHub glyph; checking only labels and exceptions cannot detect either regression.

🤖 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 `@test/adapters_test.dart` around lines 26 - 50, Add golden-image assertions to
the GoogleButton and GitHubButton widget tests, capturing each rendered branded
glyph and comparing it against approved golden files. Keep the existing label,
exception, and tap assertions, and ensure the goldens visibly validate both SVG
geometry and GitHub glyph contrast.
test/flow_form_test.dart (1)

209-238: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise the nested federated trigger actions in this fixture.

Assert that GoogleButton and GitHubButton render, then tap each and verify submission of action_xoc0 and action_zeye. The current tests ignore these fixture components and therefore pass while both triggers route to the first credential action.

Also applies to: 240-280

🤖 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 `@test/flow_form_test.dart` around lines 209 - 238, Extend the tests around
buildForm and the affected flow-form cases to find and assert both GoogleButton
and GitHubButton, tap each trigger, and verify submit receives action_xoc0 and
action_zeye respectively. Ensure the assertions exercise the nested federated
trigger actions rather than only validating the fixture renders, and preserve
existing coverage for other credential actions.
🤖 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 `@android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt`:
- Around line 74-76: Update onDetachedFromActivity to call cancelIfPending()
before or alongside detachActivity(), ensuring any suspended authentication
request is resolved when the host is permanently detached. Keep
onReattachedToActivityForConfigChanges unchanged so pending sessions remain
preserved across configuration changes.

In `@ios/Classes/ThunderIDMethodHandler.swift`:
- Around line 129-136: Remove the iOS-only rejection of missing afterSignInUrl
in the sign-in callback flow around callbackURLScheme; derive the callback
scheme from the authorization URL’s redirect_uri or pass it explicitly through
the channel instead. Preserve optional afterSignInUrl behavior consistently with
Android and the Dart API, and apply the same correction to the related logic
around the second referenced block.

In `@lib/src/widgets/adapters/github_button.dart`:
- Around line 33-42: Update the GitHub glyph color in the MultiColorSvgIcon
within the build method to use the current theme’s contrasting or foreground
color instead of the hard-coded white Color value, ensuring it remains visible
on the default light button surface.

In `@lib/src/widgets/adapters/svg_icon_path.dart`:
- Around line 189-210: Update the path application logic alongside
_argumentCount to implement smooth cubic S/s commands instead of discarding
their parsed arguments. Track the previous cubic control point, reflect it for
each S/s command when the prior segment is cubic, and update the current
point/control state so subsequent relative segments retain correct geometry;
preserve existing handling for explicit C/c and other commands.

In `@lib/src/widgets/flow_form.dart`:
- Around line 289-313: Update the HTML-to-spans logic around _stripTags in the
link parsing loop and trailing-text handling so whitespace surrounding inline
anchors is preserved; avoid trimming the plain-text segments before adding them,
while retaining empty-segment filtering and existing link recognizer behavior.
- Around line 374-405: Update the trigger handling in the flow component so
nested ACTION/BLOCK wrappers are unwrapped and their child trigger components
are resolved before determining the action and button. Ensure identifiers such
as action_xoc0 and action_zeye are selected from the nested children rather than
block_per3/block_dzuu or the first action, while preserving the existing
provider-specific button rendering and submitting the resolved trigger action.
- Line 58: Update _renderRichText and the _linkRecognizers field so
TapGestureRecognizer instances are reused by href across rebuilds, or relocate
link rendering into a dedicated stateful widget that disposes recognizers when
replaced; ensure obsolete recognizers are disposed and retain disposal during
form teardown.
- Around line 330-335: Update _openLink to validate the parsed URI scheme before
calling launchUrl, allowing only http and https schemes; return without
launching for all other schemes while preserving the existing empty and
invalid-URL checks.

---

Nitpick comments:
In `@test/adapters_test.dart`:
- Around line 26-50: Add golden-image assertions to the GoogleButton and
GitHubButton widget tests, capturing each rendered branded glyph and comparing
it against approved golden files. Keep the existing label, exception, and tap
assertions, and ensure the goldens visibly validate both SVG geometry and GitHub
glyph contrast.

In `@test/flow_form_test.dart`:
- Around line 209-238: Extend the tests around buildForm and the affected
flow-form cases to find and assert both GoogleButton and GitHubButton, tap each
trigger, and verify submit receives action_xoc0 and action_zeye respectively.
Ensure the assertions exercise the nested federated trigger actions rather than
only validating the fixture renders, and preserve existing coverage for other
credential actions.
🪄 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: ac8ddb26-ff01-4385-9396-2bc3048d7fbe

📥 Commits

Reviewing files that changed from the base of the PR and between 0c47688 and 43a6d71.

📒 Files selected for processing (15)
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt
  • ios/Classes/ThunderIDMethodHandler.swift
  • lib/src/models/thunderid_error.dart
  • lib/src/thunderid_client.dart
  • lib/src/widgets/adapters/github_button.dart
  • lib/src/widgets/adapters/google_button.dart
  • lib/src/widgets/adapters/outlined_trigger_button.dart
  • lib/src/widgets/adapters/svg_icon_path.dart
  • lib/src/widgets/flow_form.dart
  • lib/src/widgets/thunderid_sign_in.dart
  • pubspec.yaml
  • test/adapters_test.dart
  • test/flow_form_test.dart
  • test/thunderid_client_test.dart

Comment thread ios/Classes/ThunderIDMethodHandler.swift Outdated
Comment thread lib/src/widgets/adapters/github_button.dart Outdated
Comment thread lib/src/widgets/adapters/svg_icon_path.dart
Comment thread lib/src/widgets/flow_form.dart
Comment thread lib/src/widgets/flow_form.dart
Comment thread lib/src/widgets/flow_form.dart
Comment thread lib/src/widgets/flow_form.dart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt (1)

74-76: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cancel pending authentication on permanent Activity detach.

If the host is permanently detached while the Custom Tab is open, it never resumes and cancelIfPending() is never called. The suspended method-channel request can therefore remain unresolved indefinitely. Cancel here, while preserving the session during configuration changes.

Proposed fix
     override fun onDetachedFromActivity() {
+        FederatedAuthSession.cancelIfPending()
         detachActivity()
     }
🤖 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 `@android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt`
around lines 74 - 76, Update onDetachedFromActivity to cancel any pending
authentication via cancelIfPending() before or while calling detachActivity().
Preserve the existing configuration-change behavior by leaving
onReattachedToActivityForConfigChanges and related temporary-detach handling
unchanged.
🤖 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 `@android/build.gradle`:
- Around line 64-66: Update the android-sdks dependency declaration in the
Gradle dependencies block to use a stable, immutable release such as v0.1.0
instead of main-SNAPSHOT, while leaving the other dependencies unchanged.

In `@lib/src/widgets/sign_in.dart`:
- Around line 143-145: Update the debug log in the sign-in redirection flow
around continueFederatedAuth to avoid hardcoding “ThunderID” in the tag. Use the
configured vendor value from the existing client/config context if a
brand-specific tag is required; otherwise remove the vendor prefix while
preserving the actionId and delegation details.

---

Duplicate comments:
In `@android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt`:
- Around line 74-76: Update onDetachedFromActivity to cancel any pending
authentication via cancelIfPending() before or while calling detachActivity().
Preserve the existing configuration-change behavior by leaving
onReattachedToActivityForConfigChanges and related temporary-detach handling
unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bc1ec0f8-068a-45e4-bc4e-49fa81e4da28

📥 Commits

Reviewing files that changed from the base of the PR and between 43a6d71 and 1ae971c.

📒 Files selected for processing (20)
  • android/build.gradle
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt
  • ios/Classes/ThunderIDMethodHandler.swift
  • lib/src/models/thunderid_error.dart
  • lib/src/thunderid_client.dart
  • lib/src/widgets/adapters/facebook_button.dart
  • lib/src/widgets/adapters/github_button.dart
  • lib/src/widgets/adapters/google_button.dart
  • lib/src/widgets/adapters/linkedin_button.dart
  • lib/src/widgets/adapters/microsoft_button.dart
  • lib/src/widgets/adapters/outlined_trigger_button.dart
  • lib/src/widgets/adapters/svg_icon_path.dart
  • lib/src/widgets/flow_form.dart
  • lib/src/widgets/sign_in.dart
  • pubspec.yaml
  • samples/quickstart/ios/Podfile
  • test/adapters_test.dart
  • test/flow_form_test.dart
  • test/thunderid_client_test.dart
🚧 Files skipped from review as they are similar to previous changes (11)
  • lib/src/widgets/adapters/google_button.dart
  • pubspec.yaml
  • lib/src/thunderid_client.dart
  • test/adapters_test.dart
  • lib/src/widgets/adapters/github_button.dart
  • test/flow_form_test.dart
  • lib/src/widgets/adapters/outlined_trigger_button.dart
  • lib/src/widgets/adapters/svg_icon_path.dart
  • test/thunderid_client_test.dart
  • ios/Classes/ThunderIDMethodHandler.swift
  • lib/src/widgets/flow_form.dart

Comment thread android/build.gradle
Comment thread lib/src/widgets/sign_in.dart
@brionmario
brionmario force-pushed the feat-federated branch 2 times, most recently from 6ff4371 to bedb79c Compare July 16, 2026 12:15
brionmario added a commit to brionmario/flutter-sdks that referenced this pull request Jul 16, 2026
- Cancel a pending federated-auth Custom Tab session on permanent Activity
  detach (Android), not just on config-change detach, so a suspended
  method-channel request can't hang indefinitely.
- Derive the iOS ASWebAuthenticationSession callback scheme from the
  authorization URL's redirect_uri first, falling back to the optional
  afterSignInUrl only if that's absent — federated sign-in no longer requires
  afterSignInUrl on iOS when Android and the Dart API don't.
- Implement S/s (smooth cubic) SVG path commands in the icon path parser; the
  GitHub glyph path uses them and they were silently dropped, corrupting
  downstream relative coordinates.
- Preserve whitespace around inline rich-text links instead of trimming it
  away, and restrict rich-text link taps to http/https schemes.
- Dispose stale TapGestureRecognizers before each rebuild instead of only at
  widget teardown, so rebuilds don't accumulate them.
- Resolve the SignIn debug log tag from ThunderIDConfig.vendor instead of a
  hardcoded "ThunderID" literal, consistent with the SDK's white-labeling
  rules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt`:
- Around line 131-134: Update the CancellationException catch block in
ThunderIDMethodHandler so it reports FEDERATED_AUTH_CANCELLED through
result.error, then rethrows the same exception to preserve coroutine
cancellation propagation and structured concurrency.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b2d0102e-9654-4bbc-8d08-b922da83ebac

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae971c and 108a3b6.

📒 Files selected for processing (21)
  • android/build.gradle
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDFlutterPlugin.kt
  • android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt
  • ios/Classes/ThunderIDMethodHandler.swift
  • lib/src/models/thunderid_error.dart
  • lib/src/thunderid_client.dart
  • lib/src/widgets/adapters/facebook_button.dart
  • lib/src/widgets/adapters/github_button.dart
  • lib/src/widgets/adapters/google_button.dart
  • lib/src/widgets/adapters/linkedin_button.dart
  • lib/src/widgets/adapters/microsoft_button.dart
  • lib/src/widgets/adapters/outlined_trigger_button.dart
  • lib/src/widgets/adapters/svg_icon_path.dart
  • lib/src/widgets/flow_form.dart
  • lib/src/widgets/sign_in.dart
  • pubspec.yaml
  • samples/quickstart/android/build.gradle.kts
  • samples/quickstart/ios/Podfile
  • test/adapters_test.dart
  • test/flow_form_test.dart
  • test/thunderid_client_test.dart
🚧 Files skipped from review as they are similar to previous changes (17)
  • lib/src/widgets/adapters/github_button.dart
  • lib/src/widgets/adapters/google_button.dart
  • pubspec.yaml
  • lib/src/thunderid_client.dart
  • lib/src/widgets/adapters/linkedin_button.dart
  • test/adapters_test.dart
  • test/thunderid_client_test.dart
  • samples/quickstart/ios/Podfile
  • lib/src/widgets/adapters/facebook_button.dart
  • test/flow_form_test.dart
  • lib/src/widgets/adapters/microsoft_button.dart
  • lib/src/widgets/adapters/outlined_trigger_button.dart
  • lib/src/models/thunderid_error.dart
  • lib/src/widgets/sign_in.dart
  • ios/Classes/ThunderIDMethodHandler.swift
  • lib/src/widgets/adapters/svg_icon_path.dart
  • lib/src/widgets/flow_form.dart

Comment on lines +131 to +134
} catch (e: CancellationException) {
// User dismissed the Custom Tab without completing federated sign-in — surfaced as a
// typed cancellation so the Dart layer can reset state silently instead of erroring.
result.error("FEDERATED_AUTH_CANCELLED", "User cancelled federated sign-in", null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Rethrow CancellationException to maintain structured concurrency.

When catching CancellationException in Kotlin coroutines, it is best practice to rethrow it after handling (such as sending the error via the MethodChannel). Swallowing it prevents the coroutine machinery from properly propagating the cancellation to the parent scope. As per static analysis hints, the caught exception is swallowed, which could lead to the original exception being lost.

🛠️ Proposed fix to rethrow the exception
         } catch (e: CancellationException) {
             // User dismissed the Custom Tab without completing federated sign-in — surfaced as a
             // typed cancellation so the Dart layer can reset state silently instead of erroring.
             result.error("FEDERATED_AUTH_CANCELLED", "User cancelled federated sign-in", null)
+            throw e
         } catch (e: Exception) {
📝 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.

Suggested change
} catch (e: CancellationException) {
// User dismissed the Custom Tab without completing federated sign-in — surfaced as a
// typed cancellation so the Dart layer can reset state silently instead of erroring.
result.error("FEDERATED_AUTH_CANCELLED", "User cancelled federated sign-in", null)
} catch (e: CancellationException) {
// User dismissed the Custom Tab without completing federated sign-in — surfaced as a
// typed cancellation so the Dart layer can reset state silently instead of erroring.
result.error("FEDERATED_AUTH_CANCELLED", "User cancelled federated sign-in", null)
throw e
🧰 Tools
🪛 detekt (1.23.8)

[warning] 131-131: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

🤖 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 `@android/src/main/kotlin/dev/thunderid/flutter/ThunderIDMethodHandler.kt`
around lines 131 - 134, Update the CancellationException catch block in
ThunderIDMethodHandler so it reports FEDERATED_AUTH_CANCELLED through
result.error, then rethrows the same exception to preserve coroutine
cancellation propagation and structured concurrency.

Source: Linters/SAST tools

@brionmario
brionmario merged commit e097cf4 into thunder-id:main Jul 16, 2026
6 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