add iss to identifier, only set email if verified

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby 2024-10-17 05:58:44 -06:00
parent f1d14f01fc
commit 0fc245d910
No known key found for this signature in database
2 changed files with 28 additions and 4 deletions

View file

@ -474,6 +474,8 @@ func NewHeadscaleDatabase(
Rollback: func(db *gorm.DB) error { return nil }, Rollback: func(db *gorm.DB) error { return nil },
}, },
{ {
// Pick up new user fields used for OIDC and to
// populate the user with more interesting information.
ID: "202407191627", ID: "202407191627",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
err := tx.AutoMigrate(&types.User{}) err := tx.AutoMigrate(&types.User{})
@ -485,6 +487,21 @@ func NewHeadscaleDatabase(
}, },
Rollback: func(db *gorm.DB) error { return nil }, Rollback: func(db *gorm.DB) error { return nil },
}, },
{
// The unique constraint of Name has been dropped
// in favour of a unique together of name and
// provider identity.
ID: "202408181235",
Migrate: func(tx *gorm.DB) error {
err := tx.AutoMigrate(&types.User{})
if err != nil {
return err
}
return nil
},
Rollback: func(db *gorm.DB) error { return nil },
},
}, },
) )

View file

@ -20,10 +20,14 @@ type UserID uint64
// that contain our machines. // that contain our machines.
type User struct { type User struct {
gorm.Model gorm.Model
// The index `idx_name_provider_identifier` is to enforce uniqueness
// between Name and ProviderIdentifier. This ensures that
// you can have multiple usersnames of the same name in OIDC,
// but not if you only run with CLI users.
// Username for the user, is used if email is empty // Username for the user, is used if email is empty
// Should not be used, please use Username(). // Should not be used, please use Username().
Name string `gorm:"unique"` Name string `gorm:"index,uniqueIndex:idx_name_provider_identifier"`
// Typically the full name of the user // Typically the full name of the user
DisplayName string DisplayName string
@ -35,7 +39,7 @@ type User struct {
// Unique identifier of the user from OIDC, // Unique identifier of the user from OIDC,
// comes from `sub` claim in the OIDC token // comes from `sub` claim in the OIDC token
// and is used to lookup the user. // and is used to lookup the user.
ProviderIdentifier string `gorm:"index"` ProviderIdentifier string `gorm:"index,uniqueIndex:idx_name_provider_identifier"`
// Provider is the origin of the user account, // Provider is the origin of the user account,
// same as RegistrationMethod, without authkey. // same as RegistrationMethod, without authkey.
@ -123,6 +127,7 @@ func (u *User) Proto() *v1.User {
type OIDCClaims struct { type OIDCClaims struct {
// Sub is the user's unique identifier at the provider. // Sub is the user's unique identifier at the provider.
Sub string `json:"sub"` Sub string `json:"sub"`
Iss string `json:"iss"`
// Name is the user's full name. // Name is the user's full name.
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -136,9 +141,11 @@ type OIDCClaims struct {
// FromClaim overrides a User from OIDC claims. // FromClaim overrides a User from OIDC claims.
// All fields will be updated, except for the ID. // All fields will be updated, except for the ID.
func (u *User) FromClaim(claims *OIDCClaims) { func (u *User) FromClaim(claims *OIDCClaims) {
u.ProviderIdentifier = claims.Sub u.ProviderIdentifier = claims.Iss + "/" + claims.Sub
u.DisplayName = claims.Name u.DisplayName = claims.Name
if claims.EmailVerified {
u.Email = claims.Email u.Email = claims.Email
}
u.Name = claims.Username u.Name = claims.Username
u.ProfilePicURL = claims.ProfilePictureURL u.ProfilePicURL = claims.ProfilePictureURL
u.Provider = util.RegisterMethodOIDC u.Provider = util.RegisterMethodOIDC