mirror of
https://github.com/juanfont/headscale.git
synced 2026-11-04 20:49:24 +09:00
types: drop a stored OIDC email the login no longer vouches for
This commit is contained in:
1 parent
17c4778b30
commit
8014416648
4 files changed
+62
-4
No files matched your search
+5
-4
@@ -127,12 +127,13 @@ are configured, a user needs to pass all of them.
|
||||
|
||||
### Control email verification
|
||||
|
||||
Headscale uses the `email` claim from the identity provider to synchronize the email address to its user profile. By
|
||||
default, a user's email address is only synchronized when the identity provider reports the email address as verified
|
||||
via the `email_verified: true` claim.
|
||||
Headscale synchronizes the `email` claim from the identity provider to its user profile on each OIDC login. By default,
|
||||
the profile uses the email only when the latest login reports `email_verified: true`. An absent, unverified, or
|
||||
malformed email clears the previously stored address.
|
||||
|
||||
Unverified emails may be allowed in case an identity provider does not send the `email_verified` claim or email
|
||||
verification is not required. In that case, a user's email address is always synchronized to the user profile.
|
||||
verification is not required. With `email_verified_required: false`, the latest login's email is stored without
|
||||
requiring verification. An absent or malformed email still clears the previously stored address.
|
||||
|
||||
```yaml hl_lines="5"
|
||||
oidc:
|
||||
|
||||
@@ -712,3 +712,30 @@ func TestOIDCRefusesAlteredSubject(t *testing.T) {
|
||||
require.Len(t, users, 1)
|
||||
assert.Equal(t, *before, users[0], "a refused subject must not update an existing row")
|
||||
}
|
||||
|
||||
func TestOIDCLoginClearsUnverifiedEmail(t *testing.T) {
|
||||
app := createTestApp(t)
|
||||
provider := &AuthProviderOIDC{
|
||||
h: app,
|
||||
cfg: &types.OIDCConfig{
|
||||
EmailVerifiedRequired: true,
|
||||
},
|
||||
}
|
||||
claims := &types.OIDCClaims{
|
||||
Iss: "https://idp.example.com", Sub: "alice", Username: "alice",
|
||||
Email: "alice@example.com", EmailVerified: true,
|
||||
}
|
||||
first, _, err := provider.createOrUpdateUserFromClaim(claims)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "alice@example.com", first.Email)
|
||||
|
||||
claims.EmailVerified = false
|
||||
second, _, err := provider.createOrUpdateUserFromClaim(claims)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, first.ID, second.ID)
|
||||
assert.Empty(t, second.Email)
|
||||
|
||||
stored, err := app.state.GetUserByID(types.UserID(first.ID))
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, stored.Email)
|
||||
}
|
||||
@@ -440,6 +440,8 @@ func (u *User) FromClaim(claims *OIDCClaims, emailVerifiedRequired bool) {
|
||||
log.Debug().Caller().Err(err).Msgf("username %s is not valid", claims.Username)
|
||||
}
|
||||
|
||||
u.Email = ""
|
||||
|
||||
if claims.EmailVerified || !FlexibleBoolean(emailVerifiedRequired) {
|
||||
_, err = mail.ParseAddress(claims.Email)
|
||||
if err == nil {
|
||||
|
||||
@@ -591,3 +591,31 @@ func TestOIDCClaimsIdentifierKeepsSubject(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromClaimReplacesEmail(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
email string
|
||||
verified FlexibleBoolean
|
||||
required bool
|
||||
want string
|
||||
}{
|
||||
{"verified", "current@example.com", true, true, "current@example.com"},
|
||||
{"unverified", "current@example.com", false, true, ""},
|
||||
{"absent", "", false, true, ""},
|
||||
{"malformed", "not-an-address", true, true, ""},
|
||||
{"verification optional", "current@example.com", false, false, "current@example.com"},
|
||||
{"optional but absent", "", false, false, ""},
|
||||
{"optional but malformed", "not-an-address", false, false, ""},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
user := User{Name: "alice", Email: "previous@example.com"}
|
||||
user.FromClaim(&OIDCClaims{
|
||||
Iss: "https://idp.example.com", Sub: "alice",
|
||||
Email: tt.email, EmailVerified: tt.verified,
|
||||
}, tt.required)
|
||||
assert.Equal(t, tt.want, user.Email)
|
||||
assert.Equal(t, "alice", user.Name)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user