How to pin public key of SSL certificate in flutter? - flutter

Could someone help me on implementing SSL public key pinning in flutter? I have searched a lot in google but I did not find a proper article that explains how this can be implemented.
Kindly help !!!

There is a package called http_certificate_pinning which provides 3 different APIs to use. You can check it here.
1-As an interceptor for Dio:
import 'package:http_certificate_pinning/certificate_pinning_interceptor.dart';
// Add CertificatePinningInterceptor in dio Client
Dio getClient(String baseUrl, List<String> allowedSHAFingerprints){
var dio = Dio(BaseOptions(baseUrl: baseUrl))
..interceptors.add(CertificatePinningInterceptor(allowedSHAFingerprints));
return dio;
}
myRepositoryMethod(){
dio.get("myurl.com");
}
2-Creating an http client:
import 'package:http_certificate_pinning/secure_http_client.dart';
// Uses SecureHttpClient to make requests
SecureHttpClient getClient(List<String> allowedSHAFingerprints){
final secureClient = SecureHttpClient.build(certificateSHA256Fingerprints);
return secureClient;
}
myRepositoryMethod(){
secureClient.get("myurl.com");
}
3-Checking if the handshake happens correctly and do whatever you want:
import 'package:http_certificate_pinning/http_certificate_pinning.dart';
Future myCustomImplementation(String url, Map<String,String> headers, List<String> allowedSHAFingerprints) async {
try{
final secure = await HttpCertificatePinning.check(
serverURL: url,
headerHttp: headers,
sha: SHA.SHA256,
allowedSHAFingerprints:allowedSHAFingerprints,
timeout : 50
);
if(secure.contains("CONNECTION_SECURE")){
return true;
}else{
return false;
}
}catch(e){
return false;
}
}

Related

Why is my flutter app not handling Spotify's API authorization after signing in?

I'm making a flutter app using Spotify's API. I have a basic homepage that uses a button to launch a browser to login to Spotify. Here is my backend code:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:uni_links/uni_links.dart';
class SpotifyAuth with ChangeNotifier {
final String CLIENT_ID = "My client ID";
final String ClIENT_SECRET = "My client secret";
final String REDIRECT_URI = "http://localhost:8000/callback";
final String SCOPE = 'user-read-private user-read-email';
// var state = 'your-state';
late String _accessToken;
late String _refreshToken;
Uri createAuthenticationUri(){
var query = [
'response_type=code',
'client_id=$CLIENT_ID',
'scope=${Uri.encodeComponent(SCOPE)}',
'redirect_uri=${Uri.encodeComponent(REDIRECT_URI)}',
];
var queryString = query.join('&');
var url = 'https://accounts.spotify.com/authorize?' + queryString;
var parsedUrl = Uri.parse(url);
return parsedUrl;
}
Future<void> launchInBrowser() async {
if (!await launchUrl(
createAuthenticationUri(),
mode: LaunchMode.externalApplication,
)){
throw Exception('Could not launch Url');
}
}
Future<void> launchAuth() async {
await launchInBrowser();
await initUniLinks();
}
Future<void> getAccessToken(String code) async {
var body = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
"client_id": CLIENT_ID,
"client_secret": ClIENT_SECRET
};
// Create a request header with the required information
var header = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization":
"Basic ${base64Encode(utf8.encode("$CLIENT_ID:$ClIENT_SECRET>"))}"
};
// Send the request to the Spotify token endpoint
var response = await http.post(
Uri.parse("https://accounts.spotify.com/api/token"),
body: body,
headers: header);
// Check if the request was successful
if (response.statusCode == 200) {
// Parse the JSON response
var data = json.decode(response.body);
// Get the access token from the response
String accessToken = data["access_token"];
// Store the access token for future use
// ...
_accessToken = accessToken;
} else {
print("Error");
}
}
Future<void> initUniLinks() async {
// Get the latest initial link
String? initialLink = await getInitialLink();
// Check if the link contains a Spotify authorization code
if (initialLink != null && initialLink.contains("code=")) {
// Extract the code from the link
String code = initialLink.split("code=")[1];
// Use the code to get an access token from Spotify
getAccessToken(code);
}
else{
print("Nothing");
}
}
}
My redirect URI is set in the spotify dashboard.
My app widget calls luanchAuth();
and then it should wait for the authentication code with initUniLinks() but it seems like initUniLinks() executes immediately without waiting for the authentication. When I authenticate in Spotify, it throws a generic "can't connect to localhost" error page but the url includes the auth code that I need.
Not sure what I'm doing wrong here. Admittedly I'm new to Oauth and app-api-connections in general but I thought this would work.
REDIRECT URI is the problem here, You cannot have redirect URI with localhost it fails. either use ngrok and provide the mapped https url or host your callback url and provide it.
Use the custom scheme for redirect_uri, something like this my-app://token/callback. See App Settings for Spotify rules.
Then configure the application for Deep Linking to receive the authentication response.

How to do certificate pinning with chopper client

I'm developing an application using ChopperClient. To improve application security I want to do certificate pinning by using http_certificate_pinning library.
What I've tried:
I try using HttpCertificatePinning.check as suggested in the library's official guide. The serverURL is my mock api url. When I run the application, the application crashed and exited. When I change the url to https://www.google.com/ the application is not crash and the result is returned as false.
Does anyone have experience using Chopper with this library?
How should I do certificate pinning with this library?
Future<bool> myCustomImplementation(String url, Map<String, String> headers,
List<String> allowedSHAFingerprints) async {
try {
final String secure = await HttpCertificatePinning.check(
serverURL: url, //mock api url
headerHttp: headers, //mock headers
sha: SHA.SHA256,
allowedSHAFingerprints: allowedSHAFingerprints, //mock fingerprints
timeout: 100);
if (secure.contains("CONNECTION_SECURE")) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}

Why can't I see a cookie I sent from Flask to Flutter in the browser?

I am creating a Flutter Web app that requires login verification. The user makes a post request with authentication information and then my Flask app with send a cookie back to the client.
Here is the code for the Flask App
#app.route('/test', methods=['POST'])
#cross_origin(supports_credentials=True)
def test():
resp = jsonify({'message' : 'Logged in!'})
resp.set_cookie('Set-Cookie', "token", httponly = True, secure = False)
return resp
Here is the Dart/Flutter code where I make the POST request and expect a cookie called 'Set-Cookie'.
class HttpService {
static var dio = Dio();
static testMethod() async {
try {
dio.options.extra['withCredentials'] = true;
var response = await dio.post('http://127.0.0.1:5000/test');
print(response);
} catch (e) {
print(e);
}
}
As you can see, I don't receive this cookie on my browser, but the request is successful and I get the JSON message!
BUT, when I make this same request on Postman, I get the JSON response AND the cookie.
Any help would be greatly appreciated! Let me know if you need any more details/code.
Thanks to Kris, I realized I was making the request from Flutter (Client) to an IP rather than the domain name localhost. Because setting a cookie is domain specific, I couldn't see the cookie set in the developer console.
Here is the updated code
static testMethod() async {
try {
dio.options.extra['withCredentials'] = true;
var response = await dio.post('http://localhost:5000/test');
print(response);
} catch (e) {
print(e);
}
}

How to use http interceptor in a flutter project?

I have to add header to all my Api's. I was told to use http interceptor for that. But i am not able to understand how to do it as i am new to flutter. Can anyone help me with example?
you can use http_interceptor.
it works as follows,
first you create your interceptor by implementing InterceptorContract
class MyInterceptor implements InterceptorContract {
#override
Future<RequestData> interceptRequest({RequestData data}) async {
try {
data.headers["Content-Type"] = "application/json";
} catch (e) {
print(e);
}
return data;
}
#override
Future<ResponseData> interceptResponse({ResponseData data}) async => data;
}
then create a client and inject this interceptor in it
Client _client = InterceptedClient.build(interceptors: [
MyInterceptor(),
]);
You can add multiple interceptors to the same client, say you want one to refresh the token, one to add/change headers, so it will be something like this:
Client _client = InterceptedClient.build(interceptors: [
RefreshTokenInterceptor(),
ContentTypeInterceptor(),
/// etc
]);
note that every interceptor must implement the InterceptorContract
Now anytime you use this client, the request will be intercepted and headers will be added to it. You can make this client a singleton to use the same instance across the app like this
class HttpClient {
Client _client;
static void _initClient() {
if (_client == null) {
_client = InterceptedClient.build(
interceptors: [MyInterceptor()],
);
}
}
/// implement http request with this client
}

How to convert Future<StreamedResponse> to Future<Response>?

I'm trying to send a DELETE request with body, but http.delete doesn't support adding a body to the request.
I've found this solution online but I want to return Response rather than StreamedResponse. How can I achieve that?
static Future<http.Response> deleteFavorites({Map<String, int> body}) async {
UserRepository userRepository = UserRepository();
String token = await userRepository.storage.read(key: 'token');
final client = http.Client();
var response;
try {
response =
await client.send(http.Request("DELETE", Uri.parse(favoritesUrl))
..headers["access-token"] = token
..body = jsonEncode(body));
//
} finally {
client.close();
}
return response;
}
You can use the static method in Response class fromStream , that take StreamedResponse and return the Future that you want, just change the return to :
return Response.fromStream(response);
You can check the docs https://pub.dev/documentation/http/latest/http/Response-class.html .
Hope it helps!