Skip to content

Commit abda12d

Browse files
authored
Allow specific session keys to bypass reset_session (#63)
Adds `session_keys_to_persist` config option in order to carry over specific session keys from pre-log in to post-log in (since `reset_session` is called post-log in in order to prevent session fixation: https://guides.rubyonrails.org/security.html#session-fixation-countermeasures)
2 parents 67c03ec + a5ea1ba commit abda12d

File tree

9 files changed

+56
-2
lines changed

9 files changed

+56
-2
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- Adds `session_keys_to_persist` config option to allow for specific session keys to be persisted across logins (since logging in will reset the session: https://guides.rubyonrails.org/security.html#session-fixation-countermeasures)
11+
1012
## [v3.4.0]
1113

1214
- Removes `v1_signup` param as it is no longer required (https://github.com/RaspberryPiFoundation/profile/pull/1512)

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ RpiAuth.configure do |config|
3333
config.identity_url = 'http://localhost:3002' # The url for the profile instance being used for auth
3434
config.user_model = 'User' # The name of the user model in the host app being used, use the name as a string, not the model itself
3535
config.scope = 'openid email profile force-consent' # The required OIDC scopes
36+
config.session_keys_to_persist = 'foo bar' # Optional: any session keys to persist across sessions (as the session is reset upon log in)
3637
config.success_redirect = '/' # After succesful login the route the user should be redirected to; this will override redirecting the user back to where they were when they started the log in / sign up flow (via `omniauth.origin`), so should be used rarely / with caution. This can be a string or a proc, which is executed in the context of the RpiAuth::AuthController.
3738
config.bypass_auth = false # Should auth be bypassed and a default user logged in
3839
end
@@ -130,7 +131,7 @@ registration form, then you should supply a parameter called `returnTo` which
130131
is then used to redirect after log in/sign up are successful.
131132

132133
```ruby
133-
button_to 'Log in to start registraion', rpi_auth_login_path, params: { returnTo: '/registration_form' }
134+
button_to 'Log in to start registration', rpi_auth_login_path, params: { returnTo: '/registration_form' }
134135
```
135136

136137
If this parameter is missing [Omniauth uses the HTTP Referer

app/controllers/rpi_auth/auth_controller.rb

+12-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ class AuthController < ActionController::Base
1010

1111
def callback
1212
# Prevent session fixation. If the session has been initialized before
13-
# this, and we need to keep the data, then we should copy values over.
13+
# this, and certain data needs to be persisted, then the client should
14+
# pass the keys via config.session_keys_to_persist
15+
old_session = session.to_hash
16+
1417
reset_session
1518

19+
keys_to_persist = RpiAuth.configuration.session_keys_to_persist
20+
21+
unless keys_to_persist.nil? || keys_to_persist.empty?
22+
keys_to_persist.split.each do |key|
23+
session[key] = old_session[key]
24+
end
25+
end
26+
1627
auth = request.env['omniauth.auth']
1728
self.current_user = RpiAuth.user_model.from_omniauth(auth)
1829

lib/rpi_auth/configuration.rb

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Configuration
1616
:identity_url,
1717
:response_type,
1818
:scope,
19+
:session_keys_to_persist,
1920
:success_redirect,
2021
:user_model
2122

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Used to modify session variables within request tests
2+
class SessionsController < ApplicationController
3+
def create
4+
vars = params.permit(session_vars: {})
5+
vars[:session_vars].each do |var, value|
6+
session[var] = value
7+
end
8+
head :created
9+
end
10+
end

spec/dummy/config/initializers/rpi_auth.rb

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
config.brand = 'codeclub'
77
config.host_url = 'http://localhost:3009'
88
config.identity_url = 'http://localhost:3002'
9+
config.session_keys_to_persist = 'foo bar'
910
config.user_model = 'User'
1011

1112
# Redurect to the next URL

spec/dummy/config/routes.rb

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
33
root to: 'home#show'
44

5+
resource :session, only: %i[create]
6+
57
# Make sure we don't match auth routes
68
get '/*slug', to: 'home#show', constraints: { slug: /(?!(rpi_)?auth\/).*/ }
79
end

spec/dummy/spec/requests/auth_request_spec.rb

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
let(:bypass_auth) { false }
1919
let(:identity_url) { 'https://my.example.com' }
20+
let(:session_keys_to_persist) {}
2021
# We need to make sure we match the hostname Rails uses in test requests
2122
# here, otherwise `returnTo` redirects will fail after login/logout.
2223
let(:host_url) { 'https://www.example.com' }
@@ -29,6 +30,7 @@
2930
# This would normally be in the initializer, but because we're toggling the
3031
# option on or off, we need to explicitly call it here.
3132
RpiAuth.configuration.enable_auth_bypass
33+
RpiAuth.configuration.session_keys_to_persist = session_keys_to_persist
3234
OmniAuth.config.test_mode = true
3335
end
3436

@@ -180,6 +182,21 @@
180182
expect(session.id).not_to eq previous_id
181183
end
182184

185+
context 'when session_keys_to_persist is set' do
186+
let(:session_keys_to_persist) { 'foo' }
187+
188+
it 'persists provided session keys on login' do
189+
set_session(foo: 'bar')
190+
post '/auth/rpi'
191+
previous_foo = session[:foo]
192+
193+
expect(response).to redirect_to('/rpi_auth/auth/callback')
194+
follow_redirect!
195+
196+
expect(session[:foo]).to eq previous_foo
197+
end
198+
end
199+
183200
context 'when having visited a page first' do
184201
it 'redirects back to the original page' do
185202
post '/auth/rpi', headers: { Referer: 'http://www.example.com/foo#foo' }

spec/support/request_helpers.rb

+9
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,13 @@ def sign_in(user)
1313
post '/auth/rpi'
1414
follow_redirect!
1515
end
16+
17+
def set_session(vars = {})
18+
post session_path, params: { session_vars: vars }
19+
expect(response).to have_http_status(:created)
20+
21+
vars.each_key do |var|
22+
expect(session[var]).to be_present
23+
end
24+
end
1625
end

0 commit comments

Comments
 (0)