I'm trying to access GetDealItems API and i have a nightmare to get this working. Even though I use the valid client_id','client_secret','ruName' i keep getting
{'error': 'invalid_client', 'error_description': 'client authentication failed'}
below is the ebay doc
https://developer.ebay.com/api-docs/buy/deal/resources/deal_item/methods/getDealItems
I guess i need to use this scope and url in my request
scopes:'https://api.ebay.com/oauth/api_scope/buy.deal' and the
url='https://api.ebay.com/buy/deal/v1/deal_item?limit=1000'
Please see below my Python code.
import requests, urllib, base64
def getAuthToken():
AppSettings = {
'client_id':'xxxx7c8ec878c-c80c4c69',
'client_secret':'xxxx56db-4b4a-97b4-fad2',
'ruName':'xxxxx-gscrcsrtj'}
authHeaderData = AppSettings['client_id'] + ':' + AppSettings['client_secret']
encodedAuthHeader = base64.b64encode(str.encode(authHeaderData))
headers = {
"Content-Type" : "application/x-www-form-urlencoded",
"Authorization" : "Bearer " + str(encodedAuthHeader)
}
body= {
"grant_type" : "client_credentials",
"redirect_uri" : AppSettings['ruName'],
"scope" : "https://api.ebay.com/oauth/api_scope/buy.deal"
}
data = urllib.parse.urlencode(body)
tokenURL = "https://api.ebay.com/identity/v1/oauth2/token"
response = requests.post(tokenURL, headers=headers, data=data)
return response.json()
response = getAuthToken()
print(response)
response['access_token'] #access keys as required
response['error_description'] #if errors
The most obvious problem I see is that you are using Bearer when you should be using Basic in your Authorization header.
Also, You are urlencoding your redirect_url when you pass the entire dictionary into urlencode. The docs say you are supposed to urlencode the scope parameter, but honestly, I never encode the scope and it still works for me.
Here is your modified code, with a few formatting changes:
import requests, urllib, base64
client_id='xxxx7c8ec878c-c80c4c69'
client_secret='xxxx56db-4b4a-97b4-fad2'
ruName='xxxxx-gscrcsrtj'
scope = urllib.parse.quote('https://api.ebay.com/oauth/api_scope/buy.deal')
def basic_token(key, secret):
return 'Basic ' + base64.b64encode((key + ':' + secret).encode()).decode()
def getAuthToken():
headers = {
"Content-Type" : "application/x-www-form-urlencoded",
"Authorization" : basic_token(client_id, client_secret)
}
data = (
'grant_type=client_credentials&'
f'redirect_uri={ruName}&'
f'scope={scope}'
)
tokenURL = "https://api.ebay.com/identity/v1/oauth2/token"
response = requests.post(tokenURL, headers=headers, data=data)
return response.json()
Update:
I think you need to use the authorization_code grant instead of client_credentials.
To use the authorization_code grant, modify your body to look like this:
data = (
'grant_type=authorization_code&'
f'code={authorization_code}&'
f'redirect_uri={ruName}&'
f'scope={scope}'
)
Also, you will need to follow your "redirect url" to get the actual authorization code. Execute the following:
redirect_url = (
'https://auth.ebay.com/oauth2/authorize?'
f'client_id={client_id}&'
f'response_type=code&'
f'redirect_uri={ruName}&'
f'scope={scope}'
)
print(redirect_url)
Copy/paste the url from stdout, follow the link, and click "accept", then you will be redirected to a url that looks like this:
https://signin.ebay.com/ws/eBayISAPI.dll?ThirdPartyAuthSucessFailure&isAuthSuccessful=true&code=<authorization code here>&expires_in=299
Copy/paste the authorization code into your code, then see if it works.
Realistically, eBay expects you to automate this within your application using a server, but it doesn't make sense for you to go through the trouble if you are building an app for personal use.
GetDealItems API uses client_credentials grant as evident from the docs
The authorization should be using client_id and secret as described in getting access tokens
curl -X POST 'https://api.ebay.com/identity/v1/oauth2/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Authorization: Basic UkVTVFRlc3...wZi1hOGZhLTI4MmY=' \
-d 'grant_type=client_credentials&scope=https%3A%2F%2Fapi.ebay.com%2Foauth%2Fapi_scope%2Fbuy.deal'
Note: if the error is client_authorization_failed, ensure that the correct Keyset for production is used for production. Also ensure that the keyset is also enabled for Oauth
Finally, you can use/refer to the official python SDK as well here
A simple way to check if the particular scope, in this case https://api.ebay.com/oauth/api_scope/buy.deal is even allowed for this app, is to navigate to the keyset page under Keys link and click on "Oauth scopes" under the keyset which details the scopes allowed and their purpose. If the application is once authorized for buy.deal, then the scope will appear there.
UPDATE
GetDeals API is restricted in Production for authorized applications only. Please reach out to the eBay developer program as provided in the link on the page below.
https://developer.ebay.com/api-docs/buy/deal/overview.html#API
Related
I'm building a discord bot with discord.py and I'm trying to get the spotify user saved tracks.
This is my auth def:
#classmethod
def get_token(self):
CLIENT_ID = 'myclientid'
CLIENT_SECRET = "myclientsecret"
SPOTIFY_TOKEN_URL = "https://accounts.spotify.com/api/token"
client_token = base64.b64encode("{}:{}".format(CLIENT_ID, CLIENT_SECRET).encode('UTF-8')).decode('ascii')
headers = {"Authorization": "Basic {}".format(client_token)}
payload = {"grant_type": "client_credentials"}
token_request = requests.post(SPOTIFY_TOKEN_URL, data=payload, headers=headers)
access_token = json.loads(token_request.text)["access_token"]
return access_token
This is my def where I try to get the user saved tracks:
#commands.command(name='splayfav', aliases=['splayfavorites', 'splaysavedtracks'], description="Command to see the info from spotify \n __Example:__ \u200b \u200b *!infos*")
async def play_saved_tracks_from_spotify(self, ctx):
token = self.get_token(ctx)
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}',
}
response = requests.get('https://api.spotify.com/v1/me/tracks', headers=headers)
print(response.text)
I get this error:
{
"error" : {
"status" : 401,
"message" : "Missing token"
}
}
But If I go in the Spotify API console and get manually a token and put it manually after bearer, then it works, obviously I'm trying to automate the process so I can't take it by hand every time. If I print the token it's actually a token(I mean it's not None or similar), but it's like if It is the wrong one.
How can I fix this problem?
This error message is stupid. What it should tell you is that your authorization token does not have the grant to access the user.
There's 2 different ways to authorize:
client credentials (as you do, can't access any user related stuff)
Authorization Code Flow, see: https://developer.spotify.com/documentation/general/guides/authorization/code-flow/
The second way requires a bit more setup in your app. Basically get redirected to spotify webpage, then the user selects allow for the required permissions, then you get back a code and with that you can get a token and the refresh token (this one is the interesting part).
Now this is quite a hustle to get through, but once you have the refresh token you can basically do almost the same call you do just to get a refreshed access token. (See "Request a refreshed Access Token" at the bottom of the page I linked above)
So with refresh token you can do:
POST https://accounts.spotify.com/api/token
HEADER:
Authorization: Basic $base64(clientId:clientSecret)
BODY Parameters (form url encoded)
grant_type: refresh_token
refresh_token: <your value here>
Use this improved access token and it will work.
I'm a newbie and I am still in the learning phase.
Environment: JIRA Server.
I tried writing this simple program to fetch custom field ID using Python.
Code :
import requests
def custom_fields():
response = requests.get("https://"JIRA URL"/rest/api/2/field")
# # To test the response is working you need to get an output : 200
# print(response)
#
# # This will give you the output in String
# print(response.text)
# print(type(response.text))
#
# If you want to get the output in JSON format then try the
# following which will give you the output in list format.
#
# print (response.json())
# print(type(response.json()))
my_fields = response.json()
for field in my_fields:
print(" Field Name : {} Field Id : {}".format(field['name'], field['id']))
custom_fields()
and getting an sample output as:
Field Name : Key Field Id : issuekey
Field Name : Time Spent Field Id : timespent
Field Name : Original Estimate Field Id : timeoriginalestimate
Field Name : Project Field Id : project
Field Name : Σ Time Spent Field Id : aggregatetimespent
I believe this is because I am not using credentials to authenticate in the code. I tried following the link:
https://developer.atlassian.com/server/jira/platform/basic-authentication/
and tried updating the variable as:
response = requests.get ("curl -u username:password -X GET -H 'Content-Type: application/json' https://"JIRA URL"/rest/api/2/field")
and getting an error:
"No connection adapters were found for {!r}".format(url))
requests.exceptions.InvalidSchema: No connection adapters were found for "curl -u username:password -X GET -H 'Content-Type: application/json' https://"JIR URL"rest/api/2/field"
Could you please guide me.
Yes, you're absolutely right. You've missed the authentication to send a request to the API endpoint.
It can be done in two ways:
Using Basic Auth
headers = {
'Authorization': '<Basic Auth Token>',
'Content-Type': 'application/json',
}
response = requests.request("GET", JIRA_ENDPOINT, headers=headers)
Using JIRA-Token-Based Authentication
auth = HTTPBasicAuth("demo_email#demo.com", "<JIRA_api_token>")
headers = {
"Accept": "application/json"
}
response = requests.request(
"GET",
url,
headers=headers,
auth=auth
)
For more details, you can always refer to their documentation
header ={"Accept':'application/json",'authorization':f'Bearer {token}'}
response=request.get("{link}",headers=header)
print(response.json())
I think this might help you.
I would like to convert a request from Python code to M used in Power BI Power Query
My Python code is
\\
import requests
import datetime as dt
headers = {'Authorization': 'Bearer tok_123', 'Accept': 'text/csv'}
url = 'https://api.123/'
params = (
('start_time', start_date),
('end_time', tomorrow),
)
response = requests.get(url, headers=headers, params=params)
data = response.text
\\
The M code I have made (which does not work) is below. Could you let me know where I am going wrong?
Am getting error 400 invalid request. I think there is something wrong with the way I am translating params.
\\
let
apiUrl = "https://api.123/",
options = [Headers =[#"Authorization"="Bearer tok_123", #"start_time"="2021-01-05", #"end_time"="2021-02-05"]],
result = Web.Contents(apiUrl , options)
in
result
\\
Thanks
I am getting a 400
See the sections below, "verifying request", and "error code details"
Without your url, or the docs, I don't know what endpoint you're using, so here's an example
HTTP GET https://www.example.com/api/user/search?limit=1000®ion=US
Using RelativePath and Query
Note: It's important to use options[RelativePath] and options[Query] to prevent service refresh errors on the service. Check out docs: Web.Contents
let
Headers = [
Accept="application/json"
],
BaseUrl = "https://www.example.com",
Options = [
RelativePath = "/api/user/search",
Headers = Headers,
Query = [
limit = 1000,
region = "US"
],
Response = Web.Contents(BaseUrl, Options),
Result = Json.Document(Response) // skip if it's not JSON
in
Result
Authorization
Setting options[ApiKeyName]lets you specify your API token / password using the credential store instead of in the code itself.
Otherwise you can set it using the Headers record just like you are in python.
Error Code details
For details, first specify the HTTP Status codes you want to handle
Options = [
// add this param to Options
ManualStatusHandling = {"400"}
]
Then view error details in the metadata
details = Value.Metadata(Response)
Verifying Request
You can view the actual HTTP requests fired by either using
Query Diagnostics which is built in,
or a tool like fiddler
I'm trying to implement the daemon authentication flow.
The following post request returns me an access token with the right scope:
p_url = 'https://login.microsoftonline.com/' + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + '/oauth2/token'
data = { 'grant_type':'client_credentials',
'client_id': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
'client_secret': 'L------------------------------------------=',
'resource':'https://analysis.windows.net/powerbi/api' }
r = requests.post(url=p_url, data=data)
I receive the following response
{
"access_token" : "ey------------"
"expires_on" : "1454857253",
"not_before" : "1454853353",
"expires_in" : "3600",
"token_type" : "Bearer",
"scope" : "Dashboard.Read.All Data.Alter_Any Dataset.Read.All Dataset.ReadWrite.All Report.Read.All",
"resource" : "https://analysis.windows.net/powerbi/api"
}
response = json.loads(r.text)
token = response['access_token']
headers = { 'Authorization': 'Bearer ' + token }
response = requests.get('https://api.powerbi.com/v1.0/myorg/datasets', headers=headers)
I use the endpoint from the applications "view endpoints" page.
However, when I attempt to get list of "datasets" I always receive 403. What might be missing from the acquire token process?
Your flow is a bit short. REST call for datasets seems OK, but as far as I know, you have to request the access token by authorization code, not client credentials alone.
1) Get authorization code
Depends on your flow, for website it will be received during logon process or call to /oauth2/authorize with { 'response_type':'code }
2) Get access token
With authorization code in a variable, you have to modify your request to include to authorization code, like this (grant_type and code fields are altered):
p_url = 'https://login.microsoftonline.com/' + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + '/oauth2/token'
data = { 'grant_type':'authorization_code',
'client_id': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
'client_secret': 'L------------------------------------------=',
'code': authorizationCodeForSingedInUser,
'resource':'https://analysis.windows.net/powerbi/api' }
r = requests.post(url=p_url, data=data)
Basically saying, you have to have a user account that accesses the Power BI resource. Your website (clientid + secret) are not authorized by itself. There must be a user involved.
What's more, afaik only "organization account" users can access power bi.
To be explicit and underline the main cause in this thread, post and comments: Power BI REST API can only be used via User with credentials with Organizational Account and be already signed in (activated) Power BI on Power BI portal. You can check if REST Api will work by checking if this user is able to use Power BI portal manually.
I have the following code, which should perform the first part of creating a new download at github. It should send the json-data with POST.
jsonstring = '{"name": "test", "size": "4"}'
req = urllib2.Request("https://api.github.com/repos/<user>/<repo>/downloads")
req.add_header('Authorization', 'token ' + '<token>')
result = urllib2.urlopen(req, jsonstring)
If I remove the , jsonstring from the urlopen(), it does not fail, and gives me the list of available downloads. However, if I try to POST the json-string, I get 404 error.
The problem has to be with the json, or in the way I send it, but I can't figure out what the problem is.
The strings at <...> are right in the actual code, I just removed them from the post
I tried roughly the same with curl on the command-line, with slightly different method of authentication, and it worked.
Tested:
Works(returns the wanted json):
curl -u "user:password" --data "json..." https://api.github.com/repos/<user>/<repo>/downloads
Works:
curl -H 'Authorization: token <token>' https://api.github.com/
Does not work (returns "invalid credentials"):
curl -H 'Authorization: token <invalid_token>' https://api.github.com/
Does not work ("not found"):
curl -H 'Authorization: token <valid_token>' --data "json..." https://api.github.com/repos/<user>/<repo>/downloads
This does not seem to be an issue specific to the python code. The json POST data seems to be fine, and the OAuth token authorization seems to be (atleast partly) working. But when these are put together, it stops working.
I was finally able to work out, why it did not work.
I did not have the autorization scopes for the authorization token set correctly. The token I was using, was not "authorized" to do any modifications, and every action using it, that tried to modify something (add a download), failed.
I had to add the correct scopes to the authorization to make it work.
I have provided an answer as to how to POST JSON data to the V3 API without any authentication, but seen as you have identified that the original problem was with not setting up your OAUTH tokens correctly, I thought I would provide a programatic solution to getting one (this implementation gets a token every time the script is run, whereas in practice it would be done just once, and the token would be stored localy).
import urllib2
import json
import getpass
import base64
# Generate a token from the username and password.
# NOTE: this is a naive implementation. Store pre-retrieved tokens if possible.
username = 'pelson'
passwd = getpass.getpass() # <- this just puts a string in passwd (plaintext)
req = urllib2.Request("https://api.github.com/authorizations")
# add the username and password info to the request
base64string = base64.encodestring('%s:%s' % (username, passwd)).replace('\n', '')
req.add_header("Authorization", "Basic %s" % base64string)
data = json.dumps({"scopes":["repo"], "note":"Access to your repository."})
result = urllib2.urlopen(req, data)
result = json.loads('\n'.join(result.readlines()))
token = result['token']
Once you have this token, it can be used for any "repo" scope actions. Lets add a new issue to a repository:
# add an issue to the tracker using the new token
repo = 'name_of_repo'
data = json.dumps({'title': 'My automated issue.'})
req = urllib2.Request("https://api.github.com/repos/%s/%s/issues" % (username, repo))
req.add_header("Authorization", "token %s" % token)
result = urllib2.urlopen(req, data)
result = json.loads('\n'.join(result.readlines()))
print result['number']
Hope that helps somebody.
The v3 github api has a nice feature where it can convert markdown to html. No authentication is needed to request this information, hence it makes a nice example to try before delving into the more tricky world of authentication.
The API docs state:
Render an arbritrary Markdown document
POST /markdown Input
text Required string - The Markdown text to render
And even give an example of a markdown string to be converted:
{"text": "Hello world github/linguist#1 **cool**, and #1!"}
Given this information, lets build an urllib2 Request for the html-ified version of that markdown.
import urllib2
import json
data = {"text": "Hello world github/linguist#1 **cool**, and #1!"}
json_data = json.dumps(data)
req = urllib2.Request("https://api.github.com/markdown")
result = urllib2.urlopen(req, json_data)
print '\n'.join(result.readlines())
And the result is some html representing the markdown.
Given this example, it is not the JSON which is causing the 404 when posting your request, but more likely the authentication (as you have already answered).
Isn't the data meant to be URL-encoded, not JSON? Try:
data = {"name": "test", "size": "4"}
req = urllib2.Request("https://api.github.com/repos/<user>/<repo>/downloads")
req.add_header('Authorization', 'token ' + '<token>')
result = urllib2.urlopen(req, urllib.parse.urlencode(data))