Thursday, July 3, 2014

How to provide seamless Single Sign On on Google Apps

Context

Before OAuth, in order to authenticate a user an application would redirect him to the OpenID login page.


It was possible to avoid the consent window by registering your the application through the Google Apps Console.


However, OpenID is going to be deprecated and replaced by OAuth2. This document details how to achieve seamless Single Sign On (no consent window) with OAuth2 through the GApps Marketplace.

OAuth authentication flow



1. Redirect the user to the following URL :
   https://accounts.google.com/o/oauth2/auth   ?client_id=815629710953-nd6a8ofur4prtau84mt67r9h25hvkd4d.apps.googleusercontent.com
     &response_type=code
     &scope=email+profile
     &redirect_uri=https://localhost:8080
     &state=12345678


This is standard OAuth behavior, here are the different parts :
  • client_id : a client_id generated in the API Console
  • response_type : always keep "code"
  • scope : keep "email+profile", otherwise the seamless SSO does not work
  • redirect_uri : URL of your application
  • state : a random string generated by your app to ensure that nobody is stealing the account info


2. Google will bypass the consent window and send the user to the redirect_uri, passing in parameters the same state and the code. In our example :
   https://localhost:8080/?state=12345678&code=4/PE4pFdMWdSU89L5BxCfQYl7rrCe4.sg3ll1ncyWAadJfo-QBMszv26aO4jQI


3. Check that the state is valid, typically by checking with a value generated from the user's session. Exchange the code against a refresh and access token. This is standard OAuth behavior, executed server side, here is the request :


HTTP POST to https://accounts.google.com/o/oauth2/token HTTP/1.1


code=4%2FNa7-XZQEVy4rSpoWHI0g-eanF2pW.os7UPIcmZBUWdJfo-QBMszs6qtm4jQI&redirect_uri=https%3A%2F%2Flocalhost:8080&client_id=815629710953-nd6a8ofur4prtau84mt67r9h25hvkd4d.apps.googleusercontent.com&scope=&client_secret=YOURCLIENTSECRET&grant_type=authorization_code


4. Here's a typical answer from Google :


{
 "access_token": "ya29.MQ...UlmQ7g",
 "token_type": "Bearer",
 "expires_in": 3600,
 "refresh_token": "1/V09...4C8",
 "id_token": "eyJhbGciOi...WlOvPsnHeBEuChaeCziau_MggFWoBCuozy0ZoBdXXc"


}


From this answer, the best option is to JWT-decode the "id_token". You can use this online tool for testing. Here's the result :


{
   "sub": "109177816070718727151",
   "cid": "407408718192.apps.googleusercontent.com",
   "iss": "accounts.google.com",
   "email_verified": true,
   "id": "109177816070718727151",
   "at_hash": "rAxrTmUjD7rk-5H2EJFnZw",
   "exp": 1403860943,
   "azp": "407408718192.apps.googleusercontent.com",
   "iat": 1403857043,
   "verified_email": true,
   "token_hash": "rAxrTmUjD7rk-5H2EJFnZw",
   "email": "david.hatanian@revevol.eu",
   "hd": "revevol.eu",
   "aud": "407408718192.apps.googleusercontent.com"
}


There you go ! you have the email of the user, and in "id" this is a unique id internal to Google, that will never change even if the email changes. It's also the G+ id for G+ users. Now you can store that in the user's session.


Configuration steps

Here are all the required setup steps to perform the authentication process described above :


  1. Create an API project through the API Console
  2. Enable the G+ API and the Marketplace SDK





  1. Generate an OAuth2 webapp credential. Put your application URL as a registered redirect URI.





  1. Setup the marketplace SDK. (click on the cog wheel next to the Marketplace SDK name) You must provide icons and basic info on your app. You will need to choose either "Universal Navigation Link" or "Drive App". See below for the pros and cons, but we recommend "Universal Navigation Link".





  1. Go to the Chrome Webstore web developer dashboard, and upload a zip file composed of :
    1. A manifest.json
    2. 16px and 128px icons for your app


Here's an example of manifest.json :
{
   "name": "Revevol SSO to marketplace",
   "version": "0.1",
"manifest_version": 2,
   "description": "Demonstration of the seamless SSO with the marketplace SDK",
   "icons": {
   "128": "images/pdf-icon-128x128.png",
       "16": "images/Pdf_16x16_Crystal_SVG.png"
   },
   "container": ["DOMAIN_INSTALLABLE"],
   "api_console_project_id": "815629710953",
"app": { "launch": { "web_url": "https://yourapplication.com" } }
}


  1. Limit the access to the marketplace application to a defined list of trusted testers. Publish it. Here is what you can see in the Chrome Webstore developer dashboard after publication :



  1. After publication (can take up to 60 minutes), as a domain admin, install the app from the Chrome Webstore onto the domain by going on the application’s URL in the Chrome Web Store. To do this, click on the “Integrate with Google” button.





Limitations

There are two important limitations with this process :


Iframes : You cannot redirect to the OAuth screen in an iframe (like in a sites gadget). In this case you will have to display a button to the users, on which they will have to click. The button will open an authentication popup, the user will be seamlessly authenticated. Then your javascript code must automatically close the popup and refresh the iframe.


More apps menu : When setting up the marketplace SDK, you have the choice between "Navigation link" and "Drive App". If you choose "Navigation Link" the app will show in the "More" menu on the top right. If you choose the "Drive App" then the app will show in the "Manage Apps" menu of Drive. We haven't found a way yet to remove all mentions of the app, and have reported a feature request to Google for this.





Example project

Testing

An example project is available on Github here : https://github.com/dhatanian/demo-marketplace-sso

This project contains the source code for a very simple Java App Engine web application that displays the email of the user :




It is deployed on App Engine on this URL : https://revevol-marketplace-sso-demo.appspot.com/secured


To experience seamless SSO, remember to install the app on your domain through the Chrome Web Store before accessing the URL above. Here’s the app’s URL in the Chrome Web Store : https://chrome.google.com/webstore/detail/demo-of-sso-to-marketplac/fajllpdcdhkpkhnhknjnkfignojcbojd

To compile the sample project

To compile and deploy this project, you will need Maven. Maven is the build tool in charge of retrieving all the required libraries and compiling the project for us.




After the installation of Maven, you can run the following commands :

  • To deploy the project on a server local to your workstation : mvn appengine:devserver
  • To deploy the project on App Engine : mvn appengine:update .
    • This will update the default revevol-marketplace-sso-demo application.
    • But you can specify a different one like this : mvn appengine:update -Dappengine.appId=myappid
  • To generate an Eclipse project : mvn eclipse:eclipse .
    • Maven will generate the required Eclipse project files
    • After this, you can use the “File > Import” feature of Eclipse to import your project

14 comments:

  1. Hi David,

    Excellent tutorial!

    I get an error while publishing my demo app to chrome web store.

    "API Console project with the id specified in the manifest's api_console_project_id field, does not have Google Apps Marketplace SDK enabled."

    I have enabled the marketplace sdk for the project.

    Did you have to get your app reviewed before you could publish your demo app to web store?

    ReplyDelete
  2. Hey, I believe you're the one who posted on Stack Overflow here : http://stackoverflow.com/questions/25635068/error-while-publishing-to-chrome-web-store

    I have added a comment on your question -)

    ReplyDelete
  3. Hi there,

    Excellent tutorial, but it no longer works. When installing the app, I get prompted at a domain level. However when attempting to access the app, I get prompted for access again as a user.

    ReplyDelete
    Replies
    1. Hey Grant, Adrian just commented below to warn that the client id I was using was not valid. Can you retry now ?

      Delete
    2. Hi David,

      Yup, seems fixed now. Thanks!

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi David,

    I just noticed the webstore link installed a an app called "Marketplace SSO Demo" with ID 368362145081.

    But when I hit https://revevol-marketplace-sso-demo.appspot.com/secured it shows the consent screen which reads "Demo Marketplace SSO 2 wants" and in the url you can see the client_id param starts with 233290130899.

    So.. if I understand it correctly it installs one app but asks for permissions for another?

    ReplyDelete
    Replies
    1. Thank you for spotting this Adrian. It was probably an old version of the application. I updated it.

      Delete
  6. Our app get rejected by the chrome store reason given by them is it does not provide domain-wide SSO, while investigating the code I came across the issue is that when asking for https://www.googleapis.com/auth/plus.login scope in the request as it will causes confirmation popup for access to users' circles for each user and its broke the seamless Domain-Wide SSO for new Google Marketplace apps. Is this an accepted behavior ?

    ReplyDelete
    Replies
    1. Yes you must not use the plus.login scope anymore, but rather the "email+profile" scope as explained in my article.

      Delete
    2. Thanks for such a nice article at very right time... it just solved our SSO problem since last one month we are facing this issue and no solution mentioned anywhere else...

      Delete
  7. Hi David,

    We're having a specific issue where installing our app via the chrome web store listing is not allowing us to access data to the scopes we requested. We are consistently getting a ""Requested client not authorized." error. If we manually add the service account and scopes to the domain, then everything works fine.

    This is making me think that maybe our marketplace settings are incorrect. Have you ran into this issue with your test app? Also, could you post up a screenshot of your marketplace settings?

    Thanks so much!

    ReplyDelete
    Replies
    1. Hi Jordan, I have added this screenshot (step 4). But since I only worked with authentication, I did not add any specific scope, like Admin SDK or GMail API.

      Delete
  8. Thank you David for this great tutorial. I am able to manage my SSO app https://chrome.google.com/webstore/detail/gapps-manager/idgedhhbbnblmnieoobodnjlogfgjgae on web store using this tutorial. And is working fine.

    Thanks
    Santlal
    https://ssofordemo.appspot.com/index.jsp

    ReplyDelete