Skip to content

Allow signing in to SP after session timeout#1275

Merged
monfresh merged 2 commits intomasterfrom
mb-preserve-sp-request-after-timeout
Mar 28, 2017
Merged

Allow signing in to SP after session timeout#1275
monfresh merged 2 commits intomasterfrom
mb-preserve-sp-request-after-timeout

Conversation

@monfresh
Copy link
Contributor

Why: We rely on the original SP request URL to be available in the
session in order to complete the authentication flow and redirect the
user back to the SP. If a user comes from an SP, but sits on the sign
in page long enough for the session to time out, the request URL will
be deleted from the session, and after they sign back in, they will end
up on the profile page instead of going back to the SP.

How:

  • Take advantage of the request_id param to restore the SP info
    in the session after the user signs in, the same way we do when a user
    confirms their email in a different browser
  • Create a signed_in_path method that only redirects to the profile
    path if the user is fully authenticated. Otherwise, it redirects to
    user_two_factor_authentication_path. Use this new method in
    after_sign_in_path_for. Why? Because any controller that includes a
    before_action that gets executed will set the stored location for the
    user to that controller. This means that if we redirect to the
    profile_path while there is no stored location (which will be the case
    if the session times out) and the user isn't yet fully authenticated, it
    will call the confirm_two_factor_authenticated before_action, and
    the stored location will now be the profile page. So, after the user
    enters their OTP, they will be sent to the profile page instead of back
    to SamlIdpController#auth to complete the auth flow back to the SP.

@monfresh monfresh force-pushed the mb-preserve-sp-request-after-timeout branch 2 times, most recently from 27ffd2e to b157186 Compare March 28, 2017 04:08
@monfresh monfresh requested review from jessieay and pkarman March 28, 2017 14:21
@monfresh monfresh force-pushed the mb-preserve-sp-request-after-timeout branch 2 times, most recently from 4eef502 to e57cc78 Compare March 28, 2017 14:32
Copy link
Contributor

@pkarman pkarman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm % comments

.reek Outdated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hallelujah! 🎉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woooooo! Care to comment on why this now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I was getting a bunch of Reek errors locally, and instead of adding a bunch of methods to the exclusion list, I figured we'd turn it off since we'll probably keep getting more of these in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In isolation this change looks odd to me. Isn't the logic more subtle than simply always going to profile_path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that in this scenario, after_sign_in_path will always be profile_path, so might as well go there directly but I will revert this since it's not really related to this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having the name of a class be a verb seems like an odd pattern to introduce.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the first introduction of this pattern. See CreateVerifiedAccountEvent, RequestPasswordReset, UpdateUser, and UpdateUserPassword.

Micropurchase does this as well.

I think it's fine to have some classes be verbs and others nouns, depending on the best way to describe them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind verb names in service class methods. I do feel like some of the service classes in this app could perhaps be classified as something else...eg RandomPhrase...but that's for a separate PR :)

**Why**: We rely on the original SP request URL to be available in the
session in order to complete the authentication flow and redirect the
user back to the SP. If a user comes from an SP, but sits on the sign
in page long enough for the session to time out, the request URL will
be deleted from the session, and after they sign back in, they will end
up on the profile page instead of going back to the SP.

**How**:
- Take advantage of the `request_id` param to restore the SP info
in the session after the user signs in, the same way we do when a user
confirms their email in a different browser
- Create a `signed_in_path` method that only redirects to the profile
path if the user is fully authenticated. Otherwise, it redirects to
user_two_factor_authentication_path. Use this new method in
`after_sign_in_path_for`. Why? Because any controller that includes a
`before_action` that gets executed will set the stored location for the
user to that controller. This means that if we redirect to the
profile_path while there is no stored location (which will be the case
if the session times out) and the user isn't yet fully authenticated, it
will call the `confirm_two_factor_authenticated` `before_action`, and
the stored location will now be the profile page. So, after the user
enters their OTP, they will be sent to the profile page instead of back
to SamlIdpController#auth to complete the auth flow back to the SP.
@monfresh monfresh force-pushed the mb-preserve-sp-request-after-timeout branch from e57cc78 to f19d61f Compare March 28, 2017 15:13
@monfresh
Copy link
Contributor Author

These Travis failures are not due to this PR. I see the same failure in Jessie's earlier branch, which doesn't seem related to this failure either: https://travis-ci.org/18F/identity-idp/builds/215722877

@monfresh
Copy link
Contributor Author

I'll restart Travis and open an issue to fix that flickering spec.

@monfresh
Copy link
Contributor Author

This failing test was introduced in #1262.

Copy link
Contributor

@jessieay jessieay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Had some smaller questions / comments but overall 🥇

.reek Outdated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woooooo! Care to comment on why this now?

@_decorated_session ||= DecoratedSession.new(sp: current_sp, view_context: view_context).call
end

def signed_in_path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put this below after_sign_in_path so that method dependencies point downward?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be a public method so it can be tested. Alternatively, we can make it a private method and only rely on integration tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooh...hm yeah I tend not to make methods public just for testing. Seems problematic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with removing the application controller spec. Since this method is called in pretty much every feature spec, I think it's got good coverage. Is that what you're proposing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done here: 15b624f

Thanks!

service_provider_attributes
)
redirect_to after_sign_in_path_for(current_user)
redirect_to sp_session[:request_url]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would someone ever get to this point without being referred by an SP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The show action requires session[:sp] to be present.


def process_locked_out_user
render 'two_factor_authentication/shared/max_login_attempts_reached'
sign_out
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think we should sign out then render? Not sure if it matters, just wondering if the page might render different content based on signed in / out status.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. I only refactored this to please Code Climate. I didn't change the behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I believe this order is like that for a reason. I recall this fixed a bug at one time.

return false unless current_user.present?

return false unless current_user
!user_locked_out?(user)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on the name of this method, I'd expect this to read current_user && !user_locked_out?(user)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not new to this PR. I didn't want to make any changes that were not related. I can add the .present? back if that makes things clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fine, we can keep as-is

@@ -91,13 +93,21 @@ def cache_active_profile
end

def user_signed_in_and_not_locked_out?(user)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the user to get passed in here? We should have access to current_user within this method, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't change this behavior. This method existed before this PR. Can we revisit later if necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure thing!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind verb names in service class methods. I do feel like some of the service classes in this app could perhaps be classified as something else...eg RandomPhrase...but that's for a separate PR :)

html: { autocomplete: 'off', role: 'form' }) do |f|
= f.input :email, required: true, input_html: { class: 'mb4' }
= f.input :password, required: true
= f.input :request_id, as: :hidden, input_html: { value: params[:request_id] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params[:_request_id] ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The underscore is only present in the confirmation email.

expect(current_path).to eq sign_up_completed_path
end

it 'after session timeout, signing in takes user back to SP' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

@jessieay jessieay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😃

@monfresh monfresh merged commit 0fa95ab into master Mar 28, 2017
@monfresh monfresh deleted the mb-preserve-sp-request-after-timeout branch March 28, 2017 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants