Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App Engine remote_api with OpenID

I've recently tried to switch my app engine app to using openID, but I'm having an issue authenticating with remote_api. The old authentication mechanism for remote_api doesn't seem to work (which makes sense) - I'm getting a 'urllib2.HTTPError: HTTP Error 302: Found', which I assume is appengine redirecting me to the openid login page I've set up.

I guess I'm missing something fairly obvious. Currently my remote_api script has the following in it -

remote_api_stub.ConfigureRemoteDatastore(app_id=app_id, path='/remote_api', auth_func=auth_func, servername=host, secure=secure)

where auth_func is

def auth_func():
  return raw_input('Username:'), getpass.getpass('Password:')

Any ideas what I need to supply to remote_api? I guess similar issues would be encountered with bulkloader too. Cheers,

Colin

like image 808
hawkett Avatar asked Jun 05 '10 16:06

hawkett


3 Answers

This was a fun one.

Looking at remote_api, the flow for authentication seems to be something like this:

  • Prompt the user for Google credentials
  • Post the credentials to https://www.google.com/accounts/ClientLogin
  • Parse the auth token out of the response body
  • Pass the token to https://myapp.appspot.com/_ah/login
  • Grab ACSID cookie set in the response
  • Pass the ACSID cookie in subsequent requests that require authorization

I couldn't find a lot of documentation on the new OpenID support, though Nick's blog entry was informative.

Here's the test app I wrote to see how things work:

app.yaml:

handlers:
- url: /remote_api
  script: $PYTHON_LIB/google/appengine/ext/remote_api/handler.py
  login: admin
- url: /.*
  script: test.py

test.py:

class MainPage(webapp.RequestHandler):
  def get(self):
    user = users.get_current_user()
    if user:
      self.response.out.write("Hi, %s!<hr>admin is %s" % (user.user_id(),
        users.is_current_user_admin()))
    else:
      self.redirect(users.create_login_url('/', None,
        'https://www.google.com/accounts/o8/id'))

Flipping my auth mode between Google Accounts and Federated Login, I noticed a few things:

  • Admin users are correctly recognized by is_current_user_admin() with OpenID
  • Mixing modes doesn't work. With authentication set to Google Accounts, calling create_login_url with a federated_identity throws a NotAllowedError
  • An ACSID cookie is still produced at the end of the login process, only it comes from /_ah/openid_verify instead of /_ah/login

So what's happening with remote_api when using Federated Login? If we're using the default appengine_rpc.HttpRpcServer, it's dutifully following the same Google Account authentication process described at the top, only the app no longer considers the ACSID cookie returned by /_ah/login to be valid, so since you're still unauthenticated, you get a 302 redirect to the OpenID login page, /_ah/login_required.

I dunno what the right solution is here. Seems like it would require an API update. Maybe Nick or one of the other Googlers can weigh in.

For now, here's a hacky workaround:

  • Turn on Federated Login for your app
  • Make sure you're passing save_cookies=True when calling remote_api_stub.ConfigureRemoteDatastore for your console script
  • Attempt console authentication and get the 302 error
  • Login as an admin via your app's web interface
  • In your browser cookies, find the ACSID cookie for myapp.appspot.com
  • Find and edit your local ~/.appcfg_cookies file
  • Replace the ACSID cookie for myapp.appspot.com with the one from your browser

The next time you try to use remote_api, it should work without prompting for credentials. You'll have to repeat the last 4 steps every time the cookie expires, though. You can bump the expiration from 1 day to as high as 2 weeks in the admin console to minimize the annoyance. Have fun!

like image 95
Drew Sears Avatar answered Nov 10 '22 02:11

Drew Sears


This is definitely an issue... mark your interest in getting Google to fix this by starring the ticket at http://code.google.com/p/googleappengine/issues/detail?id=3258 and feel free to add any of your workarounds there.

On a related note, we also recognize that the docs are somewhat sparse, so I'm working on an article which hopefully fills-in some of those holes... stay tuned and keep your eyes open at http://code.google.com/appengine/articles

like image 40
wescpy Avatar answered Nov 10 '22 02:11

wescpy


Here's a workaround you can use until there's a more permanent solution in place.

like image 38
Nick Johnson Avatar answered Nov 10 '22 03:11

Nick Johnson