2023-05-21 19:37:59 +03:00
|
|
|
package types
|
|
|
|
|
|
|
|
|
|
import (
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
"cmp"
|
2024-11-18 17:33:46 +01:00
|
|
|
"database/sql"
|
2024-12-16 11:26:32 +01:00
|
|
|
"encoding/json"
|
2026-02-06 21:45:32 +01:00
|
|
|
"errors"
|
2024-12-16 11:26:32 +01:00
|
|
|
"fmt"
|
2024-11-24 00:13:27 +01:00
|
|
|
"net/mail"
|
2025-04-23 13:21:51 +02:00
|
|
|
"net/url"
|
2023-05-21 19:37:59 +03:00
|
|
|
"strconv"
|
2025-03-10 16:20:29 +01:00
|
|
|
"strings"
|
2023-05-21 19:37:59 +03:00
|
|
|
|
|
|
|
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
|
|
|
|
"github.com/juanfont/headscale/hscontrol/util"
|
types: add MarshalZerologObject to domain types
Implement zerolog.LogObjectMarshaler interface on domain types
for structured logging:
- Node: logs node.id, node.name, machine.key (short), node.key (short),
node.is_tagged, node.expired, node.online, node.tags, user.name
- User: logs user.id, user.name, user.display, user.provider
- PreAuthKey: logs pak.id, pak.prefix (masked), pak.reusable,
pak.ephemeral, pak.used, pak.is_tagged, pak.tags
- APIKey: logs api_key.id, api_key.prefix (masked), api_key.expiration
Security: PreAuthKey and APIKey only log masked prefixes, never full
keys or hashes. Uses zf.* constants for consistent field naming.
2026-01-28 13:37:48 +00:00
|
|
|
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
|
|
|
|
|
"github.com/rs/zerolog"
|
2025-01-16 18:04:54 +01:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-05-21 19:37:59 +03:00
|
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
|
)
|
|
|
|
|
|
2026-02-06 21:45:32 +01:00
|
|
|
// ErrCannotParseBoolean is returned when a value cannot be parsed as boolean.
|
|
|
|
|
var ErrCannotParseBoolean = errors.New("cannot parse value as boolean")
|
|
|
|
|
|
2026-05-04 14:26:53 +01:00
|
|
|
// ErrCannotParseStringSlice is returned when a value cannot be parsed as string or []string.
|
|
|
|
|
var ErrCannotParseStringSlice = errors.New("cannot parse value as string or []string")
|
|
|
|
|
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
type UserID uint64
|
|
|
|
|
|
2025-03-10 16:20:29 +01:00
|
|
|
type Users []User
|
|
|
|
|
|
2025-12-02 12:01:25 +01:00
|
|
|
const (
|
|
|
|
|
// TaggedDevicesUserID is the special user ID for tagged devices.
|
|
|
|
|
// This ID is used when rendering tagged nodes in the Tailscale protocol.
|
|
|
|
|
TaggedDevicesUserID = 2147455555
|
|
|
|
|
)
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// TaggedDevices is a special user used in [tailcfg.MapResponse] for tagged nodes.
|
2025-12-02 12:01:25 +01:00
|
|
|
// Tagged nodes don't belong to a real user - the tag is their identity.
|
|
|
|
|
// This special user ID is used when rendering tagged nodes in the Tailscale protocol.
|
|
|
|
|
var TaggedDevices = User{
|
|
|
|
|
Model: gorm.Model{ID: TaggedDevicesUserID},
|
|
|
|
|
Name: "tagged-devices",
|
|
|
|
|
DisplayName: "Tagged Devices",
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-10 16:20:29 +01:00
|
|
|
func (u Users) String() string {
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
sb.WriteString("[ ")
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-03-10 16:20:29 +01:00
|
|
|
for _, user := range u {
|
|
|
|
|
fmt.Fprintf(&sb, "%d: %s, ", user.ID, user.Name)
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-03-10 16:20:29 +01:00
|
|
|
sb.WriteString(" ]")
|
|
|
|
|
|
|
|
|
|
return sb.String()
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-21 19:37:59 +03:00
|
|
|
// User is the way Headscale implements the concept of users in Tailscale
|
|
|
|
|
//
|
|
|
|
|
// At the end of the day, users in Tailscale are some kind of 'bubbles' or users
|
|
|
|
|
// that contain our machines.
|
|
|
|
|
type User struct {
|
2026-02-06 21:45:32 +01:00
|
|
|
gorm.Model //nolint:embeddedstructfieldcheck
|
|
|
|
|
|
2024-10-17 05:58:44 -06:00
|
|
|
// The index `idx_name_provider_identifier` is to enforce uniqueness
|
|
|
|
|
// between Name and ProviderIdentifier. This ensures that
|
2024-11-17 19:49:51 -07:00
|
|
|
// you can have multiple users with the same name in OIDC,
|
2024-10-17 05:58:44 -06:00
|
|
|
// but not if you only run with CLI users.
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
|
2025-01-30 10:35:49 +00:00
|
|
|
// Name (username) for the user, is used if email is empty
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// Should not be used, please use [User.Username].
|
|
|
|
|
// It is unique if [User.ProviderIdentifier] is not set.
|
2024-11-22 17:45:46 +01:00
|
|
|
Name string
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
|
|
|
|
|
// Typically the full name of the user
|
|
|
|
|
DisplayName string
|
|
|
|
|
|
|
|
|
|
// Email of the user
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// Should not be used, please use [User.Username].
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
Email string
|
|
|
|
|
|
2025-01-30 10:35:49 +00:00
|
|
|
// ProviderIdentifier is a unique or not set identifier of the
|
|
|
|
|
// user from OIDC. It is the combination of `iss`
|
|
|
|
|
// and `sub` claim in the OIDC token.
|
|
|
|
|
// It is unique if set.
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// It is unique together with [User.Name].
|
2024-11-22 17:45:46 +01:00
|
|
|
ProviderIdentifier sql.NullString
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
|
|
|
|
|
// Provider is the origin of the user account,
|
|
|
|
|
// same as RegistrationMethod, without authkey.
|
|
|
|
|
Provider string
|
|
|
|
|
|
|
|
|
|
ProfilePicURL string
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-26 07:22:55 -08:00
|
|
|
func (u *User) StringID() string {
|
|
|
|
|
if u == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-02-26 07:22:55 -08:00
|
|
|
return strconv.FormatUint(uint64(u.ID), 10)
|
|
|
|
|
}
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// TypedID returns a pointer to the user's ID as a [UserID] type.
|
2025-12-02 12:01:25 +01:00
|
|
|
// This is a convenience method to avoid ugly casting like ptr.To(types.UserID(user.ID)).
|
|
|
|
|
func (u *User) TypedID() *UserID {
|
|
|
|
|
uid := UserID(u.ID)
|
|
|
|
|
return &uid
|
|
|
|
|
}
|
|
|
|
|
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
// Username is the main way to get the username of a user,
|
|
|
|
|
// it will return the email if it exists, the name if it exists,
|
|
|
|
|
// the OIDCIdentifier if it exists, and the ID if nothing else exists.
|
|
|
|
|
// Email and OIDCIdentifier will be set when the user has headscale
|
|
|
|
|
// enabled with OIDC, which means that there is a domain involved which
|
|
|
|
|
// should be used throughout headscale, in information returned to the
|
|
|
|
|
// user and the Policy engine.
|
|
|
|
|
func (u *User) Username() string {
|
2025-02-26 07:22:55 -08:00
|
|
|
return cmp.Or(
|
|
|
|
|
u.Email,
|
|
|
|
|
u.Name,
|
|
|
|
|
u.ProviderIdentifier.String,
|
2025-03-10 16:20:29 +01:00
|
|
|
u.StringID(),
|
|
|
|
|
)
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// Display returns the [User.DisplayName] if it exists, otherwise
|
|
|
|
|
// it will return the [User.Username].
|
2025-03-10 16:20:29 +01:00
|
|
|
func (u *User) Display() string {
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
return cmp.Or(u.DisplayName, u.Username())
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
|
|
|
|
|
2024-09-21 12:05:36 +02:00
|
|
|
// TODO(kradalby): See if we can fill in Gravatar here.
|
2024-07-19 09:03:18 +02:00
|
|
|
func (u *User) profilePicURL() string {
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
return u.ProfilePicURL
|
2024-07-19 09:03:18 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:17:43 +02:00
|
|
|
func (u *User) TailscaleUser() tailcfg.User {
|
|
|
|
|
return tailcfg.User{
|
2026-02-06 21:45:32 +01:00
|
|
|
ID: tailcfg.UserID(u.ID), //nolint:gosec // UserID is bounded
|
2025-03-10 16:20:29 +01:00
|
|
|
DisplayName: u.Display(),
|
2024-07-19 09:03:18 +02:00
|
|
|
ProfilePicURL: u.profilePicURL(),
|
|
|
|
|
Created: u.CreatedAt,
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
2025-10-16 12:17:43 +02:00
|
|
|
}
|
2023-05-21 19:37:59 +03:00
|
|
|
|
2025-10-16 12:17:43 +02:00
|
|
|
func (u UserView) TailscaleUser() tailcfg.User {
|
|
|
|
|
return u.ж.TailscaleUser()
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-02 12:01:25 +01:00
|
|
|
// ID returns the user's ID.
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// This is a custom accessor because [gorm.Model].ID is embedded
|
2025-12-02 12:01:25 +01:00
|
|
|
// and the viewer generator doesn't always produce it.
|
|
|
|
|
func (u UserView) ID() uint {
|
|
|
|
|
return u.ж.ID
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:17:43 +02:00
|
|
|
func (u *User) TailscaleLogin() tailcfg.Login {
|
|
|
|
|
return tailcfg.Login{
|
2026-02-06 21:45:32 +01:00
|
|
|
ID: tailcfg.LoginID(u.ID), //nolint:gosec // safe conversion for user ID
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
Provider: u.Provider,
|
|
|
|
|
LoginName: u.Username(),
|
2025-03-10 16:20:29 +01:00
|
|
|
DisplayName: u.Display(),
|
2024-07-19 09:03:18 +02:00
|
|
|
ProfilePicURL: u.profilePicURL(),
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
2025-10-16 12:17:43 +02:00
|
|
|
}
|
2023-05-21 19:37:59 +03:00
|
|
|
|
2025-10-16 12:17:43 +02:00
|
|
|
func (u UserView) TailscaleLogin() tailcfg.Login {
|
|
|
|
|
return u.ж.TailscaleLogin()
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
|
|
|
|
|
2024-07-19 09:03:18 +02:00
|
|
|
func (u *User) TailscaleUserProfile() tailcfg.UserProfile {
|
|
|
|
|
return tailcfg.UserProfile{
|
2026-02-06 21:45:32 +01:00
|
|
|
ID: tailcfg.UserID(u.ID), //nolint:gosec // UserID is bounded
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
LoginName: u.Username(),
|
2025-03-10 16:20:29 +01:00
|
|
|
DisplayName: u.Display(),
|
2024-07-19 09:03:18 +02:00
|
|
|
ProfilePicURL: u.profilePicURL(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-16 12:17:43 +02:00
|
|
|
func (u UserView) TailscaleUserProfile() tailcfg.UserProfile {
|
|
|
|
|
return u.ж.TailscaleUserProfile()
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-18 08:20:03 -06:00
|
|
|
func (u *User) Proto() *v1.User {
|
2026-01-14 08:48:21 +00:00
|
|
|
// 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()
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2023-05-21 19:37:59 +03:00
|
|
|
return &v1.User{
|
2024-12-10 16:23:55 +01:00
|
|
|
Id: uint64(u.ID),
|
2026-01-14 08:48:21 +00:00
|
|
|
Name: name,
|
2024-10-18 08:20:03 -06:00
|
|
|
CreatedAt: timestamppb.New(u.CreatedAt),
|
|
|
|
|
DisplayName: u.DisplayName,
|
|
|
|
|
Email: u.Email,
|
2024-11-18 17:33:46 +01:00
|
|
|
ProviderId: u.ProviderIdentifier.String,
|
2024-10-18 08:20:03 -06:00
|
|
|
Provider: u.Provider,
|
|
|
|
|
ProfilePicUrl: u.ProfilePicURL,
|
2023-05-21 19:37:59 +03:00
|
|
|
}
|
|
|
|
|
}
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// MarshalZerologObject implements [zerolog.LogObjectMarshaler] for safe logging.
|
types: add MarshalZerologObject to domain types
Implement zerolog.LogObjectMarshaler interface on domain types
for structured logging:
- Node: logs node.id, node.name, machine.key (short), node.key (short),
node.is_tagged, node.expired, node.online, node.tags, user.name
- User: logs user.id, user.name, user.display, user.provider
- PreAuthKey: logs pak.id, pak.prefix (masked), pak.reusable,
pak.ephemeral, pak.used, pak.is_tagged, pak.tags
- APIKey: logs api_key.id, api_key.prefix (masked), api_key.expiration
Security: PreAuthKey and APIKey only log masked prefixes, never full
keys or hashes. Uses zf.* constants for consistent field naming.
2026-01-28 13:37:48 +00:00
|
|
|
func (u *User) MarshalZerologObject(e *zerolog.Event) {
|
|
|
|
|
if u == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.Uint(zf.UserID, u.ID)
|
|
|
|
|
e.Str(zf.UserName, u.Username())
|
|
|
|
|
e.Str(zf.UserDisplay, u.Display())
|
|
|
|
|
|
|
|
|
|
if u.Provider != "" {
|
|
|
|
|
e.Str(zf.UserProvider, u.Provider)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// MarshalZerologObject implements [zerolog.LogObjectMarshaler] for [UserView].
|
types: add MarshalZerologObject to domain types
Implement zerolog.LogObjectMarshaler interface on domain types
for structured logging:
- Node: logs node.id, node.name, machine.key (short), node.key (short),
node.is_tagged, node.expired, node.online, node.tags, user.name
- User: logs user.id, user.name, user.display, user.provider
- PreAuthKey: logs pak.id, pak.prefix (masked), pak.reusable,
pak.ephemeral, pak.used, pak.is_tagged, pak.tags
- APIKey: logs api_key.id, api_key.prefix (masked), api_key.expiration
Security: PreAuthKey and APIKey only log masked prefixes, never full
keys or hashes. Uses zf.* constants for consistent field naming.
2026-01-28 13:37:48 +00:00
|
|
|
func (u UserView) MarshalZerologObject(e *zerolog.Event) {
|
|
|
|
|
if !u.Valid() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u.ж.MarshalZerologObject(e)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 14:26:53 +01:00
|
|
|
// FlexibleStringSlice handles OIDC providers (e.g. JumpCloud) that return the
|
|
|
|
|
// groups claim as a plain string when the user belongs to a single group,
|
|
|
|
|
// instead of a single-element array.
|
|
|
|
|
type FlexibleStringSlice []string
|
|
|
|
|
|
|
|
|
|
func (f *FlexibleStringSlice) UnmarshalJSON(data []byte) error {
|
|
|
|
|
var arr []string
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
|
2026-05-04 14:26:53 +01:00
|
|
|
err := json.Unmarshal(data, &arr)
|
|
|
|
|
if err == nil {
|
|
|
|
|
*f = arr
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var single string
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
|
2026-05-04 14:26:53 +01:00
|
|
|
err = json.Unmarshal(data, &single)
|
|
|
|
|
if err == nil {
|
|
|
|
|
*f = []string{single}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Errorf("%w: %s", ErrCannotParseStringSlice, string(data))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-06 21:45:32 +01:00
|
|
|
// FlexibleBoolean handles JumpCloud's JSON where email_verified is returned as a
|
2024-12-16 11:26:32 +01:00
|
|
|
// string "true" or "false" instead of a boolean.
|
|
|
|
|
// This maps bool to a specific type with a custom unmarshaler to
|
|
|
|
|
// ensure we can decode it from a string.
|
|
|
|
|
type FlexibleBoolean bool
|
|
|
|
|
|
|
|
|
|
func (bit *FlexibleBoolean) UnmarshalJSON(data []byte) error {
|
2025-04-30 12:54:13 +03:00
|
|
|
var val any
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2024-12-16 11:26:32 +01:00
|
|
|
err := json.Unmarshal(data, &val)
|
|
|
|
|
if err != nil {
|
2026-02-05 16:29:54 +00:00
|
|
|
return fmt.Errorf("unmarshalling data: %w", err)
|
2024-12-16 11:26:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch v := val.(type) {
|
|
|
|
|
case bool:
|
|
|
|
|
*bit = FlexibleBoolean(v)
|
|
|
|
|
case string:
|
|
|
|
|
pv, err := strconv.ParseBool(v)
|
|
|
|
|
if err != nil {
|
2026-02-05 16:29:54 +00:00
|
|
|
return fmt.Errorf("parsing %s as boolean: %w", v, err)
|
2024-12-16 11:26:32 +01:00
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2024-12-16 11:26:32 +01:00
|
|
|
*bit = FlexibleBoolean(pv)
|
|
|
|
|
|
|
|
|
|
default:
|
2026-02-06 21:45:32 +01:00
|
|
|
return fmt.Errorf("%w: %v", ErrCannotParseBoolean, v)
|
2024-12-16 11:26:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
type OIDCClaims struct {
|
|
|
|
|
// Sub is the user's unique identifier at the provider.
|
|
|
|
|
Sub string `json:"sub"`
|
2024-10-17 05:58:44 -06:00
|
|
|
Iss string `json:"iss"`
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
|
|
|
|
|
// Name is the user's full name.
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
Name string `json:"name,omitempty"`
|
2026-05-04 14:26:53 +01:00
|
|
|
Groups FlexibleStringSlice `json:"groups,omitempty"`
|
|
|
|
|
Email string `json:"email,omitempty"`
|
|
|
|
|
EmailVerified FlexibleBoolean `json:"email_verified,omitempty"`
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
ProfilePictureURL string `json:"picture,omitempty"`
|
|
|
|
|
Username string `json:"preferred_username,omitempty"`
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
}
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// Identifier returns a unique identifier string combining the [OIDCClaims.Iss] and [OIDCClaims.Sub] claims.
|
|
|
|
|
// The format depends on whether [OIDCClaims.Iss] is a URL or not:
|
2025-05-14 17:45:14 +03:00
|
|
|
// - For URLs: Joins the URL and sub path (e.g., "https://example.com/sub")
|
|
|
|
|
// - For non-URLs: Joins with a slash (e.g., "oidc/sub")
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// - For empty [OIDCClaims.Iss]: Returns just "sub"
|
|
|
|
|
// - For empty [OIDCClaims.Sub]: Returns just the Issuer
|
2025-05-14 17:45:14 +03:00
|
|
|
// - For both empty: Returns empty string
|
|
|
|
|
//
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// The result is cleaned using [CleanIdentifier] to ensure consistent formatting.
|
2024-10-18 06:59:27 -06:00
|
|
|
func (c *OIDCClaims) Identifier() string {
|
2025-05-14 17:45:14 +03:00
|
|
|
// Handle empty components special cases
|
|
|
|
|
if c.Iss == "" && c.Sub == "" {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
if c.Iss == "" {
|
|
|
|
|
return CleanIdentifier(c.Sub)
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
if c.Sub == "" {
|
|
|
|
|
return CleanIdentifier(c.Iss)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We'll use the raw values and let CleanIdentifier handle all the whitespace
|
|
|
|
|
issuer := c.Iss
|
|
|
|
|
subject := c.Sub
|
|
|
|
|
|
|
|
|
|
var result string
|
2026-04-08 12:26:06 +00:00
|
|
|
// Always use simple string concatenation with a slash separator.
|
|
|
|
|
// url.JoinPath resolves path-traversal segments like ".." and ".",
|
|
|
|
|
// which can silently drop the subject and cause identifier collisions
|
|
|
|
|
// between distinct OIDC users (e.g., Sub=".." produces the same
|
|
|
|
|
// identifier as an empty Sub).
|
|
|
|
|
issuer = strings.TrimSuffix(issuer, "/")
|
|
|
|
|
subject = strings.TrimPrefix(subject, "/")
|
|
|
|
|
result = issuer + "/" + subject
|
2025-05-14 17:45:14 +03:00
|
|
|
|
|
|
|
|
// Clean the result and return it
|
|
|
|
|
return CleanIdentifier(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CleanIdentifier cleans a potentially malformed identifier by removing double slashes
|
|
|
|
|
// while preserving protocol specifications like http://. This function will:
|
|
|
|
|
// - Trim all whitespace from the beginning and end of the identifier
|
|
|
|
|
// - Remove whitespace within path segments
|
|
|
|
|
// - Preserve the scheme (http://, https://, etc.) for URLs
|
|
|
|
|
// - Remove any duplicate slashes in the path
|
|
|
|
|
// - Remove empty path segments
|
|
|
|
|
// - For non-URL identifiers, it joins non-empty segments with a single slash
|
|
|
|
|
// - Returns empty string for identifiers with only slashes
|
2025-07-10 23:38:55 +02:00
|
|
|
// - Normalize URL schemes to lowercase.
|
2025-05-14 17:45:14 +03:00
|
|
|
func CleanIdentifier(identifier string) string {
|
|
|
|
|
if identifier == "" {
|
|
|
|
|
return identifier
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trim leading/trailing whitespace
|
|
|
|
|
identifier = strings.TrimSpace(identifier)
|
|
|
|
|
|
|
|
|
|
// Handle URLs with schemes
|
|
|
|
|
u, err := url.Parse(identifier)
|
|
|
|
|
if err == nil && u.Scheme != "" {
|
|
|
|
|
// Clean path by removing empty segments and whitespace within segments
|
|
|
|
|
parts := strings.FieldsFunc(u.Path, func(c rune) bool { return c == '/' })
|
|
|
|
|
for i, part := range parts {
|
|
|
|
|
parts[i] = strings.TrimSpace(part)
|
|
|
|
|
}
|
|
|
|
|
// Remove empty parts after trimming
|
|
|
|
|
cleanParts := make([]string, 0, len(parts))
|
|
|
|
|
for _, part := range parts {
|
|
|
|
|
if part != "" {
|
|
|
|
|
cleanParts = append(cleanParts, part)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-10 23:38:55 +02:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
if len(cleanParts) == 0 {
|
|
|
|
|
u.Path = ""
|
|
|
|
|
} else {
|
|
|
|
|
u.Path = "/" + strings.Join(cleanParts, "/")
|
|
|
|
|
}
|
|
|
|
|
// Ensure scheme is lowercase
|
|
|
|
|
u.Scheme = strings.ToLower(u.Scheme)
|
2025-07-10 23:38:55 +02:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
return u.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle non-URL identifiers
|
|
|
|
|
parts := strings.FieldsFunc(identifier, func(c rune) bool { return c == '/' })
|
|
|
|
|
// Clean whitespace from each part
|
|
|
|
|
cleanParts := make([]string, 0, len(parts))
|
|
|
|
|
for _, part := range parts {
|
|
|
|
|
trimmed := strings.TrimSpace(part)
|
|
|
|
|
if trimmed != "" {
|
|
|
|
|
cleanParts = append(cleanParts, trimmed)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
if len(cleanParts) == 0 {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
2025-07-10 23:38:55 +02:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
return strings.Join(cleanParts, "/")
|
2024-10-18 06:59:27 -06:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 12:54:13 +03:00
|
|
|
type OIDCUserInfo struct {
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
Sub string `json:"sub"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
GivenName string `json:"given_name"`
|
|
|
|
|
FamilyName string `json:"family_name"`
|
|
|
|
|
PreferredUsername string `json:"preferred_username"`
|
|
|
|
|
Email string `json:"email"`
|
2026-05-04 14:26:53 +01:00
|
|
|
EmailVerified FlexibleBoolean `json:"email_verified,omitempty"`
|
|
|
|
|
Groups FlexibleStringSlice `json:"groups"`
|
|
|
|
|
Picture string `json:"picture"`
|
2025-04-30 12:54:13 +03:00
|
|
|
}
|
|
|
|
|
|
all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.
Pattern applied across the tree:
In-package [Foo], [Foo.Bar]
Cross-package [pkg.Foo], [pkg.Foo.Bar]
Stdlib [netip.Prefix], [errors.Is], [context.Context]
Tailscale [tailcfg.MapResponse], [tailcfg.Node.CapMap],
[tailcfg.NodeAttrSuggestExitNode]
Skip rules:
- File:line refs left as plain text
- HuJSON wire keys inside backtick raw strings untouched
- ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
symbols, left as plain text
- JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
link tags, decorative dividers — all left as-is
2026-05-18 18:35:53 +00:00
|
|
|
// FromClaim overrides a [User] from OIDC claims.
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
// All fields will be updated, except for the ID.
|
2025-12-18 06:42:32 -05:00
|
|
|
func (u *User) FromClaim(claims *OIDCClaims, emailVerifiedRequired bool) {
|
2025-01-16 18:04:54 +01:00
|
|
|
err := util.ValidateUsername(claims.Username)
|
2024-11-24 00:13:27 +01:00
|
|
|
if err == nil {
|
|
|
|
|
u.Name = claims.Username
|
2025-01-16 18:04:54 +01:00
|
|
|
} else {
|
2026-02-05 13:59:26 +00:00
|
|
|
log.Debug().Caller().Err(err).Msgf("username %s is not valid", claims.Username)
|
2024-11-24 00:13:27 +01:00
|
|
|
}
|
|
|
|
|
|
2025-12-18 06:42:32 -05:00
|
|
|
if claims.EmailVerified || !FlexibleBoolean(emailVerifiedRequired) {
|
2024-11-24 00:13:27 +01:00
|
|
|
_, err = mail.ParseAddress(claims.Email)
|
|
|
|
|
if err == nil {
|
|
|
|
|
u.Email = claims.Email
|
|
|
|
|
}
|
2024-10-17 05:58:44 -06:00
|
|
|
}
|
2024-11-24 00:13:27 +01:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
// Get provider identifier
|
|
|
|
|
identifier := claims.Identifier()
|
|
|
|
|
// Ensure provider identifier always has a leading slash for backward compatibility
|
|
|
|
|
if claims.Iss == "" && !strings.HasPrefix(identifier, "/") {
|
|
|
|
|
identifier = "/" + identifier
|
|
|
|
|
}
|
2026-02-06 21:45:32 +01:00
|
|
|
|
2025-05-14 17:45:14 +03:00
|
|
|
u.ProviderIdentifier = sql.NullString{String: identifier, Valid: true}
|
2024-11-24 00:13:27 +01:00
|
|
|
u.DisplayName = claims.Name
|
Redo OIDC configuration (#2020)
expand user, add claims to user
This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.
This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.
remove usernames in magic dns, normalisation of emails
this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.
In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.
Email are no longer normalised as part of the policy processing.
untagle oidc and regcache, use typed cache
This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.
try to make reauth/register branches clearer in oidc
Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.
This commit tries to split this into what to do if the node
exists, if it needs to register etc.
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
|
|
|
u.ProfilePicURL = claims.ProfilePictureURL
|
|
|
|
|
u.Provider = util.RegisterMethodOIDC
|
|
|
|
|
}
|