I work at a small company whose mail is hosted through Google. As one of the administrators responsible for managing employee accounts I'm attempting to automate some tasks using Google APIs and one or more dedicated service accounts. I wrote the short Python script below to request an access token according to the documentation Google provides but I continue to get an "invalid_grant" error.
I've done the following:
Logged into the Google Developers Console as the service account and created a project
Created a client ID of type service account
Logged into the Google Admin Console and added client email address and scope URLs I wish the account to have access to
Searches online have yielded answers indicating that either the system time of the machine is off, the limit for refresh tokens has been exceeded or that the client ID instead of the client email has been used. I've synchronized the time on a Windows machine and a Linux machine prior to running the script and the same error appears. I'm not even getting an access token so I doubt the refresh limit has been exceeded. As can be seen below, the iss value is set to the client email address ending with "#developer.gserviceaccount.com".
I suspect that there is something else I am missing and, as I'm new to Python, I suspect it's something embarrassingly simple. Perhaps someone can assist?
import json
import time
import base64
import requests
from datetime import datetime
utc = datetime.utcnow()
iat = time.mktime(utc.timetuple())
exp = iat + 3600
jwt_header = {
"alg":"RS256",
"typ":"JWT"
}
jwt_claim_set = {
"iss":"pleasehelpme#developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/admin.directory.user",
"aud":"https://www.googleapis.com/oauth2/v3/token",
"iat":int(iat),
"exp":int(exp),
"sub":"someadminfrustrated#googleoauth.com"
}
jwt_jws = bytearray(str(jwt_header) + '.' + str(jwt_claim_set))
jwt_unencoded = str(jwt_header) + '.' + str(jwt_claim_set) + '.' + str(jwt_jws)
jwt_encoded = base64.urlsafe_b64encode(str(jwt_unencoded))
url = 'https://www.googleapis.com/oauth2/v3/token'
headers = {
'content-type': 'application/x-www-form-urlencoded'
}
payload = {
'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion': jwt_encoded
}
response = requests.post(url, headers=headers, params=payload)
print response
print response.json()
The output is the following:
<Response [400]>
{u'error_description': u'Bad Request', u'error': u'invalid_grant'}
I would look at the output http and url to make sure, but it does seem like you are having authentication issues.
I ran into a similar, albeit less complicated problem, and couldn't get my Google Developer credentials in through a python script. I ended up using my Chrome browser cookies with this script to get through that part: http://n8henrie.com/2014/05/decrypt-chrome-cookies-with-python/
Makes everything pretty simple:
url = 'http://www.example.com'
s = requests.Session()
cookies = pyCookieCheat.chrome_cookies(url)
s.get(url, cookies = cookies)
Related
I am trying to capture my access token that is returned by an API during authentication process. The official method of doing the authentication process from the API is:
from xyz_api import accessToken
app_id = "your_app_id"
app_secret = "your_app_secret"
app_session = accessToken.SessionModel(app_id, app_secret)
response = app_session.auth()
The above will return a json like:
{
"code" : 200,
"data" : {
"authorization_code": "some random code"
},
}
Now, the following code is used to generate a URL:
authorization_code = “your_authorization_code”
app_session.set_token(authorization_code)
url = app_session.generate_token()
Now this is where I start having issue.
At this stage, what is recommended by the API author is:
1. Use the generated URL and copy paste it into a browser.
2. The browser will then do the authentication and return the access token to a redirect
url (I used http://localhost:5000).
3. Copy and paste the access token from redirect URL
What I want:
To be able to finish the authentication and get the access_token from
python code itself.
What I tried:
Using requests.get(url), but it doesn't work.
Is there any way to do this all using python only, without having a need to open a browser?
PS: The API I am trying to use is: https://api-docs.fyers.in/v1#authorization
Further Investigation
On further investigation, I discovered the following:
The API uses oauth
The URL obtained from the following code provided by the API author
url = app_session.generate_token()
is same as when I write the following code:
oauth = OAuth2Session('app_id')
authorization_url, state = oauth.authorization_url(url)
The url returned in both cases is of the form:
https://api.fyers.in/api/v1/genrateToken?authorization_code=some_auth_code&appId=my_app_id
If I copy paste this URL in a browser, it send back the access_token to my redirect_url (http://localhost:5000)
I am trying to get the access token using oauth::fetch_token with the following code, which is failing:
oauth1 = OAuth2Session('app_id', state=state, redirect_uri="http://localhost:5000")
token = oauth1.fetch_token('https://api.fyers.in/api/v1/genrateToken/',
client_secret='app_secret',
code='the_auth_code_returned')
Truly appreciate the help.
you are accessing only with Python...
you dont need to do anything manually in a browser..
This API not sure if what you wnat to.
I Believe you registred an APP , got your app_secret
the step
response = app_session.auth()
answer you with a authorization_code
"data" : {
"authorization_code": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqaGdqZzg3NiQ3ODVidjVANjQ3NTZ2NSZnNyM2OTg3Njc5OHhkIjoiWElHVFVYVjBSSSIsImV4cCI6MTU2MTU5NzM5Ny41NjQxNTV9.Agl-Uus63NforrUEdbG7YUlPcbFXu9hLYm4akGuIBkU"
you may access that authorization token like that
import json
r = json.dumps(reponse)
authorization_token = r['data']['authorization_code']
The authorization token AND you app_id must be passed as querystring to https://api.fyers.in/api/v1/genrateToken?
the REAL user will be then asked to accept login trought your app
and if accepted , the user will be redirected to the URL *I imagined you inform in your app register.
let me know if it make sense
I have been working on using the powerbi REST API and I haven't been able to properly make use of it. I made use of this and I was able to register an app and get as far as getting an access token, but still I get 401 statuses on my requests.
My major points of confusion are with regards to the app registration:
1) I am trying to read and write data from a python script. Is this a Native-App or a Web Side Server?
2) What is the meaning of the redirect and home urls on the app registration page? I am currently using my localhost:5000 with different /paths. Could this be the source of the issue?
3) My research indicates that there should be some sort of login interaction. I don't have one, is this an indication that something isn't being done properly?
My code is as follows:
import adal
import requests
AUTHORITY_URL = 'https://login.microsoftonline.com/{my_company}.onmicrosoft.com'
RESOURCE = 'https://analysis.windows.net/powerbi/api'
CLIENT_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
CLIENT_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
def make_headers(access_token):
return {
'Authorization': "Bearer {}".format(access_token)
}
context = adal.AuthenticationContext(AUTHORITY_URL)
token = context.acquire_token_with_client_credentials(RESOURCE, CLIENT_ID, CLIENT_SECRET)
access_token = token['accessToken']
headers = make_headers(access_token)
url = "https://api.powerbi.com/v1.0/myorg/datasets"
resp = requests.get(url, headers=headers)
As I said above this works to give me an access token though a get a status 401 response on the request and there is no sign in prompt.
Any help/guidance would be tremendously appreciated.
1) In your case you should register a Native app.
2) Native apps has only Redirect URI. Redirect URI gives AAD more details about the specific application it authenticates. For Native apps you should set it to https://login.live.com/oauth20_desktop.srf.
3) It's hard to say why you are getting Unauthorized response. Check what rights you gave to your application - does it has rights to read or write all datasets? Try to decode the access token at https://jwt.io and look at scp - does it contain "Dataset.Read.All" or "Dataset.ReadWrite.All"?
In our team we use gitlab (https://git.example) and the bundled mattermost chat (https://chat.example).
For mattermost we'd like to have a dedicated bot-user (the web hooks have limitations wrt. private channels etc.), which actually logs in exactly like a normal user.
We created that user in gitlab, and can log into our chat with it via chrome (chat login redir --> gitlab oauth, type in username and pw --> redir back to chat --> authed).
Now i searched for python libraries that can actually do this, but i can only find some which require a client_id and client_secret... From my understanding (please correct me if i'm wrong) this is not what we're searching for, as we don't want to create another application to auth via gitlab, but login to our chat (which already has an id (known) and a secret (unknown)) as a user via gitlab.
As we couldn't find such a lib, we also inspected the network requests in chrome and tried to re-create it in python via requests, but can't get it to work (needless to say it involves parsing html and csrf tokens)...
Taking yet another attempt and a lot of guesswork we tried to acquire an access_token manually via
client_id = 'the one of mattermost in our gitlab'
user = 'username'
pw = 'password'
r = requests.post(
'https://git.example/oauth/token',
data={
"grant_type": "password",
"username": user,
"password": pw,
"client_id": client_id,
}
)
access_token = r.json()['access_token']
This seems to work (and the token looks good), but using it in the mattermost API only results in a 401:
ri = requests.get(
'https://chat.example/api/v1/users/me',
headers={'Authorization': 'Bearer ' + access_token}
)
ri.status_code, ri.json()
(401,
{u'detailed_error': u'token=...xxx...',
u'id': u'api.context.session_expired.app_error',
u'is_oauth': False,
u'message': u'Invalid or expired session, please login again.',
u'request_id': u'...yyy...',
u'status_code': 401})
Sadly http://docs.mattermost.com/developer/web-service.html#oauth2 currently doesn't shed more light into this, which is why I'm asking here. Did i maybe miss something obvious to "activate" that access_token in mattermost?
Actually i finally got this running by imitating the browser's behavior as follows, but i'd still be interested in a more generic solution that doesn't involve parsing any of the gitlab server's html...:
import requests
from pyquery import PyQuery as pq
client_id = '...your_mattermost_client_id...'
user = '...username...'
pw = '...userpass...'
gitlab = 'https://git.example'
chat = 'https://chat.example'
team = '/your_team'
r = requests.get(
chat + team + '/login/gitlab'
)
q = pq(r.content)
csrf_token = q('#new_ldap_user input[name="authenticity_token"]')[0].value # watch out, several tokens for ldap vs. normal login, inspect the page to find correct one
r2 = requests.post(
gitlab + '/users/auth/ldapmain/callback', # or whatever the browser callback for your auth-method was
cookies=r.cookies,
data={
'authenticity_token': csrf_token,
'username': user,
'password': pw,
}
)
# print [h.url for h in r2.history] + [r2.url] # to check the redirects
me = requests.get(
chat + '/api/v1/users/me',
cookies=r2.cookies,
)
print me.json() # if everything went well you're now authorized
# remember to set cookies in the follow-up requests
I have some code where I am trying to authenticate against Azure's Resource Manager REST API.
import json
import requests
tenant_id = "TENANT_ID"
app_id = "CLIENT_ID"
password = "APP_SECRET"
token_endpoint = 'http://login.microsoftonline.com/%s/oauth2/token' % tenant_id
management_uri = 'https://management.core.windows.net/'
payload = { 'grant_type': 'client_credentials',
'client_id': app_id,
'client_secret': password
}
auth_response = requests.post(url=token_endpoint, data=payload)
print auth_response.status_code
print auth_response.reason
This returns:
200
OK
However, when I print auth_response.content or auth_reponse.text, I get back a 400 HTML error code and an error message.
HTTP Error Code: 400
Sorry, but we’re having trouble signing you in.
We received a bad request.
I am able to get back the correct information using PostMan, however, with the same URI and payload. I used the "Generate Code" option in Postman to export my request to a Python requests script and tried running that. But, I get the same errors.
Anybody have any idea why this is happening?
Only modify your token_endpoint to https Protocols. E.G:
token_endpoint = 'https://login.microsoftonline.com/%s/oauth2/token' % tenant_id.
You can refer to https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx for more details.
Meanwhile, you can leverage Microsoft Azure Active Directory Authentication Library (ADAL) for Python for acquire the access token in a ease.
You should use HTTPS instead of HTTP for token_endpoint, and you should specify API version too. Here is what you should use.
token_endpoint = 'https://login.microsoftonline.com/%s/oauth2/token?api-version=1.0' % tenant_id
I've got a Django template that initiates the Facebook OAuth process by doing:
window.location='https://www.facebook.com/dialog/oauth?client_id=MY_CLIENT_ID&redirect_uri=http%3A//localhost%3A8000/fbpanel/explore-python'
Then, in my Django view, I get the resulting code as follows:
import cgi
import urllib
# [...]
code = request.GET['code']
args = {
'client_id': MY_CLIENT_ID,
'redirect_uri': 'http://localhost:8000/fbpanel/explore_python'
'client_secret': MY_CLIENT_SECRET,
'code': code,
}
url = 'https://graph.facebook.com/oauth/access_token?' + \
urllib.urlencode(args)
raw_response = urllib.urlopen(url).read()
response = cgi.parse_qs(raw_response)
if response:
error = ''
if response['access_token']:
access_token = response['access_token'][0]
if response['expires']:
expires = response['expires'][0]
else:
access_token = 'No access token returned'
expires = 'No expiration given'
error = raw_response
Invariably, response is None and raw_response contains Error: {"error":{"message":"Error validating verification code.","type":"OAuthException"}}
The same credentials validate fine with the JavaScript SDK. I was binding my server to localhost in that case too, and have localhost:8000 set in my official app domain. Before I did that, the JavaScript wouldn't work. After, it did. So I don't think localhost is the issue.
I've manually stepped through the code, printing the code returned by FB out,
then manually running the above code line by line in the python interpreter, and
I can't see anything I'm doing wrong. What are my next debugging steps? The FB
documentation offers little guidance if you receive an OAuthException.
The value used for redirect_uri must be exactly the same when you request the token as it was when you requested the code. Since no actual redirect occurs when requesting the token, it can be easy to miss small differences. The error message is not particularly descriptive in this case; what it really means is "The parameters you are sending don't correspond with the code value you are sending".