DEV: Update associate_accounts_controller to use secure_session

This is much cleaner than using redis directly. It also opens the door to more complex association change flows which may happen during login.
This commit is contained in:
David Taylor 2021-08-10 14:09:30 +01:00
parent 97f701bc4c
commit 2cae29f644
3 changed files with 34 additions and 15 deletions

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Users::AssociateAccountsController < ApplicationController
REDIS_PREFIX ||= "omniauth_reconnect"
SECURE_SESSION_PREFIX ||= "omniauth_reconnect"
def connect_info
auth = get_auth_hash
@ -17,7 +17,7 @@ class Users::AssociateAccountsController < ApplicationController
def connect
auth = get_auth_hash
Discourse.redis.del "#{REDIS_PREFIX}_#{current_user&.id}_#{params[:token]}"
secure_session[self.class.key(params[:token])] = nil
provider_name = auth.provider
authenticator = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
@ -34,9 +34,13 @@ class Users::AssociateAccountsController < ApplicationController
def get_auth_hash
token = params[:token]
json = Discourse.redis.get "#{REDIS_PREFIX}_#{current_user&.id}_#{token}"
json = secure_session[self.class.key(token)]
raise Discourse::NotFound if json.nil?
OmniAuth::AuthHash.new(JSON.parse(json))
end
def self.key(token)
"#{SECURE_SESSION_PREFIX}_#{token}"
end
end

View File

@ -30,7 +30,7 @@ class Users::OmniauthCallbacksController < ApplicationController
if session.delete(:auth_reconnect) && authenticator.can_connect_existing_user? && current_user
# Save to redis, with a secret token, then redirect to confirmation screen
token = SecureRandom.hex
Discourse.redis.setex "#{Users::AssociateAccountsController::REDIS_PREFIX}_#{current_user.id}_#{token}", 10.minutes, auth.to_json
secure_session.set "#{Users::AssociateAccountsController.key(token)}", auth.to_json, expires: 10.minutes
return redirect_to "#{Discourse.base_path}/associate/#{token}"
else
DiscourseEvent.trigger(:before_auth, authenticator, auth, session, cookies, request)

View File

@ -56,17 +56,6 @@ RSpec.describe Users::AssociateAccountsController do
expect(data["provider_name"]).to eq("google_oauth2")
expect(data["account_description"]).to eq("someemail@test.com")
# Request as different user, should not work
sign_in(user2)
get "#{uri.path}.json"
expect(response.status).to eq(404)
# Back to first user
sign_in(user)
get "#{uri.path}.json"
data = response.parsed_body
expect(data["provider_name"]).to eq("google_oauth2")
# Make the connection
events = DiscourseEvent.track_events { post "#{uri.path}.json" }
expect(events.any? { |e| e[:event_name] == :before_auth }).to eq(true)
@ -80,6 +69,32 @@ RSpec.describe Users::AssociateAccountsController do
expect(response.status).to eq(404)
end
it 'should only work within the current session' do
sign_in(user)
post "/auth/google_oauth2?reconnect=true"
expect(response.status).to eq(302)
expect(session[:auth_reconnect]).to eq(true)
OmniAuth.config.mock_auth[:google_oauth2].uid = "123456"
get "/auth/google_oauth2/callback.json"
expect(response.status).to eq(302)
expect(session[:current_user_id]).to eq(user.id) # Still logged in
expect(UserAssociatedAccount.count).to eq(0) # Reconnect has not yet happened
uri = URI.parse(response.redirect_url)
get "#{uri.path}.json"
data = response.parsed_body
expect(data["provider_name"]).to eq("google_oauth2")
expect(data["account_description"]).to eq("someemail@test.com")
cookies.delete "_forum_session"
get "#{uri.path}.json"
expect(response.status).to eq(404)
end
it "returns the correct response for non-existent tokens" do
get "/associate/12345678901234567890123456789012.json"
expect(response.status).to eq(404)