viraptor
viraptor

Reputation: 34145

HTTP authentication in App Engine Dev Server

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

Answers (4)

Evan Plaice
Evan Plaice

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:

  • Modify __basic_lookup() if you need your passwords are stored somewhere else (ex database).
  • Modify __basic_hash() if your passwords are plaintext or encoded using a different method.
  • Modify __basic_login() if you want a different response to a failed login attempt. Such as an artificial delay to prevent brute-force attacks, or a redirect.

Upvotes: 2

Nick Johnson
Nick Johnson

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

Peter Recore
Peter Recore

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

Steven Kryskalla
Steven Kryskalla

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

Related Questions