I'm trying to request access tokens from the fitbit API, but it keeps returning 401 Unauthorized status, even though I configure the request identical to a corresponding curl query - which succeeds. The error message returned says: "errorType":"invalid_client","message":"Invalid authorization header format. Is there some nuance of how httplib2 builds its requests that is throwing me off here?...
(Working) curl query:
curl -X POST -i
-H 'Authorization: Basic <LONG_CODE>'
-H 'Content-Type: application/x-www-form-urlencoded'
-d "clientId=<CLIENT_ID>"
-d "grant_type=authorization_code"
-d "redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Ffitbit-callback"
-d "code=<AUTHORIZATION_GRANT_CODE>"
https://api.fitbit.com/oauth2/token
Non-working python request (edited):
TOKEN_URL = 'https://api.fitbit.com'/oauth2/token'
CALLBACK_URI = 'http://127.0.0.1:5000/fitbit-callback'
auth_header = base64.b64encode(bytes(<CLIENT_ID> + ':' + <CLIENT_SECRET>, 'utf-8'))
headers = {
'Authorization': 'Basic %s' % auth_header,
'Content-Type' : 'application/x-www-form-urlencoded'
}
params = {
'client_id': <CLIENT_ID>,
'grant_type': 'authorization_code',
'redirect_uri': CALLBACK_URI,
'code': <AUTHORIZATION_GRANT_CODE>
}
urlparams = urlencode(params)
resp, content = h.request(TOKEN_URL,
'POST',
urlparams,
headers)
Not evident from code:
the auth_header-variable in python matches <LONG_CODE>
Terminal response after python3 fitbit.py:
send: b"POST /oauth2/token HTTP/1.1\r\nHost: api.fitbit.com\r\nContent-Length: 153\r\nauthorization: Basic b'<LONG_CODE>'\r\ncontent-type: application/x-www-form-urlencoded\r\nuser-agent: Python-httplib2/0.10.3 (gzip)\r\naccept-encoding: gzip, deflate\r\n\r\n"
send: b'client_id=<CLIENT_ID>&grant_type=authorization_code&redirect_uri=http%3A%2F%2F127.0.0.1%3A5000%2Ffitbit-callback&code=<AUTHORIZATION_GRANT_CODE>'
reply: 'HTTP/1.1 401 Unauthorized\r\n'
header: Date header: Content-Type header: Transfer-Encoding header: Connection header: Cache-control header: WWW-Authenticate header: Content-Language header: Content-Encoding header: Vary header: X-Frame-Options header: Server header: CF-RAY
Running print(content):
b'{"errors":[{"errorType":"invalid_client","message":"Invalid authorization header format. Visit https://dev.fitbit.com/docs/oauth2 for more information on the Fitbit Web API authorization process."}],"success":false}'
Well this is awkward. I didn't notice it before I directed the requests towards a request capture service that let me analyze them (like runscope), but I seem to simply have missed the b'<LONG_CODE>' formatting in the python example. This fixed it:
auth_header.decode("utf-8")
Maybe this question should be deleted, if it is unlikely to help anyone else...
Related
I want to replicate the below with Client config.
Curl -D- -X GET -H "Authorization: Basic ZnJlZDpmcmVk" -H "Content-Type: application/json" http://localhost:7990/rest/api/1.0/projects
https://developer.atlassian.com/server/bitbucket/how-tos/example-basic-authentication/
This is to download a file for updating exe in a private repo.
I'm getting 400 errors on a regular basis with bitbucket.
What I had:
client = Client(ClientConfig(), headers={'basic_auth':'U:P' }, refresh=True)
I want to include this header
header = {'Content-Type': 'application/json'}
So something like:
client = Client(ClientConfig(), headers={'basic_auth': 'brofewfefwefewef:EKAXsWkdt5H6yJEmtexN'}, header = {'Content-Type': 'application/json'}, refresh=True)
Should fix?
At least according to....
"Some http client software expects to receive an authentication challenge before it will send an authorization header and this may mean that it may not behave as expected. In this case you may need to configure it to supply the authorization header as described above rather than relying on its default mechanism."
and...
https://stackoverflow.com/questions/8840303/urllib2-http-error-400-bad-request
For me though, I always get error even with this change 400 https://pastebin.com/V9ibxTRX (full code here or short version below)
Optional errors message (it's not pretty but I did reduce it):
~~~~~ACCESSING PAGE AND FOUND!~~~
DEBUG:pyupdater.client.downloader:Url for request: https://api.bitbucket.org/2.0/repositories/Username/repository/downloads/keys.gz
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): api.bitbucket.org
send: b'GET /2.0/repositories/Username/repository/downloads/keys.gz HTTP/1.1\r\nHost: api.bitbucket.org\r\nAccept-Encoding: identity\r\nauthorization: Basic ywafdwafawffwawffawafwfwaawfawfg==\r\n\r\n'
reply: 'HTTP/1.1 302 Found\r\n'
~~~~~~RETRYING REDIRECTION PRESENT (IS THIS THE CAUSE OF ISSUES)??~~
DEBUG:urllib3.connectionpool:https://api.bitbucket.org:443 "GET /2.0/repositories/Username/repository/downloads/keys.gz HTTP/1.1" 302 0
DEBUG:urllib3.util.retry:Incremented Retry for (url='https://api.bitbucket.org/2.0/repositories/Username/repository/downloads/keys.gz'): Retry(total=2, connect=None, read=None, redirect=None, status=None)
INFO:urllib3.poolmanager:Redirecting https://api.bitbucket.org/2.0/repositories/Username/repository/downloads/keys.gz -> https://bbuseruploads.s3.amazonaws.com/a0e395b6-0c54-4efb-9074-57ec4190020b/downloads/1c87431a-98de-4d97-8c80-000243f81cba/keys.gz?Signature=FvA9X7K9ryM2Ft2mTV7PZefidJY%3D&Expires=1515817377&AWSAccessKeyId=AKIAIQWXW6WLXMB5QZAQ&versionId=6J830UBC1RFvWz.R6pMDwIiJQNKJjSkm&response-content-disposition=attachment%3B%20filename%3D%22keys.gz%22
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): bbuseruploads.s3.amazonaws.com
~~HEADERS MIGHT BE ISSUE ACCORDING TO DOCS https://developer.atlassian.com/server/bitbucket/how-tos/example-basic-authentication/~~
header: Server header: Vary header: Content-Type header: X-OAuth-Scopes header: Strict-Transport-Security header: Date header: Location header: X-Served-By header: ETag header: X-Static-Version header: X-Content-Type-Options header: X-Accepted-OAuth-Scopes header: X-Credential-Type header: X-Render-Time header: Connection header: X-Request-Count header: X-Frame-Options header: X-Version header: Content-Length send: b'GET /a0e395b6-0c54-4efb-9074-57ec4190020b/downloads/1c87431a-98de-4d97-8c80-000243f81cba/keys.gz?Signature=FvA9X7K9ryM2Ft2mTV7PZefidJY%3D&Expires=1515817377&AWSAccessKeyId=AKIAIQWXW6WLXMB5QZAQ&versionId=6J830UBC1RFvWz.R6pMDwIiJQNKJjSkm&response-content-disposition=attachment%3B%20filename%3D%22keys.gz%22 HTTP/1.1\r\nHost: bbuseruploads.s3.amazonaws.com\r\nAccept-Encoding: identity\r\nauthorization: Basic YnJvZmV3ZmVmd2VmZXdlZjpFS0FYc1drZHQ1SDZ5SkVtdGV4Tg==\r\n\r\n'
DEBUG:urllib3.connectionpool:https://bbuseruploads.s3.amazonaws.com:443 "GET /a0e395b6-0c54-4efb-9074-57ec4190020b/downloads/1c87431a-98de-4d97-8c80-000243f81cba/keys.gz?
Signature=FvA9X7K9ryM2Ft2mTV7PZefidJY%3D&Expires=1515817377&AWSAccessKeyId=AKIAIQWXW6WLXMB5QZAQ&versionId=6J830UBC1RFvWz.R6pMDwIiJQNKJjSkm&response-content-disposition=attachment%3B%20filename%3D%22keys.gz%22 HTTP/1.1" 400 None
~~UNABLE TO ACCESS PAGE (WAIT, BEFORE IT HAD 'HTTP/1.1 302 Found\r\n' SEE TOP)~~
reply: 'HTTP/1.1 400 Bad Request\r\n'
DEBUG:pyupdater.client.downloader:Resource URL: https://api.bitbucket.org/2.0/repositories/Username/repository/downloads/keys.gz
DEBUG:pyupdater.client.downloader:Got content length of: None
DEBUG:pyupdater.client.downloader:Content-Length not in headers
DEBUG:pyupdater.client.downloader:Callbacks will not show time left or percent downloaded.
DEBUG:pyupdater.client.downloader:Using file as storage since the file is too large
I'm not familiar with the library you're using for your client object, but you should be able to set the Content-Type header in your headers dictionary.
headers = {
'basic_auth': 'brofewfefwefewef:EKAXsWkdt5H6yJEmtexN',
'Content-Type': 'application/json'
}
client = Client(ClientConfig(), headers=headers, refresh=True)
Unfortunately this doesn't seem to be possible because headers is passed (unpacked) to urllib3.util.make_headers and it does not accept a content_type argument.
Also you can't access FileDownloader._http.headers in Client, because it's a local variable.
A possible FileDownloader 'hack':
class FileDownloader(object):
...Line 152...
def _get_http_pool(self, secure=True):
if secure:
_http = urllib3.PoolManager(cert_reqs=str('CERT_REQUIRED'),
ca_certs=certifi.where())
else:
_http = urllib3.PoolManager()
if self.headers:
content_type = self.headers.get('Content-Type')
if 'Content-Type' in self.headers:
del self.headers['Content-Type']
_headers = urllib3.util.make_headers(**self.headers)
_http.headers.update(_headers)
if content_type:
_http.headers['content-type'] = content_type
print(_http.headers)
return _http
This should allow you to pass a Content-Type header in Client.
I don't recommend modifying the source code of your libs, but if you have no other choice...
I'm using the standard oauth2 web flow example from requests-oauthlib to get a token from QuickBooks Online's API but whilst I've successfully redirected to the QBO site, authorised and has a code returned to request a token I am getting a:
oauthlib.oauth2.rfc6749.errors.InvalidClientError
Error when I am actually making the token request. Having googled a lot it seems like I am not authorising correctly. QBO says the actual request might look like the following:
POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer HTTP/1.1
Accept: application/json
Authorization: Basic UTM0dVBvRDIwanp2OUdxNXE1dmlMemppcTlwM1d2
NzRUdDNReGkwZVNTTDhFRWwxb0g6VEh0WEJlR3dheEtZSlVNaFhzeGxma1l
XaFg3ZlFlRzFtN2szTFRwbw==
Content-Type: application/x-www-form-urlencoded
Host: oauth.platform.intuit.com
Body: grant_type=authorization_code&
code=L3114709614564VSU8JSEiPkXx1xhV8D9mv4xbv6sZJycibMUI&
redirect_uri=https://www.mydemoapp.com/oauth-redirect
And that I need to generate the authorization headers like this:
"Basic " + base64encode(client_id + ":" + client_secret)
I've created the above like this:
auth_code = client_id + ":" + client_secret
auth_code = base64.b64encode(bytes(auth_code, 'utf-8'))
auth_code = auth_code.decode('utf-8')
But passing this through in the token code like:
token = qbo.fetch_token(token_url, code=code, authorization_response=redirect_uri, headers={'Authorization' : auth_code})
Resulted in the InvalidClientError message. I feel like I'm really close but failing at the final oauth2 hurdle but can someone help me authorise for the token please?
Many thanks,
John
------ edit 1 -----
Thanks stovfl, that's super helpful and I've tried a couple of new things now based on your advice. I've just updated the code so that it reads like this:
headers = {'Accept':'application/json'}
headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
auth = HTTPBasicAuth(client_id, client_secret)
body = 'grant_type=authorization_code&code=' + code + '&redirect_uri=' + redirect_uri_token
token = qbo.fetch_token(token_url, auth=auth, body=body, headers=headers)
That produced another error:
ValueError: Please supply either code or authorization_response parameters.
Checking the actual oauth library code it's mandatory to give the code/authresponse so I updated again to get the code from the callback and use it in my fetch_token request:
all_args = request.args.to_dict()
code = all_args['code']
headers = {'Accept':'application/json'}
headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
auth = HTTPBasicAuth(client_id, client_secret)
body = 'grant_type=authorization_code&code=' + code + '&redirect_uri=' + redirect_uri_token
token = qbo.fetch_token(token_url, code=code, auth=auth, body=body, headers=headers)
I've also tried removing the code and just having in the main call so the body looks like this:
body = 'grant_type=authorization_code' + '&redirect_uri=' + redirect_uri_token
But either way it now goes back to the previous error of:
oauthlib.oauth2.rfc6749.errors.InvalidClientError: (invalid_client)
I'm not sure where to go from here.
Question: QuickBooks intuit Developer: Exchange authorization code for access token, using OAut2.0.
According to the given Request Example, you should do:
LINE: POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer HTTP/1.1
POST to https://oauth.plat... HTTP Version 1.1, Defaults using OAuth2.fetch_token(....
url_endpoint = 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer'
LINE: Accept: application/json
You have to accept application/json, OAuth2.fetch_token(... accepts anything by default.
Nothing to do, but for completeness you can define it as follows:
headers = {'Accept':'application/json'}
LINE: Authorization: Basic a2V5OnNlY3JldA==
It's required that you pass auth credentials in a Basic Auth header
from requests.auth import HTTPBasicAuth
auth = HTTPBasicAuth(client_id, client_secret)
LINE: Content-Type: application/x-www-form-urlencoded
The body Data has to passed form-urlencoded.
headers.update({'Content-Type': 'application/x-www-form-urlencoded'})
LINE: Host: oauth.platform.intuit.com
User specific, nothing to do
LINE: Body: grant_type=authorization_code&code=L311...&redirect_uri=https://www.mydemoapp.com/oauth-redirect
Data placement in body.
body = 'grant_type=authorization_code&code=L311...&redirect_uri=https://www.mydemoapp.com/oauth-redirect'
Exchange authorization code for access token
token = qbo.fetch_token(url_endpoint, auth=auth, body=body, headers=headers)
Note: Cant't verify with QuickBooks intuit Developer but requests give me the following Request Message:
Request Message:
headers:
Accept: application/json
Accept-Encoding: gzip, deflate
Content-Length: 139
User-Agent: python-requests/2.11.1
Authorization: Basic a2V5OnNlY3JldA==
Host: ... omited, because user specific
Content-Type: application/x-www-form-urlencoded
form:
grant_type: authorization_code
code: L3114709614564VSU8JSEiPkXx1xhV8D9mv4xbv6sZJycibMUI
redirect_uri: https://www.mydemoapp.com/oauth-redirect
Tested with Python:3.4.2 - requests:2.11.1
Point out the right document, tutorial, example, or provide one, showing how to add a specific authentication token to a specific header in the Swagger generated API client in Python?
Here's what I've tried:
My API call works just fine with the right curl command:
curl -v -H 'X-CAG-Authorization: AG_CONSUMER_TOKEN access-key=31337-70k3n' \
'https://api.company.net/api/v1/user/detail?user=1'
* Trying 10.10.1.10...
* Connected to api.company.net (10.10.1.10) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
* Server certificate: *.company.net
* Server certificate: COMODO RSA Organization Validation Secure Server CA
* Server certificate: COMODO RSA Certification Authority
> GET /api/v1/user/detail?user=1 HTTP/1.1
> Host: api.company.net
> User-Agent: curl/7.49.1
> Accept: */*
> X-CAG-Authorization: AG_CONSUMER_TOKEN access-key=31337-70k3n
>
< HTTP/1.1 200 OK
< Server: openresty
< Date: Thu, 22 Dec 2016 19:46:05 GMT
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Connection: close
< Vary: Accept-Encoding
< Vary: Accept-Encoding
< X-UA-Compatible: IE=edge
<
{"successful":true,"message":"SUCCESS","body":{"…
However, when I try the same basic request in my Python (2.7.12) client I get an authorization failure, despite confirming that the token makes it into the header about to be used. More details on the right way to use the client OR on how to get more details of the exact request and response would be appreciated.
/Users/me/VEnvs/sku-grade/bin/python /Users/me/prj/code/python_client/api_example.py
HEADERS:
{'X-CAG-Authorization': 'AG_CONSUMER_TOKEN access-key=31337-70k3n', 'User-Agent': 'Swagger-Codegen/1.0.0/python'}
Exception when calling SupplierApi->get_api_v1_user_details: (401)
Reason: Unauthorized
HTTP response headers: HTTPHeaderDict({'Date': 'Thu, 22 Dec 2016 21:09:30 GMT', 'Content-Length': '636', 'Content-Type': 'application/json; charset=UTF-8', 'Connection': 'keep-alive', 'Server': 'nginx'})
HTTP response body: {
"code" : "PRECONDITION_FAILED",
"type" : "UnauthorizedApiDeniedException",
"message" : "Target API(/api/v1/user/details) is not available, you have to get a grant in advance.",
"messages" : {…
Here's a swagger api spec: swagger.yaml
---
swagger: "2.0"
info:
description: "API"
version: "TEMPORARY"
title: "User Details"
termsOfService: "http://wiki.company.net/tos"
contact:
name: "…"
license:
name: "…"
host: "api.company.net"
basePath: "/api/v1"
tags:
- name: "supplier"
description: "Supplier"
schemes:
- "https"
produces:
- "application/json"
paths:
/user/details:
get:
tags:
- "supplier"
summary: "userDetails"
operationId: "getApiV1UserDetails"
consumes:
- "application/json"
produces:
- "application/json;charset=utf-8"
parameters:
- name: "user"
in: "query"
description: "user id"
required: true
type: "integer"
format: "Long"
responses:
200:
description: "OK"
schema:
$ref: "#/definitions/SupplierResponseOfUserDetailsDto"
401:
description: "Unauthorized"
403:
description: "Forbidden"
404:
description: "Not Found"
definitions:
SupplierResponseOfUserDetailsDto:
type: "object"
properties:
body:
$ref: "#/definitions/UserDetailsDto"
message:
type: "string"
successful:
type: "boolean"
UserDetailsDto:
type: "object"
properties:
name:
type: "string"
The swagger-codegen was run from http://editor.swagger.io/ and I followed the api example trying to add in the extra header: api_example.py
from __future__ import print_function
import time
import swagger_client
from swagger_client import ApiClient
from swagger_client import Configuration
from swagger_client.rest import ApiException
from pprint import pprint
# Setup the authentication token header
conf = Configuration()
conf.api_key_prefix = {"teamname": "AG_CONSUMER_TOKEN"}
conf.api_key = {
"teamname": "access-key=31337-70k3n"
}
conf.api_client = ApiClient(None, "X-CAG-Authorization",
conf.get_api_key_with_prefix("teamname"))
# create an instance of the API class
api_instance = swagger_client.SupplierApi()
user = 1
try:
api_response = api_instance.get_api_v1_user_details(user)
pprint(api_response)
except ApiException as e:
print("Exception when calling "
"SupplierApi->get_api_v1_user_details: %s\n" % e)
By putting a print(self.api_client.default_headers) into the supplier_api.py I could see that the header did appear to be set.
{'X-CAG-Authorization': 'AG_CONSUMER_TOKEN access-key=31337-70k3n', 'User-Agent': 'Swagger-Codegen/1.0.0/python'}
So again what should I change in my example to get it to pass on the header and get authorized exactly the way a simple curl call does?
Update I've also tried defining it:
security:
- api_key: []
securityDefinitions:
api_key:
type: "apiKey"
name: "X-CAG-Authorization"
in: "header"
and then only setting up the key with:
swagger_client.configuration.api_key['X-CAG-Authorization'] = \
'access-key=31337-70k3n'
swagger_client.configuration.api_key_prefix['X-CAG-Authorization'] = \
'AG_CONSUMER_TOKEN'
But that didn't change much other than the header disappears from the default headers I was printing.
I've tried your code example and it looks like your headers are actually passed to server.
You can confirm this by adding print headers to swagger_client/rest.py file, just before this:
r = self.pool_manager.request(method, url,
fields=query_params,
preload_content=_preload_content,
timeout=timeout,
headers=headers)
Are you sure there is no problem on server side? Maybe some headers breaking the authentication?
Does the following curl command also work?
curl -v \
-H 'X-CAG-Authorization: AG_CONSUMER_TOKEN access-key=31337-70k3n' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json;charset=utf-8' \
-H 'User-Agent: Swagger-Codegen/1.0.0/python' \
'https://api.company.net/api/v1/user/detail?user=1'
Because this should give you the exact same answer that returned to swagger, which is 401 error. If it does, you can debug from there on server side. If it doesn't, I have no idea.
In your spec, you will need to describe the security setting (API key in your case) similar to this example defined in the securityDefinitions section of the OpenAPI/Swagger spec.
Then in the endpoint, you will need to "apply" the security definition similar to this example
After that, you can set the API key in configuration.py of the auto-generated Python API client and the HTTP request will include the API key accordingly (either in the header or URL query string as defined in the security setting of the spec)
Since the last stable release of Swagger Codegen, there has been quite a lot of enhancements to the auto-generated Python API client so I would suggest you to pull the latest master and build the JAR locally to generate API clients.
I'm trying to bulk add songs to a playlist. I cannot for the life of me find the difference between the API documentation and what I am sending, but it errors anyway. Here is the function I'm using and how I'm calling it:
def addToPlaylist(songs, playlistUrl, positions = None):
data = { 'uris': songs }
if position != None:
data.update({ 'position': position })
headers = authHeader.copy()
headers.update({'Content-Type': 'application/json'})
print(headers)
print(json.dumps(data))
req = requests.post(playlistUrl, headers = headers, data = json.dumps(data))
if req.status_code != 201:
print('Error: Request returned status code {}. Returned: {}'.format(req.status_code, req.text))
songs = ["spotify:track:1i1fxkWeaMmKEB4T7zqbzK", "spotify:track:2VKqMKKCFhTPczQb10TMKB", "spotify:track:7Gl9cKtVjRN6KHNMfV1gD3"]
url = "https://api.spotify.com/v1/users/username/playlists/2...Q/tracks"
addToPlaylist(songs, url, 0)
The two debug print()s tell me the following:
{'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': 'Bearer BlahblahblA'}
{"position": 0, "uris": ["spotify:track:1i1fxkWeaMmKEB4T7zqbzK", "spotify:track:2VKqMKKCFhTPczQb10TMKB", "spotify:track:7Gl9cKtVjRN6KHNMfV1gD3"]}
The error output tells me this:
Error: Request returned status code 400. Returned: {
"error" : {
"status" : 400,
"message" : "Error parsing JSON."
}
}
When changing the URL to http://localhost:3001 and listening with netcat nc -l -p 3001, I can see the following request:
POST / HTTP/1.1
Host: 0:3001
Accept: application/json
Connection: keep-alive
User-Agent: python-requests/2.11.1
Accept-Encoding: gzip, deflate
Content-Type: application/json
Authorization: Bearer BlahblahblA
Content-Length: 145
{"position": 0, "uris": ["spotify:track:1i1fxkWeaMmKEB4T7zqbzK", "spotify:track:2VKqMKKCFhTPczQb10TMKB", "spotify:track:7Gl9cKtVjRN6KHNMfV1gD3"]}
I might think the body should be URL-encoded, even though that's not what the Content-Type header mentions, but another requests (the DELETE to delete songs from a playlist) works and there I don't use url encoding.
Finally the documentation mentions the following:
POST https://api.spotify.com/v1/users/{user_id}/playlists/{playlist_id}/tracks
[...]
Content-Type: Required if URIs are passed in the request body, otherwise ignored. The content type of the request body: application/json
[...]
uris: array of Spotify URI strings | Optional. A JSON array of the Spotify track URIs to add. For example:
{"uris": ["spotify:track:4iV5W9uYEdYUVa79Axb7Rh",
"spotify:track:1301WleyT98MSxVHPZCA6M"]}
Am I doing something wrong?
While writing this post, it caught my attention that the position parameter was not supposed to be in the request body. It has to be in the URI parameters. Testing this, it works.
In my defense, Spotify's error suggested the JSON data was not syntactically correct, which was not the case. This took entirely too much time.
This works:
if position is not None:
playlistUrl += '?position=' + str(position)
Instead of data.update({'position': position}) (<= this is wrong).
There are lot of questions posted how to consume the REST services with python, but none of them worked for me,
currently with the below curl cli i can get the authentication token.
curl cli
curl -v --user username:pass1234 -H "content-type: application/json" -X POST -d "" https://mywebsite/api/v1/auth/token-services --insecure
when i execute the above cli i get the json response as below :
output snip from above curl cli
< HTTP/1.1 200 OK
< Server: nginx/1.4.2
< Date: Mon, 14 Apr 2014 23:22:41 GMT
< Content-Type: application/json
< Content-Length: 201
< Connection: keep-alive
Connection #0 to host <ipaddress> left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
{"kind": "object#auth-token", "expiry-time": "Mon Apr 14 23:37:41 2014", "token-id": "l3CvWcEr5rKvooOaCymFvy2qp3cY18XCs4JrW4EvPww=", "link": "https://mywebsite/api/v1/auth/token-services/1634484805"}
NOW MY QUESTION is, how to achieve this using python. WHAT libraries i should use? i need to extract the token-id from the json response. so that i will use that token for further request to consume REST services.
if some one can post the PYTHON code snippet for this that would be great.
Have a look at the following HOWTO of the python documentation: HOWTO Fetch Internet Resources Using urllib2. There you also find a section with an code example for Basic authentication. The HOWTO describes how you can use the module urllib2.
Other useful libraries:
requests
mechanize
I got the solution for what i am looking for...
Complete Sample code ...
import requests
from requests.auth import HTTPBasicAuth
import json
token = ""
def get_token(username, password, url):
global token
#verify=False will not verify the ssl
resp = requests.post(url, auth=HTTPBasicAuth(username, password), verify=False)
print "\n", dir(resp)
print "Status Code:", resp.status_code, "\n"
print "text:", resp.text, "\n"
print "json:", resp.json(), "\n"
print "Content:", resp.content, "\n"
print "Headers:", resp.headers, "\n"
print "Header(Content-type:)", resp.headers.get('content-type'), "\n"
print "Header(Content-length:)", resp.headers.get('content-length'), "\n"
print "OK:", resp.ok, "\n"
print "json dump:", json.dumps(resp.json())
json_dict = json.loads(resp.text)
token = json_dict['token-id']
print "\ntoken-id:", json_dict['token-id']
for key, value in json_dict.items():
print key, "=>", value
return token
def get_global_users(token):
print "\nexecuting get_global_users.."
print "Token", token
url = 'https://xxx.xxx.xxx.xxx/api/v1/global/users'
headers_dict = {'content-type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': token}
resp = requests.get(url, headers=headers_dict, verify=False)
print "Status Code:", resp.status_code, "\n"
print "text:", resp.text, "\n"
print "json:", resp.json(), "\n"
json_users = json.loads(resp.text)
print "all users:\n", json_users['users']
print "\n"
for users in json_users['users']:
for key, value in users.items():
print key, "=>", value
def post_global_user(token):
print "\nexecuting post_global_users.."
print "Token:", token
url = 'https://xxx.xxx.xxx.xxx/api/v1/global/users'
headers_dict = {'content-type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': token}
payload = {'username': 'myuser', 'password': 'pas1234', 'pw-type': 0, 'privilege': 15}
resp = requests.post(url, data=json.dumps(payload), headers=headers_dict, verify=False)
print "Status Code:", resp.status_code, "\n"
To emulate the curl command:
$ curl -v --user username:pass1234 -H "accept: application/json" \
-X POST -d "" https://mywebsite/api/v1/auth/token-services --insecure
in Python using only stdlib:
#!/usr/bin/env python
import base64
import json
from urllib2 import urlopen, Request
credentials = base64.b64encode(b'username:pass1234')
headers={'Authorization': b'Basic ' + credentials,
'Accept': 'application/json'}
response = urlopen(Request("https://example.com/post", b"", headers))
data = json.load(response)
token_id = data["token-id"]
If you want to see what is sent to/received from a server for https requests; enable debugging:
import urllib2
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPSHandler(debuglevel=1)))