Flask Backend with React Frontend, Using OAuth - python

This question's being asked a ton before, but I really don't think there's a clear answer anywhere (that I've read, anyway).
The Set Up
I have a Flask backend, solely serving an API. I've configured OAuth (with Google), using LoginManager. The login flow works perfectly, and I end up with session cookie, which permits me to access endpoints decorated with the #login_required decorator.
The frontend is to be written separately in React.
The Complication
The cookie set by going through the OAuth flow does not get used by the frontend doing the calling. All calls from the frontend to the backend end up with a 401, because unsurprisingly, the URL for the cookie is not the same as the URL for the backend which set it.
Idea
I have an unprotected /login endpoint, which I allow the frontend to call. Similarly the /login/callback endpoint can be called to finish the flow, and get back tokens. Could these somehow be used safely with the frontend, but with the flow still happening in the backend?
Compromise
What we've ended up with now, is the frontend doing the OAuth flow completely, then sending the token it gets along with every single call. The backend then does a check with Google every time to see if the token is indeed valid for who the frontend says it is.
This is less than ideal.
What is the recommended and correct way of separating the frontend and backend, but keeping the auth in the backend?

Related

Twitter / Tweepy OAuth - How to save oauth_token to verify it on callback in a REST API setup

I am writing a simple Twitter authentication flow with a REST API setup. Client side is just plain javascript/Vue code - there is no server-side rendering. This consumes a backend REST API built with Django Rest Framework, using tweepy library to wrap the Twitter API.
I have the authentication working, with the following steps:
User clicks a 'log in to twitter' button, which fires a call to my backend API /get-url/
API calls Twitter's get_authorization_url (using my Twitter app credentials), gets the token, and returns this redirection URL to the client
Client redirects user to the Twitter URL to complete authentication
On callback, client passes all params thru to backend API endpoint
API gets token and secret; creates a tweepy API with user auth; and calls verify_credentials
If everything worked fine, we create a user account in a Django database tied to the Twitter user, storing their token and secret; logs the user in with a JWT; and returns a 200 success with token.
Front end deals with doing JWT token refreshing etc; but otherwise uses this for authentication on the rest of the API.
However, there is one bit I'm stumped on: The Twitter docs suggest that in my step 2, I should save the request_token so that, on step 5, I can check it matches what is returned from the Twitter API (https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter).
The tweepy docs example (http://docs.tweepy.org/en/latest/auth_tutorial.html) suggests storing it in the session, then on callback checking them and deleting from the session.
However, this doesn't make sense (at least to me) in a REST API setup like mine. I'm stumped on how to temporarily store this on the server so that on callback I can check its value - prior to the redirect, I know nothing about the user to "tie" this token to, there is no user record to store in the database.
All I can think to do is for the API on (2) to return it to the client; then it can be stored in a cookie before redirect; then on callback, pass it back to the server... but this seems very convoluted and doing it all in plain text I assume nullifies the value of doing this extra check.
Hopefully I'm missing something obvious here.
Have you tried using python-social-auth library?
from_the_docs: twitter is in supported Authentication Backends.
You can try letting the user register with basic details, then have a 'link to Twitter' button that shows they have to activate their twitter connection to do anything else on your web app

Best pattern for AWS Cognito / React frontend / Django Rest backend

Struggling to get this fully working - here is my app workflow:
React frontend - user authenticates w/ Cognito directly (using AWS Amplify) - I've got this working fine. The frontend needs to handle this, as it makes separate direct calls to other AWS services.
DRF backend - React then makes API calls to DRF endpoints using Amplify.API, which includes the x-amz-security-token in the request header (this appears to be the Session Token returned by Amplify's call to the cognito-idp service). So now the backend has the Session Token (but not the Access Token or Refresh Token)
The backend then simply needs to verify that the frontend user (whose authenticated identity I assume is represented by the Session Token) is a valid and currently authenticated user according to Cognito. So the backend needs to make a separate call to Cognito to verify this.
This is where I'm failing - I've looked at django-warrant, but I can't discern from the documentation whether this is appropriate for my use case (or even how to really use it - the default setup suggested results in various boto3 errors to do w/ lack of credentials, etc.). I've also looked at warrant and directly at boto3 and botocore but it is not clear to me where to make all this happen. Am I missing seeing some kind of is_user_valid and is_session_token_valid methods in these libraries?
If someone knows how to do this directly w/ django-warrant it would help, and if not a little advice on the best way forward would also be appreciated (do I write my own authentication backed or middleware to intercept the request, authenticate somehow w/ boto3 or botocore, or what?)
Many thanks

How is Flask-Login's request_loader related to user_loader?

I apologize in advance for asking a rather cryptic question. However, I did not understand it despite going through a lot of material. It would be great if you could shed some light on this.
What is the purpose of a request_loader in flask-login? How does it interact with the user_loader decorator?
If I am using a token based authentication system (I am planning on sending the token to my angularJS front end, storing the token there and sending that token in the authorization-token header), will I need a request_loader or will a user_loader (where I check the auth header and see if the user exists) suffice?
From the Flask-Login documentation:
Sometimes you want to login users without using cookies, such as using
header values or an api key passed as a query argument. In these cases,
you should use the request_loader callback. This callback should
behave the same as your user_loader callback, except that it accepts
the Flask request instead of a user_id.
So, to answer your question, they both serve the same function for Flask-Login. They are both used to load the user. request_loader, however, is appropriate for custom logins.
Here's a great tutorial I found that utilizes request_loader to take advantage of token based authentication (The post is not my own, I'm merely sharing the link): http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html
I need to make this clear.
This is the reason why you shoud use request_loader with flask_login.
There will be a lot of #login_required from flask_login used in your api to guard the request access.
You need to make a request to pass the check of auth.
And there will be a lot of current_user imported from flask_login,
Your app need to use them to let the request act as the identity of the current_user.
There are two ways to achieve the above with flask_login.
Using user_loader makes the request to be OK for #login_required.
It is often used for UI logins from browser.
It will store session cookies to the browser and use them to auth later.
So you need to login only once and the session will keep for a time.
Using request_loader will also be OK with #login_required.
But it is often used with api_key or basic auth.
For example used by other apps to interact with your flask app.
There will be no session cookies,
so you need to provide the auth info every time you send request.
With both user_loader and request_loader,
now you got 2 ways of auth for the same api,
protected by #login_required,
and with current_user usable,
which is really smart.
To verify users with Flask-Login's session_id for frontend requests through Angular, you must set the withCredentials configuration flag to true.
That is, if you are using Angular's $http.post(url,data [,config]) or $http.get(url [,config]), make sure the config object contains the property withCredentials set to true. This will instruct the browser to use its cookies in the same way it would for a full-on page visit.
For example,
$http.post('/api/login',{username:'myusername',password:'mypassword'},{withCredentials:true})
will post the data {username:'myusername',password:'mypassword'} to your site/app's /api/login route and, if you're using Flask-Login and are logged in, Flask will know.
You can set this behavior for all $http service requests by setting
$httpProvider.defaults.withCredentials=true
somewhere in your app. Currently, I have that line of code in my app.config block, which seems appropriate to me:
var myApp = angular.module('myApp');
myApp.config(function ($httpProvider) {
$httpProvider.defaults.withCredentials = true;
});
(Since this post is about Flask, folks may want to send form data through Angular in such a way that it can be found in request.form, which has a similar solution, fyi.)

Django OAuth2 provider and resources on different servers?

I'm looking to set up Django to use OAuth2 to authenticate users for a service that I'm running, but I'm having a bit of difficulty understanding how the tokens are passed around.
I've been working my way through this tutorial: https://django-oauth-toolkit.readthedocs.org/en/0.7.0/tutorial/tutorial_01.html. I've been able to get a server up and running as the OAuth provider, and it seems to be working as it should. I'm able to log in to it and set up an application. The difficulty I'm having is figuring out how the various tokens are passed around.
Suppose that my OAuth provider is sitting on one server - let's call this Provider.com - and my service that I'm wanting authenticated is on service.com. When a user first tries to make a request to the service, they first need to authenticate against the Provider. So they click on a login button which directs them to Provider.com. They enter their credentials. If everything is set up correctly on the server, they should be presented with a prompt that gives them a chance to allow or deny Service.com from accessing their account on Provider.com. Supposing that they click Allow, they are then redirected to Service.com, and are given a token. On future calls to Service.com, they pass in the token, and are, in theory, able to make authenticated calls.
The problem I'm having understanding is this: At what point do the Provider and the Service communicate? If a call comes in to the Service, how does it know that the authentication token passed in with the call is valid? There's know way it could know that a particular token is valid unless: A) it recognizes that same token from a previous call which was also authenticated or B) it talks to the OAuth 2 provider and verifies the authenticity of the token.
A diagram like the one found here shows the process in the browser:
At the end of this, it has the Client App sending the authentication code, client id, and client secret to the OAuth2 provider. In the previously mentioned tutorial, it isn't really clear how this is actually done. In the tutorial, the provider and the service are on the same machine, and it would appear that they also share the same database.
This this brings about my question: How does one host a Django-based OAuth provider on a separate server than the resource/service being accessed? Is this possible?
From this other post, it indicates that this might not be possible: https://stackoverflow.com/a/26656538/1096385 Is that indeed the case, at least with the existing Django OAuth2 provider framework?
It depends on the oauth2 flow you're using. It seems like you're using authentication code.
In that case:
service.com sends the browser to provider.com for user authentication (uri contains service.com client_id and redirect_uri)
User authenticates on provider.com, then the browser is redirected to service.com's redirect_uri with a ?code parameter.
On your server side, handle this code parameter and ask for a token with it.
See https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified#web-server-apps

How to create an API with a "remember me" function in Flask?

I'm going to build an API in Flask for a (to be created) app which will be built using PhoneGap. In the API many calls will need authentication.
To get into the topic I was reading this tutorial on creating authentication for a Flask-built API. In this tutorial they first show how a user can use basic password authentication for every call, after which token based authentication is introduced.
As far as I understand, the client who calls the API should simply get a token and authenticate every subsequent call with that. In the meantime, the client should keep track of time and either get a new token every 9 minutes (before the old token expires) or simply keep on calling with the token until the client gets an Unauhorized Access message. Am I understanding this correctly?
Moving on, I wonder how it works with Apps on which you login on your phone and then are always logged in whenever you open the app (like for example the Facebook app). This is obviously more convenient to the user than always needing to provide the username/password and I would like to implement something like that as well. I wonder though; how is a permanent logged in feature like this implemented on the server side? Is it done by providing the password and username for every call, or using a never expiring token, or yet a different way?
All tips are welcome!
I've done what you want to do with:
Flask-security https://pythonhosted.org/Flask-Security/:
To manage users and permissions.
Flask-oauth-lib https://flask-oauthlib.readthedocs.org/en/latest/:
Provide oauth functionnality.
So, you have to take a look at Oauth flow, implements a user backend (like Flask-security) and implements an oauth server (with flask oauth lib for example) and bind it to your user backend.
After that, it's oauth standard flow. You just have to give the right token on each api calls and TADA !
With this way you can also, if you want, give access to your api to third-party app thanks to oAuth :)

Categories

Resources