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:
parent
97f701bc4c
commit
2cae29f644
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Users::AssociateAccountsController < ApplicationController
|
class Users::AssociateAccountsController < ApplicationController
|
||||||
REDIS_PREFIX ||= "omniauth_reconnect"
|
SECURE_SESSION_PREFIX ||= "omniauth_reconnect"
|
||||||
|
|
||||||
def connect_info
|
def connect_info
|
||||||
auth = get_auth_hash
|
auth = get_auth_hash
|
||||||
|
@ -17,7 +17,7 @@ class Users::AssociateAccountsController < ApplicationController
|
||||||
|
|
||||||
def connect
|
def connect
|
||||||
auth = get_auth_hash
|
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
|
provider_name = auth.provider
|
||||||
authenticator = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
|
authenticator = Discourse.enabled_authenticators.find { |a| a.name == provider_name }
|
||||||
|
@ -34,9 +34,13 @@ class Users::AssociateAccountsController < ApplicationController
|
||||||
|
|
||||||
def get_auth_hash
|
def get_auth_hash
|
||||||
token = params[:token]
|
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?
|
raise Discourse::NotFound if json.nil?
|
||||||
|
|
||||||
OmniAuth::AuthHash.new(JSON.parse(json))
|
OmniAuth::AuthHash.new(JSON.parse(json))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.key(token)
|
||||||
|
"#{SECURE_SESSION_PREFIX}_#{token}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ class Users::OmniauthCallbacksController < ApplicationController
|
||||||
if session.delete(:auth_reconnect) && authenticator.can_connect_existing_user? && current_user
|
if session.delete(:auth_reconnect) && authenticator.can_connect_existing_user? && current_user
|
||||||
# Save to redis, with a secret token, then redirect to confirmation screen
|
# Save to redis, with a secret token, then redirect to confirmation screen
|
||||||
token = SecureRandom.hex
|
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}"
|
return redirect_to "#{Discourse.base_path}/associate/#{token}"
|
||||||
else
|
else
|
||||||
DiscourseEvent.trigger(:before_auth, authenticator, auth, session, cookies, request)
|
DiscourseEvent.trigger(:before_auth, authenticator, auth, session, cookies, request)
|
||||||
|
|
|
@ -56,17 +56,6 @@ RSpec.describe Users::AssociateAccountsController do
|
||||||
expect(data["provider_name"]).to eq("google_oauth2")
|
expect(data["provider_name"]).to eq("google_oauth2")
|
||||||
expect(data["account_description"]).to eq("someemail@test.com")
|
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
|
# Make the connection
|
||||||
events = DiscourseEvent.track_events { post "#{uri.path}.json" }
|
events = DiscourseEvent.track_events { post "#{uri.path}.json" }
|
||||||
expect(events.any? { |e| e[:event_name] == :before_auth }).to eq(true)
|
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)
|
expect(response.status).to eq(404)
|
||||||
end
|
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
|
it "returns the correct response for non-existent tokens" do
|
||||||
get "/associate/12345678901234567890123456789012.json"
|
get "/associate/12345678901234567890123456789012.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
|
|
Loading…
Reference in New Issue