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.