We have a use case in which Admin can set Google Or Microsoft's Apps Client Id and Secrets that can later be used for all users who sign up with this Admin's link, Is there a way to verify these fields before saving/using them?
like for Microsoft,How do I verify this info that admin is providing? Is there any API call to verify it?
Update:
I am able to do this for G suite, what I did was:
1- Create a request to get token from client id and secret, (with wrong code).
2- In response, google returns 'invalid_client' in case the credentials are wrong and 'invalid_grant' in case the credentials are correct. (because Code is wrong too.)
try:
discovery_url = settings.GOOGLE_DISCOVERY_URL
callback_url = settings.BASE_URL + "/accounts/invite/google/signup/callback/"
client = WebApplicationClient(client_id)
identity_provider_config = requests.get(
discovery_url,
).json()
token_endpoint = identity_provider_config["token_endpoint"]
token_url, headers, body = client.prepare_token_request(
token_endpoint, redirect_url=callback_url, code='**code'**
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(client_id, client_secret),
).json()
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response))
except Exception as error:
json_object = json.loads(error.json)
pairs = json_object.items()
if list(pairs)[0][1] == 'invalid_grant':
return "Your credentials are correct"
if list(pairs)[0][1] == 'invalid_client':
return "Your credentials are NOT correct"
FOR MICROSOFT:
For microsoft, additional tenant id is required in order to do this:
We did this in following way:
def validate_azure_credentials(client_id, client_secret, tenant_id):
"""
validate client id and secret from microsoft and google
"""
try:
app = msal.ConfidentialClientApplication(
client_id=client_id,
client_credential=client_secret,
authority=f"https://login.microsoftonline.com/{tenant_id}",
)
# call for default scope in order to verify client id and secret.
scopes = ["https://vault.azure.net/.default"]
token = app.acquire_token_for_client(scopes=scopes)
if token.get("access_token") is None:
return IdpResponseMessages.INVALID_CREDENTIALS
return IdpResponseMessages.VALID_CREDENTIALS
except Exception as error:
logger.debug(f"Exception {error}")
return str(error)
Related
I'm trying to write a script that creates a playlist on my spotify account in python, from scratch and not using a module like spotipy.
My question is how do I authenticate with my client id and client secret key using the requests module or grab an access token using those credentials?
Try this full Client Credentials Authorization flow.
First step – get an authorization token with your credentials:
CLIENT_ID = " < your client id here... > "
CLIENT_SECRET = " < your client secret here... > "
grant_type = 'client_credentials'
body_params = {'grant_type' : grant_type}
url='https://accounts.spotify.com/api/token'
response = requests.post(url, data=body_params, auth = (CLIENT_ID, CLIENT_SECRET))
token_raw = json.loads(response.text)
token = token_raw["access_token"]
Second step – make a request to any of the playlists endpoint. Make sure to set a valid value for <spotify_user>.
headers = {"Authorization": "Bearer {}".format(token)}
r = requests.get(url="https://api.spotify.com/v1/users/<spotify_user>/playlists", headers=headers)
print(r.text)
As it is referenced here, you have to give the Bearer token to the Authorization header, and using requests it is done by declaring the "headers" optional:
r = requests.post(url="https://api.spotify.com/v1/users/{your-user}/playlists",
headers={"Authorization": <token>, ...})
The details of how can you get the Bearer token of your users can be found here
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
I am trying to follow some companies registered on LinkedIn through python code and as per LinkedIn API documentation I need to use oauth2 - POST method to follow a company.
My queries are below:
How to specify a particular company name via python code to follow a company?
Can someone advise the python code for this?
My code is below:
oauth_token = oauth.Token(key=access_token_key, secret=access_token_secret)
oauth_consumer = oauth.Consumer(key=api_key, secret=api_secret)
signature_method_hmac_sha1 = oauth.SignatureMethod_HMAC_SHA1()
http_method = "POST"
http_handler = urllib.HTTPHandler(debuglevel=_debug)
https_handler = urllib.HTTPSHandler(debuglevel=_debug)
def linkedinreq(url, method, parameters):
req = oauth.Request.from_consumer_and_token(oauth_consumer,
token=oauth_token,
http_method=http_method,
http_url=url,
parameters=parameters)
req.sign_request(signature_method_hmac_sha1, oauth_consumer, oauth_token)
req.to_postdata()
def fetchsamples():
url = "https://api.linkedin.com/v1/people/~/following/companies"
parameters = []
response = linkedinreq(url, "POST", parameters)
fetchsamples()
The python modules python-linkedin and python-linkedin-v2 are outdated. Thus, I suggest you to use the requests_oauthlib module instead.
from requests_oauthlib import OAuth2Session
from requests_oauthlib.compliance_fixes import linkedin_compliance_fix
# In case the `redirect_url` does not implement https
import os
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
# Credentials you get from registering a new application
client_id = '<the client id you get from linkedin>'
client_secret = '<the client secret you get from linkedin>'
redirect_url = '<authorized redirect URL from LinkedIn config>'
# OAuth endpoints given in the LinkedIn API documentation (check for updates)
authorization_base_url = 'https://www.linkedin.com/oauth/v2/authorization'
token_url = 'https://www.linkedin.com/oauth/v2/accessToken'
# Authorized Redirect URL (from LinkedIn config)
linkedin = OAuth2Session(client_id, redirect_uri=redirect_url)
linkedin = linkedin_compliance_fix(linkedin)
# Redirect user to LinkedIn for authorization
authorization_url, state = linkedin.authorization_url(authorization_base_url)
print('Please go here and authorize,', authorization_url)
# Get the authorization verifier code from the callback url
redirect_response = input('Paste the full redirect URL here:')
# Fetch the access token
linkedin.fetch_token(token_url, client_secret=client_secret,
authorization_response=redirect_response)
# Fetch a protected resource, i.e. user profile
r = linkedin.get('https://api.linkedin.com/v1/people/~')
print(r.content)
Instead of reinventing the wheel, use python-linkedin wrapper.
Example code to search for the companies:
from linkedin import linkedin
CONSUMER_KEY = 'your key'
CONSUMER_SECRET = 'your secret'
USER_TOKEN = 'your token'
USER_SECRET = 'your user secret'
RETURN_URL = ''
# Instantiate the developer authentication class
authentication = linkedin.LinkedInDeveloperAuthentication(CONSUMER_KEY, CONSUMER_SECRET,
USER_TOKEN, USER_SECRET,
RETURN_URL, linkedin.PERMISSIONS.enums.values())
# Pass it in to the app...
application = linkedin.LinkedInApplication(authentication)
print application.search_company(selectors=[{'companies': ['name', 'universal-name', 'website-url']}],
params={'keywords': 'apple microsoft'})
To follow the company, use follow_company() method, see more information and examples here:
COMPANY_ID = 1035 # this you would get from the `search_company()`
application.follow_company(COMPANY_ID)
I'm trying to issue a basic UbuntuOne API call.
As explained on https://one.ubuntu.com/developer/account_admin/auth/otherplatforms, I'm getting the OAUTH token and then passing it to the UbuntuOne service.
I get the token and consumer info alright
I'm then trying to issue a /api/file_storage/v1 API call (see: https://one.ubuntu.com/developer/files/store_files/cloud.) The request is signed using the OAUTH token.
The code snippet below is the exact code I'm executing (minus the email.password/description fields.) The token and consumer data is returned properly. I'm getting a '401 UNAUTHORIZED' from the server when issuing the /api/file_storage/v1 request... any idea why?
import base64
import json
import urllib
import urllib2
import oauth2
email = 'bla'
password = 'foo'
description = 'bar'
class Unauthorized(Exception):
"""The provided email address and password were incorrect."""
def acquire_token(email_address, password, description):
"""Aquire an OAuth access token for the given user."""
# Issue a new access token for the user.
request = urllib2.Request(
'https://login.ubuntu.com/api/1.0/authentications?' +
urllib.urlencode({'ws.op': 'authenticate', 'token_name': description}))
request.add_header('Accept', 'application/json')
request.add_header('Authorization', 'Basic %s' % base64.b64encode('%s:%s' % (email_address, password)))
try:
response = urllib2.urlopen(request)
except urllib2.HTTPError, exc:
if exc.code == 401: # Unauthorized
raise Unauthorized("Bad email address or password")
else:
raise
data = json.load(response)
consumer = oauth2.Consumer(data['consumer_key'], data['consumer_secret'])
token = oauth2.Token(data['token'], data['token_secret'])
# Tell Ubuntu One about the new token.
get_tokens_url = ('https://one.ubuntu.com/oauth/sso-finished-so-get-tokens/')
oauth_request = oauth2.Request.from_consumer_and_token(consumer, token, 'GET', get_tokens_url)
oauth_request.sign_request(oauth2.SignatureMethod_PLAINTEXT(), consumer, token)
request = urllib2.Request(get_tokens_url)
for header, value in oauth_request.to_header().items():
request.add_header(header, value)
response = urllib2.urlopen(request)
return consumer, token
if __name__ == '__main__':
consumer, token = acquire_token(email, password, description)
print 'Consumer:', consumer
print 'Token:', token
url = 'https://one.ubuntu.com/api/file_storage/v1'
oauth_request = oauth2.Request.from_consumer_and_token(consumer, token, 'GET', url)
oauth_request.sign_request(oauth2.SignatureMethod_PLAINTEXT(), consumer, token)
request = urllib2.Request(url)
request.add_header('Accept', 'application/json')
for header, value in oauth_request.to_header().items():
request.add_header(header, value)
response = urllib2.urlopen(request)
The issue was with the 'description' field. It must be in the following format:
Ubuntu One # $hostname [$application]
Else, the UbuntuOne service returns a "ok 0/1" and does not register the token.
I am trying to create generic class in python which will do all the oAuth process and then will allow to retrieve data from any oAuth supporting service (for example Twitter,LinkedIn).
Edited:
I have customer key and secret and access token key and secret,when I try to request any resource request I get the following error:
{"error":"Could not authenticate with OAuth.","request":"\/1\/statuses\/retweeted_by_me.json}'
any idea why?
My Code is:
import httplib
import time
import oauth as oauth
# settings for the local test consumer
SERVER = 'api.twitter.com'
RESOURCE_URL = 'https://api.twitter.com/1/statuses/retweeted_by_me.json'
CONSUMER_KEY = 'MY_CUSTOMER_KEY'
CONSUMER_SECRET = 'MY_CUSTOMER_SECRET'
ACCESS_TOKEN_KEY = 'MY_ACCESS_TOKEN_KEY'
ACCESS_TOKEN_SECRET = 'MY_ACCESS_TOKEN_SECRET'
# example client using httplib with headers
class SimpleOAuthClient(oauth.OAuthClient):
def __init__(self, server):
self.server = server
self.connection = httplib.HTTPSConnection(self.server)
def access_resource(self, oauth_request):
# via post body
# -> some protected resources
self.connection.request(oauth_request.http_method, RESOURCE_URL)
response = self.connection.getresponse()
return response.read()
def run_example2():
print '** OAuth Python Library Example **'
client = SimpleOAuthClient(SERVER, )
consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
pause()
# access some protected resources
print '* Access protected resources ...'
pause()
token = oauth.OAuthToken('ACCESS_TOKEN_KEY', 'ACCESS_TOKEN_SECRET')
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, http_method='GET', http_url=RESOURCE_URL)
oauth_request.sign_request(signature_method_hmac_sha1, consumer, token)
print 'REQUEST (via post body)'
print 'parameters: %s' % str(oauth_request.parameters)
pause()
params = client.access_resource(oauth_request)
print 'GOT'
print 'non-oauth parameters: %s' % params
pause()
def pause():
print ''
time.sleep(1)
if __name__ == '__main__':
run_example2()
print 'Done.'
AUTHORIZATION_URL = 'https://api.twitter.com/oauth/authenticate'
This is the incorrect URL to use for OAuth. If you look at Twitter's 3-legged OAuth documentation, they state "The GET oauth/authorize endpoint is used instead of /oauth/authenticate". Change the URL to "https://api.twitter.com/oauth/authorize" and try again.
I managed to fix it by changing self.connection.request(oauth_request.http_method, RESOURCE_URL)
to self.connection.request(oauth_request.http_method, oauth_request.to_url())
Notice that will will work only if oauth_request.http_method is GET