Created
January 18, 2013 22:38
-
-
Save chrisdavies/4569299 to your computer and use it in GitHub Desktop.
In Rails (3.2.8), I needed to allow different browser tabs to behave as if they were different apps (my rails app will be embedded in an iframe in various apps). For a variety of reasons, I couldn't track apps via sub-domains. Cookies failed, because browser tabs all share the same cookies, so there were race-conditions and other buggery. My sol…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'addressable/uri' | |
module MyApp | |
# This rack-filter gets run before any urls are processed by the routing | |
# subsystem, enabling us to extract the app_id query parameter and cache it for | |
# the duration of the current request thread. | |
class ClientIdManager | |
def initialize(app) | |
@app = app | |
end | |
def call(env) | |
url = Addressable::URI.parse(env['REQUEST_URI']) | |
if (url.query_values && url.query_values.has_key?('app_id')) | |
ClientIdProvider.app_id = url.query_values['app_id'] | |
else | |
ClientIdProvider.app_id = '-1' | |
end | |
@app.call(env) | |
end | |
end | |
end | |
module MyApp | |
# This provides convenient access to the current app_id | |
# It also (via to_s) is able to provide said app_id to | |
# external classes which are configured in application | |
# initialization | |
class ClientIdProvider | |
def self.app_id | |
Thread.current['app_id'] | |
end | |
def self.app_id=(val) | |
Thread.current['app_id'] = val | |
end | |
def to_s | |
ClientIdProvider.app_id | |
end | |
end | |
end | |
# In your application.rb | |
# Ensure that app_id is persisted in all urls generated from this app | |
Rails.application.routes.default_url_options = { 'app_id' => MyApp::ClientIdProvider.new } | |
# Add our filter to the rack middleware | |
config.middleware.use 'MyApp::ClientIdManager' | |
# In your omniauth configuration: | |
# Pass the app_id to external providers (for Google, this is in the 'state' parameter) | |
# Be sure to set provider_ignores_state: true | |
# This prevents an annoying issue where specifying state makes | |
# calls to Google fail. | |
config.omniauth :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], { state: MyApp::ClientIdProvider.new, provider_ignores_state: true, scope: 'userinfo.email,userinfo.profile', access_type: 'online', approval_prompt: ''} | |
# In your omniauth controller processing methods | |
# Be sure to set the app_id (or whatever param you want to persist) | |
# When it comes back from external omniauth calls. | |
# Example Omniauth callback controller Google processing function: | |
def process_google_response | |
MyApp::ClientIdProvider.app_id = params['state'] | |
# ... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment