Python Requests HTTP POST response cookie missing / hiding fields - python

new to using Python requests lib, and looking for a little help around accessing cookies...
I am unable to get access to all of the fields within a cookie that I get from the following code using the Requests library - similar code from GoLang or Postman all work fine, but for some reason i am missing a few key fields that I need from Python-land. Sample code is as follows:
import requests
# Host base URL
host = 'sampleurl.link/endpoint'
# Username and password for login to API endpoint
credentials = "username=sausage123%40spaniel.org&password=Passw0rd"
ses = requests.Session()
authString = ''
def auth(host):
payload = credentials
headers = {
'Content-Type': "application/x-www-form-urlencoded",
}
ses.post("https://" + host + "/auth", data=payload, headers=headers)
cookies = ses.cookies.get_dict()
print(cookies.items())
authString = "; ".join([str(x)+"="+str(y) for x,y in cookies.items()])
print(authString)
auth(host)
The output is as follows:
[('systemid3', 'eSgRCbaH2EWyBVbRyfBS7xfftYCAqE-BRaon1Uc350pi14qTVgsmDXLrK9TDJvPsKmAzgw==')]
However, from the same API call in GoLang or Postman equiv. i get all of the required fields including path and expires:
systemid3=eSgRCbaH2EWyBVbRyfBS7xfftYCAqE-BRaon1Uc350pi14qTVgsmDXLrK9TDJvPsKmAzgw==; Path=/; Expires=Sat, 21 Sep 2019 09:37:46 GMT
Note that Postman also gives me the same 'Path' and 'Expires' fields etc.
How do i get Requests lib to give me access to all of the fields in the cookie, not just the first 2? Why is it hiding / removing the othe cookie fields?
Thanks in advance!

It's because you're using .items(), it only gets cookie name and value
You can access other parts like this:
for cookie in s.cookies:
print(cookie.name)
print(cookie.value)
print(cookie.expires)
print(cookie.path)

Related

Not persistent connection for flutter/dart http.Client()

I have a running django-server that works with sessions. A simple example from my views.py that should be enough to reproduce my problem is given here:
def test(request):
print("Session objects(")
for k,v in request.session.items():
print(k,v)
print(")")
request.session["a"] = "b"
So this does just print everything in the current session and after that saving some dummy-data in the session. If I do access this via my browser the first time the output is
Session objects(
)
so the session is empty just like expected. Then after refreshing the site the output is:
Session objects(
a b
)
also as expected, so everything seems to work just fine.
But now I want to use the site with my flutter app. For that I used the flutter packacke import 'package:http/http.dart' as http like this:
var client = http.Client();
String host = ...; // just the ip:port to my host
void my_request() async {
var response = await client.get(host + "/path/to/test/");
response = await client.get(host + "/path/to/test/");
}
So everything this should do is requesting my site twice just like i did before in the browser manually. But now my server just logges twice:
Session objects(
)
So obviously the client has a not persistent connection where the session is not preserved. But according to the doc https://pub.dev/packages/http this should work
If you're making multiple requests to the same server, you can keep open a persistent connection by using a Client rather than making one-off requests
is this a problem with my flutter/dart app or is the problem on my server? Is it maybe a big in the flutter package?
note: I first thought this could be a problem with csrf-authentication so deactivated it on my server, but this doesn't change anything ...
You don't need a 3rd-party library. It's a fairly small amount of code. So, after a first authorized request, server will respond with a cookie in the response headers, under the key set-cookie. The useful information is in the value of set-cookie key, from the beginning, to the 1st occurrence of the ; character (more on this later). For example, the value of set-cookie might look like this:
sessionid=asfweggv7bet4zw11wfpb4u415yx; expires=Fri, 06 Nov 2020 11:14:40 GMT;
You need to save it and use every time in your next authorized requests.
To save it, I created a method, which you should call after the first authorized response. You can call it after every response (if you have generic response handling), since it won't mess with the existing cookie if the server didn't send a new one.***
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
const kCookie = 'my_fancy_cookie';
// ...
void _storeCookie(http.Response response) async {
String rawCookie = response.headers['set-cookie'];
if (rawCookie != null) {
int index = rawCookie.indexOf(';');
String cookie = (index == -1) ? rawCookie : rawCookie.substring(0, index);
await FlutterSecureStorage().write(key: kCookie, value: cookie);
}
}
And then before I send my request, I add the cookie to headers:
// endpoint, payload and accessToken are defined earlier
cookie = await FlutterSecureStorage().read(key: kCookie);
http.Response response = await http.post(
endpoint,
body: json.encode(payload),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer ' + accessToken,
'cookie': cookie,
}
);
Remember to clear the cookie from secure storage after logout. :)
*** - Servers might change the session id (to reduce things like clickjacking that we don't need to consider yet), so it's good to keep extracting the cookie from every response.
The browser is saving/sending the Django session cookie. The flutter app http requests are not.
"Persistent" connection does not mean it will handle cookies for you. You will need to do that yourself or find a third-party library that does it.

Any idea how to open bytes object in new tab?

I am requesting an api to grant access to communicate with application for integration. While requesting I am getting response which is in bytes object that is why i am unable to open it in new tab because I want login to access my application.
header = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.get(
'https://api.getbase.com/oauth2/authorize',
data='client_id=' + self.client_id + '&response_type=code&redirect_uri='
+ self.redirect_uri, headers=header).content
webbrowser.open_new_tab(response)
just fixing your stated problem, you need to decode the bytes into a string or just use the text attribute.
that said, your code seems unnecessarily fragile and might be nicer as:
response = requests.post('https://api.getbase.com/oauth2/authorize', data={
'client_id': self.client_id,
'response_type': 'code',
'redirect_uri': self.redirect_uri
})
# make sure we throw an exception on failure
response.raise_for_status()
webbrowser.open_new_tab(response.text)
that way you can let requests deal with encoding/escaping the parameters appropriately. the default encoding/data type with an HTTP POST request is application/x-www-form-urlencoded as you need. GET requests don't send a body, so I'm not sure how your previous code was working

Not getting complete API details in Python like Postman

I am just trying to access an API through Postman and its wokring fine.The Postman header response is returning some details like below
Authentication-Token →/DwG7gAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Connection →keep-alive
Content-Length →16
Content-Type →application/json;charset=UTF-8
Date →Tue, 25 Sep 2018 17:44:01 GMT
Server →Apache-Coyote/1.1
But when I am trying to do same in Python I am just receivig the response status.(200)
How can I get the above Authentication-Token etc details like Postman in python code.
import requests
import json
url = 'https://test-orchestrator.lmig.com/baocdp/rest/login/'
headers = {"Content-Type": "application/json"}
data1 = {"username":"abc", "password":"abc"}
print("Testing authentication for Remedy test environment...")
change_response=requests.post(url,data=json.dumps(data1),headers=headers)
print(change_response)
If you print change_response, it will most likely look like this <status [200]> or something to that effect. If you want to see the contents of the response, you can use the vars response.text, response.content, or response.headers (among others) or since this is a json response, you can use the method response.json() to convert the contents of the response into a dictionary full of native Python data types.
I would reccommend x = response.json(), as the contents of your response seem to contain an auth token that you will most likely need to communicate with this device further. You can then use auth+token = x[token_key] to isolate that token.

Microsoft Dynamics CRM 2016 Web API Put request clears value

I'm trying to use a put request to update Dynamics CRM. When I run the following python code I get a 204 response, but instead of updating a value it clears it. For example firstname gets updated to null instead of "WebApiUpdate".
import adal
import json
import urllib2
import requests
endpoint = "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000002"
username = "example#example.com"
password = "password"
resource_uri = "https://orgname.crm.dynamics.com"
token_response = adal.acquire_token_with_username_password(endpoint,username,password,resource= resource_uri)
print token_response['accessToken']
payload = { "value": "WebApiUpdate"}
url = "http://xxxxx.crm.dynamics.com/api/data/v8.0/contacts(00000000-0000-0000-0000-000000000001)/firstname"
headers = {"Authorization":"Bearer "+token_response['accessToken']}
r = requests.put(url,data=payload,headers=headers)
print r.content
Solution:
set the url to be https:// instead of http://
set data = payload to json = payload in the request. ex:
r = requests.put(url,json=payload,headers=headers)
What was going wrong:
The initial call, which included the payload (regardless of if it was set to data or json), received a response of HTTP/1.0 301 Moved Permanently. In this response was a header with the new location. This location simply included https:// in the url. Then my code sent out a second put request that went to the correct https url, had the correct header parameters, but did not include the payload. This caused my put request to not include the value to update to, so Microsoft Dynamics CRM set the value to null.
Microsoft Dynamics CRM requires the put request payload to be formatted in json. After I updated the link to be https:// and the payload was included, it was formatted as x-www-form-urlencoded.That means the payload was appended to the request as value=WebApiUpdate. This caused a reply of HTTP/1.1 400 Bad Request. Changing the payload to be formatted as json means the payload will be appended to the request as {"value": "WebApiUpdate"}.
How I solved it:
I set my program to output the http put call it was making by adding the below code that I found from this stack overflow question. I then noticed that there were two non identical put calls and went from there.
import logging
try:
import http.client as http_client
except ImportError:
# Python 2
import httplib as http_client
http_client.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
I read the documentation on the request method here which I found on this stack overflow question

Unable to login to website through python

I am trying to login to a website in order to get some data. I have noticed that there is not form-data in the 'post' method but there is a 'request payload'. Furthermore, when I login in I cannot see anymore the login post method. Here is a screenshot of the network post login method:
When I login the next page showed is I use the following code in order to login:
import requests
urlData = 'https://b*********.dk/Account/Market'
urlLogin = 'https://b**********an.dk/
with requests.Session() as c:
urlLogin = 'https://b*************n.dk/Authorization/
c.get(urlLogin)
NetSession = c.cookies['ASP.NET_SessionId']
login_data = {
'ASP.NET_SessionId': NetSession,
'username':"A******",
'Password':"q******",
'remmemberMe': True
}
lol = c.post(urlLogin, data=login_data)
print(lol.text)
Running this code the following is outputed:
{"Processed":true,"Message":"The user name or password provided is incorrect.","NeedResetPassword":false}
When i input a wrong password the Processed value is false, while with correct credentials is true. But it deosnt login. Any idea why this could happen?
As you've already correctly noticed, the original credentials are not sent using form encoding (meaning &user=alice&password=secret), but are JSON encoded (so rather {"user":"alice", "password": "secret"}). You can also see this in the request's Content-Type header, which is application/json where (as opposed to application/x-www-form-urlencoded otherwhise).
For your custom request to work, you propably also need to send JSON-encoded data. This is documented in length in the official Documentation, so I'll just give the short version:
import json
# Build session and request body just like you already did in your question
# ...
headers = {"Content-Type": "application/json"}
lol = c.post(urlLogin, data=json.dumps(login_data), headers=headers)
print(lol.json())

Categories