How do you implement Google OAuth2 or Microsoft (Azure) OAuth2 login on Flutter Desktop?
How do you implement Google OAuth2 or Microsoft (Azure) OAuth2 login on Flutter Desktop?
Answering my own question. The overarching process to get the OAuth2 result is:
http://localhost:#####/ that the dart app is listening to.Setup the OAuth flows you want to support. This is what I used:
Authorized redirect URIs the localhost url: http://localhost/enum LoginProvider { google, azure }
extension LoginProviderExtension on LoginProvider {
String get key {
switch (this) {
case LoginProvider.google:
return 'google';
case LoginProvider.azure:
return 'azure';
}
}
String get authorizationEndpoint {
switch (this) {
case LoginProvider.google:
return "https://accounts.google.com/o/oauth2/v2/auth";
case LoginProvider.azure:
return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
}
}
String get tokenEndpoint {
switch (this) {
case LoginProvider.google:
return "https://oauth2.googleapis.com/token";
case LoginProvider.azure:
return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
}
}
String get clientId {
switch (this) {
case LoginProvider.google:
return "GOOGLE_CLIENT_ID";
case LoginProvider.azure:
return "AZURE_CLIENT_ID";
}
}
String? get clientSecret {
switch (this) {
case LoginProvider.google:
return "GOOGLE_SECRET"; // if applicable
case LoginProvider.azure:
return "AZURE_SECRET"; // if applicable
}
}
List<String> get scopes {
return ['openid', 'email']; // OAuth Scopes
}
}
Setup the OAuth Manager to create listen for the oauth2 redirect
import 'dart:async';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:url_launcher/url_launcher.dart';
import 'package:window_to_front/window_to_front.dart';
class DesktopLoginManager {
HttpServer? redirectServer;
oauth2.Client? client;
// Launch the URL in the browser using url_launcher
Future<void> redirect(Uri authorizationUrl) async {
var url = authorizationUrl.toString();
if (await canLaunch(url)) {
await launch(url);
} else {
throw Exception('Could not launch $url');
}
}
Future<Map<String, String>> listen() async {
var request = await redirectServer!.first;
var params = request.uri.queryParameters;
await WindowToFront.activate(); // Using window_to_front package to bring the window to the front after successful login.
request.response.statusCode = 200;
request.response.headers.set('content-type', 'text/plain');
request.response.writeln('Authenticated! You can close this tab.');
await request.response.close();
await redirectServer!.close();
redirectServer = null;
return params;
}
}
class DesktopOAuthManager extends DesktopLoginManager {
final LoginProvider loginProvider;
DesktopOAuthManager({
required this.loginProvider,
}) : super();
void login() async {
await redirectServer?.close();
// Bind to an ephemeral port on localhost
redirectServer = await HttpServer.bind('localhost', 0);
final redirectURL = 'http://localhost:${redirectServer!.port}/auth';
var authenticatedHttpClient =
await _getOAuth2Client(Uri.parse(redirectURL));
print("CREDENTIALS ${authenticatedHttpClient.credentials}");
/// HANDLE SUCCESSFULL LOGIN RESPONSE HERE
return;
}
Future<oauth2.Client> _getOAuth2Client(Uri redirectUrl) async {
var grant = oauth2.AuthorizationCodeGrant(
loginProvider.clientId,
Uri.parse(loginProvider.authorizationEndpoint),
Uri.parse(loginProvider.tokenEndpoint),
httpClient: _JsonAcceptingHttpClient(),
secret: loginProvider.clientSecret,
);
var authorizationUrl =
grant.getAuthorizationUrl(redirectUrl, scopes: loginProvider.scopes);
await redirect(authorizationUrl);
var responseQueryParameters = await listen();
var client =
await grant.handleAuthorizationResponse(responseQueryParameters);
return client;
}
}
class _JsonAcceptingHttpClient extends http.BaseClient {
final _httpClient = http.Client();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers['Accept'] = 'application/json';
return _httpClient.send(request);
}
}
Begin the login flow using
GOOGLE:
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
final provider = DesktopOAuthManager(loginProvider: LoginProvider.google);
provider.login();
}
AZURE:
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
final provider = DesktopOAuthManager(loginProvider: LoginProvider. azure);
provider.login();
}
You're done!
Credits to this tutorial for explaining this process of Github OAuth2 login on Flutter Desktop.