Post request with Cookies in Flutter - post

I'm trying to add Cookies to my request:
Here i get csrftoken with a GET request:
Future<String> getCsrftoken() async{
var response = await http.get(Uri.encodeFull('http://test/accounts/login/'));
var csrftoken = response.headers.remove('set-cookie').substring(10,74); //csrf
64 chars
return csrftoken;
}
Here i'm trying to perform the POST (application/x-www-form-urlencoded) request using the package Dio.
getSessionId() async {
var csrf = await getCsrftoken();
var cj = new CookieJar();
List<Cookie> cookies = [new Cookie("csrftoken", csrf)];
cj.saveFromResponse(Uri.parse("http://test/accounts/login/"), cookies);
List<Cookie> results = cj.loadForRequest(Uri.parse("http://test/accounts/login/"));
var dio = new Dio(new Options(
baseUrl: "http://test/accounts/login/",
connectTimeout: 5000,
receiveTimeout: 100000,
// 5s
headers: {
},
contentType: ContentType.JSON,
// Transform the response data to a String encoded with UTF8.
// The default value is [ResponseType.JSON].
responseType: ResponseType.PLAIN
));
Response<String> response;
response = await dio.post("",
data: {
"username": "username",
"password": "password",
"csrfmiddlewaretoken" : getCsrftoken()
},
// Send data with "application/x-www-form-urlencoded" format
options: new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded")),
);
print(response.statusCode);
}
I get 403 status code, because i need to add as a cookie csrftoken.
How should I proceed?

From the Dio Dart API Docs:
Cookie Manager
You can manage the request/response cookies using cookieJar .
The dio cookie manage API is based on the withdrawn cookie_jar.
You can create a CookieJar or PersistCookieJar to manage cookies automatically, and dio use the CookieJar by default, which saves the cookies in RAM. If you want to persists cookies, you can use the PersistCookieJar class, the example codes as follows:
var dio = new Dio();
dio.cookieJar=new PersistCookieJar("./cookies");
PersistCookieJar is a cookie manager which implements the standard cookie policy declared in RFC. PersistCookieJar persists the cookies in files, so if the application exit, the cookies always exist unless call delete explicitly.
More details about cookie_jar see : https://github.com/flutterchina/cookie_jar .

Check if the csrftoken needs to be passed in the header and the cookie or just one of them. It sometimes needs to be included as a header, which is shown in the example below, but the header name varies. To persist cookies, use a PersistCookieJar. Other options are persisted through BaseOptions (previously named Options).
Add to pubspec.yaml the latest versions of these plugins
path_provider: ^1.1.0
dio: ^2.1.6
cookie_jar: ^1.0.0
In a new class named webFunctions:
import 'dart:io';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';
class webFunctions {
final Dio _dio = Dio();
PersistCookieJar persistentCookies;
final String URL = "http://test/";
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<Directory> get _localCoookieDirectory async {
final path = await _localPath;
final Directory dir = new Directory('$path/cookies');
await dir.create();
return dir;
}
Future<String> getCsrftoken() async{
try {
String csrfTokenValue;
final Directory dir = await _localCoookieDirectory;
final cookiePath = dir.path;
persistentCookies = new PersistCookieJar(dir: '$cookiePath');
persistentCookies.deleteAll(); //clearing any existing cookies for a fresh start
_dio.interceptors.add(
CookieManager(persistentCookies) //this sets up _dio to persist cookies throughout subsequent requests
);
_dio.options = new BaseOptions(
baseUrl: URL,
contentType: ContentType.json,
responseType: ResponseType.plain,
connectTimeout: 5000,
receiveTimeout: 100000,
headers: {
HttpHeaders.userAgentHeader: "dio",
"Connection": "keep-alive",
},
); //BaseOptions will be persisted throughout subsequent requests made with _dio
_dio.interceptors.add(
InterceptorsWrapper(
onResponse:(Response response) {
List<Cookie> cookies = persistentCookies.loadForRequest(Uri.parse(URL));
csrfTokenValue = cookies.firstWhere((c) => c.name == 'csrftoken', orElse: () => null)?.value;
if (csrfTokenValue != null) {
_dio.options.headers['X-CSRF-TOKEN'] = csrfTokenValue; //setting the csrftoken from the response in the headers
}
return response;
}
)
);
await _dio.get("/accounts/login/");
return csrfTokenValue;
} catch (error, stacktrace) {
print("Exception occured: $error stackTrace: $stacktrace");
return null;
}
}
getSessionId() async {
try {
final csrf = await getCsrftoken();
FormData formData = new FormData.from({
"username": "username",
"password": 'A *passphrase* is stronger than a password.',
"csrfmiddlewaretoken" : '$csrf'
});
Options optionData = new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
);
Response response = await _dio.post("/accounts/login/", data: formData, options: optionData);
print(response.statusCode);
} on DioError catch(e) {
if(e.response != null) {
print( e.response.statusCode.toString() + " " + e.response.statusMessage);
print(e.response.data);
print(e.response.headers);
print(e.response.request);
} else{
print(e.request);
print(e.message);
}
}
catch (error, stacktrace) {
print("Exception occured: $error stackTrace: $stacktrace");
return null;
}
}
}

Related

How to auth in SignalR wih token in Dart?

How to get access token in SignalR package?
I get access token doing POST request and after that I get the access token. I have a model where I have parsed JSON and have token field.
Auth authFromJson(String str) => Auth.fromJson(json.decode(str));
String authToJson(Auth data) => json.encode(data.toJson());
class Auth {
Auth({
this.token,
this.user,
});
final String? token;
final User? user;
POST request to API to get accesss token which I got succesfully:
Future<Auth> getToken() async {
String _email = "admin";
String _password = "admin";
Map<String, String> headers = {
'Content-Type': 'application/json',
'accept': ' */*'
};
final body = {
'username': _email,
'password': _password,
};
var response = await http.post(
Uri.parse("http://******/login"),
headers: headers,
body: jsonEncode(body),
);
print(response.body);
print(response.statusCode);
var jsonResponse = jsonDecode(response.body);
return Auth.fromJson(jsonResponse);
}
What I have in print in my console:
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA","user":{}}
After all this stuff I opened docs and found out how SignalR package handle token auth and did the same thing:
Future<List> fetchLists() async {
final httpConnectionOptions = HttpConnectionOptions(
accessTokenFactory: () => getToken().then((value) => value.token ?? ''),
);
final hubConnection = HubConnectionBuilder()
.withUrl('http://*****/hub',
options: httpConnectionOptions)
.build();
await hubConnection.start();
So after all of this I got this error [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: 302: Found
it means what I should add access token to each of requests and I do, but still get this error. How can i solve it or may be there is anoher way to add token in HubConnectionBuild?
There is parameter in accessTokenFactory which accept a function and have return type String so make a function which return token .
below attached code for your reference-
_hubConnection = HubConnectionBuilder()
.withUrl(chaturl,
options: HttpConnectionOptions(
headers: defaultHeaders,
accessTokenFactory: () async => await getToken() //define a function which return token
))
withAutomaticReconnect(retryDelays: [
20000,
]
).build();
//get token method
Future<dynamic> getToken() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
if (sharedPreferences.containsKey("token")) {
print(sharedPreferences.getString("token"));
return sharedPreferences.getString("token");
} else {
return null;
}
}

How to redirect to original page after login with remix-auth-auth0

Related to : How to redirect back to original page after login / signup in remix-auth?
If a user try to access to a protected routes, I want him to login, and then be redirected to this route.
With remix-auth, I have this in protected route :
export let loader: LoaderFunction = async ({ request }) => {
return await authenticator.isAuthenticated(request, {
failureRedirect: "/login?redirectTo=/search",
});
};
Then in my login route :
export let loader: LoaderFunction = async ({ request }) => {
return await login(request);
};
With function login() to be :
export async function login(request: Request) {
let url = new URL(request.url);
let returnTo = url.searchParams.get("redirectTo") as string | null;
try {
// call authenticate as usual, in successRedirect use returnTo or a fallback
return await authenticator.authenticate("auth0", request, {
successRedirect: returnTo ?? "/search",
failureRedirect: "/",
});
} catch (error) {
if (error instanceof Response && error.redirected) {
const returnToCookie = createCookie("returnToCookie");
error.headers.append(
"Set-Cookie",
await returnToCookie.serialize(returnTo)
);
}
throw error;
}
}
And finally the callback route is :
export let loader: LoaderFunction = async ({ request }) => {
//get the returnTo from the cookie
const returnToCookie = createCookie("returnToCookie");
const result = await returnToCookie.parse(request.headers.get("Cookie"));
let returnTo = (result) ?? "/";
return await authenticator.authenticate("auth0", request, {
successRedirect: returnTo,
failureRedirect: "/",
});
};
The problem is that login() function currently redirect the user to callback url (with the code in url), but with nothing special in cookie, so the callback route use the fallback.
If I try&catch login() function in login route, the erreur obtained is a Response, but not a redirect, so nothing special in the cookie too (and I don't really know what to do with this Response error)
I not sure of what I missed in the original post.
After some tries, I found the solution :
The login() function return a 302 HTTP code which is a redirection, but do not trigger the if (error instanceof Response && error.redirected)
Simply remove && error.redirected of the condition make the redirection work perfectly.

image uploading failing using dio package

I am trying to upload an image in flutter using Dio package but its failing. I need to send an image in formdata.
API >> needs request body as imageUpload:image
Code for image upload
static Future uploadProfilePicToS3(File imageFile) async {
try {
FormData formData = new FormData.from(
{'imageUpload': new UploadFileInfo(imageFile, "profile_pic.jpg")});
var response =
await Dio().post(UPLOAD_PROFILE_PIC, data: {'imageUpload': formData});
print(response.statusCode);
} catch (error) {
throw (error);
}
}
Error >>>
E/flutter ( 4025): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)]
Unhandled Exception: DioError [DioErrorType.DEFAULT]: Converting
object to an encodable object failed: Instance of 'UploadFileInfo'#0
Let me know if there is any other way.
I used dio for post a file path with some other information in this way :
Dio dio = new Dio();
FormData formData = new FormData();
formData.add(
"apiKey",
"my_api_key",
);
formData.add(
"file",
"image_path",
);
Response response = await dio.post(
"https://localhost",
data: formData,
onSendProgress: (int sent, int total) {
// do something
},
).catchError((onError) {
throw Exception('something');
});
use this code
Future<ImageProperty> uploadImage(File imageFile, processfunction) async {
final StringBuffer url = new StringBuffer(BASE_URL + "/wp-json/wp/v2/media");
Dio dio = new Dio();
var token = await _getToken();
try {
FormData formData = FormData.fromMap(
{"file": await MultipartFile.fromFile(imageFile.path)},
);
print(url);
if (token != null) {
dio.options.headers["Authorization"] = "Bearer $token";
print(dio.options.headers);
}
var response = await dio.post(
url.toString(),
data: formData,
onSendProgress: processfunction,
);
print(response.data);
return Future.value(response.data);
} on DioError catch (e) {
print(e);
}
}

download attachments from mail using microsoft graph rest api

I've been successfully getting the list of mails in inbox using microsoft graph rest api but i'm having tough time to understand documentation on how to download attachments from mail.
For example : This question stackoverflow answer speaks about what i intend to achieve but i don't understand what is message_id in the endpoint mentioned : https://outlook.office.com/api/v2.0/me/messages/{message_id}/attachments
UPDATE
i was able to get the details of attachment using following endpoint : https://graph.microsoft.com/v1.0/me/messages/{id}/attachments and got the following response.
I was under an impression that response would probably contain link to download the attachment, however the response contains key called contentBytes which i guess is the encrypted content of file.
For attachment resource of file type contentBytes property returns
base64-encoded contents of the file
Example
The following Node.js example demonstrates how to get attachment properties along with attachment content (there is a dependency to request library):
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
const fileContent = new Buffer(attachment.contentBytes, 'base64');
//...
where
const requestAsync = options => {
return new Promise((resolve, reject) => {
request(options, (error, res, body) => {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
};
const getAttachment = (userId, messageId, attachmentId, accessToken) => {
return requestAsync({
url: `https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}).then(data => {
return JSON.parse(data);
});
};
Update
The following example demonstrates how to download attachment as a file in a browser
try {
const attachment = await getAttachment(
userId,
mesasageId,
attachmentId,
accessToken
);
download("data:application/pdf;base64," + attachment.contentBytes, "Sample.pdf","application/pdf");
} catch (ex) {
console.log(ex);
}
where
async function getAttachment(userId, messageId, attachmentId, accessToken){
const res = await fetch(
`https://graph.microsoft.com/v1.0/users/${userId}/messages/${messageId}/attachments/${attachmentId}`,
{
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json;odata.metadata=none"
}
}
);
return res.json();
}
Dependency: download.js library
I don't know if this would help but you just have to add /$value at the end of your request :
https://graph.microsoft.com/v1.0/me/messages/{message_id}/attachments/{attachment_id}/$value

Flutter how to send multiple files to http post

I'd like to send two files to http post
curl looks like this
curl -X POST "https://api-us.faceplusplus.com/facepp/v3/compare" \
-F "api_key=<api_key>" \
-F "api_secret=<api_secret>" \
-F "image_file1 =file1" \
-F "image_file1 =file2"
I tried like this.
File first;
File second;
var uri = Uri.parse('https://api-us.faceplusplus.com/facepp/v3/compare');
var request = new http.MultipartRequest("POST", uri);
request.fields['api_key'] = apiKey;
request.fields['api_secret'] = apiSecret;
request.files.add(await http.MultipartFile.fromPath('image_file1', first.path, contentType: new MediaType('application', 'x-tar')));
request.files.add(await http.MultipartFile.fromPath('image_file2', second.path, contentType: new MediaType('application', 'x-tar')));
var response = await request.send();
print(response);
But it returns this
NoSuchMethodError: Class 'String' has no instance getter 'path'.
How can I send these properly?
It doesn't look like first and second are actually Files. When they are definitely files, as in the following example, I get 401 (as expected, as I have a dummy api key).
main() async {
File first = File('pubspec.yaml');
File second = File('analysis_options.yaml');
Uri uri = Uri.parse('https://api-us.faceplusplus.com/facepp/v3/compare');
http.MultipartRequest request = new http.MultipartRequest('POST', uri);
request.fields['api_key'] = 'apiKey';
request.fields['api_secret'] = 'apiSecret';
request.files.add(await http.MultipartFile.fromPath('image_file1', first.path,
contentType: new MediaType('application', 'x-tar')));
request.files.add(await http.MultipartFile.fromPath(
'image_file2', second.path,
contentType: new MediaType('application', 'x-tar')));
http.StreamedResponse response = await request.send();
print(response.statusCode);
}
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
class Post {
String title = '';
String description = '';
List<File> files = [];
Future<http.StreamedResponse> sendFiles() async {
try {
var uri = Uri.parse('https://example.com/api-endpoint');
var request = http.MultipartRequest('POST', uri);
// Headers
request.headers.addAll({
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
// Fields
request.fields['title'] = title;
request.fields['description'] = description;
// Files
await Future.forEach(
files,
(file) async => {
request.files.add(
http.MultipartFile(
'files',
(http.ByteStream(file.openRead())).cast(),
await file.length(),
filename: basename(file.path),
),
)
},
);
return await request.send();
} catch (err) {
print(err);
return null;
}
}
}
You can try with Flutter DIO Package
https://pub.dev/packages/dio
import 'package:dio/dio.dart';
// Create a Dio instance
final Dio dio = Dio();
// Set up the request options
Options options = Options(
method: 'POST',
);
// Create a FormData instance to hold the files you want to upload
FormData formData = FormData();
// Add the files to the FormData instance
formData.files.addAll([
MapEntry('file1', await MultipartFile.fromFile(file1Path)),
MapEntry('file2', await MultipartFile.fromFile(file2Path)),
// Add more files here if you want
]);
try {
// Send the POST request
Response response = await dio.post(
url,
data: formData,
options: options,
);
// Do something with the response
print(response.data);
} catch (e) {
// show my error
log(e);
}

Resources