[keycloak-dev] Improve back-button and refreshes in authenticators?

Stian Thorgersen sthorger at redhat.com
Mon Mar 20 09:35:31 EDT 2017


On 20 March 2017 at 09:53, Marek Posolda <mposolda at redhat.com> wrote:

> On 20/03/17 08:54, Stian Thorgersen wrote:
>
> Let's go with the "page expired" page then, but we wouldn't need it for
> the refresh page would we? That can just re-display the same thing again.
>
> IMO if you are on TOTP page and you click "refresh", you should just
> receive the TOTP page.
>
> However for the case that you are on TOTP page and you clicked "back", you
> are on our "page expired" page. Now you click "Refresh" you should be still
> on "page expired" page IMO. If user wants to go to last step, he can click
> on the link, which we provide on our "page expired" page and then he will
> go to last step and TOTP page is shown. I think it will be challenging to
> do it other way as there is no easy way to track if request was triggered
> with back, forward or refresh button.
>

Yes, refresh should always just show the same again.


>
>
>
> +1 I kinda thought about writing authenticators, but you're right that
> should be made as simple as possible and we should ideally handle all of
> this outside the authenticators themselves. Something that would be
> impossible if users could step backwards/forwards in the flow as you'd have
> to have "roll back" events or something so authenticators could undo stuff
> if needed.
>
> Another posibility is to track the complete history of loginSession. Hence
> clicking back-button will revert also the loginSession to the step before
> last authenticator and all the changes to the state of loginSession (notes,
> attributes authenticatedUser etc) will be reverted to that stage. However
> this looks like quite challenging and not optimal for memory footprint... I
> guess not an option?
>

There could also be changes outside of the login session that we can't
track.


>
>
> Marek
>
>
> On 17 March 2017 at 16:21, Bill Burke <bburke at redhat.com> wrote:
>
>> The best user experience would be that the user can click the
>> back/forward/refresh buttons as they wanted and things would work as they
>> would expect, i.e. if you're on the OTP page, you could click the back
>> button and go to the username/password page and re-enter username
>> password.  I didn't implement support for this approach as its really
>> freakin hard to get right.
>> The real question is, do we want to support going backwards or forwards
>> in a flow?  If we don't, then there are considerations and limitations on
>> what we can do for the user experience which I was trying to get at
>> before.  Specifically:
>>
>> * Is it possible to determine the difference between the back, forward,
>> or refresh button event?
>>
>> That is the question I struggled the most with when implementing auth
>> flow processing.
>>
>> I'd say we set the bar low and minimally try to provide this experience:
>>
>> * If back, forward, or refresh button is pushed, show the "Page expired"
>> page you suggested before Stian.
>>
>> Anything different than that will be dependent on the limitations of the
>> browser and our auth flow implementation.
>>
>> There's also another type of user experience you aren't considering that
>> should be involved with the discussion.  Specifically the user experience
>> of writing an Authenticator.  You want the Authenticator development to be
>> simple...you also want to make sure that its implemented in a way that
>> developers can't make mistakes and introduce security holes by accident.
>> All this stuff is tied together which makes the problem really complex.
>>
>>
>> On 3/17/17 3:45 AM, Stian Thorgersen wrote:
>>
>> I repeat:
>>
>> Before we discuss implementation though, let's figure out what the ideal
>> user experience would be then figure out how to implement it. What about:
>>
>> * Refresh just works
>> * Back button will display a nicer page, something like "Page has
>> expired. To restart the login process click here. To continue the login
>> process click here.". Or Back button could just go to the start of the flow
>> always.
>> * Resubmitting forms will just display the page above
>> * No need to do redirects. Redirects is bad for performance, but also has
>> twice the response time which is not good from a usability perspective
>>
>> Is this the optimal user experience? Or should we do something else?
>>
>> On 17 March 2017 at 08:44, Stian Thorgersen <sthorger at redhat.com> wrote:
>>
>>> Can we please get back to discussing what the best user experience is
>>> first. Then we can discuss implementations?
>>>
>>> On 16 March 2017 at 18:37, Bill Burke <bburke at redhat.com> wrote:
>>>
>>>>
>>>>
>>>> On 3/16/17 10:50 AM, Marek Posolda wrote:
>>>>
>>>> On 16/03/17 15:27, Bill Burke wrote:
>>>>
>>>> * Hidden field in a form is not a good approach.  Its very brittle and
>>>> will not work in every situation.  So huge -1 there.
>>>>
>>>> * browser back button is not required to resubmit the HTTP request as
>>>> the page can be rendered from cache.  Therefore you couldn't have a "Page
>>>> Expired" page displayed when the back button is pressed without setting the
>>>> header "Cache-Control: no-store, must-revalidate, max-age=0"
>>>>
>>>> Maybe we can do some javascript stuff like this:
>>>> http://stackoverflow.com/questions/9046184/reload-the-site-w
>>>> hen-reached-via-browsers-back-button
>>>>
>>>> But that would mean that we will need to inject some common javascript
>>>> stuff into every HTML form displayed by authentication SPI. Could we rely
>>>> on that?
>>>>
>>>> I don't think this is a good approach as Authenticator develoeprs would
>>>> have to do the same thing.
>>>>
>>>>
>>>>
>>>> * Furthermore, without some type of code/information within the URL,
>>>> you also wouldn't know if somebody clicked the back button or not or
>>>> whether this was a page refresh or some other GET request.
>>>>
>>>> Once we have the cookie with loginSessionID, we can lookup the
>>>> loginSession. And loginSession will contain last code (same like
>>>> clientSession now) and last authenticator. Then we just need to compare the
>>>> code from the loginSession with the code from request. If it matches, we
>>>> are good. If it doesn't match, it's likely the refresh of some previous
>>>> page and in that case, we can just redirect to last authenticator.
>>>>
>>>> This is the current behavior, but instead of using a cookie, the "code"
>>>> is stored in the URL.
>>>>
>>>> With only a cookie though and no URL information, you won't know the
>>>> different between a Back Button and a Page Refresh for GET requests.  For
>>>> POST requests, you won't be able to tell the differencee between a Back
>>>> Button, Page Refresh, or whether the POST is targeted to an actual
>>>> Authenticator.
>>>>
>>>> The more I think about it, things should probably stay the way it
>>>> currently is, with improvements on user experience.  I think we can support
>>>> what Stian suggested with the current implementation.
>>>>
>>>>
>>>> Not sure if we also need to track all codes, so we are able to distinct
>>>> between the "expired" code, and between the "false" code, which was never
>>>> valid and was possibly used by some attacker for CSRF. Maybe we can sign
>>>> codes with HMAC, so we can verify if it is "expired" or "false" code
>>>> without need to track the list of last codes.
>>>>
>>>>
>>>> This has been done in the past.  Then it was switched to using the same
>>>> code throughout the whole flow, then Stian switched it to changing the code
>>>> throughout the flow.  I don't know if he uses a hash or not.
>>>>
>>>> Bill
>>>>
>>>
>>>
>>
>>
>
>


More information about the keycloak-dev mailing list