From c4a0152dc6227f4a56ad93cfc601ff4193115bc8 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 27 Aug 2013 15:56:12 +1000 Subject: [PATCH] recover from bad CSRF tokens without requiring a hard refresh of the browser --- app/assets/javascripts/discourse.js | 8 ++++---- app/assets/javascripts/discourse/mixins/ajax.js | 11 ++++++++--- app/controllers/application_controller.rb | 2 +- app/controllers/session_controller.rb | 2 +- .../users/omniauth_callbacks_controller.rb | 2 ++ 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index a9f00b07e86..254a8b4d07b 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -132,13 +132,13 @@ Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { }); // Add a CSRF token to all AJAX requests - var csrfToken = $('meta[name=csrf-token]').attr('content'); + Discourse.csrfToken = $('meta[name=csrf-token]').attr('content'); + + console.log(Discourse.csrfToken); $.ajaxPrefilter(function(options, originalOptions, xhr) { if (!options.crossDomain) { - // This may be delay set - csrfToken = csrfToken || $('meta[name=csrf-token]').attr('content'); - xhr.setRequestHeader('X-CSRF-Token', csrfToken); + xhr.setRequestHeader('X-CSRF-Token', Discourse.csrfToken); } }); diff --git a/app/assets/javascripts/discourse/mixins/ajax.js b/app/assets/javascripts/discourse/mixins/ajax.js index 11799c67022..e28f54b8f61 100644 --- a/app/assets/javascripts/discourse/mixins/ajax.js +++ b/app/assets/javascripts/discourse/mixins/ajax.js @@ -56,6 +56,12 @@ Discourse.Ajax = Em.Mixin.create({ var oldError = args.error; args.error = function(xhr) { + // note: for bad CSRF we don't loop an extra request right away. + // this allows us to eliminate the possibility of having a loop. + if (xhr.status === 403 && xhr.responseText == "['BAD CSRF']") { + Discourse.csrfToken = null; + } + // If it's a parseerror, don't reject if (xhr.status === 200) return args.success(xhr); @@ -73,12 +79,11 @@ Discourse.Ajax = Em.Mixin.create({ // For cached pages we strip out CSRF tokens, need to round trip to server prior to sending the // request (bypass for GET, not needed) - var csrfToken = $('meta[name=csrf-token]').attr('content'); - if(args.type && args.type !== 'GET' && !csrfToken){ + if(args.type && args.type !== 'GET' && !Discourse.csrfToken){ return Ember.Deferred.promise(function(promise){ $.ajax(Discourse.getURL('/session/csrf')) .success(function(result){ - $('head').append(''); + Discourse.csrfToken = result.csrf; performAjax(promise); }); }); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 740dd502ea3..a9b6abbc95a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,7 +22,7 @@ class ApplicationController < ActionController::Base unless is_api? super clear_current_user - raise Discourse::CSRF + render text: "['BAD CSRF']", status: 403 end end diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 1f47cbedb9f..74c54cf65ea 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -67,7 +67,7 @@ class SessionController < ApplicationController end def destroy - session[:current_user_id] = nil + reset_session cookies[:_t] = nil render nothing: true end diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index 25e8a96b891..5118378acc1 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -83,6 +83,8 @@ class Users::OmniauthCallbacksController < ApplicationController # log on any account that is active with forum access if Guardian.new(user).can_access_forum? && user.active log_on_user(user) + # don't carry around old auth info, perhaps move elsewhere + session[:authentication] = nil @data.authenticated = true else if SiteSetting.invite_only?