I am building both an iOS client and a django backend service. The connection made between the systems is OAUTH2, implemented by the django-oauth2-toolkit.
Although the following command done in curl works (returns an access token):
curl -X POST -d "grant_type=password&username=<user>&password=<password>" http://<clientID>:<clientSecret>#localhost:8000/o/token/
The following Swift snippet, that uses Alamofire, receives "invalid_client", as a response.
let request = "http://\(Authentication.clientId):\(Authentication.clientSecret)#localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password", "username": in_username.text!, "password": in_password.text!]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
URLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then traced the InvalidClientError in the django-oauth2-toolkit source, and found that the exception was raised in the highlighted snippet of the following file:
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py
if self.request_validator.client_authentication_required(request):
log.debug('Authenticating client, %r.', request)
print(request) # I included this print message to inspect the request variable in console.
if not self.request_validator.authenticate_client(request):
log.debug('Client authentication failed, %r.', request)
raise errors.InvalidClientError(request=request) # RAISED!
I included the print(request) line to inspect the differences between the request made by curl and by Alamofire. The major difference was that the curl version included an authorization key:
'Authorization': 'Basic Z3ZFSjVXejloUGgybUJmdDNRaGhXZnlhNHpETG5KY3V6djJldWMwcjpSbVNPMkpwRFQ4bHp1UVFDYXN3T3dvVFkzRTBia01YWWxHVHNMcG5JUGZCUHFjbHJSZE5EOXQzd3RCS2xwR09MNWs1bEE4S2hmRUkydEhvWmx3ZVRKZkFXUDM4OERZa1NTZ0RvS0p3WjUyejRSQ29WRkZBS01RS1lydEpsTWNXag=='
and the Alamofire request didn't.
I highly suspect this is the culprit, but I really don't know else to do though from here on. I would really appreciate any wisdom.
Found the answer!
Was reading through a RFC document on the HTTP protocol, when this section caught my eye.
https://www.rfc-editor.org/rfc/rfc1945#section-11.1
Specifically,
To receive authorization, the client sends the user-ID and password,
separated by a single colon (":") character, within a base64 [5]
encoded string in the credentials.
It seems that Alamofire does not encode in 64 bits the clientId and clientSecret, as expected. Obviously, curl does this automatically. So I did the following:
First encode:
static let clientData: NSData = "\(clientId):\(clientSecret)".dataUsingEncoding(NSUTF8StringEncoding)!
static let client64String = clientData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.init(rawValue: 0))
Then set the request header using the resulting value:
let request = "http://localhost:8000/o/token/"
var URLRequest = NSMutableURLRequest(URL: NSURL(string: request)!)
URLRequest.HTTPMethod = "POST"
let parameters = ["grant_type": "password",
"username": in_username.text!,
"password": in_password.text!,
]
let encoding = Alamofire.ParameterEncoding.URL
(URLRequest, _) = encoding.encode(URLRequest, parameters: parameters)
// SOLUTION!
URLRequest.setValue("Basic \(Authentication.client64String)", forHTTPHeaderField: "Authorization")
Alamofire.request(URLRequest)
.responseJSON { response in
let data = response
print(data)
}
I then received the expected token as a response.
Related
I am trying to replicate the successful POST operation performed using Postman in Python. My code is something like this:
import requests
url = "https://api.appx.com/v1/gen_ticket"
headers = {
'authorizationtoken': "Bearer " + access_token,
'x-api-key': "ddQk4mlTAl5cUj0N7omg4457jXuYlH25kOdvJoeJN3",
'catalogitem': "eb29189cd00576b00dd3cf5951d96197d",
'Content-Type': "application/json",
}
response = requests.request("POST", url, headers=headers)
print(response.text)
but when i run this, i keep getting the following error (This is working properly when using Postman but not in Python code).
{"errorMessage":"RequestId: af75334a-f955-11e8-b2e7-17baf7f53f31 Process exited before completing request"}
Can someone please suggest how to fix this?
One more thing to above is, i am passing a JSON file in Postman body section (how can i use the same in python code as well?)
try change authorizationtoken to Authorization
import requests
url = "https://api.appx.com/v1/gen_ticket"
headers = {
'Authorization': "Bearer " + access_token,
'x-api-key': "ddQk4mlTAl5cUj0N7omg4457jXuYlH25kOdvJoeJN3",
'catalogitem': "eb29189cd00576b00dd3cf5951d96197d",
'Content-Type': "application/json",
}
response = requests.post(url, headers=headers)
print(response.text)
I am trying to get an accessToken from Microsoft Graph API using Postman tool. I am trying it in Authorization tab with Type=oauth2.0, so that I can use this accessToken for subsequent Get,Post requests.
Everything works fine, if the grant_type is "Authorization Code". But when the grant_type is client_credentials, postman throws the below error
error getting access token from client_credentials flow. Could not
send request
I have looked at the Network tab in DeveloperTools, it does not submit Client_Id, Client_Secret for client_credentials flow.
Question: How can I get Postman work for client_credentials grant_type scenario?
Am I missing any step here? Please guide me..
Thanks
I'm late to the party on this, but hopefully if someone else looks for this, they can find it, since we ran into the same issue.
The previous two answers (https://stackoverflow.com/a/65708126/6772160 and https://stackoverflow.com/a/50270148/6772160) guided us in the right direction, but we still had to translate this to work properly in Postman:
As Mahmoud mentioned, you can send in the client_id and the client_secret as basic auth:
Basic Auth
The main part is handling the grant_type as client_credentials though. To do that, we input:
grant_type=client_credentials in the Body of the request.
Click Body > select x-www-form-urlencoded > key = grant_type and value = client_credentials.
The Body tab should look like this when completed:
Body Tab
Hopefully that helps!
Try providing the following details:
Request Type: POST
URL: https://XXXXXXXXXXXXXXXXXXXXXXXX/token?grant_type=client_credentials
Authorization: Basic [client_id]:[client_secret]
Content-Type: application/x-www-form-urlencoded
The output should look like:
{
"access_token": "90778b6abce64fc124892ce66f7a8ecd",
"token_type": "Bearer",
"expires_in": 60,
"scope": ""
}
Note: Provide [client_id]:[client_secret] as BASE64Encoded.
You should send it as a raw or x-www-form-urlencoded data as in the following example:
Important header >>>> Content-Type: application/x-www-form-urlencoded
var data = "grant_type=client_credentials";
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "https://########/oauth/v2/token");
xhr.setRequestHeader("Authorization", "Basic ###############");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(data);
I am running a school project and I am new from swift3.
By searching, I know how to pass a data from one view to anther:
Passing data from a tableview to webview
In the post above, he is using http get request to pass data to website, then reload the webivew:
let URL = NSURL(string: "https://www.example.com?data=\(passData)")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
I see some useful links like about code with http post request in here:
HTTP Request in Swift with POST method. In a result, the code can print out the http response.
My question is that, how to implement a webview with sending a http post reuqest, like id, name, etc, instead of get method.
In anther words: I want to reload the webview(like example.com) and that website will contain the value I sent via http post request.
example.com:
$id = $_POST['id'];
echo $id;
Thanks.
Just create a URLRequest for POST as shown in the second link, and pass it to the webView:
var request = URLRequest(url: URL(string: "http://www.example.com/")!)
request.httpMethod = "POST"
let postString = "id=\(idString)"
request.httpBody = postString.data(using: .utf8)
webView.loadRequest(request) //if your `webView` is `UIWebView`
(Consider using WKWebView rather than UIWebView.)
You may need idString to be escaped if it contains some special characters.
By the way, the two line code:
let URL = NSURL(string: "https://www.example.com?data=\(passData)")
webView.loadRequest(NSURLRequest(url: URL! as URL) as URLRequest)
does not seem to be a good Swift 3 code. It can be written as:
let url = URL(string: "https://www.example.com?data=\(passData)")!
webView.loadRequest(URLRequest(url: url))
I simply cannot get this to work.
header = {"Content-Type": "application/json; charset=utf8"}
params = {"api_dev_key": dev_key, "api_user_name": username, "api_user_password": password}
req = requests.post("http://pastebin.com/api/api_login.php", params = json.dumps(params), headers = header)
print(req.status_code, req.reason, req.text)
The variables (my credentials) are just strings.
The response I get:
(200, 'OK', u'Bad API request, invalid api_dev_key')
There's nothing wrong with the key, this POST works fine when I use https://www.hurl.it
You need to just use data=params:
req = requests.post("http://pastebin.com/api/api_login.php", data=params)
Please can anyone help out? i am trying to POST a SOAP request in python but i am getting the error Response 403: Forbidden. my code looks like below:
i am using the python imports:
import httplib
import base64
import string
#the message
message = """<soap:Envelope ...rest message </soap:Envelope>"""
host = "host.test.com"
url = 'https://server.etc.com' #End point url
i need to use the Basic Authentication too so i need the username and password in the http header
username = 'username'
password = 'password'
webSoapAction = 'urn:etc-com:document...'
#the Authentication in base64 encoding form for Basic Authentication
auth = 'Basic' + string.strip(base64.encodestring(username +'.'+ password))
webservice = httplib.HTTP(host) #connect to the server(host)
here i try to build the header:
webservice.putrequest("POST", url)
webservice.putheader("Host", host)
webservice.putheader("User-Agent", "Python http auth")
webservice.putheader("Content-Type:", "text/xml; charset=\"UTF-8\"")
webservice.putheader("Content-length", "%d" % len(message))
webservice.putheader("SOAPAction",webSoapAction)
webservice.putheader('Authorization', auth)
webservice.endheaders()
webservice.send(message)
i should get the response here
#get the response
statuscode, statusmessage, header = webservice.getreply()
print "Response: ", statuscode, statusmessage
print "Headers: ",header
res = webservice.getfile().read()
print 'Content: ', res
Two things regard to your basic auth header construction:
Put a single space between "Basic" and your secret
Use ':' instead of '.' in between username and password
So it should looks like:
#the Authentication in base64 encoding form for Basic Authentication
auth = 'Basic ' + string.strip(base64.encodestring(username +':'+ password))