Multiple authentication options with Tornado - python

Just started playing with Tornado and want to offer multiple methods of authentication. Currently my app is working fine with Google's hybrid OpenID/oAuth using tornado.auth.GoogleMixin and the unauthenticated users are automatically sent to Google's auth page.
If an unauthenticated user wants to use another option (ie. local auth or tornado.auth.TwitterMixin), how can I implement the logic to choose an auth mechanism within the login handler?
I added the decorator 'tornado.web.authenticated' to all of my exposed methods, and here is the my login handler class (pretty much straight from the Tornado examples) which is currently working with Google OpenID/oAuth:
class AuthLoginHandler(BaseHandler, tornado.auth.GoogleMixin):
#tornado.web.asynchronous
def get(self):
if self.get_argument('openid.mode', None):
self.get_authenticated_user(self.async_callback(self._on_auth))
return
## redirect after auth
self.authenticate_redirect()
def _on_auth(self, user):
## auth fail
if not user:
raise tornado.web.HTTPError(500, 'Google auth failed')
## auth success
identity = self.get_argument('openid.identity', None)
## set identity in cookie
self.set_secure_cookie('identity', tornado.escape.json_encode(identity))
self.redirect('/')
Appreciate any suggestions for a solution. Thanks

I think the easiest way to do it would be to change the AuthLoginHandler to something more specific, like GoogleAuthHandler, and create an appropriate route for that:
(r"/login/google/", GoogleAuthHandler),
(r"/login/facebook/", FacebookAuthHandler),
etc.
Then simply create links to each authentication provider on the page ala:
<a href="/login/google/>Login with Google</a>
Login with Facebook
If you wanted to make it fancier, you could provide the providers as a select box, or if you wanted to get REALLY fancy, you could parse their 'openid' URL (e.g., if username.google.com, self.redirect("/login/google"), but that assumes that users know their OpenID provider URLs, which is usually not the case. I'd guess if you gave them a google / facebook / twitter icon or something to click on that would confuse the least number of people.

I came upon this problem myself but in a slightly different circumstance.
One solution is actually to do something like this.
class AuthLoginHandler(BaseHandler, tornado.auth.GoogleMixin, tornado.auth.TwitterMixin):
def get(self):
if want_google:
tornado.auth.GoogleMixin.get_authenticated_user(self)
#...
elif want_twitter:
tornado.auth.TwitterMixin.get_authenticated_user(self)
#...

Related

Require new login for each page view

I am currently using code found here:
http://flask.pocoo.org/snippets/8/
And I decorate my function accordingly to have the admin authenticate when requesting a specific admin page. However, instead of requiring the admin to keep authenticating each time they admin page, I noticed that it somehow keeps track of the session and no longer requires authentication after successfully authenticating once. Is there some way to force flask to re-authenticate every time an admin requests the given decorated admin page?
Using the included snippet, there is no good way to force a user to log in every time they request the given page.
This is because that snippet is using HTTP Basic Auth and there is no good way to ask the browser to stop sending that header.
What you are looking for can be done with a custom decorator. You can use the sample below. Note that your case will be different, but you can use this as a guide.
from web import app, has_role
#app.route("/admin/my_page")
#login_required
#has_role(role="admin")
def admin_my_page():
//do stuff
Then, in your project init, or an include file you can add the following:
def has_role(role=None):
def _initial_decorator(view_func):
def _decorator(*args, **kwargs):
response = view_func(*args, **kwargs)
if g.user.user_level != role:
from flask import redirect, url_for
return redirect(url_for("no_access"))
return response
return wraps(view_func)(_decorator)
return _initial_decorator
This should at lease give you an idea of how to create a custom decorator, and then check for role permissions. You can expand this to however you need. You can put engine logic, or other checks to fit your project.

Python Social Auth - How to know if social media connect is successful?

I have installed Python Social Auth. I use it to associate user site account with his social media accounts.
Facebook connect link is:
Connect
Redirection works but how to know if social media association is successful?
If an exception is catch, I can display get_messages() function. It's perfect!
But any return if it's successful.
I have tried to custom a pipeline but I have not access to request variable to set message like it: messages.success(request, "Successful message!')
You do have access to request in your custom pipeline:
The pipeline functions will get quite a lot of arguments, ranging from the backend in use, different model instances, server requests and provider responses.
You can read more about it here.
and you can get access to request object like this:
def custom_pipeline(strategy, *args, *kwargs):
request = strategy.request
and do whatever you wanted with messages.
There is an option
SOCIAL_AUTH_LOGIN_ERROR_URL = '/error/'
in python social auth settings which redirects to the url you mention in above setting.
This way you will know whether the connect is success or not.
Hope this is helpful.

Create Django token-based Authentication [duplicate]

I am trying to figure out the best way to implement token based authentication in my django app. An external, non-django application is setting a cookie, with a token, and I have a webservice that can retrieve user information based off of that token. If the user has the cookie set, they should not need to authenticate on my site and should be automatically logged in based on the info passed back by the web service. As I see it, there are a few different options to perform the actual check and I'm not sure which is best:
Write a custom decorator like the one in this snippet and use it instead of
login_required.
Call a custom authenticate method inside base_site through an ajax call. On every page, a check would be made and if the cookie exists and is valid, then the user would be automatically logged in.
Add some javascript to the LOGIN_REDIRECT_URL page that will check/validate the cookie in an ajax call, and automatically redirect back to the referrer if the cookie authenticated.
Is there an option I am missing? Ideally, there would be a way to build this into login_required, without having to write a custom decorator.
Before searching for code, be sure you read the documentation. http://docs.djangoproject.com/en/1.2/topics/auth/#other-authentication-sources
Also read the supplied Django source.
You want to create three things.
Middleware to capture the token. This is where most of the work happens. It checks for the token, authenticates it (by confirming it with the identity manager) and then logs in the user.
Authentication backend to find Users. This is a stub. All it does is create users as needed. Your identity manager has the details. You're just caching the current version of the user on Django's local DB.
Here's the middleware (edited).
from django.contrib.auth import authenticate, login
class CookieMiddleware( object ):
"""Authentication Middleware for OpenAM using a cookie with a token.
Backend will get user.
"""
def process_request(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured()
if "thecookiename" not in request.COOKIES:
return
token= request.COOKIES["thecookiename"]
# REST request to OpenAM server for user attributes.
token, attribute, role = identity_manager.get_attributes( token )
user = authenticate(remote_user=attribute['uid'][0])
request.user = user
login(request, user)
The identity_manager.get_attributes is a separate class we wrote to validate the token and get details on the user from the IM source. This, of course, has to be mocked for testing purposes.
Here's a backend (edited)
class Backend( RemoteUserBackend ):
def authenticate(**credentials):
"""We could authenticate the token by checking with OpenAM
Server. We don't do that here, instead we trust the middleware to do it.
"""
try:
user= User.objects.get(username=credentials['remote_user'])
except User.DoesNotExist:
user= User.objects.create(username=credentials['remote_user'] )
# Here is a good place to map roles to Django Group instances or other features.
return user
This does not materially change the decorators for authentication or authorization.
To make sure of this, we actually refresh the User and Group information from our
identity manager.
Note that the middleware runs for every single request. Sometimes, it's okay to pass the token to the backed authenticate method. If the token exists in the local user DB, the request can proceed without contacting the identity manager.
We, however, have complex rules and timeouts in the identity manager, so we have to examine every token to be sure it's valid. Once the middleware is sure the token is valid, we can then allow the backend to do any additional processing.
This isn't our live code (it's a little too complex to make a good example.)

Subclassing User to differentiate between an admin and an App with an API key?

This is my first API, so please forgive/correct me if I make any wrong claims.
I have an app that has an API. I would only like external apps (mobile clients etc.) with a valid API key to have access to it, and I think django-rest-framework's TokenAuthentication is the right fit for it. I would also like users to be able to log in with their username & password, and I'm looking to use OAuth2Authentication for that. But I don't want apps that use TokenAuthentication to have a "User" instance in the database (as they are not users in the traditional sense) so I'm thinking about doing something like this:
class Client(User):
pass
django-rest-framework says that request.user will be an instance of User. Will I bump into any problems if I use Client instead?
Is this the standard way of handling this situation? It just doesn't feel right to me (mainly because of question 1)
For mobile clients to have access to your API, using django-restframework's TokenAuthentication will work just fine. As you have already ascertained. You need to design a mechanism for handing out your tokens. If you are doing this dynamically then you will need to have your API request handle this.
Mobile Client: ( initial API request )
request /API/rabbit/1/
( no token)
Server: 401
test for the token ( fails )
'login please' via HTTP 401 response. ( or some other custom header or response information )
You can define your api on how to accomplish this, most folks use the http:
401 Unauthorized error code. I point this out because its clearly a design decision.
Mobile Client: ( request login)
prompts user for username and password, and makes a request too /login/ this could be a special mobileclient login like /mobile/login/ whose difference is that it hands back a token on a successful login.
Server: 200
verifies valid user and hands out token.
you could write this logic, or you could use 'rest_framework.authtoken.views.obtain_auth_token' which I recommend. See rest-frameworks
token authentication for details on this. Heed its warning about https.
Mobile Client: ( re-request API with token in http header)
receives token
now remakes initial request to /API/rabbit/1/ with its token in the header.
Server: 200
verifies valid token in the header and provides access to API. You will be writing this code.
Finally: You will need to design a strategy to 'age-out' your tokens, and or lock out users.
Also: make sure you add 'rest_framework.authtoken' to your INSTALLED_APPS, and make sure you call manage.py syncdb
Aside: you do not specifically have to use the TokenAuthentication ( request.user, request.auth) you can write your own code to peek in the header and see if the token is set. This is fairly easy to do with the python Cookie lib. You are still using-heavly the token management features of django-rest-framework. To be honest I think there documentation is a bit incomplete on configuring the 'TokenAuthentication' authentication back-ends.
Hope this helps!

Django sessions with Oauth - cannot pass variables thru request.session

I am building an app for making API calls to websites like (FB, LinkedIn, etc.) I need to use OAuth to authorize my application to request data on behalf of the user. I am stuck with a problem of storing the instance of my website interface library (LinkedIn) across views. I used request.session - with file back end.
Below is the code http://pastebin.com/QTgqSr7W
Am I doing something wrong? can see the value being set in login() but I cannot see the same value in token(). Is this wrong to expect? Any workaround for passing the value of the api instance?
Thanks and Regards,
Atul.
hmm, i think its because you are saving the entire api python instance, i dont think that sessions support that kind of data, why not just redirect user to auth url without saving something in session, then in callback view, you instantiate the linkedin.LinkedIn class like so
from django.conf import settings
key = settings.KEY
secret = settings.SECRET
return_url = settings.CALLBACK
# You make the api connection here, so its not tied to any function
api = linkedin.LinkedIn(key, secret, return_url)
def login(request):
if api.request_token():
auth_url = api.get_authorize_url()
return HttpResponseRedirect(auth_url)
#below is the view that will get called with the oauth oken.
def token(request, param):
#do stuff with the api.

Categories

Resources