I’m implementing an OAuth2 authentication flow in my Flutter app using flutter_web_auth_2
. The authentication works fine after the first login (when the browser already has an active session), but during the first login or after a logout, the OAuth2 provider doesn’t redirect back to my app’s callback URL (com.app.app://oauthredirect
).
com.app.app
is scheme, oauthredirect
is a host.
AndroidManifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
</activity>
<activity
android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
android:exported="true">
<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="com.app.app" android:host="oauthredirect" />
</intent-filter>
</activity>
<activity android:name="com.android.camera.CropImage" />
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<!-- Flutter Metadata -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
This is login
functionality
// Clear existing tokens
await _clearExistingTokens();
// Determine platform type
final String? platformType = _getPlatformType();
// Generate PKCE parameters
final codeVerifier = _generateCodeVerifier();
final codeChallenge = _generateCodeChallenge(codeVerifier);
// OAuth2 Configuration
final String clientId = 'ad6jnO5ZuqIbrSQEtF05xA67Fc0JW7JJ6vQNYzuW';
final String redirectUri = 'com.app.app://oauthredirect';
final List<String> scopes = ['openid'];
// Perform authentication
final result = await FlutterWebAuth2.authenticate(
url: _constructAuthorizationUrl(
clientId: clientId,
redirectUri: redirectUri,
codeChallenge: codeChallenge,
scopes: scopes,
),
callbackUrlScheme: "com.app.app",
options: FlutterWebAuth2Options(
preferEphemeral: true,
));
// Extract authorization code
final Uri uri = Uri.parse(result);
final String? code = uri.queryParameters['code'];
if (code == null) {
throw Exception('Authorization code not found');
}
// Exchange authorization code for tokens
final Map<String, dynamic> tokenResponse = await _exchangeCodeForTokens(
code: code,
clientId: clientId,
redirectUri: redirectUri,
codeVerifier: codeVerifier,
);
// Extract tokens
final String? accessToken = tokenResponse['access_token'];
final String? idToken = tokenResponse['id_token'];
if (accessToken == null || idToken == null) {
throw Exception('Failed to retrieve tokens');
}
// Store tokens securely
await _storeTokens(accessToken, idToken);
These are the logs on the backend side. As you can see it doesn’t send any request to token
endpoint after authentication
"POST /login/?next=/api/oauth2/authorize/%3Fclient_id%3Dad6jnO5ZuqIbrSQEtF05xA67Fc0JW7JJ6vQNYzuW%26redirect_uri%3Dcom.app.app%253A%252F%252Foauthredirect%26response_type%3Dcode%26scope%3Dopenid%26code_challenge%3DNigW_Vlfoj5B0pi81a4Vp6OPt8g8QRn05TaOOCp5pi4%26code_challenge_method%3DS256%26nonce%3D1732190076328 HTTP/1.1" 302 0
[21/Nov/2024 11:54:42] "GET /api/oauth2/authorize/?client_id=ad6jnO5ZuqIbrSQEtF05xA67Fc0JW7JJ6vQNYzuW&redirect_uri=com.app.app%3A%2F%2Foauthredirect&response_type=code&scope=openid&code_challenge=NigW_Vlfoj5B0pi81a4Vp6OPt8g8QRn05TaOOCp5pi4&code_challenge_method=S256&nonce=1732190076328 HTTP/1.1" 302 0
What can cause this problem? I’ve checked redirect uri a thousand times already and it seems correct so it can’t be the problem. Additionally, I’ve tried to implement authentication with flutter_appauth
still the same problem. Oauth2 provider is custom, not google, github etc.