diff options
author | Scott Escue <scott.escue@gmail.com> | 2018-05-22 23:04:19 +0300 |
---|---|---|
committer | Mike Greiling <mike@pixelcog.com> | 2019-01-10 09:00:38 +0300 |
commit | 6540a9468a8bce3f496423179db1862cfb9f5c8c (patch) | |
tree | 28a3c968eeaf8c21377fc80cf0b16c2700b76689 | |
parent | 4a6c7661edae664a7f6366201d017e24d8f42026 (diff) |
Preserve URL fragment across sign-in and sign-up redirects
If window.location contains a URL fragment, append the fragment to all sign-in forms, the sign-up form, and all button based providers.
-rw-r--r-- | app/controllers/omniauth_callbacks_controller.rb | 16 | ||||
-rw-r--r-- | app/views/devise/sessions/new.html.haml | 30 | ||||
-rw-r--r-- | spec/controllers/omniauth_callbacks_controller_spec.rb | 34 |
3 files changed, 79 insertions, 1 deletions
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 30be50d4595..12f11976439 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -75,6 +75,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private def omniauth_flow(auth_module, identity_linker: nil) + omniauth_params = request.env['omniauth.params'] + + if omniauth_params.present? && omniauth_params['redirect_fragment'].present? + store_redirect_fragment(omniauth_params['redirect_fragment']) + end + if current_user log_audit_event(current_user, with: oauth['provider']) @@ -189,4 +195,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController request_params = request.env['omniauth.params'] (request_params['remember_me'] == '1') if request_params.present? end + + def store_redirect_fragment(redirect_fragment) + key = stored_location_key_for(:user) + location = session[key] + unless location.to_s.strip.empty? + uri = URI.parse(location) + uri.fragment = redirect_fragment + store_location_for(:user, uri.to_s) + end + end end diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index 34d4293bd45..3840cbb0b31 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,6 +1,34 @@ - page_title "Sign in" -%div +- content_for :page_specific_javascripts do + -# haml-lint:disable InlineJavaScript + :javascript + document.addEventListener('DOMContentLoaded', function() { + // Determine if the current URL location contains a fragment identifier (aka hash or anchor ref). + // This should be present if the user was redirected to sign in after attempting to access a + // protected URL that included a fragment identifier. + var fragment = window.location.hash; + if (fragment && fragment !== '') { + // Append the fragment to all signin/signup form actions so it is preserved when + // the user is eventually redirected back to the originally requested URL. + $('div#signin-container form').attr('action', function (index, action) { + return action + fragment; + }); + + // Append a redirect_fragment query param to all oauth provider links. The redirect_fragment + // query param will be passed to the omniauth callback upon successful authentication + $('div#signin-container a.oauth-login').attr('href', function (index, href) { + const tokens = href.split('?'); + let url = tokens[0] + '?redirect_fragment=' + encodeURIComponent(fragment.substr(1)); + if (tokens.length > 1) { + url += '&' + tokens[1]; + } + return url; + }); + } + }); + +#signin-container - if form_based_providers.any? = render 'devise/shared/tabs_ldap' - else diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index d377d69457f..21936491ffc 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -45,6 +45,40 @@ describe OmniauthCallbacksController, type: :controller do end end + context 'when a redirect fragment is provided' do + let(:provider) { :jwt } + let(:extern_uid) { 'my-uid' } + + before do + request.env['omniauth.params'] = { 'redirect_fragment' => 'L101' } + end + + context 'when a redirect url is stored' do + it 'redirects with fragment' do + post provider, nil, { user_return_to: '/fake/url' } + + expect(response).to redirect_to('/fake/url#L101') + end + end + + context 'when a redirect url with a fragment is stored' do + it 'redirects with the new fragment' do + post provider, nil, { user_return_to: '/fake/url#replaceme' } + + expect(response).to redirect_to('/fake/url#L101') + end + end + + context 'when no redirect url is stored' do + it 'does not redirect with the fragment' do + post provider + + expect(response.redirect?).to be true + expect(response.location).not_to include('#L101') + end + end + end + context 'strategies' do context 'github' do let(:extern_uid) { 'my-uid' } |