discord_oauth2 1.0.1
discord_oauth2: ^1.0.1 copied to clipboard
A lightweight, secure, and ready-to-use Flutter package to integrate Discord OAuth2 login with PKCE support.
discord_oauth2 #
A lightweight, secure, and ready-to-use Flutter package to integrate Discord OAuth2 login with PKCE (Proof Key for Code Exchange) support.
Developed with Clean Architecture principles, this package handles the client-side system browser flow and safely hands off the temporary authorization code and code verifier to pass to your backend server.
Features #
- Secure PKCE (RFC 7636) Flow: Generates cryptographically secure code verifier and SHA-256 code challenge pairs automatically.
- Web & Mobile Support: Responsive design support that works on iOS, Android, and Web browsers.
- Pre-styled Brand Components: Includes the official
<DiscordSignInButton>widget styled with the official Discord brand color (#5865F2 Blurple) and Discord icon. - Robust Exception Model: Strongly typed exceptions for network issues, cancellation, protocol errors, or invalid callback parsing.
- CSRF Protection: Supports generating and passing custom
stateparameters to match OAuth2 security standards.
Getting Started #
1. Register Your Discord Application #
- Go to the Discord Developer Portal.
- Create a new Application and navigate to the OAuth2 tab.
- Add your Redirect URIs.
Important
Discord does not support custom schemes (e.g. my-app-scheme://...) directly in the developer portal redirect list. You must register a standard https:// URL (like https://api.yourdomain.com/auth/discord/callback) for production, or http://localhost:3000/auth/discord/callback for local testing/web.
See the Intermediary Server Redirect section below on how to easily redirect from your HTTPS server back to your mobile app custom scheme.
2. Configure Platforms #
Android Configuration
Add the custom scheme intent filter to your android/app/src/main/AndroidManifest.xml (inside the <activity> tag of your main activity):
<intent-filter android:label="flutter_web_auth_2">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="my-app-scheme" />
</intent-filter>
iOS Configuration
Add the custom scheme to your ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>auth_callback</string>
<key>CFBundleURLSchemes</key>
<array>
<string>my-app-scheme</string>
</array>
</dict>
</array>
Handling Redirection on Mobile (Server-Side Intermediary Redirect) #
Since Discord requires a valid https redirect URI for production applications, you must set up a simple redirect endpoint on your server. When Discord redirects the user back to your HTTPS server, the server forwards the parameters back to your app's custom scheme (my-app-scheme://).
Node.js (Express) Server Redirect Endpoint #
Here is an example setup using Node.js and Express:
app.get('/auth/discord/callback', (req, res) => {
const { code, state, error, error_description } = req.query;
let redirectUri = 'my-app-scheme://auth/discord/callback';
const params = [];
if (code) params.push(`code=${code}`);
if (state) params.push(`state=${state}`);
if (error) params.push(`error=${error}`);
if (error_description) params.push(`error_description=${error_description}`);
if (params.length > 0) {
redirectUri += `?${params.join('&')}`;
}
// Forward to mobile custom scheme deep link
res.redirect(redirectUri);
});
Usage #
Complete Integration Example #
import 'package:flutter/material.dart';
import 'package:discord_oauth2/discord_oauth2.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const LoginScreen(),
);
}
}
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
// 1. Initialize DiscordSignIn configuration
// For mobile: Use the HTTPS URL of your server-side intermediary as redirectUri.
final DiscordSignIn _discordSignIn = DiscordSignIn(
clientId: 'YOUR_DISCORD_CLIENT_ID',
redirectUri: 'https://auth.yourdomain.com/discord/callback',
customScheme: 'my-app-scheme',
);
bool _isLoading = false;
Future<void> _login() async {
setState(() => _isLoading = true);
try {
// 2. Launch browser auth flow
final result = await _discordSignIn.getAuthCode(
state: 'secure_random_state_string',
scopes: ['identify', 'email'],
);
// 3. Send authorization code and verifier to your backend
print('Auth Code: ${result.code}');
print('PKCE Verifier: ${result.codeVerifier}');
print('State: ${result.state}');
await _exchangeCodeWithBackend(result.code, result.codeVerifier);
} on DiscordAuthException catch (e) {
if (e is DiscordAuthCancelledException) {
print('Authentication cancelled.');
} else {
print('Authentication failed: ${e.message}');
}
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _exchangeCodeWithBackend(String code, String verifier) async {
// Send code and verifier to your backend server to exchange for tokens.
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _isLoading
? const CircularProgressIndicator()
: DiscordSignInButton(onPressed: _login),
),
);
}
}
Exception Hierarchy #
All authentication exceptions subclass DiscordAuthException:
DiscordAuthCancelledException: User closed the web flow prematurely.DiscordAuthFailedException: The flow failed (e.g., Discord returned an authorization error, invalid client credentials).DiscordAuthCodeMissingException: The redirect was parsed but did not contain the authorization code parameter.DiscordAuthNetworkException: A connection or Socket exception occurred during the browser request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.