Flutter OAuth request fails when using self-signed certificate
DART OPTION 1
Looks like Dart has its own root certificates. The preferred option is to avoid writing any security code. Instead in a development environment, configure your self signed host's root certificate as trusted by Dart, according to this guide.
DART OPTION 2
Looks like Dart also supports the C# certificate callback model, where there is a Bad Certificate Callback that you can override. Not sure if you have to subclass HttpClient to achieve this.
/* PSEUDOCODE */bool callback(X509Certificate cert, String host, int port) { // Don't allow any exceptions in production if (currentEnvironment == "DEV" && host == "myhost.com") { return true; } // Use system return base.callback(cert, host, port)}
MOBILE OAUTH RECOMMENDATIONS
I see you are trying a few different libraries to solve your SSL trust problem. So I thought I'd point out what I look for in a mobile OAuth library, in line with mobile security standards, where these are the key recommendations:
- Use Authorization Code Flow (PKCE)
- Login via the System Browser
- Prefer HTTPS redirect URLs (claimed HTTPS schemes)
I would at least aim to use the correct flow as above. I'm always a bit wary of new tech stacks and their OAuth libraries, since they often don't implement the recommended behaviour.
The preferred library from a security viewpoint is probably Flutter AppAuth. I have often used AppAuth libraries with self signed certificates, but the AppAuth library comes with these challenges:
- Login on a System Browser is tricky to make reliable
- Could be quite a bit more work than your stakeholders want to pay for
- User experience aspects may be different to what people are used to
- The Flutter bridge may come with its own problems
APP AUTH RESOURCES OF MINE
When you get some time it may be worth browsing my blog posts and running my Swift / Kotlin code samples, to see if you think any of this behaviour would be useful to you:
Completing some previous answers, I discovered that it's possible to pass an http.Client
as a named argument in the oauth2.AuthorizationCodeGrant
constructor.
So I made one that hooks a badCertificateCallback
where I can implement some rules to ignore certificate validation under some particular circumstances (like calling 10.0.2.2 from an emulator in a dev environment). I think it could go as far as looking at some X509Certificate
attributes to make the decision.
bool _certificateCheck(X509Certificate cert, String host, int port) => host == '10.0.2.2'; http.Client devEmulatorClient() { var ioClient = new HttpClient() ..badCertificateCallback = _certificateCheck; return new IOClient(ioClient); } final grant = oauth2.AuthorizationCodeGrant( _clientId, _authorizationEndpoint, _tokenEndpoint, httpClient: devEmulatorClient());
And later whereas this call use to throw the certificate check exception, it is now accepting my dev self-signed certificate.
var client = await grant.handleAuthorizationResponse(responseUrl.queryParameters);
Required imports:
import 'dart:io';import 'package:http/http.dart' as http;import 'package:http/io_client.dart';import 'package:flutter_web_auth/flutter_web_auth.dart';import 'package:oauth2/oauth2.dart' as oauth2;