How to create a JWT DRF consumer, with external provider? - python

I have to set up the API part of an application.
To summarize, there is a private oauth provider, a front-end angular app, and a django rest framework resource API.
The Angular app is unaccessible if not logged, and redirect to the oauth provider. The user have to login there and is redirected to the front-end.
So far so good, the provider give the front-end a JWT access token.
Then the front ask for resources to the DRF API providing the JWT.
That's where I'm stuck, all the tutorials I find on google explain how to create your own provider.
But I just want to get the token, check with the oauth provider that it's valid, login the user, provide the resources, and then logout the user until the next request.
If someone can give me some usefull tuto/doc/library?
Thanks.

So, I ended up creating my own custom authentication class extending BaseAuthentication (and custom user, user manager, ...) as I did not found any existing library.
If someone in interested in details, you can check the DRF documentation and many resources on the internet.
P.S. don't forget to extend authenticate_header is you want to return 401 with "raise AuthenticationFailed" and not 403 (for some reason).

Related

Microsoft MSAL React SPA, and RESTful Django API

I don't know why I can't find confirmation in the docs, maybe I am not navigating them correctly, although MSAL seems to have options to fit it into any application. This is my first time integrating a SAML sso procedure into any of my web-apps. I am just looking for some clarity on the correct, and secure way to verify the person attempting to login, is actually logged in with the IDP.
I am confused at the part after confirmation of login is given to my redirect API, I currently have it all happening on the front-end, then submitting the response to my back-end. Which is a RESTful API built with Django, and postgres database. At this point, I am thinking I need to verify my accessToken for authenticity, but I am unsure if I should be creating another PublicClient instance in python, and then sending the same commands to the IDP.
To guess at this point, I'm thinking this is wrong, as I need to verify the token, rather than get another Access and Refresh token. I'm thinking I just need to verify there is a session open with the IDP, and that the Access Token matches. Can anyone shed some light on this, possibly provide even just some direction.
The client Python Django Web App uses the Microsoft Authentication Library (MSAL) to sign-in and obtain an Access Token from Azure AD.
The access token is used as a bearer token to authorize the user to call the Python Flask Web API protected by Azure AD.
The Python Flask Web API then receives a token for Azure Resource Management API using the On-Behalf-Of flow.
To learn more about handing access token validation at the API layer, look into this sample walkthrough: https://github.com/Azure-Samples/ms-identity-python-on-behalf-of#about-the-code
https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens#validating-tokens

How do I secure a Python API with in-the-open authorization tokens?

I have setup a simple REST API server (using Django REST framework) that responds to POST requests by doing some processing on an image uploaded to the server in the request. Previously I used it to power my own frontend (http://lips.kyleingraham.com) as a demonstration but I would now like to open the API to other users.
I would like for an end-user to be able to sign up and, from a dashboard, generate a token based on their credentials that could then be hard-coded into their web app. The sign-up part I believe I can handle but I am unclear on how to restrict a generated token to a user's web app domain. I know that the code for a web app is easily inspected so any API token I provide would need to be policed on my backend.
How can I restrict an authorization token to a users' web app domain so that even if the token was leaked, another user would not be able to utilize it?
If you want to hard-code url into user web app, in that way you can't guarantee that if someone get the token, he won't be able to use it.
The only idea is to set some time limit for each token

How to revoke app authorization in Django OAuth Toolkit?

I'm implementing OAuth2 provider with Django Rest Framework and Django OAuth Toolkit.
Django OAuth Toolkit already has a set of views for managing OAuth Applications. This views let third-party application developer to do basic CRUD on an Application model. There's also a sample view for a resource owner to authorize the third-party app.
However I can't figure out a proper way for the resource owner to revoke the third-party app authorization. There's a revoke-token endpoint but if I get it right it serves a different job.
Essentially I try to build a view similar to the GitHub's "Authorized applications" page:
It looks like the RefreshToken model is the one I should use to list the connections. But works not as expected when the user reauthorizes the app. The RefreshToken instance is created after each successful authorization prompts and the connection list grows with duplicates.
Has anyone successfuly implemented a similar view with Django OAuth Toolkit?

How can I use Django OAuth Toolkit with Python Social Auth?

I'm building an API using Django Rest Framework. Later this API is supposed to be consumed by iOS and Android devices. I want to allow my users to sign-up with oauth2-providers like Facebook and Google. In this case, they shouldn't have to create an account with my platform at all. But users should also be able to sign-up when not having a Facebook/Google account, for which I'm using django-oauth-toolkit, so I have my own oauth2-provider.
For external providers I'm using python-social-auth, which works fine and automatically creates the user objects.
I want the clients to authenticate by using bearer tokens, which works fine for users that signed up with my provider (django-oauth-toolkit provides authentication scheme and permission classes for Django REST Framework).
However, python-social-auth only implements session based authentication, so there is no straightforward way to make authenticated API requests on behalf of users that registered by an external oauth2 provider.
If I use an access_token that has been generated by django-oauth-toolkit, doing a request like this works:
curl -v -H "Authorization: Bearer <token_generated_by_django-oauth-toolkit>" http://localhost:8000/api/
However, the following doesn't work since there is no corresponding authentication scheme for Django REST Framework and the AUTHENTICATION_BACKENDS provided by python-social-auth only work for session-based authentication:
curl -v -H "Authorization: Bearer <token_stored_by_python-social-auth>" http://localhost:8000/api/
Using the browseable API provided by Django REST Framework after authenticating with python-social-auth works just fine, only API calls without a session cookie don't work.
I'm wondering what the best approach is for this problem. The way I see it, I have basically two options:
A: When a user signs up with an external oauth2 provider (handled by python-social-auth), hook into the process to create an oauth2_provider.models.AccessToken and continue to use 'oauth2_provider.ext.rest_framework.OAuth2Authentication', now authenticating also users that registered with an external provider. This approach is suggested here:
https://groups.google.com/d/msg/django-rest-framework/ACKx1kY7kZM/YPWFA2DP9LwJ
B: Use python-social-auth for API request authentication. I could get my own users into python-social-auth by writing a custom backend and using register_by_access_token. However, since API calls cannot utilize Django sessions this would mean I would have to write an authentication scheme for Django Rest Framework that utilizes the data stored by python-social-auth. Some pointers on how to do this can be found here:
http://psa.matiasaguirre.net/docs/use_cases.html#signup-by-oauth-access-token
http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/
http://cbdev.blogspot.it/2014/02/facebook-login-with-angularjs-django.html
However, the way I understand it python-social-auth only verifies the token when doing a login and relies on the Django session afterwards. This would mean I would have to find a way to prevent python-social-auth from doing the whole oauth2-flow for each stateless API request and rather check against the data stored in the DB, which isn't really optimized for querying since it's stored as JSON (I could use UserSocialAuth.objects.get(extra_data__contains=) though).
I would also have to take care of verifying the scopes of an access token and use them to check permissions, something django-oauth-toolkit already does (TokenHasScope, required_scopes etc).
At the moment, I'm leaning towards using option A, since django-oauth-toolkit provides good integration with Django Rest Framework and I get everything I need out of the box. The only drawback is that I have to "inject" the access_tokens retrieved by python-social-auth into the AccessToken model of django-oauth-toolkit, which feels wrong somehow, but would probably be by far the easiest approach.
Does anybody have any objections on doing that or has maybe tackled the same problem in a different way? Am I missing something obvious and making my life harder than necessary?
If anybody has already integrated django-oauth-toolkit with python-social-auth and external oauth2 providers I would be very thankful for some pointers or opinions.
A lot of the difficulty in implementing OAuth comes down to understanding how the authorization flow is supposed to work. This is mostly because this is the "starting point" for logging in, and when working with a third-party backend (using something like Python Social Auth) you are actually doing this twice: once for your API and once for the third-party API.
Authorizing requests using your API and a third-party backend
The authentication process that you need is go through is:
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Facebook : User signs in
Facebook -> Django Login : User authorizes your API
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
I'm using "Facebook" as the third-party backend here, but the process is the same for any backend.
From the perspective of your mobile app, you are only redirecting to the /authorize url provided by Django OAuth Toolkit. From there, the mobile app waits until the callback url is reached, just like in the standard OAuth authorization flow. Almost everything else (Django login, social login, etc.) is handled by either Django OAuth Toolkit or Python Social Auth in the background.
This will also be compatible with pretty much any OAuth libraries that you use, and the authorization flow will work the same no matter what third party backend is used. It will even handle the (common) case where you need to be able to support Django's authentication backend (email/username and password) as well as a third-party login.
Mobile App -> Your API : Authorization redirect
Your API -> Django Login : Displays login page
Django Login -> Your API : User signs in
Your API -> Mobile App : User authorizes mobile app
What's also important to note here is that the mobile app (which could be any OAuth client) never receives the Facebook/third-party OAuth tokens. This is incredibly important, as it makes sure your API acts as an intermediary between the OAuth client and you user's social accounts.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives OAuth token
Mobile App -> Your API : Requests the display name
Your API -> Facebook : Requests the full name
Facebook -> Your API : Sends back the full name
Your API -> Mobile App : Send back a display name
Otherwise, the OAuth client would be able to bypass your API and make requests on your behalf to the third-party APIs.
Mobile App -> Your API : Authorization redirect
Your API -> Mobile App : Receives Facebook token
Mobile App -> Facebook : Requests all of the followers
Facebook -> Mobile App : Sends any requested data
You'll notice that at this point you would have lost all control over the third-party tokens. This is especially dangerous because most tokens can access a wide range of data, which opens the door to abuse and eventually goes down under your name. Most likely, those logging into your API/website did not intend on sharing their social information with the OAuth client, and were instead expecting you to keep that information private (as much as possible), but instead you are exposing that information to everyone.
Authenticating requests to your API
When the mobile application then uses your OAuth token to make requests to your API, all of the authentication happens through Django OAuth Toolkit (or your OAuth provider) in the background. All you see is that there is a User associated with your request.
Mobile App -> Your API : Sends request with OAuth token
Your API -> Django OAuth Toolkit : Verifies the token
Django OAuth Toolkit -> Your API : Returns the user who is authenticated
Your API -> Mobile App : Sends requested data back
This is important, because after the authorization stage it shouldn't make a difference if the user is coming from Facebook or Django's authentication system. Your API just needs a User to work with, and your OAuth provider should be able to handle the authentication and verification of the token.
This isn't much different from how Django REST framework authenticates the user when using session-backed authentication.
Web Browser -> Your API : Sends session cookie
Your API -> Django : Verifies session token
Django -> Your API : Returns session data
Your API -> Django : Verifies the user session
Django -> Your API : Returns the logged in user
Your API -> Web Browser : Returns the requested data
Again, all of this is handled by Django OAuth Toolkit and does not require extra work to implement.
Working with a native SDK
In most cases, you are going to be authenticating the user through your own website and using Python Social Auth to handle everything. But the one notable exception is when using a native SDK, as authentication and authorization is handled through the native system, which means you are bypassing your API entirely. This is great for applications which need to sign in with a third party, or applications which don't use your API at all, but it's a nightmare when both come together.
This is because your server can't validate the login and is forced to assume that the login is valid and genuine, which means it bypasses any and all security that Python Social Auth gives you.
Mobile App -> Facebook SDK : Opens the authorization prompt
Facebook SDK -> Mobile App : Gets the Facebook token
Mobile App -> Your API : Sends the Facebook token for authorization
Your API -> Django Login : Tries to validate the token
Django Login -> Your API : Returns a matching user
Your API -> Mobile App : Sends back an OAuth token for the user
You'll notice that this skips over your API during the authentication phase, and then forces your API to make assumptions about the token that is passed in. But there are definitely cases where this risk may be worth it, so you should evaluate that before throwing it out. It's a trade off between quick and native logins for your user and potentially handling bad or malicious tokens.
I solved it by using your A. option.
What I do is registering users that use a third party to sign up by their third party access token.
url(r'^register-by-token/(?P<backend>[^/]+)/$',
views.register_by_access_token),
This way, I can issue a GET request like this one:
GET http://localhost:8000/register-by-token/facebook/?access_token=123456
And register_by_access_token gets called. request.backend.do_auth will query the provider for the user info from the token and magically register a user account with the info or sign in the user if he's already registered.
Then, I create a token manually and return it as JSON for letting the client query my API.
from oauthlib.common import generate_token
...
#psa('social:complete')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter, if it's needed,
# request.backend and request.strategy will be loaded with the current
# backend and strategy.
third_party_token = request.GET.get('access_token')
user = request.backend.do_auth(third_party_token)
if user:
login(request, user)
# We get our app!
app = Application.objects.get(name="myapp")
# We delete the old token
try:
old = AccessToken.objects.get(user=user, application=app)
except:
pass
else:
old.delete()
# We create a new one
my_token = generate_token()
# We create the access token
# (we could create a refresh token too the same way)
AccessToken.objects.create(user=user,
application=app,
expires=now() + timedelta(days=365),
token=my_token)
return "OK" # you can return your token as JSON here
else:
return "ERROR"
I'm just not sure about the way I generate the token, is this good practice? Well, in the mean time, it works!!
Maybe django-rest-framework-social-oauth2 is what you're looking for. This package depends on python-social-auth and django-oauth-toolkit, which you already use. I quickly scanned through the documentation, and it seems to implement just what you are trying to do.
I was doing React Native with expo and Django with Django REST framework. This blogpost ended being the way I solved registration (signup) with facebook https://medium.com/#gabriel_gamil/react-native-expo-django-facebook-authentication-sign-in-83625c49da7
tldr; use django-rest-auth https://django-rest-auth.readthedocs.io/en/latest/index.html
use Django-allauth https://django-allauth.readthedocs.io/en/latest/

Simple registration+login using OAuth 2.0

I am a bit confused about OpenID OAuth stuff.
(by OAuth - i mean OAuth 2.0 here and later)
My target is to create a site with OpenID (or OAuth) auth as well as with legacy
email + password auth. The site will be on django framework. (python)
I understand difference between OAuth and OpenID and diff between authorization and authentification.
My primary goal is to implement openid login with google as identity provider.
The login and register flow must be just like on many sites.
As i see:
register scenario:
1) user enters openid url (clicks google button)
2) user gets redirected to auth provider ( google )
page says:
"Awesome site" ask for your:
email, language, country...
3) user says yes.. gets redirected back
behind the scenes "Awesome site"
retries email language and other user info
using access token
4) User fill other stuff needed in profile..
and this is it.. hes now registered.
Login scenario:
1) user enters openid url (clicks google button)
1.a) if user already logged in on auth provider
hmm... it is unclear to me.. but somehow user
get logged in without displaying a provider page (maybe it is closes fast? )
1.b) if the user not logged on provider
then provider displays login page and redirect back
to awesome site on success.
Because i have to get some user data
it is seem to me that i have to use:
OAuth or Hybrid ( OpenID + OAuth extentions )
protocol.
The things that are not clear to me:
How do i get scopes for info i need..
i searched but cant find.. found on someones blog
"https://www.googleapis.com/auth/userinfo#email" for email
but how about user language, country.. etc.. where it is documented ?
If i will use OAuth - will it be enough
to do both - the registration and login
or i will have to fetch user data wuth OAuth
and login with OpenID?
Is it ok to use OAuth 2.0 in my scenario ?
or it will be more simple to use 1.0 because i dont need
user data anymore after initial account registration ?
(i think 1.0 is more complex because it has 3 phases ..
but 2.0 is more complex because access token expires.. but
expiration wont be a problem in my scenario as i don't need user data
after registration)
There are few libs lying out there:
while reading google api docs i found:
google-api-python-client
openid-python-openid
gdata-python-client
( supposed to be api to google services dont know if it has oauth in it
hm.. according to this http://code.google.com/intl/ru/apis/gdata/docs/auth/oauth.html
it have oauth in it
)
python-oauth-client (http://code.google.com/p/python-oauth-client/)
i though thats a lot but then found:
http://your-move.appspot.com ( source files: https://github.com/sje397/Chess )
which seems to be using google.app.engine lib
from google.appengine.api import users
So which one to choose ?
So far i came that the simplest implementation would be:
Like this:
http://code.google.com/p/google-api-python-client/source/browse/samples/oauth2/django_sample/buzz/views.py
But lines 38-44: in my case would fetch user email language and other stuff (and also i will use other scope .. line 29 )
for a new registered user.. or if it is registered user just ignore credential and delete it. ( continue with the site session )
Am i wrong ?
I saw so many implementation on libs mentioned above that i doubt that i am right.
And also another question:
my fallback legacy auth will use email as login..
If an identity provider is email provider ( google )..
When user tries to log in i can fetch email from provider.. and search
email fetched from provider in database.. so i can find user.
What if identity provider not an email provider:
like facebook ? How i will search user in database ?
In my opinion, OpenID and OAuth 2.0 are two different concepts.
OpenID as its name explained mainly focused in manage Identity. So it is more like a framework or protocol to manage User Identity.
OAuth 2.0 was designed to provide a protocol which can make internet based applications manage authorization with some third party Authorization Providers. I do suggest you go through the IEFT OAuth 2.0 Spec before get your hand dirty. You can as well find some good information in this article A simplified explanation of OAuth2.0
Seems you wish your own website can handle user authorization by itself, that means in OAuth 2.0 area your own website is an AuthorizationServer.
To you question:
The scope are provided by the Authorization Provider, please refer to their documentation.
OAuth only provide the framework for authorizing your Web APP to access the Resource Owner's protected information. For the google example, the end user who authorize your app to access his/her protected information are still a google user unless your Web APP automatically create a user account for him/her in your own user account database - I think this step is what you mean Registration. OAuth doesn't cover anything about registration because it is out of the scope of authorization.
In you scenario you still have your own account database and wish users can authenticate themselves with username and password. OAuth 2.0 really can handle this scenario if you implement your own AuthorizationServer and use the “Resource Owner Password Credentials” auth flow.
I am not python guy, but I can tell you before you evaluate these libs you must understand different roles defined in OAuth 2.0 spec. Some of the libs play Authorization Server role, some play Resource Server role.
Why not just use the library, that does all the hard work of integrating with the open-id providers.
You can use SocialAuth, or any one of the other auth packages with similar functionality.
I would recommend not using Google or Facebook (or even worst Twitter) for testing OAuth/OpenID login as their setup is confusing, the scope is not clearly documented and requires SSL even on local to test. Instead I would suggest Github as it's more developer-friendly. SimpleLogin is also a good option (Disclaimer: I'm SimpleLogin founder) as it whitelists by default localhost to facilitate local development. Its doc is on https://docs.simplelogin.io.
If you want to learn more about OAuth/OpenID I think it's better to implement the redirection and callback yourself. Later it's better to use library to handle this though as they follow more closely the protocol. A very good article on this topic for Django is https://scotch.io/tutorials/django-authentication-with-facebook-instagram-and-linkedin.

Categories

Resources