How to use google python oauth libraries to implement OpenID Connect? - python

I am evaluating different options for authentication in a python App Engine flex environment, for apps that run within a G Suite domain.
I am trying to put together the OpenID Connect "Server flow" instructions here with how google-auth-library-python implements the general OAuth2 instructions here.
I kind of follow things up until 4. Exchange code for access token and ID token, which looks like flow.fetch_token, except it says "response to this request contains the following fields in a JSON array," and it includes not just the access token but the id token and other things. I did see this patch to the library. Does that mean I could use some flow.fetch_token to create an IDTokenCredentials (how?) and then use this to build an OpenID Connect API client (and where is that API documented)? And what about validating the id token, is there a separate python library to help with that or is that part of the API library?
It is all very confusing. A great deal would be cleared up with some actual "soup to nuts" example code but I haven't found anything anywhere on the internet, which makes me think (a) perhaps this is not a viable way to do authentication, or (b) it is so recent the python libraries have not caught up? I would however much rather do authentication on the server than in the client with Google Sign-In.
Any suggestions or links to code are much appreciated.

It seems Google's python library contains a module for id token validation. This can be found at google.oauth2.id_token module. Once validated, it will return the decoded token which you can use to obtain user information.
from google.oauth2 import id_token
from google.auth.transport import requests
request = requests.Request()
id_info = id_token.verify_oauth2_token(
token, request, 'my-client-id.example.com')
if id_info['iss'] != 'https://accounts.google.com':
raise ValueError('Wrong issuer.')
userid = id_info['sub']
Once you obtain user information, you should follow authentication process as described in Authenticate the user section.

OK, I think I found my answer in the source code now.
google.oauth2.credentials.Credentials exposes id_token:
Depending on the authorization server and the scopes requested, this may be populated when credentials are obtained and updated when refresh is called. This token is a JWT. It can be verified and decoded [as #kavindu-dodanduwa pointed out] using google.oauth2.id_token.verify_oauth2_token.
And several layers down the call stack we can see fetch_token does some minimal validation of the response JSON (checking that an access token was returned, etc.) but basically passes through whatever it gets from the token endpoint, including (i.e. if an OpenID Connect scope is included) the id token as a JWT.
EDIT:
And the final piece of the puzzle is the translation of tokens from the (generic) OAuthSession to (Google-specific) credentials in google_auth_oauthlib.helpers, where the id_token is grabbed, if it exists.
Note that the generic oauthlib library does seem to implement OpenID Connect now, but looks to be very recent and in process (July 2018). Google doesn't seem to use any of this at the moment (this threw me off a bit).

Related

Salesforce API - This session is not valid for use with the REST API - Invalid Session ID

For over a year, I have connected to Salesforce using the simple_salesforce package in order to pull some data from various objects and load it to a data lake.
I have used the authentication method using username / password / security token.
client = Salesforce(
username="****************",
password="*************",
security_token="****************"
)
On the 1st of February came the enforcement of multi factor auth. Starting on that day, I consistently hit the same error over and over.
[{'message': 'This session is not valid for use with the REST API', 'errorCode': 'INVALID_SESSION_ID'}]
After some research, I tried to add a permission set with API Enabled and then API Only user. Result: still the same error, but now I am locked out of the UI.
Has anyone else encountered similar issues and could point me towards the right resources, please? Thanks!
MFA shouldn't matter for API access according to https://help.salesforce.com/s/articleView?id=000352937&type=1 (Ctrl+F "API"), it's probably something else your admin did.
Username, password+token sounds like you're use SOAP login method.
See if you can create a "connected app" in SF to use the OAuth2 login method, more natural for REST API. I wrote a bit about it in https://stackoverflow.com/a/62694002/313628. In the connected app you should be able to allow API access, even full if needed. No idea if Simple has natural place for the keys though, it's bit rubbish if you'll have to craft raw http requests yourself.
Simple's documentation also mentions using JWT to log in (and that requires connected app anyway), basically instead of username + pass you go username + certificate + the fact admin preauthorised this user... You'll be fine until certificate expires.
The text part of https://gist.github.com/booleangate/30d345ecf0617db0ea19c54c7a44d06f can help you with the connected app creation; sample code's probably not needed if you're going with Simple

Authlib 0.6+ does not work with Auth0 impersonation and Flask

Auth0 impersonation directly calls the on success authentication callback URL skipping the initial step of authentication. This causes an issue because flask/client/OAuth.authorize_access_token(), which is called as part of the callback processing, requires the presence of the session variable _auth0_callback_ but this variable is not set because the only place it is set, flask/client/OAuth.authorize_redirect(), is not called in this situation.
We've added a hack to set the _auth0_callback_ session variable if it doesn't exist just before the call to flask/client/OAuth.authorize_access_token() but this doesn't seem right and I'm just wondering if we're doing something wrong.
We've also experienced a similar issue with the _auth0_state_ session variable but this might have been fixed in v0.7 that I'm testing now. We currently are using Authlib v0.6 in production.
Have raised this issue with Auth0 but have not had any response.
Any help appreciated, thx
UPDATE...
Looking deeper into Authlib it looks like that for the flask/client/OAuth.authorize_access_token() call the callback URL is not required in OAuth2Session.fetch_access_token() to fetch the token if the token is included in the authorisation response and it kind of makes sense that the access token is included in the authorisation response in the case of the impersonation - see OAuth2Session.fetch_access_token()#152.
So maybe that authorisation response access token from Auth0 impersonation doesn't exist or is not being properly passed through...
According to my acknowledge, Auth0 accepts a redirect_uri parameter. Check this example: https://github.com/lepture/auth0-python-web-app/blob/patch-1/01-Login/server.py
The Flask integration is a wrapper on OAuth2Session which provides authorize_redirect and authorize_access_token methods to handle everything automatically for you. If you find the high level integration can't meet your need, you can always use the methods of OAuthClient.

Is there a boto3 funciton to convert authorization_code into authorization_token

My project is python and using boto3 lib.
I'm using aws cognito Authorization code grant flow with return_type=code instead of return_type=token (implicit flow). Once my user is authorized my redirect url is injected with the queryStringParameter code=4d55a121-8ffc-4058-844b-xxxx.
outlined here
I need to be able to verify this code. Because of course someone can take the redirect url and make a fake code and paste it into the browser. According to this doc I can exchange the code for a token. This works as expected via a rest client. I get the token and can continue to pass the token as the Authorization header. But what I'm asking is there has to be a boto3 method that takes this code and converts it into a token for me. If i have to use the requests lib I will.
I have tried for days. get_user isnt the answer as that requires a token not the code.
For reference on what I'm trying to do heres my repo. The focus is in def edit(). I'm currently using requests to achieve the same thing but would like to use the boto library
https://github.com/knittledan/python-lambda-cognito
Nope, believe you should use an https client to exchange the authorization code for tokens with the token endpoint provided:
https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html

Getting invalid_grant when trying to refresh token

When an OAuth2Credential object tries to refresh its access_token, sometimes it gets an error of invalid_grant and then it becomes unable to be refreshed. The code I used is based on Google's python API and Mirror API examples.
Background:
Using oauth2client module for authentication and OAuth2Credential object.
Storing the OAuth2Credential object pickled and base64'd into the database like Google's own example code
Using apiclient module to make calls to the Mirror API
This code runs on 3 different servers, all exhibiting the same issue when trying to send
The scopes I ask for are "https://www.googleapis.com/auth/glass.timeline" and "https://www.googleapis.com/auth/userinfo.profile"
I can confirm that access_type is set to "offline"
I ask for approval_prompt to be "force" just in case
Here is the code that is being used to call the mirror API:
from apiclient.discovery import build
http = credential.authorize(http=httplib2.Http())
service = build("mirror", "v1", http=http)
payload = <JSON_PAYLOAD_HERE>
service.timeline().insert(body=payload).execute()
When the service is called, there is the potential for it to issue a 401 which means the access_token needs to be refreshed. It then calls the refresh method which excepts with AccessTokenRefreshError with the error invalid_grant. At this point, the credential is as good as bunk, since the access_token is expired and the refresh_token will only give the same error.
I have seen pages that say this can happen due to either NTP problems, but I have confirmed (and even switched NTP servers) that my servers are in sync. The other documented possibility is that only 25 refresh tokens can exist before they get recycled, but I have implemented a store() method on the Credential object so when it is refreshed, the new credentials are saved in place (I can confirm that this works as I see new information in the database when it is refreshed).
Since I can't get a user's credentials to start exhibiting this problem on demand, I can't explain any other conditions to recreate the issue other than "waiting some time". I have seen the issue happen soon after authenticating and sending one call, all the way to a week's worth of time after a hundred calls.
The only way for now to get the issue to be resolved is to ask the user to reauthorize, but that isn't a solution since I am expecting to use the api's offline without user interaction. I'd also have no way to notify the user that they need to reauthorize.
Answer from the comment thread: the user had toggled off the Glassware from the MyGlass website which resulted in the token being revoked.
The user needs to go through the authorization flow again in order to be able to use the Glassware by either visiting the Glassware authorization endpoint or toggling it back "on" on MyGlass if available.

Authenticating with imaplib.IMAP4 and OAuth without OAuth2

I'm using web.py to build a simple server that learns on a user's gmail messages. I've gone through the OAuth flow using the rauth library and I now have the access token. I now want to use imaplib to pull down the messages for processing. However, it's extremely unclear to me how I use the IMAP4.authenticate method. From the documentation:
Authenticate command — requires response processing.
mechanism specifies which authentication mechanism is to be used - it should appear in the nstance variable capabilities in the form AUTH=mechanism.
authobject must be a callable object:
data = authobject(response)
It will be called to process server continuation responses. It should return data that will be encoded and sent to server. It should return None if the client abort response * should be sent instead.
All of the examples I can find online doing this use the authenticate method of the oauth2 library or xoauth library, but I've read that oauth2 is deprecated and xoauth is not fit for production. What's the move here? What's the library for my job?
Thanks!
The library I wanted is Google's new OAuth2 python library. I was confused by naming because oauth2-python, which is deprecated, is also 'import oauth2'. With their library it's dead simple cause they have a function called GenerateOAuth2String which just takes an email and a token and generates something you can pass write to imaplib, which they demo in the function TestImapAuthentication. perfect.

Categories

Resources