Flutter REST Calls Time Out - flutter

I am making api calls via flutter to my server. I have checked my android manifest settings and I have placed
<uses-permission android:name="android.permission.INTERNET"/>
But, the api calls time out. Here is my call:
Future driverLogin(userdetails) async {
var url = 'http://$_server/api/token/';
print(userdetails.values);
await http.post(url, body: json.encode(userdetails), headers: {
"Content-Type": "application/json"
}).then((http.Response response) {
print(response.body);
final Map<String, dynamic> responseData = json.decode(response.body);
Token tkn = Token.fromJSON(responseData);
_accessTkn = tkn.access;
_refreshTkn = tkn.refresh;
print('Refreshingly refreshed ${tkn.refresh}');
if (response.statusCode == 200) {
addToken(_accessTkn);
addRefresh(_refreshTkn);
print(responseData);
// tokenType = 'email';
print('Signed In');
// preformLogin();
} else {
// popupSwitch(2);
print('Error Could not sign in');
}
});
return _accessTkn;
}
When I run it I get this error:
Bad state: Insecure HTTP is not allowed by platform:

make sure to import like this.
import 'package:http/http.dart' as http;
and make your url like this.
var url = 'https://$_server/api/token/';
Also check this links maybe will help you
https://flutter.dev/docs/release/breaking-changes/network-policy-ios-android
https://medium.com/flutter-community/solving-the-new-https-requirements-in-flutter-7abe240fbf23

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.

api calls - Flutter

I'm trying to make a api call but I can't success, everything goes fine until the call, and there stop everything and the function finish, what I do wrong?
Future<String> get() async {
var url = Uri.parse("https://10.0.2.2:7021/api/Auth/login");
var headers = {"content-type": "application/json"};
final msg = jsonEncode(
{"username": "string", "password": "string", "email": "string"});
var response = await http!.post(url, body: msg, headers: headers);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
print("Correct");
return "Correct";
} else {
print(response.statusCode);
print("User not allowed");
throw Exception("User not allowed");
}
}
Mistake comes here
debug window
You have probably imported the "wrong" http. Do it like that:
import 'package:http/http.dart' as http;
You dont need to write http! because http is a package and is never null and since you are doing http! I guess you imported something wrong in your code.
EDIT:
Maybe your device can't reach 10.0.2.2 (or that port), you should set a timeout to find it out fast:
http.post(url, body: msg, headers: headers).timeout(
const Duration(seconds: 3),
onTimeout: () {
// request timed out
return http.Response('Error', 408);
},
);
Have you imported http package like this?
import 'package:http/http.dart' as http;
And use case like this
var response = await http.post(url, body: msg, headers: headers);
add .php to login in the url :
https://10.0.2.2:7021/api/Auth/login.php
if you are working with php
or .py for python

How do I hook up a cross platform Flutter app with Azure AD

I have a cross platform application (mobile, desktop and web) created in Flutter that I would like to set up to be authenticated with Azure AD.
I know that there are some packages that you can add for mobile and maybe even for web but I am unable to find a working solution for desktop.
I thought that I could open the browser on the device and use that to sign the user in, but it would need a URI to redirect to when the user is authenticated and for the application to be able to get the token that I can then use to make calls to my API. I can't see how that would work though, due to the application being hosted on the users device and not on a server with a set IP like with websites.
Any possible solutions or guidance would be greatly appreciated.
I ended up using a combination of this older tutorial for Facebook authentication along with Microsoft documentation on how to get a token for native apps to create a small authenticating service seen below.
I used the following pub packages:
url_launcher
flutter_dotenv
http
Auth Service:
import 'dart:async';
import 'dart:io';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:research_library_viewer/Models/Token.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:http/http.dart' as http;
class AuthenticationService {
String tenant = dotenv.env['MSAL_TENANT']!;
String clientId = dotenv.env['MSAL_CLIENT_ID']!;
String clientSecret = dotenv.env['MSAL_CLIENT_SECRET']!;
String redirectURI = dotenv.env['MSAL_LOGIN_REDIRECT_URI']!;
String scope = dotenv.env['MSAL_CLIENT_SCOPE']!;
String authority = dotenv.env['MSAL_AUTHORITY_URI']!;
Future<Stream<String>> _server() async {
final StreamController<String> onCode = StreamController();
HttpServer server =
await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);
server.listen((HttpRequest request) async {
final String? code = request.uri.queryParameters["code"];
request.response
..statusCode = 200
..headers.set("Content-Type", ContentType.html.mimeType)
..write("<html><h1>You can now close this window</h1></html>");
await request.response.close();
await server.close(force: true);
if (code != null) {
onCode.add(code);
await onCode.close();
}
});
return onCode.stream;
}
String getAuthUrl() {
String authUrl =
"http://$authority/$tenant/oauth2/v2.0/authorize?client_id=$clientId&response_type=code&redirect_uri=$redirectURI&response_mode=query&scope=$scope";
return authUrl;
}
Map<String, dynamic> getTokenParameters(String token, bool refresh) {
Map<String, dynamic> tokenParameters = <String, dynamic>{};
tokenParameters["client_id"] = clientId;
tokenParameters["scope"] = scope;
tokenParameters["client_secret"] = clientSecret;
if (refresh) {
tokenParameters["refresh_token"] = token;
tokenParameters["grant_type"] = "refresh_token";
} else {
tokenParameters["code"] = token;
tokenParameters["redirect_uri"] = redirectURI;
tokenParameters["grant_type"] = "authorization_code";
}
return tokenParameters;
}
Future<Token> getToken() async {
String url = getAuthUrl();
Stream<String> onCode = await _server();
if (await canLaunch(url)) {
await launch(url);
} else {
throw "Could not launch $url";
}
final String code = await onCode.first;
final Map<String, dynamic> tokenParameters =
getTokenParameters(code, false);
final response = await http.post(
Uri.https(
'login.microsoftonline.com',
'$tenant/oauth2/v2.0/token',
),
headers: <String, String>{
'Content-Type': 'application/x-www-form-urlencoded'
},
body: tokenParameters);
if (response.statusCode == 200) {
return tokenFromJson(response.body);
} else {
throw Exception('Failed to acquire token');
}
}
Future<Token> refreshToken(String? refreshToken) async {
if (refreshToken == null) {
return getToken();
} else {
final Map<String, dynamic> tokenParameters = getTokenParameters(refreshToken, true);
final response = await http.post(
Uri.https(
'login.microsoftonline.com',
'$tenant/oauth2/v2.0/token',
),
headers: <String, String>{
'Content-Type': 'application/x-www-form-urlencoded'
},
body: tokenParameters);
if (response.statusCode == 200) {
return tokenFromJson(response.body);
} else {
throw Exception('Failed to acquire token');
}
}
}
}
Token:
import 'dart:convert';
Token tokenFromJson(String str) {
final jsonData = json.decode(str);
return Token.fromJson(jsonData);
}
class Token {
String accessToken;
String tokenType;
num? expiresIn;
String? refreshToken;
String? idToken;
String? scope;
Token({
required this.accessToken,
required this.tokenType,
this.expiresIn,
this.refreshToken,
this.idToken,
this.scope,
});
factory Token.fromJson(Map<String, dynamic> json) => Token(
accessToken: json["access_token"],
tokenType: json["token_type"],
expiresIn: json["expires_in"],
refreshToken: json["refresh_token"],
idToken: json["id_token"],
scope: json["scope"],
);
Map<String, dynamic> toJson() => {
"access_token": accessToken,
"token_type": tokenType,
"expires_in": expiresIn,
"refresh_token": refreshToken,
"id_token": idToken,
"scope": scope,
};
}
I think that this could still be improved a lot, but it is definitely something to start with if you are sitting with a similar challenge.
Found an MS document you can follow to add Azure Authentication in your Desktop application.
Refer this : Sign-in a user with the Microsoft Identity Platform in a WPF Desktop application and call an ASP.NET Core Web API
There is also another way for the same but with Azure AD B2C : Configure authentication in a sample WPF desktop app by using Azure AD B2C
The application registration and architecture are illustrated in the following diagrams:

Is there something wrong with the way [Flutter Web] sends a request POST message to the API server?

The open api request I'm trying to use requires an image binary value with content-type of multipart/form-data format.
I know you can't use dart:io in flutter web. I tried to upload an image in multipart/form-data format to the api server in flutter web while looking at several posts.
However, only a message appeared stating that the image could not be recognized.
This is the last thing I tried to create multipart types in flutter web.
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
PlatformFile? objFile;
pickImage() async {
var result = await FilePicker.platform.pickFiles(
withReadStream: true,
);
setState(() {
objFile = result!.files.single;
});
uploadImage();
}
uploadImage() async {
FormData formData = FormData.fromMap({'image' : MultipartFile(test!, objFile!.size, filename: objFile!.name)});
Dio dio = new Dio();
var response = await dio.post('API url', data: formData);
}
I additionally used Multipart.form Bytes from http , Multipart.form Bytes from dio . But the result was the same.
The value checked by the request body through the postman interceptor.
content-type=multipart/form-data;bounary=--dio-boundary-1105759322
----dio-boundary-1105759322
content-disposition: form-data; name="image"; filename="test.jpeg"
content-type: application/octet-stream
ÿØÿÛC
%# , #&')*)-0-(0%()(ÿÛC
(((((((((((((((((((((((((((((((((((((((((((((((((((ÿÀŽv"ÿÄÿÄC!1AQaq"‘2¡±#BÁÑR3CðñSbr’á‚Â$&4c“ÿÄÿÄ&!1A2Q"a3BRÿÚ?ù× „É<$/cŸt8D`aú¦Ä#bálŒZVM„ٔʓTL›eOò¢“
èKÇ(p¢‰¥C’ÄÙ‚Ñx²Ù1Jcœ)B›¢$ ¢‚&
‚7› ˜Žp”{&ÊÀÁAî¤Æ
‚nÈ CØÃêOýÒ›§á$sÊ‚r¡ìLÂ…;"éMI½î«gæV<æ6οÙ%_ƒY®}7Òû€¯MŒ&g¹å|µ£ëÐúc\tÚƵƈúÕ]#kQ‹D/Ÿú·cu9«Hà/¢lÚ–êè·¼&Þt
¯H‚&ɶìÛà®iƒh²SöãÔTs[l›/?[s(’˜¨o€¤Û‹*¥AÖ”ðbUgYR’!äJ!M‹™‹«›î©aÉ*ᕨ4p SÉ…¤)‰ì§=‘âJ» oÙGDRåÌy0—²û r ò€·²?Te8±KSTR8ŹDAååþ7)Oˆk)õ²Qk#Ù€Œ ?DÜû&Ä›„ÍÅ”lQjð¡NÑ%HTWP˜²wýÒc(Ÿð¤ð¢S<*6º>ÊaCœ „Ù0
^J(ª%¢ƒFPm‘^u4^èM‘åL…##•0Qÿ ºi…32§ÙC•D¿&Èw’ˆº‘Ü"…”<&ýРwP {p ¸DCd¼&ÿ©#¨ˆ› La~¨p¦„)’÷‚ˆº²æÒ›ªĘ̀Šaá€0‹n <ò¦M“YM„ L«=ÕnæÊlªŽÂƒóc„m‚—È™Uó ªºäªÛ•F†\…}7?¨ªZL`*£è¾ŽÝÌ1¤ÜBúk6­
---------------------------SKIP------------------------------
PTiMÂ!¢(èÊ€YÊÂœ"ÑÂ_T<Ñ5îPp™ð ¨„ôOˤ?¢z\ÂÚ¡½ÐiÊc쨟ÝHŸ¢“3ÝA˜( ‘ÊH›(l€Å¼)Ä‘rEÈ[€‹¬”¼x
W7q?ΣHt®“§¤y\½Ìÿ:ÿÍtÖ§T°AÊÕ\ËZVƒÔPha30%1*¶›Ž!7è¥|f›„îÕQ±„9N6åW,¨^Ù8PHN./Ê€îª2ß*{(l¡™šOU¢Ôå3œ*ꜨŠ‹“3¼$«B*ÌŒS„+EÒ‘Ý VHpV±`²³ó€µgܪ‚#“Ü)À!NPCƒÝIÅԛ–”xý”²™# ?U‚‹n€å!Œ¦&é*ƒ™¨wÄÖØY¢>«}&ü¢×\Ý?ó*9ç%Òº˜#çò H€¥&ꃒ¤(
‚0O8##EÎéÊœ#TÕr‚ºT¹ÈÔ7T“2¢ƒœbÅsuOî¶Ô0>‹ŸT|Gô•Óa®ïšÔÇe¤T
he<,¨[ü¶[…·M#ZOˆjtˤÝE© QÿÙ
----dio-boundary-1105759322--
When I use the MultipartFile.fromFile method used in flutter ios, I got the response normally. So I'm pretty sure there must be some mistake or misinformation in the flutter web setup.
Thanks in advance!
this is how I managed to upload an image to Laravel backend using Flutter Web
import 'dart:io';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
Future pickupUserImage2() async {
PlatformFile? objFile;
var picked = await FilePicker.platform.pickFiles();
objFile = picked!.files.single;
String? apiUrl = Get.find<MainController>().apiUrl;
String url = '';
if (apiUrl != null) url = apiUrl + 'images';
List<int> list;
list = List<int>.from(objFile.bytes!);
dio.FormData formData = dio.FormData.fromMap({'file': dio.MultipartFile.fromBytes(list, filename: objFile.name)});
DioNetworking _dio = new DioNetworking();
dynamic result = await _dio.postData(
url,
formData,
contentType: 'multipart/form-data',
);
if (result['status'] == true) {
showToast(result['message']);
return ImageModel.Image.fromJson(result['data']);
}
}
I added a file using MultipartFile.fromBytes which has the first parameter as List which I created from this method "List.from(objFile.bytes!);"
Notes :
ImageModel.Image is a model I created to handle the image result
DioNetworking is a class that perform dio requests , I just created it to do the
authentication stuff
import 'package:dio/dio.dart';
class DioNetworking {
Dio _dio = new Dio();
Future postData(
String url,
dynamic data, {
String? contentType,
}) async {
try {
Response response = await _dio.post(url,
data: data,
options: Options(headers: {
'content-type': contentType != null ? contentType : 'application/json',
'Accept': 'application/json',
// 'Authorization': 'Bearer ${token ?? ''}'
// other headers
}));
if (response.statusCode == 200) {
dynamic data = response.data;
return data;
} else {
print(response.statusCode);
}
} on DioError catch (e) {
print(e);
if (e.type == DioErrorType.connectTimeout) {
return {'status': 'Connect Timed Out'};
}
if (e.type == DioErrorType.receiveTimeout) {
return {'status': 'Receive Timed Out'};
}
if (e.type == DioErrorType.response) {
print(e.response!.data);
print(e.response!.headers);
}
}
}
}

Dart / flutter login Post request

I'm trying to log in on this forum, I tried several ways (Dio, Requests ) without result.
I tested it with postman using other languages ​​and libraries (like curl, python, node) and it works like a charm.
[EDIT]
Ok finally got it!
once logged in, the server returns the status code 303.
and since followredirects is set to true by default, a new request was made without session cookies.
That's why I never found the session cookie in the response header.
A method similar to "history" from lib python requests would be great here.
new:
import 'package:http/http.dart';
void main() async {
final request = new Request(
'POST', Uri.parse('https://xenforo.com/community/login/login'))
..headers.addAll({"Content-Type": "application/x-www-form-urlencoded"})
..bodyFields = {'login':'myuser',
'password': 'mypass'}
..followRedirects = false; // don't follow the damn 303 code if you're not
// going to set the cookies automatically.
final response = await request.send();
print(response.statusCode); // 303 redirected successfully logged in!
print(response.headers); // session token: xf_session=oisufhisuefhsef...
}
or
import 'dart:io';
void main() async {
final client = HttpClient();
final request = await
client.postUrl(Uri.parse("https://xenforo.com/community/login/login"));
request.headers.set(HttpHeaders.contentTypeHeader, "application/x-www_form-urlencoded");
request.followRedirects = false;
request.write("login=myusername&password=mypass");
final response = await request.close();
print(response.statusCode);
print(response.headers);
}
You should either use await or then, try the following code:
import 'package:http/http.dart' as http;
void main() {
http.get("https://xenforo.com/community/").then((response) {
var ls = response.headers['set-cookie'].split(';');
final cookie = ls[0] + '; ' + ls[5].split(',')[1];
login(cookie);
});
}
void login(String cookie) {
http.post("https://xenforo.com/community/login/login/", headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': cookie
}, body: {
'login': 'myusernam',
'password': 'mypass'
}).then((response) {
print(response.statusCode);
print(response.body);
});
}
ok finally got it Every time the login was successful, the request was automatically redirected without the session cookies. FollowRedirects need set to false to work properly and http.post does not have this option.
A method similar to the "history" of lib python requests would be great.