Custom OAuth2 Flow
GoogleProvider and GitHubProvider are OAuth2Provider preconfigured for one identity provider. For anything else, LinkedIn, Spotify, your company's own OIDC provider, configure OAuth2Provider directly. Full field reference: OAuth2 Provider →. (Sign in with Apple is a separate case, see Apple Provider →: it uses a native SDK, not this browser-based flow.)
1. Find the three endpoints and your scopes
Every OAuth2/OIDC provider publishes an authorization endpoint, a token endpoint, and (for OIDC) a userinfo endpoint. Check the provider's developer docs, or its /.well-known/openid-configuration if it's OIDC-compliant.
2. Configure OAuth2Provider
import 'package:authyra_flutter/authyra_flutter.dart';
final linkedInProvider = OAuth2Provider(
config: OAuth2Config(
providerName: 'linkedin',
clientId: 'YOUR_LINKEDIN_CLIENT_ID',
authorizationEndpoint: 'https://www.linkedin.com/oauth/v2/authorization',
tokenEndpoint: 'https://www.linkedin.com/oauth/v2/accessToken',
userInfoEndpoint: 'https://api.linkedin.com/v2/userinfo',
redirectUri: 'myapp://auth/callback',
scopes: ['openid', 'profile', 'email'],
userExtractor: (json) => AuthUser(
id: json['sub'] as String,
email: json['email'] as String?,
name: json['name'] as String?,
avatarUrl: json['picture'] as String?,
),
),
);
userExtractor maps whatever JSON the userinfo endpoint returns into an AuthUser. This is provider-specific, read the response shape from the provider's docs.
3. Wire the deep-link callback
No per-provider registration needed, each sign-in attempt registers its own CSRF state with OAuth2CallbackHandler for routing:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
AppLinks().uriLinkStream.listen(OAuth2CallbackHandler.handleCallback);
await Authyra.initialize(
client: AuthyraClient(
providers: [linkedInProvider],
storage: SecureAuthStorage(),
),
);
runApp(const MyApp());
}
Full platform (Android/iOS manifest) setup: Deep Links →.
4. Sign in
try {
final user = await Authyra.instance.signIn('linkedin');
} on AuthenticationCancelledException {
// user closed the browser
} on AuthenticationFailedException catch (e) {
showError('LinkedIn sign-in failed: $e');
}
Client secret: PKCE vs confidential
Most mobile/desktop apps should use PKCE (usePkce: true, the default) and omit clientSecret entirely, a secret embedded in the app binary isn't actually secret. If your provider requires a confidential client (like GitHub, which doesn't support PKCE), either accept that trade-off or delegate the exchange to your backend with ProxyOAuthProvider →, which keeps the secret server-side.
Provider-specific quirks
Extra query params some providers require go in additionalAuthParams:
OAuth2Config(
// ...
additionalAuthParams: const {
'access_type': 'offline', // Google: request a refresh token
'prompt': 'consent', // Google: always show the consent screen
},
)
Overriding scopes for one sign-in call
await Authyra.instance.signIn('linkedin', params: OAuth2SignInParams(
scopes: ['openid', 'profile'],
));