Authlib 0.6+ does not work with Auth0 impersonation and Flask - python

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.

Related

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

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).

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.

Get Pyramid View callable by it's path (request context)

I am making an app that would translate websocket messages to AJAX requests to the server. Mainly the decision is based on the fact that Pyramid already has a good URL dispatch system and it would be stupid not to use it.
The question is if there is an easy way to dispatch a URL in Pyramid (possibly an instanced Request object) to it's according view callable and get the view callable? Or at least get the output of the view callable related to the request?
I have tried the script from "prequest.py" which basically emulates a whole HTTP client and gives you the response (I have still not managed to get it work, but a look through the sources makes sense anyway) and I wouldn't like to do it that way.
You can reuse the code from the pview command to turn a path into a view reference:
from pyramid.scripts.pviews import PViewsCommand
pvcomm = PViewsCommand([])
view = pvcomm._find_view(path, request.registry)
The actual code to do this is a little involved, but the PViewsCommand does it all for us already.
I have managed to do it using Router.invoke_subrequest in the latest version of Pyramid (1.4a1).
This enables all the features related to routing. URL dispatch, parameter passing, tweens.
You can read about it here: http://docs.pylonsproject.org/projects/pyramid/en/latest/api/request.html#pyramid.request.Request.invoke_subrequest

How do I develop against OAuth locally?

I'm building a Python application that needs to communicate with an OAuth service provider. The SP requires me to specify a callback URL. Specifying localhost obviously won't work. I'm unable to set up a public facing server. Any ideas besides paying for server/hosting? Is this even possible?
Two things:
The OAuth Service Provider in question is violating the OAuth spec if it's giving you an error if you don't specify a callback URL. callback_url is spec'd to be an OPTIONAL parameter.
But, pedantry aside, you probably want to get a callback when the user's done just so you know you can redeem the Request Token for an Access Token. Yahoo's FireEagle developer docs have lots of great information on how to do this.
Even in the second case, the callback URL doesn't actually have to be visible from the Internet at all. The OAuth Service Provider will redirect the browser that the user uses to provide his username/password to the callback URL.
The two common ways to do this are:
Create a dumb web service from within your application that listens on some port (say, http://localhost:1234/) for the completion callback, or
Register a protocol handler (you'll have to check with the documentation for your OS specifically on how to do such a thing, but it enables things like <a href="skype:555-1212"> to work).
(An example of the flow that I believe you're describing lives here.)
In case you are using *nix style system, create a alias like 127.0.0.1 mywebsite.dev in /etc/hosts (you need have the line which is similar to above mentioned in the file, Use http://website.dev/callbackurl/for/app in call back URL and during local testing.
This was with the Facebook OAuth - I actually was able to specify 'http://127.0.0.1:8080' as the Site URL and the callback URL. It took several minutes for the changes to the Facebook app to propagate, but then it worked.
This may help you:
http://www.marcworrell.com/article-2990-en.html
It's php so should be pretty straightforward to set up on your dev server.
I've tried this one once:
http://term.ie/oauth/example/
It's pretty simple. You have a link to download the code at the bottom.
localtunnel [port] and voila
http://blogrium.wordpress.com/2010/05/11/making-a-local-web-server-public-with-localtunnel/
http://github.com/progrium/localtunnel
You could create 2 applications? 1 for deployment and the other for testing.
Alternatively, you can also include an oauth_callback parameter when you requesting for a request token. Some providers will redirect to the url specified by oauth_callback (eg. Twitter, Google) but some will ignore this callback url and redirect to the one specified during configuration (eg. Yahoo)
So how I solved this issue (using BitBucket's OAuth interface) was by specifying the callback URL to localhost (or whatever the hell you want really), and then following the authorisation URL with curl, but with the twist of only returning the HTTP header. Example:
curl --user BitbucketUsername:BitbucketPassword -sL -w "%{http_code} %{url_effective}\\n" "AUTH_URL" -o /dev/null
Inserting for your credentials and the authorisation url (remember to escape the exclamation mark!).
What you should get is something like this:
200 http://localhost?dump&oauth_verifier=OATH_VERIFIER&oauth_token=OATH_TOKEN
And you can scrape the oath_verifier from this.
Doing the same in python:
import pycurl
devnull = open('/dev/null', 'w')
c = pycurl.Curl()
c.setopt(pycurl.WRITEFUNCTION, devnull.write)
c.setopt(c.USERPWD, "BBUSERNAME:BBPASSWORD")
c.setopt(pycurl.URL, authorize_url)
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.perform()
print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL)
I hope this is useful for someone!

Categories

Resources