I am trying to get this access token with Python. With Postman I can get an access token. After configurations in the screenshots, I click the Get Access Token button, and A pop-up throws for username and password. Then I fill them. After that, I can get an access token from Postman.
To get an access token with Python, I wrote the code below. In the first get request, I get the cookies and Redirect URL(to post user credentials). Then, I post user credentials and cookies to Redirected URL. After that, the response header["location"] must include a code parameter to get the token. But, the header parameter does not have a code parameter. It has an Authorization URL with query parameters. How can get this code parameter? Finally, I will post a request(to token URL) with this code to get an access token on the response body.
import base64
import hashlib
import json
import os
import re
import urllib.parse
import requests
from bs4 import BeautifulSoup
from rich import print
username = 'username '
password = 'password '
client_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxx'
client_secret = 'yyyyyyyyyyyyyyyyyyyyyyyyyy'
prod_url = 'https://url:port'
callback_url = prod_url + '/main/ifsapplications/projection/oauth2/callback'
authorization_url = prod_url + '/openid-connect-provider/idp/authorization'
token_url = prod_url + '/openid-connect-provider/idp/token'
code_challenge_method = "S256" #SHA-256
scope = 'openid'
response_type ='code'
#Add auth data to request headers
#Grant type = authorization code with pkce
#send client credentials in body
code_verifier = base64.urlsafe_b64encode(os.urandom(40)).decode('utf-8')
code_verifier = re.sub('[^a-zA-Z0-9]+', '', code_verifier)
code_challenge = hashlib.sha256(code_verifier.encode('utf-8')).digest()
code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8')
code_challenge = code_challenge.replace('=', '')
resp = requests. Get(
url=authorization_url,
params={
"response_type": response_type,
"client_id": client_id,
"scope": scope,
"redirect_uri": callback_url,
"code_challenge": code_challenge,
"code_challenge_method": code_challenge_method,
},
allow_redirects=False
)
cookie = resp.headers['Set-Cookie']
cookie = '; '.join(c.split(';')[0] for c in cookie. Split(', '))
soup = BeautifulSoup(resp.text, 'html.parser')
form_action = soup. Find('a').text
resp = requests. Post(
url=form_action,
data={
"username": username,
"password": password
},
headers={"Cookie": cookie,
"Referer": form_action},
allow_redirects=False
)
redirect = resp.headers['Location']
print(resp.text)
print(resp.headers)
OUTPUT:
I want to connect to the seller partner api in python using boto3.
The steps assumeRole to get temporary credentials for a session client I get. But sp-api is not in the list of aws services to handle with boto3. Is there a reference for the sp-api to use with python or what would be the equivalent to s3 = boto3.client('s3') for the sp-api?
I had also issues and questions like you have, I managed to connect successfully, maybe this can help:
import boto3
# ======== GET AUTH ========
# Credentials for user created following the docs
amw_client = boto3.client(
'sts',
aws_access_key_id=self.access_key,
aws_secret_access_key=self.secret_key,
region_name=self.region
)
# ROLE created following the docs
# STS assume policy must be included in the role
res = amw_client.assume_role(
RoleArn='arn:aws:iam::xxxx:role/xxxx',
RoleSessionName='SellingPartnerAPI'
)
Credentials = res["Credentials"]
AccessKeyId = Credentials["AccessKeyId"]
SecretAccessKey = Credentials["SecretAccessKey"]
SessionToken = Credentials["SessionToken"]
from requests_auth_aws_sigv4 import AWSSigV4
aws_auth = AWSSigV4('execute-api',
aws_access_key_id=AccessKeyId,
aws_secret_access_key=SecretAccessKey,
aws_session_token=SessionToken,
region=self.region
)
import requests
# ======== GET ACCESS TOKEN ======
body = \
{
'grant_type': 'refresh_token',
'client_id': amazon_app_client_id,
'refresh_token': amazon_app_refresh_token,
'client_secret': amazon_app_client_secret
}
h = {'Content-Type': 'application/json'}
access_token_response = \
requests.post('https://api.amazon.com/auth/o2/token', json=body, headers=h)
access_token = self.access_token_response.json().get('access_token')
# ======== CONSUME API ========
resp = requests.get(
request_url, auth=aws_auth, headers={'x-amz-access-token': access_token})
Let me know if I can help further :)
I'm trying to get an access token instead of getting this error message
AdalError: Get Token request returned http error: 400 and server response: {"error":"invalid_grant","error_description":"AADSTS50034: The user account {EmailHidden} does not exist in the XXXXX directory. To sign into this application, the account must be added to the directory.\r\nTrace ID: f18021a8-b10a-40bf-9f0a-7975b38e2300\r\nCorrelation ID: 4f61c8f5-ed06-41f1-8d7b-756dd7c09e10\r\nTimestamp: 2020-12-16 03:27:49Z","error_codes":[50034],"timestamp":"2020-12-16 03:27:49Z","trace_id":"f18021a8-b10a-40bf-9f0a-7975b38e2300","correlation_id":"4f61c8f5-ed06-41f1-8d7b-756dd7c09e10","error_uri":"https://login.windows.net/error?code=50034"}
My python code
import adal
import json
import pyodbc
import requests
AUTHORITY_URL = 'https://login.windows.net/XXXXX'
RESOURCE = 'https://analysis.windows.net/powerbi/api'
CLIENT_ID = 'XXXXX'
userid='nurdin#xxxxx.com.my'
userpassword='xxxxx'
completedtime = []
verify = []
def make_headers(access_token):
return {
'Authorization': 'Bearer {}'.format(access_token)
}
context = adal.AuthenticationContext(AUTHORITY_URL)
token = context.acquire_token_with_username_password(RESOURCE,userid,userpassword,CLIENT_ID)
access_token = token['accessToken']
print(access_token)
Your code works fine on my side, make sure your work account is under the tenant in the AUTHORITY_URL, if not, you could follow this link to create a new user without MFA-enabled.
import adal
AUTHORITY_URL = 'https://login.windows.net/<tenant-id>'
RESOURCE = 'https://analysis.windows.net/powerbi/api'
CLIENT_ID = '<client-id>'
userid='username#tenantname.onmicrosoft.com'
userpassword='xxxxxx'
context = adal.AuthenticationContext(AUTHORITY_URL)
token = context.acquire_token_with_username_password(RESOURCE,userid,userpassword,CLIENT_ID)
access_token = token['accessToken']
print(access_token)
I am learning Python and I am trying to create a playlist using the Spotify web api but get a http 400 error: Error parsing json. I guess it has to do with an incorrect variable type in the token but I am having a really hard time debugging it as I can't figure out a way to see the post request in raw format.
Posting through the API requires authorizing and this is the script I've created for that:
import requests
import base64
requests.packages.urllib3.disable_warnings()
client_id = 'ID'
client_secret = 'SECRET'
redirect_uri = 'http://spotify.com/'
scope = 'playlist-modify-private playlist-read-private'
def request_token():
# 1. Your application requests authorization
auth_url = 'https://accounts.spotify.com/authorize'
payload = {'client_id': client_id, 'response_type':'code','redirect_uri':redirect_uri}
auth = requests.get(auth_url,params = payload)
print '\nPlease go to this url to authorize ', auth.url
# 2. The user is asked to authorize access within the scopes
# 3. The user is redirected back to your specified URI
resp_url = raw_input('\nThen please copy-paste the url you where redirected to: ')
resp_code= resp_url.split("?code=")[1].split("&")[0]
# 4. Your application requests refresh and access tokens
token_url = 'https://accounts.spotify.com/api/token'
payload = {'redirect_uri': redirect_uri,'code': resp_code, 'grant_type': 'authorization_code','scope':scope}
auth_header = base64.b64encode(client_id + ':' + client_secret)
headers = {'Authorization': 'Basic %s' % auth_header}
req = requests.post(token_url, data=payload, headers=headers, verify=True)
response = req.json()
return response
This is the function actually trying to create the playlist using the authorization token (import authorizer is the function above):
import requests
import authorizer
def create_playlist(username, list_name):
token = authorizer.request_token()
access_token = token['access_token']
auth_header = {'Authorization': 'Bearer {token}'.format(token=access_token), 'Content-Type': 'application/json'}
api_url = 'https://api.spotify.com/v1/users/%s/playlists' % username
payload = {'name': list_name, 'public': 'false'}
r = requests.post(api_url, params=payload, headers=auth_header)
But whatever I try it only leads to a 400 error. Can anyone please point out my error here?
Solved by adding a json.dumps for the input: json.dumps(payload) and changing the payload to be 'data' and not 'params' in the request.
So the new functioning request equals:
r = requests.post(api_url, data=json.dumps(payload), headers=auth_header)
I'm building a website + backend with the FLask Framework in which I use Flask-OAuthlib to authenticate with google. After authentication, the backend needs to regularly scan the user his Gmail. So currently users can authenticate my app and I store the access_token and the refresh_token. The access_token expires after one hour, so within that one hour I can get the userinfo like so:
google = oauthManager.remote_app(
'google',
consumer_key='xxxxxxxxx.apps.googleusercontent.com',
consumer_secret='xxxxxxxxx',
request_token_params={
'scope': ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/gmail.readonly'],
'access_type': 'offline'
},
base_url='https://www.googleapis.com/oauth2/v1/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://accounts.google.com/o/oauth2/token',
authorize_url='https://accounts.google.com/o/oauth2/auth'
)
token = (the_stored_access_token, '')
userinfoObj = google.get('userinfo', token=token).data
userinfoObj['id'] # Prints out my google id
Once the hour is over, I need to use the refresh_token (which I've got stored in my database) to request a new access_token. I tried replacing the_stored_access_token with the_stored_refresh_token, but this simply gives me an Invalid Credentials-error.
In this github issue I read the following:
regardless of how you obtained the access token / refresh token (whether through an authorization code grant or resource owner password credentials), you exchange them the same way, by passing the refresh token as refresh_token and grant_type set to 'refresh_token'.
From this I understood I had to create a remote app like so:
google = oauthManager.remote_app(
'google',
# also the consumer_key, secret, request_token_params, etc..
grant_type='refresh_token',
refresh_token=u'1/xK_ZIeFn9quwvk4t5VRtE2oYe5yxkRDbP9BQ99NcJT0'
)
But this leads to a TypeError: __init__() got an unexpected keyword argument 'refresh_token'. So from here I'm kinda lost.
Does anybody know how I can use the refresh_token to get a new access_token? All tips are welcome!
This is how I get a new access_token for google:
from urllib2 import Request, urlopen, URLError
from webapp2_extras import json
import mimetools
BOUNDARY = mimetools.choose_boundary()
def refresh_token()
url = google_config['access_token_url']
headers = [
("grant_type", "refresh_token"),
("client_id", <client_id>),
("client_secret", <client_secret>),
("refresh_token", <refresh_token>),
]
files = []
edata = EncodeMultiPart(headers, files, file_type='text/plain')
headers = {}
request = Request(url, headers=headers)
request.add_data(edata)
request.add_header('Content-Length', str(len(edata)))
request.add_header('Content-Type', 'multipart/form-data;boundary=%s' % BOUNDARY)
try:
response = urlopen(request).read()
response = json.decode(response)
except URLError, e:
...
EncodeMultipart function is taken from here:
https://developers.google.com/cloud-print/docs/pythonCode
Be sure to use the same BOUNDARY
Looking at the source code for OAuthRemoteApp. The constructor does not take a keyword argument called refresh_token. It does however take an argument called access_token_params which is an optional dictionary of parameters to forward to the access token url.
Since the url is the same, but the grant type is different. I imagine a call like this should work:
google = oauthManager.remote_app(
'google',
# also the consumer_key, secret, request_token_params, etc..
grant_type='refresh_token',
access_token_params = {
refresh_token=u'1/xK_ZIeFn9quwvk4t5VRtE2oYe5yxkRDbP9BQ99NcJT0'
}
)
flask-oauthlib.contrib contains an parameter named auto_refresh_url / refresh_token_url in the remote_app which does exactely what you wanted to wanted to do. An example how to use it looks like this:
app= oauth.remote_app(
[...]
refresh_token_url='https://www.douban.com/service/auth2/token',
authorization_url='https://www.douban.com/service/auth2/auth',
[...]
)
However I did not manage to get it running this way. Nevertheless this is possible without the contrib package. My solution was to catch 401 API calls and redirect to a refresh page if a refresh_token is available.
My code for the refresh endpoint looks as follows:
#app.route('/refresh/')
def refresh():
data = {}
data['grant_type'] = 'refresh_token'
data['refresh_token'] = session['refresh_token'][0]
data['client_id'] = CLIENT_ID
data['client_secret'] = CLIENT_SECRET
# make custom POST request to get the new token pair
resp = remote.post(remote.access_token_url, data=data)
# checks the response status and parses the new tokens
# if refresh failed will redirect to login
parse_authorized_response(resp)
return redirect('/')
def parse_authorized_response(resp):
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
if isinstance(resp, dict):
session['access_token'] = (resp['access_token'], '')
session['refresh_token'] = (resp['refresh_token'], '')
elif isinstance(resp, OAuthResponse):
print(resp.status)
if resp.status != 200:
session['access_token'] = None
session['refresh_token'] = None
return redirect(url_for('login'))
else:
session['access_token'] = (resp.data['access_token'], '')
session['refresh_token'] = (resp.data['refresh_token'], '')
else:
raise Exception()
return redirect('/')
Hope this will help. The code can be enhanced of course and there surely is a more elegant way than catching 401ers but it's a start ;)
One other thing: Do not store the tokens in the Flask Session Cookie. Rather use Server Side Sessions from "Flask Session" which I did in my code!
This is how i got my new access token.
from urllib2 import Request, urlopen, URLError
import json
import mimetools
BOUNDARY = mimetools.choose_boundary()
CRLF = '\r\n'
def EncodeMultiPart(fields, files, file_type='application/xml'):
"""Encodes list of parameters and files for HTTP multipart format.
Args:
fields: list of tuples containing name and value of parameters.
files: list of tuples containing param name, filename, and file contents.
file_type: string if file type different than application/xml.
Returns:
A string to be sent as data for the HTTP post request.
"""
lines = []
for (key, value) in fields:
lines.append('--' + BOUNDARY)
lines.append('Content-Disposition: form-data; name="%s"' % key)
lines.append('') # blank line
lines.append(value)
for (key, filename, value) in files:
lines.append('--' + BOUNDARY)
lines.append(
'Content-Disposition: form-data; name="%s"; filename="%s"'
% (key, filename))
lines.append('Content-Type: %s' % file_type)
lines.append('') # blank line
lines.append(value)
lines.append('--' + BOUNDARY + '--')
lines.append('') # blank line
return CRLF.join(lines)
def refresh_token():
url = "https://oauth2.googleapis.com/token"
headers = [
("grant_type", "refresh_token"),
("client_id", "xxxxxx"),
("client_secret", "xxxxxx"),
("refresh_token", "xxxxx"),
]
files = []
edata = EncodeMultiPart(headers, files, file_type='text/plain')
#print(EncodeMultiPart(headers, files, file_type='text/plain'))
headers = {}
request = Request(url, headers=headers)
request.add_data(edata)
request.add_header('Content-Length', str(len(edata)))
request.add_header('Content-Type', 'multipart/form-data;boundary=%s' % BOUNDARY)
response = urlopen(request).read()
print(response)
refresh_token()
#response = json.decode(response)
#print(refresh_token())
With your refresh_token, you can get a new access_token like:
from google.oauth2.credentials import Credentials
from google.auth.transport import requests
creds = {"refresh_token": "<goes here>",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"client_id": "<YOUR_CLIENT_ID>.apps.googleusercontent.com",
"client_secret": "<goes here>",
"scopes": ["https://www.googleapis.com/auth/userinfo.email"]}
cred = Credentials.from_authorized_user_info(creds)
cred.refresh(requests.Request())
my_new_access_token = cred.token