Search code examples
ruby-on-railsomniauthgoogle-oauthgoogle-apps-marketplace

New Google Apps Marketplace Oauth2.0 Implementation issue on Ruby on Rails


We have encountered an issue during development of an application for the new Google Apps Marketplace using Oauth2.0 for SSO.

As a part of our application we are using the Google Calendar V3 API, on offline mode - meaning we are required to keep a 'refresh_token' for our clients.

In order to get the 'refresh_token' during the first OAuth2.0 authentication we need to configure the google_oauth2.0 provider (using omniauth-google-oauth2 gem) such as:

Rails.application.config.middleware.use OmniAuth::Builder do
    provider :google_oauth2, CONFIG[:app_id], CONFIG[:app_secret], {
    access_type: 'offline',
    prompt: 'consent' }
 end

Without specifying the access_type: 'offline, and prompt:'consent' we do not receive the refresh_token, and can not use the calendar API properly.

However, specifying the prompt: 'consent' attribute, leads to an authorization popup, which negates the whole SSO idea - where only the domain admin gets prompted during installation.

Is it possible to get the refresh_token without prompting the domain users? How does new Google Apps Marketplace support offline access without prompting of end-users?


Solution

  • It looks like a service account for offline access was what I'm looking for.

    Inspired by this thread: Trouble with Google Apps API and Service Accounts in Ruby

    I found out that I can create a service account on the apps marketplace configuration. Project -> APIs & Auth -> Credentials -> Create New Client Id -> Choose Service account. And in my code use Signet to provide domain level authorization for the end-users:

    key = Google::APIClient::PKCS12.load_key('privatekey.p12', 'notasecret')
    client = Google::APIClient.new
    
    client.authorization = Signet::OAuth2::Client.new(
            :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
            :audience => 'https://accounts.google.com/o/oauth2/token',
            :scope => 'https://www.googleapis.com/auth/calendar',
            :issuer => '<email-address-of-service-account>@developer.gserviceaccount.com',
            :signing_key => key,
            :person => email)
    client.authorization.fetch_access_token!
    

    Not only does this allowed me to authenticate without prompting the end-user at any phase during the process - but I don't require the refresh_token anymore, and I can execute the API calls with domain level authorizations. The person option allows me to specify which end-user I'm using the API call for.