Architecture Overview
magic_social_auth is the client half of magic-starter-laravel's social login: a service provider registers a singleton manager, a static facade reaches it, and the manager dispatches to interchangeable driver objects while a flow client carries the wire protocol.
On this page
Core Patterns#
| Pattern | Role |
|---|---|
| ServiceProvider | SocialAuthServiceProvider registers the SocialAuthManager singleton under social_auth; it is wired into Magic.init through the app's providers list. |
| Singleton Manager | SocialAuthManager resolves and caches drivers, and picks the driver that runs on the current platform. |
| Driver (Strategy) | SocialDriver subclasses (GoogleDriver, AppleDriver, RedirectDriver): one provider's way into the backend. |
| Flow client | SocialFlow speaks the backend's protocol: browser redirect, exchange, link ticket, native token. Drivers compose it. |
| Facade | SocialAuth is the static entry point, delegating to Magic.make<SocialAuthManager>('social_auth'). |
Component Diagram#
App
├─ SocialAuthButtons ── reads config, filters by platform + enabled flag
└─ SocialAuth (facade) ── manager · driver(name) · supports(name) · signOut()
│
SocialAuthManager (singleton)
├─ driver(name): enabled check → custom factory or built-in → cache
├─ extend(name, factory) · registerProviderDefaults(...)
└─ signOut(): each cached driver.signOut(), then clear the cache
│
SocialDriver (abstract) ── signIn · beginConnect · connect · confirm · signOut
├─ GoogleDriver native SDK on iOS/Android ──┐
├─ AppleDriver native sheet on iOS ────────┤ SocialFlow.token()
└─ RedirectDriver GitHub, Microsoft; Apple │
on Android/web; Google │
on web ──────────────────────┤ SocialFlow.signIn / authorize
▼
SocialFlow ── Pkce · Nonce · WebFlowSlot
├─ redirectUrl(): <network.drivers.api.base_url>/auth/social/{provider}/redirect
├─ browse(): flutter_web_auth_2 (iOS session, Android Auth Tab, web popup)
├─ exchange(): POST auth/social/exchange {code, code_verifier}
├─ linkTicket(): POST user/social-accounts/link-ticket
└─ token(): POST auth/social/{provider}/token
│
magic Http facade ── auth interceptor attaches the bearer
Which driver runs is decided in SocialAuthManager: Google is native off the web, Apple is native on iOS, everything else is a RedirectDriver. A driver whose supportedPlatforms lacks the current platform answers false from SocialAuth.supports(name).
Data Flow#
Signing in with GitHub on Android, from tap to session:
1. The user taps "Sign in with GitHub"
SocialAuthButtons.onPressed('github')
2. The app calls: final result = await SocialAuth.driver('github').signIn()
3. SocialAuthManager.driver('github')
- cache miss -> Config.get('social_auth.providers.github') -> enabled? -> RedirectDriver
4. RedirectDriver.signIn() -> SocialFlow.signIn('github')
- Pkce.generate() new pair for this flow only
- WebFlowSlot.claim() this flow owns the browser
- FlutterWebAuth2.authenticate(...) Auth Tab on <network base>/auth/social/github/redirect
?platform=android&challenge=<challenge>
5. Backend -> GitHub -> backend callback -> 302 to social_auth.callback.android
with ?code=<one-time code>
6. SocialFlow.exchange(code, verifier)
- POST auth/social/exchange {code, code_verifier}
- SocialAuthResult.fromJson(answer)
7. The caller decides what the result means:
- isTwoFactor -> challenge flow
- otherwise Auth.login({'token': result.token}, createUser(result.user))
The native token flow skips steps 4 to 6: the driver opens the SDK sheet and posts its ID token to auth/social/{provider}/token.
IoC Integration#
SocialAuthServiceProvider registers the manager as a singleton in register(); its boot() has nothing to do.
class SocialAuthServiceProvider extends ServiceProvider {
@override
void register() {
app.singleton('social_auth', () => SocialAuthManager());
}
}
The SocialAuth facade resolves it on every call: Magic.make<SocialAuthManager>('social_auth'). Config is read lazily when a driver is first resolved, so config can change between Magic.init and the first SocialAuth.driver(...), which is useful in tests.
Extensibility Points#
Custom drivers: extend()#
SocialAuth.manager.extend('gitlab', (config) => RedirectDriver('gitlab', config));
Registers a factory for the name and clears a cached instance. Config is read from social_auth.providers.gitlab. The enabled flag is checked before the factory runs.
Platform seams#
Each driver and SocialFlow expose the platform call as a @visibleForTesting member (initializeSdk, nativeSignIn, requestCredential, authenticate), and SocialAuthManager.platformOverride forces the platform. Tests stand in for the platform there instead of mocking a method channel.
There is no handler seam. The package never creates a session: the caller owns Auth.login, which is what lets 2FA and a cancelled deletion complete properly.
UI Layer#
SocialAuthButtons is a config-driven stateless widget. It never calls a driver; it calls the onPressed(provider) callback synchronously from the tap (a web popup needs that), so the parent decides what a press does.
Resolution order for button metadata:
config['label'] / config['icon_svg'] / config['order']
↓ fallback
SocialProviderIcons.forProvider(name) ← registered via registerProviderDefaults()
↓ fallback
Capitalised provider name, insertion-order index
Providers with enabled: false, and providers where SocialAuth.supports(name) is false, are omitted. On iOS Apple is always first.
The magic_starter Bridge#
This package does not depend on magic_starter, and magic_starter does not depend on it. magic_starter declares a MagicStarterSocialAuth contract (providers, label, icon, signIn, beginConnect, confirm, signOut) and renders the buttons, the connected accounts page and the step-up dialog itself. social:install publishes lib/app/providers/social_auth_starter_service_provider.dart into the app, the one place that sees both packages. It implements the contract over SocialAuth, registers it with MagicStarter.useSocialAuth, translates every failure into the starter's own exception type (a cancel is marked, a refusal carries its code), and signs Google out when Auth.stateNotifier goes to signed-out.
Related Documentation#
- Installation:
social:install,social:doctorand the backend values. - Configuration: the
social_authconfig keys. - Drivers: the driver contract and the platform matrix.
- Flows: the browser and native token flows, errors and guarantees.
- Connected accounts in magic_starter: the starter screens the bridge feeds.