title: How do I use Google's token from AccountManager in my web app? (Rails with omniauth-google-oauth2)
body: <ul>
  <li>I have a web application that uses <code>omniauth-google-oauth2</code> for authenticating using a Google account. The session is then kept using a cookie (<code>session[:user_id]</code>). This works.</li>
  <li>I also have an Android application that uses the website services using REST/JSON API.
  The application uses <code>AccountManager</code> to get an access token. This also works.</li>
  </ul>
  
  <p>I'm trying to get the Android application to make a request that makes the web application verify that token and start a session with the Android application (the cookies are persistent on the Android application).</p>
  
  <p>I noticed that the callback URL from Google is: <code>http://my.domain.com/auth/google_oauth2/callback</code>, so I tried adding the parameters <code>state="/"</code> and <code>code="&lt;TOKEN FROM AccountManager&gt;"</code>.</p>
  
  <p>This caused:</p>
  
  <pre><code>(google_oauth2) Callback phase initiated.
  (google_oauth2) Authentication failure! invalid_credentials: OmniAuth::Strategies::OAuth2::CallbackError, OmniAuth::Strategies::OAuth2::CallbackError
  
  Started GET "/auth/google_oauth2/callback?state=%2F&amp;code=&lt;TOKEN FROM AccountManager&gt;" for 5.102.217.111 at 2013-08-10 18:38:58 +0300
  
  OmniAuth::Strategies::OAuth2::CallbackError (OmniAuth::Strategies::OAuth2::CallbackError):
    omniauth-oauth2 (1.1.1) lib/omniauth/strategies/oauth2.rb:71:in `callback_phase'
    omniauth (1.1.4) lib/omniauth/strategy.rb:226:in `callback_call'
    omniauth (1.1.4) lib/omniauth/strategy.rb:182:in `call!'
    omniauth (1.1.4) lib/omniauth/strategy.rb:164:in `call'
    omniauth (1.1.4) lib/omniauth/builder.rb:49:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
    rack (1.4.5) lib/rack/etag.rb:23:in `call'
    rack (1.4.5) lib/rack/conditionalget.rb:25:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/head.rb:14:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/flash.rb:242:in `call'
    rack (1.4.5) lib/rack/session/abstract/id.rb:210:in `context'
    rack (1.4.5) lib/rack/session/abstract/id.rb:205:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/cookies.rb:341:in `call'
    activerecord (3.2.13) lib/active_record/query_cache.rb:64:in `call'
    activerecord (3.2.13) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
    activesupport (3.2.13) lib/active_support/callbacks.rb:405:in `_run__1566251561940761300__call__2926332968477140393__callbacks'
    activesupport (3.2.13) lib/active_support/callbacks.rb:405:in `__run_callback'
    activesupport (3.2.13) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
    activesupport (3.2.13) lib/active_support/callbacks.rb:81:in `run_callbacks'
    actionpack (3.2.13) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/reloader.rb:65:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
    railties (3.2.13) lib/rails/rack/logger.rb:32:in `call_app'
    railties (3.2.13) lib/rails/rack/logger.rb:16:in `block in call'
    activesupport (3.2.13) lib/active_support/tagged_logging.rb:22:in `tagged'
    railties (3.2.13) lib/rails/rack/logger.rb:16:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/request_id.rb:22:in `call'
    rack (1.4.5) lib/rack/methodoverride.rb:21:in `call'
    rack (1.4.5) lib/rack/runtime.rb:17:in `call'
    activesupport (3.2.13) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
    rack (1.4.5) lib/rack/lock.rb:15:in `call'
    actionpack (3.2.13) lib/action_dispatch/middleware/static.rb:63:in `call'
    railties (3.2.13) lib/rails/engine.rb:479:in `call'
    railties (3.2.13) lib/rails/application.rb:223:in `call'
    rack (1.4.5) lib/rack/content_length.rb:14:in `call'
    railties (3.2.13) lib/rails/rack/log_tailer.rb:17:in `call'
    rack (1.4.5) lib/rack/handler/webrick.rb:59:in `service'
    /home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
    /home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
    /home/oded/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread'
  
  
    Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_trace.erb (0.8ms)
    Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.7ms)
    Rendered /home/oded/.rvm/gems/ruby-2.0.0-p195/gems/actionpack-3.2.13/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (5.3ms)
  </code></pre>
  
  <p>Both the web and Android applications are authorized in Google APIs Console.</p>
  
  <p>I don't want to use <code>GoogleAuthUtil</code> for 3 reasons: I already implemented AccountManager completely, it at least doubles my application size, and I don't see how it solves the problem with my web app.</p>
  
  <p>As a temporary solution I made a WebView activity that goes through the same process as the web application. That's not a good solution because the user needs to enter his email and password and he has no way to tell if it's phishing.</p>
  
  <p>Gemfile:</p>
  
  <pre><code>gem 'omniauth-google-oauth2'
  </code></pre>
  
  <p>SessionsControler (app/controllers/sessions_controller.rb):</p>
  
  <pre><code>def new
    redirect_to "/auth/google_oauth2"
  end
  
  def create
    auth = request.env["omniauth.auth"]
    account = case auth['provider']
      when GoogleAccount::PROVIDER then GoogleAccount.find_by_omniauth(auth)
    end
    session[:user_id] = account.user.id
    respond_to do |format|
      format.html { redirect_to root_url, :notice =&gt; "Signed in!" }
      format.json { render json: { result: true } }
    end
  end
  </code></pre>
  
  <p>GoogleAccount (app/models/google_account.rb):</p>
  
  <pre><code>after_validation :create_user!, on: :create
  
  def self.find_by_omniauth(auth)
    google_account = find_by_uid(auth['uid']) || new(uid: auth['uid'])
    google_account.attributes = auth['info'].select { |k, v| k.in?(attribute_names) }
    google_account.save!
    google_account
  end
  </code></pre>
  
  <p>SessionManager.java:</p>
  
  <pre><code>public void login() {
      _accountManager = AccountManager.get(_activity);
      final Account[] accounts = _accountManager.getAccountsByType("com.google");
      String[] accountNames = new String[accounts.length];
      for (int i = 0; i &lt; accounts.length; i++) {
          accountNames[i] = accounts[i].name;
      }
  
      new AlertDialog.Builder(_activity).
      setItems(accountNames, new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              _account = accounts[which];
              getAuthToken();
          }
      }).
      show();
  }
  
  private void getAuthToken() {
      _accountManager.getAuthToken(
              _account, "ah", null, _activity,
              new AccountManagerCallback&lt;Bundle&gt;() {
                  @Override
                  public void run(AccountManagerFuture&lt;Bundle&gt; result) {
                      Bundle bundle;
                      try {
                          bundle = result.getResult();
                      } catch (OperationCanceledException e) {
                          // TODO: handle errors
                          e.printStackTrace();
                          return;
                      } catch (AuthenticatorException e) {
                          // TODO: handle errors
                          e.printStackTrace();
                          return;
                      } catch (IOException e) {
                          // TODO: handle errors
                          e.printStackTrace();
                          return;
                      }
  
                      Intent launch = (Intent) bundle.get(AccountManager.KEY_INTENT);
                      if (launch != null) {
                          _activity.startActivityForResult(launch, _accountManagerRequestCode);
                          return;
                      }
  
                      onGetAuthToken(bundle);
                  }
              },
              null
              );
  }
  
  public void onAccountManagerResult(int resultCode, Intent data) {
      switch (resultCode) {
      case Activity.RESULT_OK:
          _accountManager = AccountManager.get(_activity);
          getAuthToken();
          break;
      }
  }
  
  private void onGetAuthToken(Bundle bundle) {
      final String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
      login(
              token,
              new LoginResponseHandler() {
                  @Override
                  public void onResult(boolean result, boolean hadAnonymous, String message, Map&lt;String, List&lt;String&gt;&gt; errors) {
                      if (result) {
                          setLoggedIn(true);
                      } else {
                          // Token expired?
                          _accountManager.invalidateAuthToken("com.google", token);
                          getAuthToken();
                      }
                  }
              });
  }
  
  public static void login(String token, LoginResponseHandler loginResponseHandler) {
      RequestParams requestParams = defaultRequestParams();
      requestParams.put("code", token);
      requestParams.put("state", "/");
      _client.get(
              BASE_URL + "/auth/google_oauth2/callback", requestParams,
              new JsonLoginResponseHandler(loginResponseHandler));
  }
  </code></pre>