Skip to content

docs(examples): add Sign in with MetaMask (SIWE) example#1139

Merged
grdsdev merged 6 commits into
mainfrom
claude/web3-signin-metamask-example
Jul 22, 2026
Merged

docs(examples): add Sign in with MetaMask (SIWE) example#1139
grdsdev merged 6 commits into
mainfrom
claude/web3-signin-metamask-example

Conversation

@grdsdev

@grdsdev grdsdev commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #1138 (adds AuthClient.signInWithWeb3) — this PR adds an end-to-end example of that API to the Examples/Examples demo app.

  • Adds the MetaMask iOS SDK (0.8.10) as a package dependency of the existing Examples Xcode target — no new target.
  • New Examples/Examples/Auth/SignInWithMetaMask.swift: connects a real MetaMask wallet, builds a SIWE (EIP-4361) message, gets it signed via personal_sign, and calls AuthClient.signInWithWeb3.
  • Wired into the Auth examples list under a new "Web3" section.
  • Ethereum/MetaMask only (no Solana, no WalletConnect) — see the design spec for the reasoning.
  • iOS-only (#if canImport(UIKit)-gated), matching the existing SignInWithOAuthViewController example's platform scoping.

Two non-obvious correctness details worth knowing about, both verified against the MetaMask SDK's real source and this repo's own signInWithWeb3 backend contract:

  • personal_sign params are sent as [message, address] — the SDK's own personalSign(message:address:) convenience method sends the opposite order ([address, message]), which contradicts both the JSON-RPC spec and the SDK's own example app. This PR builds the request manually with the correct order.
  • The wallet's chainId is hex (e.g. 0x1) but SIWE requires decimal — converted before building the message (caught in review; without this the backend's SIWE parser would reject the message).

Test plan

  • xcodebuild -workspace Supabase.xcworkspace -scheme Examples -destination 'generic/platform=iOS' build** BUILD SUCCEEDED **, verified independently multiple times including after the final review fixes.
  • Confirmed the two new dependencies (MetaMask SDK + transitive deps) don't affect any other target.

Manual e2e test

ScreenRecording_07-22-2026.12-31-50_1.MP4

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d52c56b1-3bf8-4366-8e1e-d0b7c7b4148a

📥 Commits

Reviewing files that changed from the base of the PR and between 5197c08 and afc00aa.

⛔ Files ignored due to path filters (1)
  • Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved
📒 Files selected for processing (6)
  • Examples/Examples.xcodeproj/project.pbxproj
  • Examples/Examples/Auth/AuthExamplesView.swift
  • Examples/Examples/Auth/SignInWithMetaMask.swift
  • Examples/Examples/Info.plist
  • Examples/README.md
  • dictionary.txt
🚧 Files skipped from review as they are similar to previous changes (5)
  • Examples/Examples/Info.plist
  • Examples/Examples/Auth/AuthExamplesView.swift
  • Examples/README.md
  • Examples/Examples.xcodeproj/project.pbxproj
  • Examples/Examples/Auth/SignInWithMetaMask.swift

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a “Sign in with MetaMask” example using SIWE and Supabase Web3 authentication.
    • Included end-to-end MetaMask connect, wallet signing, deep-link callback handling, and signed-in status UI.
    • Integrated the MetaMask iOS SDK into the Examples app.
  • Documentation
    • Added setup guidance for enabling Web3 auth, required SIWE URI allow-listing, and iOS device testing.
  • Configuration
    • Added the MetaMask callback URL scheme and expanded URL query schemes to support the wallet handoff.

Walkthrough

The Examples app now links the metamask-ios-sdk package and configures MetaMask callback and query URL schemes. A new SwiftUI screen connects to MetaMask, creates and signs a SIWE message, and authenticates through Supabase Web3 credentials. The authentication examples list links to this screen, while the README documents required Web3 settings, redirect URL configuration, and physical-device requirements.

Sequence Diagram(s)

sequenceDiagram
  participant SignInWithMetaMaskView
  participant SignInWithMetaMaskController
  participant MetaMaskSDK
  participant SupabaseAuth
  SignInWithMetaMaskView->>SignInWithMetaMaskController: Start connection or sign-in
  SignInWithMetaMaskController->>MetaMaskSDK: Connect or request personal_sign
  MetaMaskSDK-->>SignInWithMetaMaskController: Return account, chain, signature, or callback URL
  SignInWithMetaMaskController->>SupabaseAuth: Submit Web3 credentials
  SupabaseAuth-->>SignInWithMetaMaskView: Display session or error
Loading

Possibly related PRs

Suggested labels: auth


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.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

⚠️ Capability matrix drift detected

The following capabilities are marked implemented in swift but have no registered symbols to verify:

  • auth.passkey.register_passkey (no symbols list — cannot confirm implementation exists)
  • auth.passkey.sign_in_with_passkey (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.third_party_auth (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.cross_client_token_sync (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.oauth_flow_type (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.session_url_detection (no symbols list — cannot confirm implementation exists)
  • client.session_management.custom_storage (no symbols list — cannot confirm implementation exists)
  • client.session_management.persist_session (no symbols list — cannot confirm implementation exists)
  • client.request_configuration.global_headers (no symbols list — cannot confirm implementation exists)
  • client.observability.trace_propagation (no symbols list — cannot confirm implementation exists)
  • database.query.from_table (no symbols list — cannot confirm implementation exists)
  • database.query.select (no symbols list — cannot confirm implementation exists)
  • database.query.schema_selection (no symbols list — cannot confirm implementation exists)
  • database.query.rpc (no symbols list — cannot confirm implementation exists)
  • database.mutate.insert (no symbols list — cannot confirm implementation exists)
  • database.mutate.update (no symbols list — cannot confirm implementation exists)
  • database.mutate.upsert (no symbols list — cannot confirm implementation exists)
  • database.mutate.delete (no symbols list — cannot confirm implementation exists)
  • database.mutate.select_after_mutation (no symbols list — cannot confirm implementation exists)
  • database.using_filters.eq (no symbols list — cannot confirm implementation exists)
  • database.using_filters.neq (no symbols list — cannot confirm implementation exists)
  • database.using_filters.gt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.gte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.lt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.lte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike (no symbols list — cannot confirm implementation exists)
  • database.using_filters.is (no symbols list — cannot confirm implementation exists)
  • database.using_filters.in (no symbols list — cannot confirm implementation exists)
  • database.using_filters.contains (no symbols list — cannot confirm implementation exists)
  • database.using_filters.contained_by (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_gt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_gte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_lt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_lte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_adjacent (no symbols list — cannot confirm implementation exists)
  • database.using_filters.overlaps (no symbols list — cannot confirm implementation exists)
  • database.using_filters.text_search (no symbols list — cannot confirm implementation exists)
  • database.using_filters.match (no symbols list — cannot confirm implementation exists)
  • database.using_filters.not (no symbols list — cannot confirm implementation exists)
  • database.using_filters.or (no symbols list — cannot confirm implementation exists)
  • database.using_filters.raw (no symbols list — cannot confirm implementation exists)
  • database.using_filters.regex (no symbols list — cannot confirm implementation exists)
  • database.using_filters.regex_icase (no symbols list — cannot confirm implementation exists)
  • database.using_filters.is_distinct (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like_all (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like_any (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike_all (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike_any (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.order (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.limit (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.range (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.single_row (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.strip_nulls (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.format_csv (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.format_geojson (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.max_affected_rows (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.request_cancellation (no symbols list — cannot confirm implementation exists)
  • database.configuration.auto_retry (no symbols list — cannot confirm implementation exists)
  • functions.invocation.invoke (no symbols list — cannot confirm implementation exists)
  • functions.invocation.set_auth_token (no symbols list — cannot confirm implementation exists)
  • functions.invocation.method_override (no symbols list — cannot confirm implementation exists)
  • functions.invocation.streaming_response (no symbols list — cannot confirm implementation exists)
  • functions.invocation.request_cancellation (no symbols list — cannot confirm implementation exists)
  • realtime.client.connect (no symbols list — cannot confirm implementation exists)
  • realtime.client.disconnect (no symbols list — cannot confirm implementation exists)
  • realtime.client.get_channels (no symbols list — cannot confirm implementation exists)
  • realtime.client.remove_channel (no symbols list — cannot confirm implementation exists)
  • realtime.client.remove_all_channels (no symbols list — cannot confirm implementation exists)
  • realtime.client.connection_state (no symbols list — cannot confirm implementation exists)
  • realtime.client.listen_heartbeats (no symbols list — cannot confirm implementation exists)
  • realtime.client.set_auth_token (no symbols list — cannot confirm implementation exists)
  • realtime.client.channel (no symbols list — cannot confirm implementation exists)
  • realtime.channel.subscribe (no symbols list — cannot confirm implementation exists)
  • realtime.channel.unsubscribe (no symbols list — cannot confirm implementation exists)
  • realtime.channel.send (no symbols list — cannot confirm implementation exists)
  • realtime.channel.broadcast_http (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.postgres_changes (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.subscribe_presence (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.private_channel (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_self (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_ack (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_replay (no symbols list — cannot confirm implementation exists)
  • realtime.presence.track (no symbols list — cannot confirm implementation exists)
  • realtime.presence.untrack (no symbols list — cannot confirm implementation exists)
  • realtime.presence.presence_key (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.custom_websocket_transport (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.reconnect_backoff (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.heartbeat_interval (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.access_token_callback (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.deferred_disconnect (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.custom_logger (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.binary_protocol (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.get_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.list_file_buckets (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.update_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.delete_file_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.empty_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.access_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.download (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.move (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.copy (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.remove (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_urls (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_upload_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload_with_signed_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.update_file (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.file_exists (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.file_info (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.list_files_paginated (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.copy_cross_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.move_cross_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload_with_metadata (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.url_cache_nonce (no symbols list — cannot confirm implementation exists)

These may have been renamed, removed, or never registered. Please update the capability matrix.
See: https://github.com/supabase/sdk/blob/main/docs/capability-matrix.md

@grdsdev grdsdev changed the title feat(examples): add Sign in with MetaMask (SIWE) example docs(examples): add Sign in with MetaMask (SIWE) example Jul 22, 2026
Base automatically changed from claude/web3-sign-in-auth-8f76e0 to main July 22, 2026 15:36
@grdsdev
grdsdev marked this pull request as ready for review July 22, 2026 15:37
@grdsdev
grdsdev requested a review from a team as a code owner July 22, 2026 15:37

@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 `@Examples/Examples/Auth/SignInWithMetaMask.swift`:
- Around line 81-82: Update the signRequest construction in the
SignInWithMetaMask flow to pass personalSign parameters in the required order:
address first, followed by message. Keep the existing EthereumRequest and
metamaskSDK.request flow 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d35e1f4a-156f-4f60-ad73-a619ace9320a

📥 Commits

Reviewing files that changed from the base of the PR and between d364154 and 88e28b1.

⛔ Files ignored due to path filters (2)
  • Package.resolved is excluded by !**/Package.resolved
  • Supabase.xcworkspace/xcshareddata/swiftpm/Package.resolved is excluded by !**/Package.resolved
📒 Files selected for processing (14)
  • Examples/Examples.xcodeproj/project.pbxproj
  • Examples/Examples/Auth/AuthExamplesView.swift
  • Examples/Examples/Auth/SignInWithMetaMask.swift
  • Examples/Examples/Info.plist
  • Examples/README.md
  • Package.swift
  • Sources/Auth/AuthClient.swift
  • Sources/Auth/AuthError.swift
  • Sources/Auth/Types.swift
  • Tests/AuthTests/AuthClientTests.swift
  • Tests/IntegrationTests/AuthClientIntegrationTests.swift
  • Tests/IntegrationTests/supabase/config.toml
  • dictionary.txt
  • sdk-compliance.yaml

Comment thread Examples/Examples/Auth/SignInWithMetaMask.swift

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.

I thought Solana wasn't supported? As per the PR description at least

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

i think it's supported in the auth server at least

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

I guess the PR description was just not accurate then

@grdsdev
grdsdev force-pushed the claude/web3-signin-metamask-example branch from 19ea10c to 5197c08 Compare July 22, 2026 16:36
@coveralls

coveralls commented Jul 22, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 29960064069

Coverage decreased (-0.2%) to 83.628%

Details

  • Coverage decreased (-0.2%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 17 coverage regressions across 1 file.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

17 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
Sources/RealtimeV2/WebSocket/URLSessionWebSocket.swift 17 63.35%

Coverage Stats

Coverage Status
Relevant Lines: 9779
Covered Lines: 8178
Line Coverage: 83.63%
Coverage Strength: 38.87 hits per line

💛 - Coveralls

@grdsdev
grdsdev enabled auto-merge (squash) July 22, 2026 16:38
grdsdev added 6 commits July 22, 2026 18:39
The SIWE message hardcoded `URI: https://supabase.com`, which the backend
rejects with `invalid_grant` ("message was signed for another app") for
any real project, since that URI is never on a project's own redirect
allow-list. Switch to `http://localhost:3000`, matching the placeholder
already used by this SDK's own integration tests, and document the
required Additional Redirect URLs entry in the README.
@grdsdev
grdsdev force-pushed the claude/web3-signin-metamask-example branch from 5197c08 to afc00aa Compare July 22, 2026 21:39
@grdsdev
grdsdev merged commit 1dc076c into main Jul 22, 2026
49 of 52 checks passed
@grdsdev
grdsdev deleted the claude/web3-signin-metamask-example branch July 22, 2026 22:53
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.

4 participants