Google Identity Toolkit

Using the GITkit API directly

This document describes how to use the Account Chooser widget provided by the Google Identity Toolkit.

You can access the Account Chooser demo on our sample site.

Contents

  1. Introduction
  2. Preliminary setup
  3. Detailed Steps for GITkit Integration
  4. Advanced options

Introduction


Your website needs to set up three endpoints that tell the widget if
  1. A user has an existing account
  2. A legacy user has entered a password correctly
  3. A federated login user's email address has been verified by an Identity Provider (IDP)

Your website also needs to send back a response about a user to the widget after a successful login of any kind to add an entry in the Account Chooser. That account will be shown next time the Account Chooser is opened.

You can refer to the Account Chooser logic flow for a visual diagram

Preliminary setup: Add the widget to your page


Use the Google Developers Console to generate your configuration code. For help with the Developers Console, consult our guide. The generated configuration code will look like the following sample but make sure to use the code generated for you by the console:

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/jsapi"></script>
<script type="text/javascript">
  function load() {
    google.load("identitytoolkit", "1.0", {packages: ["ac"], callback: callback});
  }
  function callback() {
    window.google.identitytoolkit.setConfig({
        developerKey: "{your developer key}",
        companyName: "Your company",
        callbackUrl: "https://yoursite.com/callback", // must be a full URL
        userStatusUrl: "/userStatus", // these can just be partial paths
        loginUrl: "/login",
        signupUrl: "/signup",
        homeUrl: "/home",
        logoutUrl: "/logout",
        realm: "", // optional
        language: "en",
        idps: ["Gmail", "AOL", "Hotmail", "Yahoo"],
        tryFederatedFirst: true,
        useCachedUserStatus: false
    });
    jQuery('#navbar').accountChooser();
  }
</script>
<script type="text/javascript" src="https://apis.google.com/js/client.js?onload=load"></script>

Add this configuration code to the <head> of the pages on your site that need show the login widget. Optionally, add some CSS style for padding or right alignment.

Detailed Steps for GITkit Integration

On your web server you will then need to implement the endpoints listed below.


  1. /userStatusUrl

    If a user enters an email address in the account chooser, and there is no identity provider for that domain, then GITkit will make a HTTP POST request to this endpoint to determine if that email address is already registered. This endpoint's URL is defined in the JavaScript widget's userStatusUrl parameter. GITkit expects a JSON response with at least one boolean parameter.

    Request: POST https://www.yoursite.com/userStatusUrl
    Parameters: email=name@domain.com
    

    • email: email address for the account

    Response: { “registered”: boolean }

    • registered: Whether or not an account exists for that email address on your site. If it does, The Account Chooser will just display a password prompt; otherwise, the Account Chooser will redirect the user to your legacy account creation flow at the endpoint defined in the JavaScript widget's signupUrl parameter. Once a user finishes the signup process, the website should redirect them to homeUrl.

  2. /loginUrl

    After prompting for a password for a legacy account, GITkit will need to know if a password is correct or incorrect. This endpoint's URL is defined in the JavaScript widget's loginUrl parameter. GITkit will make a POST to this endpoint and expects a JSON object as a response with one parameter.

    Request: POST https://www.yoursite.com/loginUrl
    Parameters: email=emailaddress&password=password
    
    • email: email address for the account
    • password: password that the user entered

    If a user entered their password correctly, you should create a user session and log the user in. Then return the following response to GITkit:

    Response: { "status": "value" }
    • status: This is one of the following:
      • "OK" - user entered password correctly
      • "passwordError" - password incorrect

  3. /callbackUrl

    If the user tries to sign in using an identity provider, then the identity provider will redirect the user to your site's callback URL. The identity provider can use either a POST or a GET, and your application needs to be able to handle both to support all users. You can specify this callback URL in the Developers Console, or with the JavaScript configuration parameter callbackUrl.

    Your server needs to call the verifyAssertion API to validate the IDP's response. To do this, simply pass along the body of the POST or GET you get at your callback URL to the verifyAssertion endpoint. If the verification is successful, the API returns an HTTP 200 status code along with a JSON object as defined in the GITkit API reference. If the assertion failed, you will get an HTTP 4xx status code. Here is some sample code for making the API call:

    Python
    import urllib2
    from django.utils import simplejson
    
    VERIFY_URL = 'https://www.googleapis.com/identitytoolkit/v1/relyingparty/verifyAssertion?key='
    API_KEY = 'YOUR_API_KEY'
    
    requestUrl = request.environ['wsgi.url_scheme'] + '://' + \
                 request.environ['HTTP_HOST'] + \
                 request.environ['PATH_INFO'] + '?' + \
                 request.environ['QUERY_STRING']
    
    idpPostBodyIO = request.environ['wsgi.input']
    postBody = idpPostBodyIO.read()
    
    response = verify(requestUrl, postBody)
    
    def post(postData):
        ''' Sends post HTTP request to the remote URL. '''
        try:
            params = simplejson.dumps(postData)
            cookies = urllib2.HTTPCookieProcessor()
            opener = urllib2.build_opener(cookies)
            request = urllib2.Request(url = VERIFY_URL + API_KEY,
                                      headers = { 'Content-Type':
                                                  'application/json' },
                                      data = params)
            response = opener.open(request)
            out = response.read()
            return simplejson.loads(out)
        except urllib2.URLError, e:
            return None
        except Exception, e:
            print e
            return None
        
    def verify(url, postBody):
        '''Sends request to the identity toolkit API end point to verify the IDP
           response.
        '''
        try:
            data = {
              'requestUri': url
              'postBody': postBody
            }
            response = post(data)
            
            if response and 'verifiedEmail' in response:
                return response
            else:
                return None
        except Exception, e:
            return None
      
    PHP
    <?php
      $url = EasyRpService::getCurrentUrl();
      $postData = @file_get_contents('php://input');
      $result = EasyRpService::verify($url, $postData);
      // Turn on for debugging.
      // var_dump($result);
    
    class EasyRpService {
      // Replace $YOUR_DEVELOPER_KEY
      private static $SERVER_URL = 'https://www.googleapis.com/rpc?key=YOUR_API_KEY';
    
      public static function getCurrentUrl() {
        $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
        $url .= $_SERVER['SERVER_NAME'];
        if ($_SERVER['SERVER_PORT'] != '80') {
          $url .= ':'. $_SERVER['SERVER_PORT'];
        }
        $url .= $_SERVER['REQUEST_URI'];
        return $url;
      }
    
      private static function post($postData) {
        $ch = curl_init();
        curl_setopt_array($ch, array(
            CURLOPT_URL => EasyRpService::$SERVER_URL,
            CURLOPT_RETURNTRANSFER => 1,
            CURLOPT_HTTPHEADER => array('Content-Type: application/json'),
            CURLOPT_POSTFIELDS => json_encode($postData)));
        $response = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($http_code == '200' && !empty($response)) {
          return json_decode($response, true);
        }
        return NULL;
      }
    
      public static function verify($continueUri, $response) {
        $request = array();
        $request['method'] = 'identitytoolkit.relyingparty.verifyAssertion';
        $request['apiVersion'] = 'v1';
        $request['params'] = array();
        $request['params']['requestUri'] = $continueUri;
        $request['params']['postBody'] = $response;
    
        $result = EasyRpService::post($request);
        if (!empty($result['result'])) {
          return $result['result'];
        }
        return NULL;
      }
    }
    ?>
      
    Ruby
    require 'uri'
    require 'restclient' # https://github.com/archiloque/rest-client
    
    def get_assertion(url, params)
      begin
        api_response = RestClient.post(url, params.to_json, :content_type => :json )
        verified_assertion = JSON.parse(api_response)
        raise StandardError unless verified_assertion.include? "verifiedEmail"
        return verified_assertion
      rescue StandardError => error
        return nil
      end
    end
    
    api_params = {
      'requestUri' => request.url,
      'postBody' => request.post? ? request.raw_post : URI.parse(request.url).query
    }
    
    api_url = "https://www.googleapis.com/identitytoolkit/v1/relyingparty/" +
              "verifyAssertion?key=#{YOUR_DEVELOPER_KEY}"
    
    assertion = get_assertion(api_url, api_params)
      

    If you get an HTTP 200 status code and the JSON response contains a verifiedEmail field, you should log the user in. First, make sure that the account exists in your database. Here is some sample logic for parsing the API response:

    • Email exists in the user database
      • Log the user in;
    • Email does not exist in the user database
      • Create a new entry in your account database with that email address;
      • Add additional information such as the user attributes from the verifyAssertion response. We suggest always saving the value of displayName and photoUrl if available because they can be used later to add the user's name and photo to the account chooser;
      • Log the user in to the newly created account;
      • By setting registered=true in the HTML response below, the user will then be redirected to your signupURL to collect any addditional information.
    def handle_assertion(response):
      # Do nothing if assertion not verified by our API
      if response.verifiedEmail is not None:
        
        user = find_user_in_db(response.verifiedEmail)
        
        if user is None:
          user = create_new_account(response)
        
        create_user_session(response.verifiedEmail)
    

    Returning the response code

    Finally, pass the verifyAssertion response back to Account Chooser by rendering some HTML that calls one of the following JavaScript functions:

    • notifyFederatedSuccess({...}) - Federated login was successful. You must also pass in a JavaScript object with the following parameters:
      • "email" - The email address of the user that just signed in.
      • "registered" - If there was already an account for this email address, this should be set to true. If a new account had to be created for this email address, but no additional information is needed, then it can still be set to true. If additional information is needed then it should be set to false. In that case the user will be redirected to signupUrl so your site can collect any other additional information. In that case the signupURL handler will need to be able to detect that the user has an identity provider and not ask for things like a password. If you do not want to implement that logic, you can modify the JavaScript added to your page to set the value of federatedSignupUrl in addition to SignupUrl. The widget will then redirect new federated users to that signup handler.
    • notifyFederatedError() - An error occurred. Your site can optionally pass information about the type of error as described in the advanced guide.

    Here is an example of an HTML response you might return:

    <script type='text/javascript' src='https://ajax.googleapis.com/jsapi'></script>
    <script type='text/javascript'> 
      google.load("identitytoolkit", "1.0", {packages: ["notify"]});
    </script> 
    <script type='text/javascript'>
      window.google.identitytoolkit.notifyFederatedSuccess({ "email": "name@email.com", "registered": true });
      // use window.google.identitytoolkit.notifyFederatedError(); in case of error
    </script>
    
  4. /homeUrl

    After a successful sign in, the Account Chooser will direct the user to the page that is specified in the homeUrl configuration parameter. To confirm the active account is listed in the account chooser for future sign ins, that page needs to include the HTML below where email is a required field and the rest are optional. If a new account was created for the user, then the signupUrl handler also needs to make sure this HTML is returned at some point. The easiest way to do that is to redirect the user to homeUrl after the account creation process:

    <script type="text/javascript">
      var userData = {
        email: 'name@idp.com', // required
        displayName: 'User Name', // optional
        photoUrl: 'http://website.com/img/user.png', // optional
      };
      window.google.identitytoolkit.updateSavedAccount(userData);
    </script>
    
  5. /signupUrl

    This is the page where a user will be redirected if they try to sign-in with an email address that is not registered with your site and is not associated with an identity provider. You will then need to show a traditional account creation form that asks them to specify a password that will be used to authenticate them on this site. Your handler should expect a POST from GITKit with a URL parameter containing an email parameter with the address the user typed into the account chooser. When the user properly submits this form you should create an account for them and redirect them to /homeUrl so their account can be added to the account chooser.

  6. Account Management pages

    If your website offers users an account management page where they can change their email address or password, then you will need to make two modifications if the logged in user has an identity provider. First, make sure not to show an option for the user to modify their password. Second, if they change their email address then they need a way to set a password on the account. You can either ask them to provide a password at the same time they change their email, or you can email them a password reset link if your site already has the code for sending such a link.


Advanced options

Once you finish the steps above your website will be ready to support an account chooser and identity providers. However, there are some more advanced features you can also implement.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.