types: use Username() in User.Proto() when Name is empty
Some checks failed
Build / build-nix (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=amd64 GOOS=linux) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=darwin) (push) Has been cancelled
Build / build-cross (GOARCH=arm64 GOOS=linux) (push) Has been cancelled
Check Generated Files / check-generated (push) Has been cancelled
Deploy docs / deploy (push) Has been cancelled
NixOS Module Tests / nix-module-check (push) Has been cancelled
Tests / test (push) Has been cancelled

User.Proto() was returning u.Name directly, which is empty for OIDC
users who have their identifier in the Email field instead. This caused
"headscale nodes list" to show empty user names for OIDC-authenticated
nodes.

Only fall back to Username() when Name is empty, which provides a
display-friendly identifier (Email > ProviderIdentifier > ID). This
ensures OIDC users display their email while CLI users retain their
original Name.

Fixes #2972
This commit is contained in:
Kristoffer Dalby 2026-01-14 08:48:21 +00:00
parent bb30208f97
commit 3689f05407

View file

@ -174,9 +174,17 @@ func (u UserView) TailscaleUserProfile() tailcfg.UserProfile {
}
func (u *User) Proto() *v1.User {
// Use Name if set, otherwise fall back to Username() which provides
// a display-friendly identifier (Email > ProviderIdentifier > ID).
// This ensures OIDC users (who typically have empty Name) display
// their email, while CLI users retain their original Name.
name := u.Name
if name == "" {
name = u.Username()
}
return &v1.User{
Id: uint64(u.ID),
Name: u.Name,
Name: name,
CreatedAt: timestamppb.New(u.CreatedAt),
DisplayName: u.DisplayName,
Email: u.Email,