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

Marek Posolda mposolda at redhat.com
Fri Mar 17 06:12:46 EDT 2017

On 17/03/17 09:40, Stian Thorgersen wrote:
> On 17 March 2017 at 09:22, Marek Posolda <mposolda at redhat.com 
> <mailto:mposolda at redhat.com>> wrote:
>     Ok, for now just ignoring the browser limitations and fact that
>     back/forward doesn't refresh the page automatically for POST
>     requests :)
>     On 17/03/17 08:45, 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.".
>     Yeah, that will be nice for UXP.
>>     Or Back button could just go to the start of the flow always.
>     Regarding UXP, I personally like your previous proposal better. If user
>     is deep after confirm many authenticator forms and he accidentally
>     clicks back-button, he will need to re-authenticate in all
>     authenticators again. Not so great for usability though?
> True - giving the user the option to choose is probably best.
>>     * Resubmitting forms will just display the page above
>     If we do any of your previous proposal, user will never see the
>     forms,
>     which he already submitted? For example if he submitted
>     username/password and now is on TOTP page, then after click "back" he will be
>     either on the "Page has expired" or start of the flow. The start of the flow usually
>     will be username/password form, but flow started from scratch, so it
>     won't be resubmitting form, but new submit?
>     Anyway yes, if some of previous forms is re-submitted, we can display "page is expired" page.
> I'm not quite following. Is it possible to prevent the back buttons 
> from "re-submitting" forms at all? If so that's ideal as you then 
> don't get the ugly message from the browser that the form is expired.
Yes, as long as we don't send any "Cache-control" header, then browser 
back/forward buttons doesn't resubmit forms and doesn't re-send any 

So follow-up on the example above
1) User successfully authenticated on username/password form and he is 
on TOTP page.
2) User press browser "back" button. Now he will see again the 
username/password form
3) User will try to re-submit the username/password form OR he press 
browser "refresh" button. In both cases, we will show our nice "Page has 
expired. To restart the login process click here. To continue the login 
process click here."

Are we in agreement that this is ideal user experience?

If yes, we can achieve that quite easily without need of javascript 
hacks or hidden form fields though.

>     Marek
>>     * 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
>>     <mailto: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
>>         <mailto: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-when-reached-via-browsers-back-button
>>>             <http://stackoverflow.com/questions/9046184/reload-the-site-when-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