10000 Improve behaviour for Access Denied with Remember Me · Issue #54318 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

Improve behaviour for Access Denied with Remember Me #54318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
pauljura opened this issue Mar 18, 2024 · 16 comments · May be fixed by #57661
Open

Improve behaviour for Access Denied with Remember Me #54318

pauljura opened this issue Mar 18, 2024 · 16 comments · May be fixed by #57661

Comments

@pauljura
Copy link

Description

This is a continuation of #16026 I thought carsonbot would re-open but it's been a week and nothing has happened, so I'm opening this.

In summary, I would like to have better control of what happens when a user who is authenticated with "remember me" is denied access. As a general rule, I expect that a user authenticated via remember me should have the same experience as a fully authenticated user, so if they are denied access to something, the response should be a 403 Forbidden. But currently, the response is a redirect to login. This is not a good experience for the user, who still won't get access even after being fully authenticated. The only exception to this is when I want to protect a sensitive part of the website (e.g. changing password) and it requires IS_AUTHENTICATED_FULLY. Any other access check should result in a 403 Forbidden, even for "remember me" users.

In fact, I don't think that redirecting "remember" me users to the login page in order to become fully authenticated is ideal, and I think this would be a good opportunity to improve the process. Being redirected to the login page makes it appear as though they aren't signed in at all. From the user's point of view, it's hard to tell whether their session has expired, or if they are simply being prompted to fully authenticate. What I would prefer is if the user sees a different page that says "you are accessing a sensitive area, please verify your password" or something along those lines. That makes it clear that they are still logged in, but they just need to provide their password (not username, because they're already signed in). Of course, I can implement this myself now by adding checks to the login page and displaying different content to anonymous users vs users authenticated with "remember me", but to me this is standard behaviour that I would add to every single project I work on, and so I feel it's worth building in to the framework. So just like we have a special case for "login" and "logout", I think it's worth having a special route for "upgrade" as well. In case you don't need different content, then you could simply configure the "upgrade" route to be the same as your "login" route.

The current behaviour also makes it difficult to write javascript that handles dynamic content. If I'm loading dynamic content with an AJAX request and the request gets redirected to the login page, it's hard to determine whether this was due to a session timeout or access denied. But if there was a different route for login vs upgrade, that would make it possible to write javascript that can distinguish these cases, and display an appropriate error message.

So finally, this is what I propose:

  • if an anonymous user gets access denied, redirect to login
  • if an authenticated user gets access denied (whether fully authenticated or "remember me") then show 403 Forbidden
  • in the special case where full authentication is required but a user only has "remember me", then redirect to the "upgrade" route

This would align closer with what I (and I assume most developers) expect should happen by default, it improves the user experience, and makes it easier to write javascript for dynamic content.

Thanks

Example

No response

@eltharin
Copy link
Contributor

I push a PR in this way, user can choose exact behaviour he want

@stof
Copy link
Member
stof commented Aug 6, 2024
  • in the special case where full authentication is required but a user only has "remember me", then redirect to the "upgrade" route

The big question (that was never answered in the previous issue even if I brought it up several time) is that it is very hard to determine that "special case": doing a security check on IS_AUTHENTICATED_FULLY is not the only security check that can depend on the trust level of the token. Any custom voter can totally include a check on the trust level as part of its decision logic (either by doing a check on IS_AUTHENTICATED_FULLY or by using the AuthenticationTrustResolverInterface).

The current behavior is based on the fact that if you don't know whether the decision depends on the trust level of the token, it makes sense to redo an attempt with a higher trust level.

Solving the issue would involve proposing a solution to resolve this "don't know" state.

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

that's why I allow in my PR configure that by a custom handler, We have 2 possibilites in the core :

  • same as actual
  • consider REMEBER_ME login as a normal login except if the attribute is "IS_AUTHENTICATED_FULLY"

and if user want a particular behaviour, he can make his handler with his particulars conditions,

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

we can declare a function in AuthenticationTrustResolverInterface for custom Resolver to be called instaed of isFullFledged if present to check with Token.

What do you think about it ?

@stof
Copy link
Member
stof commented Aug 6, 2024

having to implement a custom handler able to know about the logic implemented by all custom voters to know whether the decision that denied access included some logic depending on the trust level would be totally unmaintainable.

we can declare a function in AuthenticationTrustResolverInterface for custom Resolver to be called instaed of isFullFledged if present to check with Token.

I don't understand what you propose here.

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

whitch voters? at this point you just have an exception and a AuthenticationTrustResolverInterface.

My proposal is in AuthenticationTrustResolverInterface add a facultative function interceptExceptionNotFullFledged taken Exception and token to re 8000 spond if yes or no have to call the NotFullFledgedHandler

For in handleAccessDeniedException have :

        $token = $this->tokenStorage->getToken();
        $intercept = method_exists($this->authenticationTrustResolver, 'interceptExceptionNotFullFledged') ?
            $this->authenticationTrustResolver->interceptExceptionNotFullFledged($token) :
            !$this->authenticationTrustResolver->isFullFledged($token);
        if ($intercept) {

            $startAuthenticationResponse = function () use($exception, $event, $token) {
            ...

@stof
Copy link
Member
stof commented Aug 6, 2024

@eltharin the AccessDeniedException is created based on the result of calling the AuthorizationCheckerInterface (that's what the getAttributes and getSubject methods are about: reporting what arguments were passed to the checker).

If you want to avoid useless redirections to the login page for partially trusted tokens (to replace them with a fully trusted token) in case it won't change the decision, you need to know whether the decision depended on the trust level of the token.
Your PR does a broken implementation of that by saying that IS_AUTHENTICATED_FULLY depends on the trust level (which is true) and that anything else does not (which is wrong). Your PR replaces useless login flows before reporting an error anyway with reporting errors immediately for cases that would not report errors after going through a login flow, which is a lot worse for DX.

My proposal is in AuthenticationTrustResolverInterface add a facultative function interceptExceptionNotFullFledged taken Exception and token to respond if yes or no have to call the NotFullFledgedHandler

This still does not explain what this interceptExceptionNotFullFledged is expected to do. What is the expected behavior of this method ?

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

interceptExceptionNotFullFledged can read the exception to get all attributes and subject and decide if has to call custom notfullfledgedhandler or not.
For IS_AUTHENTICATED_FULLY it's only in a new notfullfledgedhandler whitch is not by default so user can active it if that correspond to he want or create his own custom

maybe the names are not good, same is for remember me behaviour is SAME as normal login behaviour

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

Else, I can create a notfullfledgedhandler for recreate actual behaviour, and call it directly.
I integrate the if (!$this->authenticationTrustResolver->isFullFledged($token)) condition in this handler an SameHandler (and change this name).

With that, user can create his own handler whitch is called in all cases, and he makes his conditions and changes he want and if return null, follow the rest of the handleAccessDeniedException function

@stof
Copy link
Member
stof commented Aug 6, 2024

interceptExceptionNotFullFledged cannot read the exception as you pass it only the token.

If the exception were passed, I don't see what would be the benefit over your NotFullFledgedHandlerInterface (having to customize the TrustResolver is a lot more complex than implementing an handler, as it require implementing all the other methods).

But this does not solve the fact that you expect this method to be aware of how a decision was taken by voters to know which decision involved the trust level. This is exactly the part that is totally unmaintained in any big projects. You would have to keep the implementation of your handler in sync with the implementation of all voters of the projects.

@eltharin
Copy link
Contributor
eltharin commented Aug 6, 2024

have to forget interceptExceptionNotFullFledged I think it's better to integrate condition in NotFullFledgedHandler.
if user can't determine what he have to do in a custom handler he can keep the default config and keep the actual behaviour

@eltharin
Copy link
Contributor
eltharin commented Aug 7, 2024

new Version,

all informations are passed to handler token, exception, ...

I create a handler to cover actual behaviour,
I update the handler I create to cover the Is_FULLY_AUTHENTICATED case
For other cases developers can make their own logic in their own handler (or why not propose a new PR)

so I don't see where is a prblem.

@eltharin
Copy link
Contributor
eltharin commented Aug 8, 2024

@stof :
Your PR replaces useless login flows before reporting an error anyway with reporting errors immediately for cases that would not report errors after going through a login flow, which is a lot worse for DX
My PR does'nt replace anything, Original behaviour is kept, my PR propose to choose to have another behaviour,
and I propose a new behaviour for simple case where just IS_AUTHENTICATED_FULLY is in attribute but in other cases developer can make his own behaviour to have exactly what he want.
One more time with no action from developer, original behaviour stay in place and nothing change in the projet, it's just an add-on for who wants.

@eltharin
Copy link
Contributor
eltharin commented Aug 8, 2024

I just want to understand where a problem can come,
when a developer would be incapable of making his own handler

@nicolas-grekas
Copy link
Member

What I understand here: voters can decide to reject based on the authentication level of the token, and we have no clue when this happens. This reminds me #46493, which tries to give back reasons why voters rejected. The reason is currently thought as a simple string for humans. But we could also make voters able to return a codified reasons that we could then leverage to achieve this file-grained auth UX?

@eltharin
Copy link
Contributor

I also continued the work of #46493 in #58107.
Complexity of many projects is the reason I allow developers in my PR to make a custom Handle to have the exact behaviour expected,
They can check voters results, exceptions, ... for have what they wants.
I just add a "simple" handler for those who don't want redirect to login if "remember me" login is in place but I know it's not THE solution for all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants
0