Currently my API requires to use Token authentication.
POST /api/authorize HTTP/1.1
"version": "20151130", // The version of the REST API you are using
"client_id": "01234567890123456789", // Your 20 char public client id
"client_secret": "0123456789..." // Your 40 char client secret
I get the response:
{
"auth_token": "abcdef...",
"auth_expires": "20180524T062442Z"
}
My current pattern is like this:
I have a list of items I need to pass to the API via POST method.
This is my main function: ProcessProducts which receives a Pandas Dataframe which includes a product per row.
def ProcessProducts(products):
all_results = []
for _, product in products.iterrows():
results = GetProductData(product)
if results:
all_results.extend(results)
return all_results
def GetAuthorizationToken():
payload = {
'version': api_version,
'client_id': api_client_id,
'client_secret': api_client_secret
}
request_url = '%s%s' % (api_host, '/api/authorize')
r = requests.post(request_url, data=payload)
if r.status_code != 200:
raise Exception('Failed to authorize: ' + r.text)
token_data = json.loads(r.text)
api_auth_token = token_data['auth_token']
api_auth_expires = token_data['auth_expires']
return {
"X-API-Version": api_version,
"Authorization": "token %s" % api_auth_token
}
Client function...
def GetProductData(self, product):
"""Gets Product information from API."""
url = '%s%s' % (self.api_url, _COMPANY_DATA)
request = json.dumps({'products': [product]})
form_data = {'request': request, 'start': 1, 'limit': 1000}
logging.info('Looking up: %s', url)
auth_headers = GetAuthorizationToken()
response = _SendApiRequest(url, auth_headers, form_data)
return _HandleResponse(response)
def _SendApiRequest(self, url, auth_headers, form_data):
session = requests.Session()
try:
response = session.post(
url,
timeout=(_CONNECT_TIMEOUT_SECONDS, _READ_TIMEOUT_SECONDS),
headers=auth_headers,
data=form_data,
verify=True) # Check for valid public/signed HTTPS certificate.
response.raise_for_status()
return response
except requests.exceptions.HTTPError as err:
logging.exception(err)
Questions:
API returns the code expiry field "auth_expires", Where may be the best way to check in code when token expires so I can request a new one?
Is there a better Pattern to call API, so I can control the QPS rate as well (Use RateLimiter). Right now I'm creating a Session per request, which may not be ideal.
Related
I am using Django Rest API on open stack version 2.1 Unable to update Server Tags it is giving response text:'{"itemNotFound": {"code": 404, "message": "The resource could not be found."}}'
the code explanation is there is an array of object contains multiple server objects with updated tags within a loop it will request and append responses in an array for front-end
#classmethod
def update_tags(self, params):
check(self, params)
responses = []
try:
headers = {
"X-Auth-Token": params.headers['x-auth-token'],
"Content-type": 'application/json'
}
for server in params.data['payload']:
replace_tags = {"tags": server["tags"]}
response=requests.put(os.environ.get('PROTOCOL')+'://'+os.environ.get('OPENSTACK_HOST')+':'+os.environ.get('COMPUTE_PORT')+'/v2.1/servers/' +server['id']+'/tags', data=json.dumps(replace_tags), headers=headers, verify=False)
responses.append(response)
return responses
except Exception as e:
raise e
parameters which are being passed from check method which is written below
def check(self, params):
self.connection = get_openstack_conn(params)
the get_openstack_conn method
def get_openstack_conn(params):
token = params.META.get("HTTP_X_AUTH_TOKEN")
scope = params.META.get("HTTP_X_SCOPE")
organization = params.META.get("HTTP_X_ORG")
print("environment variables")
print("PROTOCOL = ",os.environ.get('PROTOCOL'))
print("OPENSTACK_HOST = ",os.environ.get('OPENSTACK_HOST'))
print("KEY_STONE_PORT = ",os.environ.get('KEY_STONE_PORT'))
if(scope == 'projectScope'):
connector = openstack.connect(auth_url=os.environ.get('PROTOCOL')+'://'+os.environ.get('OPENSTACK_HOST') +
':'+os.environ.get('KEY_STONE_PORT')+'/v3', verify=False, auth_type="token", token=token, project_id=organization)
elif(scope == 'domainScope'):
connector = openstack.connect(auth_url=os.environ.get('PROTOCOL')+'://'+os.environ.get('OPENSTACK_HOST') +
':'+os.environ.get('KEY_STONE_PORT')+'/v3', verify=False, auth_type="token", token=token, domain_id=organization)
return connector
I'm trying to send authentication request for apple using Python and Django 1.9, but always giving me unsupported_grant_type:
def login_with_apple(self, code):
apple_url = "https://appleid.apple.com/auth/token"
client_secret = generate_apple_client_secret()
adapter = AppleOAuth2Adapter(self.request)
headers = {'content-type': 'application/x-www-form-urlencoded'}
data = {'client_id': settings.CLIENT_ID,
'client_secret': client_secret,
'code': code,
'grand_type': 'authorization_code'}
resp = requests.post(url=apple_url, data=data, headers=headers)
access_token = None
if resp.status_code in [200, 201]:
try:
access_token = resp.json()
except ValueError:
access_token = dict(parse_qsl(resp.text))
if not access_token or 'acces_token' not in access_token:
raise OAuth2Error(
'Error retrieving access token: %s' % resp.content
)
return access_token
How I generates my client_secret:
def generate_apple_client_secret():
now = datetime.utcnow()
claims = {
'iss': settings.SOCIAL_AUTH_APPLE_TEAM_ID,
'aud': 'https://appleid.apple.com',
'iat': now,
'exp': now + timedelta(days=180),
'sub': settings.CLIENT_ID,
}
headers = {'kid': settings.SOCIAL_AUTH_APPLE_KEY_ID, 'alg': 'HS256'}
client_secret = jwt.encode(
payload=claims, key=settings.SOCIAL_AUTH_APPLE_PRIVATE_KEY,
algorithm='HS256',
**strong text** headers=headers).decode('utf-8')
return client_secret
I'm sending a request to apple but always giving me this error:
user = self.login_with_apple(ser.instance.token)
File "/home/omar/PycharmProjects/Aswaq/oscarapi/views/login.py", line 488, in
login_with_apple
Error retrieving access token: %s' % resp.content
OAuth2Error: Error retrieving access token: {"error":"unsupported_grant_type"}
According to https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens, the ES256 algorithm should be used.
I am trying to get the list of all calendars for a user. This user has delegate permissions to view the calendar of all the meeting rooms (resources). If I log into the user's account and I am able to see the meeting room calendars in the "Other Calendars" section. I also created my own calendar called "Test" in the "Other Calendars" section.
When I get all the calendar groups first and then iterate through the calendar group list and get the calendars, the list for "Other Calendars" only has "Test" calendar.
Not sure why this is the case. The user is a global administrator as well.
def get_access_info_from_authcode(auth_code, redirect_uri):
post_data = { 'grant_type': 'authorization_code',
'code': auth_code,
'redirect_uri': redirect_uri,
'scope': ' '.join(str(i) for i in scopes),
'client_id': client_registration.client_id(),
'client_secret': client_registration.client_secret()
}
r = requests.post(access_token_url, data = post_data, verify = verifySSL)
try:
return r.json()
except:
return 'Error retrieving token: {0} - {1}'.format(r.status_code, r.text)
def get_access_token_from_refresh_token(refresh_token, resource_id):
post_data = { 'grant_type' : 'refresh_token',
'client_id' : client_registration.client_id(),
'client_secret' : client_registration.client_secret(),
'refresh_token' : refresh_token,
'resource' : resource_id }
r = requests.post(access_token_url, data = post_data, verify = verifySSL)
# Return the token as a JSON object
return r.json()
def get_calendars_from_calendar_groups(calendar_endpoint, token, calendar_groups):
results = []
for group_id in calendar_groups:
get_calendars = '{0}/me/calendargroups/{1}/calendars'.format(calendar_endpoint, group_id)
r = make_api_call('GET', get_calendars, token)
if (r.status_code == requests.codes.unauthorized):
logger.debug('Response Headers: {0}'.format(r.headers))
logger.debug('Response: {0}'.format(r.json()))
results.append(None)
results.append(r.json())
return results
def get_calendars(calendar_endpoint, token, parameters=None):
if (not parameters is None):
logger.debug(' parameters: {0}'.format(parameters))
get_calendars = '{0}/me/calendars'.format(calendar_endpoint)
if (not parameters is None):
get_calendars = '{0}{1}'.format(get_calendars, parameters)
r = make_api_call('GET', get_calendars, token)
if(r.status_code == requests.codes.unauthorized):
logger.debug('Unauthorized request. Leaving get_calendars.')
return None
return r.json()
Logic + Code:
Step 1) Get the authorization URL:
authority = "https://login.microsoftonline.com/common"
authorize_url = '{0}{1}'.format(authority, '/oauth2/authorize?client_id={0}&redirect_uri={1}&response_type=code&state={2}&prompt=consent')
Step 2) Opening the URL takes us to https://login.microsoftonline.com/common where I login as the user:
Step 3) This redirects back to my localhost then the following:
discovery_result = exchoauth.get_access_info_from_authcode(auth_code, Office365.redirect_uri)
refresh_token = discovery_result['refresh_token']
client_id = client_registration.client_id()
client_secret = client_registration.client_secret()
access_token_json = exchoauth.get_access_token_from_refresh_token(refresh_token, Office365.resource_id)
access_token = access_token_json['access_token']
calendar_groups_json = exchoauth.get_calendar_groups(Office365.api_endpoint, access_token)
cal_groups = {}
if calendar_groups_json is not None:
for entry in calendar_groups_json['value']:
cal_group_id = entry['Id']
cal_group_name = entry['Name']
cal_groups[cal_group_id] = cal_group_name
calendars_json_list = exchoauth.get_calendars_from_calendar_groups(Office365.api_endpoint,
access_token, cal_groups)
for calendars_json in calendars_json_list:
if calendars_json is not None:
for ent in calendars_json['value']:
cal_id = ent['Id']
cal_name = ent['Name']
calendar_ids[cal_id] = cal_name
Let me know if you need any other information
The delegate-token which request with the Auth code grant flow only able to get the calendars of sign-in user.
If you want to the get the events from the specific room, you can use the app-only token request with client credential flow.
Here is an helpful link for your reference.
Im trying to write a REST Client for a closed API. I call a specific function twice but it only works the first time. It's the same command. If i run the script twice with batch It works. Im suspecting Requests keeps the connection alive or caches data. How do i "reset" Requests ?
base_headers = {"Connection":"keep-alive",
"User-Agent":user_agent,
"Accept-Encoding":"gzip",
"Host":"xxxxxxxxxxx",
"Content-Type":"application/json; charset=UTF-8"}
global auth
auth = 'xxxxxx'
def create_header(doAuth = True):
headers = base_headers
if doAuth:
headers['Authorization'] = auth
return headers
def do_get(url):
return requests.get(url, headers=create_header());
def do_put(url, payload):
if payload is None:
return requests.put(url, headers=create_header())
return requests.put(url, data=payload, headers=create_header())
def upvote(postid):
return do_put("xxxxxxxxxxxxx" % postid, None).content
def set_pos(longtitude, latitude, place, country="DE"):
payload = {'xxxxx':'xxxx'}
json_payload = json.dumps(payload)
return do_put("xxxxxxxxxxxxxxx",json_payload).content
def do_post(url, payload, doAuth=True):
return requests.post(url, data=payload, headers=create_header(doAuth=doAuth))
def new_acc(place, latitude, longtitude):
access = get_access_token(place, latitude, longtitude)
print access
global auth
auth = "Bearer %s" % access['access_token']
set_pos(longtitude, latitude, place) # Does a POST
for i in range(1,30):
posts = new_acc(uni['city'], uni['latitude'], uni['longtitude'])
upvote('xxxxxxxxxxxxx')
Basically the first upvote() goes through every time but every succeding does not.
I'm almost positive that keep-alive is not doing this. I would suggest writing some handlers to debug the response if you don't get the expected response code after the request.
Also, might I suggest a slightly different design that avoids global?
class RESTClient(object):
BASE_HEADERS = {"Connection":"keep-alive",
"Accept-Encoding":"gzip",
"Host":"xxxxxxxxxxx",
"Content-Type":"application/json; charset=UTF-8"}
def __init__(self, user_agent, auth = None):
self.headers = dict(self.BASE_HEADERS)
self.headers['User-Agent'] = user_agent
self.auth = auth
def create_header(self):
headers = dict(self.headers)
if auth:
headers['Authorization'] = auth
return headers
def do_get(self, url):
return requests.get(url, headers=self.create_header());
def do_put(self, url, payload = None):
if not payload:
return requests.put(url, headers=self.create_header())
return requests.put(url, data=payload, headers=self.create_header())
def upvote(self, postid):
return do_put("xxxxxxxxxxxxx" % postid).content
def set_pos(self, longtitude, latitude, place, country="DE"):
payload = {'xxxxx':'xxxx'}
json_payload = json.dumps(payload)
return do_put("xxxxxxxxxxxxxxx",json_payload).content
def do_post(self, url, payload, do_auth = None):
return requests.post(url, data=payload, headers=self.create_header())
def new_acc(self, place, latitude, longtitude):
access = get_access_token(place, latitude, longtitude)
print access
self.auth = "Bearer %s" % access['access_token']
set_pos(longtitude, latitude, place)
rc = RESTClient('Mozilla/5.0 ... Firefox/38.0', 'my-auth-token')
rc.upvote(post_id)
etc...
It could be that your use of globals is breaking things. Having not run your code, I can't check, but I would avoid using globals and favor tighter control of your state via objects.
EDIT:
Given that this answer was accepted by the OP, I am assuming the defect was caused by mutation of globals.
I am trying to use the instapaper API, but I keep getting a 403 error for my requests. Here's the code:
consumer_key='...'
consumer_secret='...'
access_token_url = 'https://www.instapaper.com/api/1/oauth/access_token'
consumer = oauth.Consumer(consumer_key, consumer_secret)
client = oauth.Client(consumer)
client.add_credentials('...','...')
params = {}
params["x_auth_username"] = '..'
params["x_auth_password"] = '...'
params["x_auth_mode"] = 'client_auth'
client.set_signature_method = oauth.SignatureMethod_HMAC_SHA1()
resp, token = client.request(access_token_url, method="POST",body=urllib.urlencode(params))
result = simplejson.load(urllib.urlopen('https://www.instapaper.com/api/1/bookmarks/list?' + token))
Any ideas?
You're right about the signature method.
But my main problem was that I wasn't handling the token appropriately. Here's the working code:
consumer = oauth.Consumer('key', 'secret')
client = oauth.Client(consumer)
# Get access token
resp, content = client.request('https://www.instapaper.com/api/1/oauth/access_token', "POST", urllib.urlencode({
'x_auth_mode': 'client_auth',
'x_auth_username': 'uname',
'x_auth_password': 'pass'
}))
token = dict(urlparse.parse_qsl(content))
token = oauth.Token(token['oauth_token'], token['oauth_token_secret'])
http = oauth.Client(consumer, token)
# Get starred items
response, data = http.request('https://www.instapaper.com/api/1/bookmarks/list', method='POST', body=urllib.urlencode({
'folder_id': 'starred',
'limit': '100'
}))
res = simplejson.loads(data)
First, make sure oauth2 is the library you're using. It's the most well-maintained python oauth module.
Second, this looks suspect:
client.set_signature_method = oauth.SignatureMethod_HMAC_SHA1()
You're replacing the set_signature_method function. It should be:
client.set_signature_method(oauth.SignatureMethod_HMAC_SHA1())
You should follow the example here: https://github.com/simplegeo/python-oauth2/blob/master/example/client.py