Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

Environment: Symfony 7.3.5 (env: prod, debug: false), ApiPlatform v4.2.2
Runtime: FrankenPHP

I think AccessDecisionManager service is not stateless and may create issues when using a runtime like FrankenPHP or Swoole.

If I create a new User wihtout the right ROLE, I try to access a protected route with isGranted. Access is denied.
Then I add the required role to this User, I try again: access is still denied. Because voter decisions have been cached inside AccessDecisionManager service.

The only solution is to restart FrankenPHP worker.

Is there any solution to "clear" AccessDecisionManager caches when updating any User roles?

I see 3 locations where caching may occur:

  • API platform: AccessCheckerProvider
  • is_granted Symfony ExpressionFunction
  • Symfony AccessDecisionManager

AccessDecisionManager only cache supported Voter for an attribute or an object, but does not cache decision itself.

resources:
    RZ\Roadiz\CoreBundle\Entity\User:
        shortName: User
        types:
            - User
        operations:
            ApiPlatform\Metadata\Get:
                method: GET
                uriTemplate: '/me'
                output: App\Api\Model\UserOutput
                provider: App\State\UserProvider
                security: 'is_granted("ROLE_USER")'
image image
You must be logged in to vote

Replies: 3 comments · 3 replies

Comment options

The AccessDecisionManager does not cache decisions. It only caches the supportsAttribute and supportsType methods of voters.
And is_granted does not perform any caching.

Regarding whether the API platform AccessCheckerProvider could be the cause, I have no idea. This should be reported to the API Platform team.

You must be logged in to vote
2 replies
@lon9man
Comment options

@stof does AccessDecisionManager caches data only for the current request or it shares it through different requests also?

I am trying to understand how it works..
saying we have 10 voters, then make 2 calls of isGranted-method saying for 1 entity (example: can-edit entity Foo).
Only 1 voter supports entity Foo.

How logic will call voters for 1 request:

  1. 10 + 1 times (10 times for checking supported votes and caching it + 1 cached voter on 2nd isGranted-call). So each new request always will run voting 11 times for 1st entity (and 1 time for 2nd/3rd/… entity), because caching works only for the current request?
  2. 2 times (2 times, because logic cached needed voter already on the previous request)? So each additional isGranted-call per each next entity (including all new requests) will call only 1 voter forever till force cache clean at some time point?
  3. Some else logic

thanks!

@stof
Comment options

stof Nov 12, 2025
Collaborator

When using frankenphp worker mode, the cache should be shared between requests as the same AccessDecisionManager instance will be reused between requests.

However, which voter can support a given type and a given attribute should not change between requests (the anwser of the vote method of the voter will of course change between isGranted calls, but this is not cached)

Comment options

I think I have another issue which seems to be related.

I use Expression language #[MapEntity(expr: 'repository.find(nodeSourceId)')] for my routes. I used frankenphp runtime, in debug mode.

I need to restart frankenphp, to see my expression changes taken into account.

The common point here is Expression language: API platform uses it for is_granted on API endpoints. Does Expression language store in memory expression execution results?

You must be logged in to vote
1 reply
@stof
Comment options

stof Nov 12, 2025
Collaborator

ExpressionLanguage has a cache for the expression parser, but it has no cache for the evaluation of expressions.

I need to restart frankenphp, to see my expression changes taken into account.

Which kind of expression changes are you talking about ? Editing the code to change the expression being used in the attribute ?
If yes, that's totally expected: the worker mode of Frankenphp uses a long-running worker to handle all requests in the same PHP process (that's the whole point of this mode) and so it will never take any change of the code into account (PHP does not allow changing the code in a running process). In a development setup where you edit the code, you should either use the classic mode of Frankenphp or use a watcher to automatically restart workers when the code change.

Comment options

I might have found the root issue: lexik/LexikJWTAuthenticationBundle#1307

Initial issue was occuring on the same user (username) on API endpoint using JWT. So if JWTUserProvider was caching my User this should lead to role mismatch.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
🙏
Q&A
Labels
None yet
3 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.