Add robot helpers and a sample.
diff --git a/oauth2client/appengine.py b/oauth2client/appengine.py
index 71aa8f0..a9766c7 100644
--- a/oauth2client/appengine.py
+++ b/oauth2client/appengine.py
@@ -21,14 +21,29 @@
 
 import httplib2
 import pickle
+import time
+import base64
+import logging
+
+try: # pragma: no cover
+  import simplejson
+except ImportError: # pragma: no cover
+  try:
+    # Try to import from django, should work on App Engine
+    from django.utils import simplejson
+  except ImportError:
+    # Should work for Python2.6 and higher.
+    import json as simplejson
 
 from client import AccessTokenRefreshError
+from client import AssertionCredentials
 from client import Credentials
 from client import Flow
 from client import OAuth2WebServerFlow
 from client import Storage
 from google.appengine.api import memcache
 from google.appengine.api import users
+from google.appengine.api.app_identity import app_identity
 from google.appengine.ext import db
 from google.appengine.ext import webapp
 from google.appengine.ext.webapp.util import login_required
@@ -36,6 +51,76 @@
 
 OAUTH2CLIENT_NAMESPACE = 'oauth2client#ns'
 
+
+class AppAssertionCredentials(AssertionCredentials):
+  """Credentials object for App Engine Assertion Grants
+
+  This object will allow an App Engine application to identify itself to Google
+  and other OAuth 2.0 servers that can verify assertions. It can be used for
+  the purpose of accessing data stored under an account assigned to the App
+  Engine application itself. The algorithm used for generating the assertion is
+  the Signed JSON Web Token (JWT) algorithm. Additional details can be found at
+  the following link:
+
+  http://self-issued.info/docs/draft-jones-json-web-token.html
+
+  This credential does not require a flow to instantiate because it represents
+  a two legged flow, and therefore has all of the required information to
+  generate and refresh its own access tokens.
+
+  AssertionFlowCredentials objects may be safely pickled and unpickled.
+  """
+
+  def __init__(self, scope, user_agent,
+      audience='https://accounts.google.com/o/oauth2/token',
+      assertion_type='http://oauth.net/grant_type/jwt/1.0/bearer',
+      token_uri='https://accounts.google.com/o/oauth2/token', **kwargs):
+    """Constructor for AppAssertionCredentials
+
+    Args:
+      scope: string, scope of the credentials being requested.
+      user_agent: string, The HTTP User-Agent to provide for this application.
+      audience: string, The audience, or verifier of the assertion.  For
+        convenience defaults to Google's audience.
+      assertion_type: string, Type name that will identify the format of the
+        assertion string.  For convience, defaults to the JSON Web Token (JWT)
+        assertion type string.
+      token_uri: string, URI for token endpoint. For convenience
+        defaults to Google's endpoints but any OAuth 2.0 provider can be used.
+    """
+    self.scope = scope
+    self.audience = audience
+    self.app_name = app_identity.get_service_account_name()
+
+    super(AppAssertionCredentials, self).__init__(
+        assertion_type,
+        user_agent,
+        token_uri)
+
+  def _generate_assertion(self):
+    header = {
+      'typ': 'JWT',
+      'alg': 'RS256',
+    }
+
+    now = int(time.time())
+    claims = {
+      'aud': self.audience,
+      'scope': self.scope,
+      'iat': now,
+      'exp': now + 3600,
+      'iss': self.app_name,
+    }
+
+    jwt_components = [base64.b64encode(simplejson.dumps(seg))
+        for seg in [header, claims]]
+
+    base_str = ".".join(jwt_components)
+    key_name, signature = app_identity.sign_blob(base_str)
+    jwt_components.append(base64.b64encode(signature))
+    return ".".join(jwt_components)
+
+
 class FlowProperty(db.Property):
   """App Engine datastore Property for Flow.
 
@@ -117,7 +202,7 @@
     Args:
       model: db.Model, model class
       key_name: string, key name for the entity that has the credentials
-      property_name: string, name of the property that is an CredentialsProperty
+      property_name: string, name of the property that is a CredentialsProperty
       cache: memcache, a write-through cache to put in front of the datastore
     """
     self._model = model
@@ -189,6 +274,7 @@
         # in API calls
 
   """
+
   def __init__(self, client_id, client_secret, scope, user_agent,
                auth_uri='https://accounts.google.com/o/oauth2/auth',
                token_uri='https://accounts.google.com/o/oauth2/token'):
@@ -205,8 +291,8 @@
       token_uri: string, URI for token endpoint. For convenience
         defaults to Google's endpoints but any OAuth 2.0 provider can be used.
     """
-    self.flow = OAuth2WebServerFlow(client_id, client_secret, scope, user_agent,
-      auth_uri, token_uri)
+    self.flow = OAuth2WebServerFlow(client_id, client_secret, scope,
+      user_agent, auth_uri, token_uri)
     self.credentials = None
     self._request_handler = None
 
@@ -220,6 +306,7 @@
       method: callable, to be decorated method of a webapp.RequestHandler
         instance.
     """
+
     def check_oauth(request_handler, *args):
       user = users.get_current_user()
       # Don't use @login_decorator as this could be used in a POST request.
@@ -255,6 +342,7 @@
       method: callable, to be decorated method of a webapp.RequestHandler
         instance.
     """
+
     def setup_oauth(request_handler, *args):
       user = users.get_current_user()
       # Don't use @login_decorator as this could be used in a POST request.
@@ -308,10 +396,12 @@
     error = self.request.get('error')
     if error:
       errormsg = self.request.get('error_description', error)
-      self.response.out.write('The authorization request failed: %s' % errormsg)
+      self.response.out.write(
+          'The authorization request failed: %s' % errormsg)
     else:
       user = users.get_current_user()
-      flow = pickle.loads(memcache.get(user.user_id(), namespace=OAUTH2CLIENT_NAMESPACE))
+      flow = pickle.loads(memcache.get(user.user_id(),
+                                       namespace=OAUTH2CLIENT_NAMESPACE))
       # This code should be ammended with application specific error
       # handling. The following cases should be considered:
       # 1. What if the flow doesn't exist in memcache? Or is corrupt?
@@ -328,6 +418,7 @@
 
 application = webapp.WSGIApplication([('/oauth2callback', OAuth2Handler)])
 
+
 def main():
   run_wsgi_app(application)
 
diff --git a/oauth2client/client.py b/oauth2client/client.py
index 3c59980..523a185 100644
--- a/oauth2client/client.py
+++ b/oauth2client/client.py
@@ -83,6 +83,7 @@
     """
     _abstract()
 
+
 class Flow(object):
   """Base class for all Flow objects."""
   pass
@@ -94,7 +95,6 @@
   Store and retrieve a single credential.
   """
 
-
   def get(self):
     """Retrieve credential.
 
@@ -187,6 +187,26 @@
     self.__dict__.update(state)
     self.store = None
 
+  def _generate_refresh_request_body(self):
+    """Generate the body that will be used in the refresh request
+    """
+    body = urllib.urlencode({
+      'grant_type': 'refresh_token',
+      'client_id': self.client_id,
+      'client_secret': self.client_secret,
+      'refresh_token': self.refresh_token,
+      })
+    return body
+
+  def _generate_refresh_request_headers(self):
+    """Generate the headers that will be used in the refresh request
+    """
+    headers = {
+        'user-agent': self.user_agent,
+        'content-type': 'application/x-www-form-urlencoded',
+    }
+    return headers
+
   def _refresh(self, http_request):
     """Refresh the access_token using the refresh_token.
 
@@ -194,16 +214,9 @@
        http: An instance of httplib2.Http.request
            or something that acts like it.
     """
-    body = urllib.urlencode({
-      'grant_type': 'refresh_token',
-      'client_id': self.client_id,
-      'client_secret': self.client_secret,
-      'refresh_token' : self.refresh_token
-      })
-    headers = {
-        'user-agent': self.user_agent,
-        'content-type': 'application/x-www-form-urlencoded'
-    }
+    body = self._generate_refresh_request_body()
+    headers = self._generate_refresh_request_headers()
+
     logging.info("Refresing access_token")
     resp, content = http_request(
         self.token_uri, method='POST', body=body, headers=headers)
@@ -214,14 +227,14 @@
       self.refresh_token = d.get('refresh_token', self.refresh_token)
       if 'expires_in' in d:
         self.token_expiry = datetime.timedelta(
-            seconds = int(d['expires_in'])) + datetime.datetime.now()
+            seconds=int(d['expires_in'])) + datetime.datetime.now()
       else:
         self.token_expiry = None
       if self.store is not None:
         self.store(self)
     else:
-      # An {'error':...} response body means the token is expired or revoked, so
-      # we flag the credentials as such.
+      # An {'error':...} response body means the token is expired or revoked,
+      # so we flag the credentials as such.
       logging.error('Failed to retrieve access token: %s' % content)
       error_msg = 'Invalid response %s.' % resp['status']
       try:
@@ -232,7 +245,8 @@
           if self.store is not None:
             self.store(self)
           else:
-            logging.warning("Unable to store refreshed credentials, no Storage provided.")
+            logging.warning(
+                "Unable to store refreshed credentials, no Storage provided.")
       except:
         pass
       raise AccessTokenRefreshError(error_msg)
@@ -266,6 +280,10 @@
     def new_request(uri, method='GET', body=None, headers=None,
                     redirections=httplib2.DEFAULT_MAX_REDIRECTS,
                     connection_type=None):
+      if not self.access_token:
+        logging.info("Attempting refresh to obtain initial access_token")
+        self._refresh(request_orig)
+
       """Modify the request headers to add the appropriate
       Authorization header."""
       if headers == None:
@@ -275,8 +293,10 @@
         headers['user-agent'] = self.user_agent + ' ' + headers['user-agent']
       else:
         headers['user-agent'] = self.user_agent
+
       resp, content = request_orig(uri, method, body, headers,
                                    redirections, connection_type)
+
       if resp.status == 401:
         logging.info("Refreshing because we got a 401")
         self._refresh(request_orig)
@@ -341,6 +361,57 @@
     raise AccessTokenCredentialsError(
         "The access_token is expired or invalid and can't be refreshed.")
 
+
+class AssertionCredentials(OAuth2Credentials):
+  """Abstract Credentials object used for OAuth 2.0 assertion grants
+
+  This credential does not require a flow to instantiate because it represents
+  a two legged flow, and therefore has all of the required information to
+  generate and refresh its own access tokens.  It must be subclassed to
+  generate the appropriate assertion string.
+
+  AssertionCredentials objects may be safely pickled and unpickled.
+  """
+
+  def __init__(self, assertion_type, user_agent,
+      token_uri='https://accounts.google.com/o/oauth2/token', **kwargs):
+    """Constructor for AssertionFlowCredentials
+
+    Args:
+      assertion_type: string, assertion type that will be declared to the auth
+          server
+      user_agent: string, The HTTP User-Agent to provide for this application.
+      token_uri: string, URI for token endpoint. For convenience
+        defaults to Google's endpoints but any OAuth 2.0 provider can be used.
+    """
+    super(AssertionCredentials, self).__init__(
+        None,
+        None,
+        None,
+        None,
+        None,
+        token_uri,
+        user_agent)
+    self.assertion_type = assertion_type
+
+  def _generate_refresh_request_body(self):
+    assertion = self._generate_assertion()
+
+    body = urllib.urlencode({
+      'assertion_type': self.assertion_type,
+      'assertion': assertion,
+      'grant_type': "assertion",
+    })
+
+    return body
+
+  def _generate_assertion(self):
+    """Generate the assertion string that will be used in the access token
+    request.
+    """
+    _abstract()
+
+
 class OAuth2WebServerFlow(Flow):
   """Does the Web Server Flow for OAuth 2.0.
 
@@ -420,15 +491,16 @@
       'client_secret': self.client_secret,
       'code': code,
       'redirect_uri': self.redirect_uri,
-      'scope': self.scope
+      'scope': self.scope,
       })
     headers = {
       'user-agent': self.user_agent,
-      'content-type': 'application/x-www-form-urlencoded'
+      'content-type': 'application/x-www-form-urlencoded',
     }
     if http is None:
       http = httplib2.Http()
-    resp, content = http.request(self.token_uri, method='POST', body=body, headers=headers)
+    resp, content = http.request(self.token_uri, method='POST', body=body,
+                                 headers=headers)
     if resp.status == 200:
       # TODO(jcgregorio) Raise an error if simplejson.loads fails?
       d = simplejson.loads(content)
@@ -436,12 +508,13 @@
       refresh_token = d.get('refresh_token', None)
       token_expiry = None
       if 'expires_in' in d:
-        token_expiry = datetime.datetime.now() + datetime.timedelta(seconds = int(d['expires_in']))
+        token_expiry = datetime.datetime.now() + datetime.timedelta(
+            seconds=int(d['expires_in']))
 
       logging.info('Successfully retrieved access token: %s' % content)
-      return OAuth2Credentials(access_token, self.client_id, self.client_secret,
-                               refresh_token, token_expiry, self.token_uri,
-                               self.user_agent)
+      return OAuth2Credentials(access_token, self.client_id,
+                               self.client_secret, refresh_token, token_expiry,
+                               self.token_uri, self.user_agent)
     else:
       logging.error('Failed to retrieve access token: %s' % content)
       error_msg = 'Invalid response %s.' % resp['status']
diff --git a/samples/appengine_with_robots/apiclient b/samples/appengine_with_robots/apiclient
new file mode 120000
index 0000000..24fe0bc
--- /dev/null
+++ b/samples/appengine_with_robots/apiclient
@@ -0,0 +1 @@
+../appengine/apiclient
\ No newline at end of file
diff --git a/samples/appengine_with_robots/app.yaml b/samples/appengine_with_robots/app.yaml
new file mode 100644
index 0000000..e83ac03
--- /dev/null
+++ b/samples/appengine_with_robots/app.yaml
@@ -0,0 +1,9 @@
+application: urlshortener-robot
+version: 2
+runtime: python
+api_version: 1
+
+handlers:
+- url: .*
+  script: main.py
+
diff --git a/samples/appengine_with_robots/gflags.py b/samples/appengine_with_robots/gflags.py
new file mode 120000
index 0000000..5a2ff94
--- /dev/null
+++ b/samples/appengine_with_robots/gflags.py
@@ -0,0 +1 @@
+../../gflags.py
\ No newline at end of file
diff --git a/samples/appengine_with_robots/gflags_validators.py b/samples/appengine_with_robots/gflags_validators.py
new file mode 120000
index 0000000..25d8ce8
--- /dev/null
+++ b/samples/appengine_with_robots/gflags_validators.py
@@ -0,0 +1 @@
+../../gflags_validators.py
\ No newline at end of file
diff --git a/samples/appengine_with_robots/httplib2 b/samples/appengine_with_robots/httplib2
new file mode 120000
index 0000000..4cd2774
--- /dev/null
+++ b/samples/appengine_with_robots/httplib2
@@ -0,0 +1 @@
+../appengine/httplib2
\ No newline at end of file
diff --git a/samples/appengine_with_robots/main.py b/samples/appengine_with_robots/main.py
new file mode 100644
index 0000000..01ee3a8
--- /dev/null
+++ b/samples/appengine_with_robots/main.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Starting template for Google App Engine applications.
+
+Use this project as a starting point if you are just beginning to build a
+Google App Engine project which will access and manage data held under a role
+account for the App Engine app.  More information about using Google App Engine
+apps to call Google APIs can be found in Scenario 1 of the following document:
+
+<https://sites.google.com/site/oauthgoog/Home/google-oauth2-assertion-flow>
+"""
+
+__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+
+
+import httplib2
+import logging
+import os
+import pickle
+
+from apiclient.discovery import build
+from google.appengine.api import memcache
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+from google.appengine.ext.webapp.util import run_wsgi_app
+from oauth2client.appengine import AppAssertionCredentials
+
+credentials = AppAssertionCredentials(
+    scope='https://www.googleapis.com/auth/urlshortener',
+    user_agent='my-sample-app/1.0')
+
+http = credentials.authorize(httplib2.Http(memcache))
+service = build("urlshortener", "v1", http=http)
+
+
+class MainHandler(webapp.RequestHandler):
+
+  def get(self):
+    path = os.path.join(os.path.dirname(__file__), 'welcome.html')
+    shortened = service.url().list().execute()
+    short_and_long = [(item["id"], item["longUrl"]) for item in
+        shortened["items"]]
+
+    variables = {
+        'short_and_long': short_and_long,
+        }
+    self.response.out.write(template.render(path, variables))
+
+  def post(self):
+    long_url = self.request.get("longUrl")
+    shortened = service.url().insert(body={"longUrl": long_url}).execute()
+    self.redirect("/")
+
+
+def main():
+  application = webapp.WSGIApplication(
+      [
+       ('/', MainHandler),
+      ],
+      debug=True)
+  run_wsgi_app(application)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/samples/appengine_with_robots/oauth2 b/samples/appengine_with_robots/oauth2
new file mode 120000
index 0000000..ee61c25
--- /dev/null
+++ b/samples/appengine_with_robots/oauth2
@@ -0,0 +1 @@
+../appengine/oauth2
\ No newline at end of file
diff --git a/samples/appengine_with_robots/oauth2client b/samples/appengine_with_robots/oauth2client
new file mode 120000
index 0000000..9013119
--- /dev/null
+++ b/samples/appengine_with_robots/oauth2client
@@ -0,0 +1 @@
+../../oauth2client/
\ No newline at end of file
diff --git a/samples/appengine_with_robots/uritemplate b/samples/appengine_with_robots/uritemplate
new file mode 120000
index 0000000..1c98e41
--- /dev/null
+++ b/samples/appengine_with_robots/uritemplate
@@ -0,0 +1 @@
+../appengine/uritemplate
\ No newline at end of file
diff --git a/samples/appengine_with_robots/welcome.html b/samples/appengine_with_robots/welcome.html
new file mode 100644
index 0000000..a035ce8
--- /dev/null
+++ b/samples/appengine_with_robots/welcome.html
@@ -0,0 +1,18 @@
+<html>
+  <head>
+    <title>Welcome</title>
+  </head>
+  <body>
+    <form action="." method="post">
+      Long Url: <input name="longUrl" type="text" />
+      <input type="submit" />
+    </form>
+
+    <table>
+      <tr><th>Shortened</th><th>Original</th></tr>
+      {% for item in short_and_long %}
+        <tr><td><a href="{{ item.0 }}">{{ item.0 }}</a></td><td><a href="{{ item.1 }}">{{ item.1 }}</a></td></tr>
+      {% endfor %}
+    </table>
+  </body>
+</html>
diff --git a/tests/test_oauth2client.py b/tests/test_oauth2client.py
index 3c6e0ba..417807a 100644
--- a/tests/test_oauth2client.py
+++ b/tests/test_oauth2client.py
@@ -35,6 +35,7 @@
 from oauth2client.client import AccessTokenCredentials
 from oauth2client.client import AccessTokenCredentialsError
 from oauth2client.client import AccessTokenRefreshError
+from oauth2client.client import AssertionCredentials
 from oauth2client.client import FlowExchangeError
 from oauth2client.client import OAuth2Credentials
 from oauth2client.client import OAuth2WebServerFlow
@@ -115,6 +116,35 @@
     self.assertEqual(400, resp.status)
 
 
+class TestAssertionCredentials(unittest.TestCase):
+  assertion_text = "This is the assertion"
+  assertion_type = "http://www.google.com/assertionType"
+
+  class AssertionCredentialsTestImpl(AssertionCredentials):
+
+    def _generate_assertion(self):
+      return TestAssertionCredentials.assertion_text
+
+  def setUp(self):
+    user_agent = "fun/2.0"
+    self.credentials = self.AssertionCredentialsTestImpl(self.assertion_type,
+        user_agent)
+
+  def test_assertion_body(self):
+    body = urlparse.parse_qs(self.credentials._generate_refresh_request_body())
+    self.assertEqual(body['assertion'][0], self.assertion_text)
+    self.assertEqual(body['assertion_type'][0], self.assertion_type)
+
+  def test_assertion_refresh(self):
+    http = HttpMockSequence([
+      ({'status': '200'}, '{"access_token":"1/3w"}'),
+      ({'status': '200'}, 'echo_request_headers'),
+      ])
+    http = self.credentials.authorize(http)
+    resp, content = http.request("http://example.com")
+    self.assertEqual(content['authorization'], 'OAuth 1/3w')
+
+
 class OAuth2WebServerFlowTest(unittest.TestCase):
 
   def setUp(self):
@@ -137,7 +167,7 @@
 
   def test_exchange_failure(self):
     http = HttpMockSequence([
-      ({'status': '400'}, '{"error":"invalid_request"}')
+      ({'status': '400'}, '{"error":"invalid_request"}'),
       ])
 
     try:
@@ -159,7 +189,6 @@
     self.assertNotEqual(credentials.token_expiry, None)
     self.assertEqual(credentials.refresh_token, '8xLOxBtZp8')
 
-
   def test_exchange_no_expires_in(self):
     http = HttpMockSequence([
       ({'status': '200'}, """{ "access_token":"SlAV32hkKG",
diff --git a/tests/test_oauth2client_appengine.py b/tests/test_oauth2client_appengine.py
index 964092d..7f370b3 100644
--- a/tests/test_oauth2client_appengine.py
+++ b/tests/test_oauth2client_appengine.py
@@ -22,6 +22,7 @@
 
 __author__ = 'jcgregorio@google.com (Joe Gregorio)'
 
+import base64
 import httplib2
 import unittest
 import urlparse
@@ -31,20 +32,24 @@
 except ImportError:
     from cgi import parse_qs
 
-from apiclient.http import HttpMockSequence
 from apiclient.anyjson import simplejson
-from webtest import TestApp
+from apiclient.http import HttpMockSequence
+from google.appengine.api import apiproxy_stub
+from google.appengine.api import apiproxy_stub_map
+from google.appengine.api import users
+from google.appengine.ext import testbed
+from google.appengine.ext import webapp
 from oauth2client.client import AccessTokenRefreshError
 from oauth2client.client import FlowExchangeError
+from oauth2client.appengine import AppAssertionCredentials
 from oauth2client.appengine import OAuth2Decorator
-from google.appengine.ext import webapp
-from google.appengine.api import users
 from oauth2client.appengine import OAuth2Handler
-from google.appengine.ext import testbed
+from webtest import TestApp
 
 
 class UserMock(object):
   """Mock the app engine user service"""
+
   def user_id(self):
     return 'foo_user'
 
@@ -55,7 +60,7 @@
   content = {
       'access_token': 'foo_access_token',
       'refresh_token': 'foo_refresh_token',
-      'expires_in': 3600
+      'expires_in': 3600,
     }
 
   def request(self, token_uri, method, body, headers, *args, **kwargs):
@@ -64,6 +69,59 @@
     return (self, simplejson.dumps(self.content))
 
 
+class TestAppAssertionCredentials(unittest.TestCase):
+  account_name = "service_account_name@appspot.com"
+  signature = "signature"
+
+  class AppIdentityStubImpl(apiproxy_stub.APIProxyStub):
+
+    def __init__(self):
+      super(TestAppAssertionCredentials.AppIdentityStubImpl, self).__init__(
+          'app_identity_service')
+
+    def _Dynamic_GetServiceAccountName(self, request, response):
+      return response.set_service_account_name(
+          TestAppAssertionCredentials.account_name)
+
+    def _Dynamic_SignForApp(self, request, response):
+      return response.set_signature_bytes(
+          TestAppAssertionCredentials.signature)
+
+  def setUp(self):
+    app_identity_stub = self.AppIdentityStubImpl()
+    apiproxy_stub_map.apiproxy.RegisterStub("app_identity_service",
+                                            app_identity_stub)
+
+    self.scope = "http://www.googleapis.com/scope"
+    user_agent = "hal/3.0"
+
+    self.credentials = AppAssertionCredentials(self.scope, user_agent)
+
+  def test_assertion(self):
+    assertion = self.credentials._generate_assertion()
+
+    parts = assertion.split(".")
+    self.assertTrue(len(parts) == 3)
+
+    header, body, signature = [base64.b64decode(part) for part in parts]
+
+    header_dict = simplejson.loads(header)
+    self.assertEqual(header_dict['typ'], 'JWT')
+    self.assertEqual(header_dict['alg'], 'RS256')
+
+    body_dict = simplejson.loads(body)
+    self.assertEqual(body_dict['aud'],
+                     'https://accounts.google.com/o/oauth2/token')
+    self.assertEqual(body_dict['scope'], self.scope)
+    self.assertEqual(body_dict['iss'], self.account_name)
+
+    issuedAt = body_dict['iat']
+    self.assertTrue(issuedAt > 0)
+    self.assertEqual(body_dict['exp'], issuedAt + 3600)
+
+    self.assertEqual(signature, self.signature)
+
+
 class DecoratorTests(unittest.TestCase):
 
   def setUp(self):
@@ -79,14 +137,14 @@
                                 user_agent='foo_user_agent')
     self.decorator = decorator
 
-
     class TestRequiredHandler(webapp.RequestHandler):
+
       @decorator.oauth_required
       def get(self):
         pass
 
-
     class TestAwareHandler(webapp.RequestHandler):
+
       @decorator.oauth_aware
       def get(self):
         self.response.out.write('Hello World!')
@@ -121,7 +179,7 @@
     # Now simulate the callback to /oauth2callback
     response = self.app.get('/oauth2callback', {
         'code': 'foo_access_code',
-        'state': 'foo_path'
+        'state': 'foo_path',
         })
     self.assertEqual('http://localhost/foo_path', response.headers['Location'])
     self.assertEqual(None, self.decorator.credentials)
@@ -130,8 +188,10 @@
     response = self.app.get('/foo_path')
     self.assertEqual('200 OK', response.status)
     self.assertEqual(True, self.decorator.has_credentials())
-    self.assertEqual('foo_refresh_token', self.decorator.credentials.refresh_token)
-    self.assertEqual('foo_access_token', self.decorator.credentials.access_token)
+    self.assertEqual('foo_refresh_token',
+                     self.decorator.credentials.refresh_token)
+    self.assertEqual('foo_access_token',
+                     self.decorator.credentials.access_token)
 
     # Invalidate the stored Credentials
     self.decorator.credentials._invalid = True
@@ -161,7 +221,7 @@
     url = self.decorator.authorize_url()
     response = self.app.get('/oauth2callback', {
         'code': 'foo_access_code',
-        'state': 'bar_path'
+        'state': 'bar_path',
         })
     self.assertEqual('http://localhost/bar_path', response.headers['Location'])
     self.assertEqual(False, self.decorator.has_credentials())
@@ -171,8 +231,10 @@
     self.assertEqual('200 OK', response.status)
     self.assertEqual('Hello World!', response.body)
     self.assertEqual(True, self.decorator.has_credentials())
-    self.assertEqual('foo_refresh_token', self.decorator.credentials.refresh_token)
-    self.assertEqual('foo_access_token', self.decorator.credentials.access_token)
+    self.assertEqual('foo_refresh_token',
+                     self.decorator.credentials.refresh_token)
+    self.assertEqual('foo_access_token',
+                     self.decorator.credentials.access_token)
 
 if __name__ == '__main__':
   unittest.main()