04/28/2015
Show the logged-in user in a Twig template
Symfony hands every template the current user through the app.user
global. Guard it, then print the identifier:
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
Hi {{ app.user.userIdentifier }}!
{% endif %}
That runs on Symfony 6, 7 and 8. is_granted() comes from
symfony/security-bundle, so it exists in any application with a firewall.
If you arrived holding the older version of this snippet, the one with
is_granted("ROLE") and app.user.username, it has not worked for some
years. This page carried it until today. Here is exactly how it fails.
Why the old snippet stopped working
{% if is_granted("ROLE") %}
Hi {{ app.user.username }}!
{% endif %}
Two faults, and they fail in opposite ways.
ROLE is not a role. It was written as a placeholder meaning "your role goes here", but copied literally it matches nothing. The condition is always false, the block never renders, and nothing complains. No error, no log line, just a greeting that never appears and no clue why.
app.user.username raises an error. UserInterface::getUsername()
was replaced by getUserIdentifier() in Symfony 5.3 and left the
interface entirely in 6.0. A user class generated by make:user today has
getUserIdentifier() and getEmail() and no username of any kind, so
Twig cannot resolve the attribute and throws. That one surfaces as a 500,
not a blank.
A third thing is not an error but is worth saying. The old version offered this "from the Symfony user or the FOSUserBundle". FOSUserBundle's own README now opens by saying new projects should not use it, and points at Symfony's built-in user provider instead.
Which check do you actually want?
- Signed in during this session
- IS_AUTHENTICATED_FULLY
- Signed in at all, remember-me included
- IS_AUTHENTICATED
- Signed in only by a remember-me cookie
- IS_REMEMBERED
- Holds a specific permission
- ROLE_ADMIN, or whatever you named it
- May act on this particular object
- A Voter, checked as is_granted with the object as second argument
- An admin currently viewing as somebody else
- IS_IMPERSONATOR
is_granted takes a role, a permission, or one of the special attributes below. Reaching for a role when you meant "is anyone signed in" is the commonest way this goes wrong.
Identifier, or a name a person recognizes?
getUserIdentifier() returns whatever your firewall authenticates on. In
most applications built since Symfony 5.3 that is an email address,
because make:user proposes email and almost everyone accepts it.
Which means the snippet at the top of this page, in a default application, greets people with their own email address and prints it on screen next to whoever is standing behind them. Correct code, wrong output.
If you want a name, put one on the user class and ask for that instead:
public function getDisplayName(): string
{
return $this->firstName ?: $this->getUserIdentifier();
}
{% if is_granted('IS_AUTHENTICATED_FULLY') %}
Hi {{ app.user.displayName }}!
{% endif %}
The fallback matters more than it looks. A first name is usually optional, and somebody who never filled one in should get their identifier rather than a greeting with a hole in it.
When nobody is signed in
app.user is null for an anonymous visitor, and reaching for an
attribute on null throws. The guard above already prevents that. Without
a guard, use the null-coalescing operator, which Twig reads as "if this
cannot be resolved, use the other one":
Hi {{ app.user.displayName ?? 'there' }}!
That is the form Symfony's own template documentation uses, and it is safe both on a null user and on a user class that has no such property.
Questions this keeps raising
Why does app.user.username throw an error?
Because on a modern Symfony application there is no such property. UserInterface::getUsername() was replaced by getUserIdentifier() in Symfony 5.3 and removed from the interface in 6.0, and a user class made by make:user never had a username field to begin with. Twig cannot resolve the attribute, so it raises a RuntimeError rather than printing nothing.
What replaced getUsername() in Symfony?
getUserIdentifier() on the user class, and loadUserByIdentifier() on a user provider. The rename was deliberate: getUsername() implied the field was called "username", when in practice it is usually an email address, and can be a UUID or anything else the firewall authenticates on.
How do I show a name instead of an email address?
Add your own accessor to the user class, such as getDisplayName(), falling back to getUserIdentifier() when the name is empty, and print app.user.displayName in the template. getUserIdentifier() returns the login identifier, which in most applications is the email address, so printing it directly puts an email on screen.
Is FOSUserBundle still safe to use?
It is maintained only enough for existing projects to migrate off it. Its README states that new projects should not use it, and points at Symfony's own EntityUserProvider plus SymfonyCasts' VerifyEmail and ResetPassword bundles for the two features people usually kept it for.