Reputation: 34145
I want to setup the App Engine Dev Server, so that it's available to other people for a preview.
What I'd really like to do before that is enable HTTP authentication for any URL served from that site. I don't want anyone to access the service without passing that stage. I could, of course, build my own HTTP authentication into the app I'm developing, but that's not a perfect solution, because I don't need that feature when the app is deployed.
Is there any good way to solve it?
Upvotes: 5
Views: 1952
Reputation: 14140
Here's a pretty solid implementation I came up with
Through my search, I found tons of sloppy/incomplete implementations of BasicAuth for GAE online. As a result, I ended up writing my own. This is the best/simplest approach I have been able to come up with so far.
I consider it a 'good practice' to keep request handlers as thin as possible. To cut down on boilerplate and copypasta in the handlers themselves I decided to implement authentication as a decorator. To use it just attach the decorator to the handler's get/post/put/delete methods.
For example:
from decorators import basic_auth
class Handler(webapp2.RequestHandler):
@basic_auth
def get(self):
# load your page here
Then add the decorator to decorators.py:
import base64
import os
from webapp2_extras import security
import yaml
def basic_auth(func):
def callf(webappRequest, *args, **kwargs):
# Parse the header to extract a user/password combo.
auth_header = webappRequest.request.headers.get('Authorization')
# if the auth header is missing popup a login dialog
if auth_header == None:
__basic_login(webappRequest)
else:
(username, password) = base64.b64decode(auth_header.split(' ')[1]).split(':')
if(__basic_lookup(username) == __basic_hash(password)):
return func(webappRequest, *args, **kwargs)
else:
__basic_login(webappRequest)
return callf
def __basic_login(webappRequest):
webappRequest.response.set_status(401, message="Authorization Required")
webappRequest.response.headers['WWW-Authenticate'] = 'Basic realm="Secure Area"'
def __basic_lookup(username):
accounts_file = os.getcwd() + os.sep + 'app' + os.sep + 'accounts.yaml'
stream = file(accounts_file, 'r')
for account in yaml.load(stream):
if account['username'] == username:
return account['password']
def __basic_hash(password):
return security.hash_password(password, method='sha1')
Note: Double underscore prefixed are used here on functions that shouldn't be visible outside of the 'decorators' module.
In this case, a failed login attempt will simply popup another login dialog, I'm authenticating using a password that is stored in a separate accounts.yaml file, and the passwords are stored in a SHA1 hashed form.
The code is written to be easily customized:
Upvotes: 2
Reputation: 101149
Don't make the dev_appserver publicly accessible. It's not designed for it, and it's not secure. To name just one problem, any visitor can go to yourhost/_ah/admin/ and mess with your datastore, and the auth suggestions by lost-theory won't prevent it.
If you absolutely must do this, set up Apache or another webserver as a reverse proxy, implementing authentication and blocking access to /_ah URLs. Peter's suggestion of deploying it to App Engine is a much better one, however.
Upvotes: 2
Reputation: 14187
Deploy the app to the appengine servers, but use a different app id than the one you will eventually use in production. That way you don't have to develop any extra authentication mechanism, and you don't have to worry about how well the dev app server will handle multiple users worth of load.
Upvotes: 2
Reputation: 14649
Are you using Java or Python?
If you're using Python, you can use existing WSGI middleware to handle HTTP basic auth. Here are some options:
Upvotes: 2