chore: add --force-reset-all flag to oidc link repair cli (#26534)

Useful when the issuer is unchanged, but oidc subject claims have
changed.
This commit is contained in:
Steven Masley
2026-06-23 11:37:33 -05:00
committed by GitHub
parent bdf0e417b1
commit 854d280834
7 changed files with 257 additions and 12 deletions
+6
View File
@@ -70,6 +70,12 @@ func ResetMismatchedOIDCLinks(ctx context.Context, db database.Store, expectedIs
return count, nil
}
// UnmatchableIssuer is a synthetic issuer value that no real OIDC linked_id
// will ever start with. Passing it to AnalyzeOIDCLinks or
// ResetMismatchedOIDCLinks causes every link to be treated as "mismatched",
// which effectively resets all of them.
const UnmatchableIssuer = "00000000-0000-0000-0000-000000000000"
// ResolveIssuer uses OIDC discovery to fetch the canonical issuer string
// from the provider's .well-known/openid-configuration endpoint.
// This does not require OIDC client credentials.
+82
View File
@@ -247,6 +247,88 @@ func TestResetMismatchedOIDCLinks(t *testing.T) {
})
}
func TestResetMismatchedOIDCLinksWithUnmatchableIssuer(t *testing.T) {
t.Parallel()
t.Run("ResetsAll", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
db, _ := dbtestutil.NewDB(t)
// Correctly linked user.
correctUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC})
dbgen.UserLink(t, db, database.UserLink{
UserID: correctUser.ID,
LoginType: database.LoginTypeOIDC,
LinkedID: "https://accounts.google.com||sub-correct",
})
// Mismatched user.
mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC})
dbgen.UserLink(t, db, database.UserLink{
UserID: mismatchedUser.ID,
LoginType: database.LoginTypeOIDC,
LinkedID: "https://old-issuer.example.com||sub-mismatched",
})
// Unlinked user (empty linked_id).
unlinkedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC})
dbgen.UserLink(t, db, database.UserLink{
UserID: unlinkedUser.ID,
LoginType: database.LoginTypeOIDC,
LinkedID: "",
})
count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, authlink.UnmatchableIssuer)
require.NoError(t, err)
require.EqualValues(t, 2, count, "should reset correct + mismatched, not unlinked")
// Verify the correct link was reset.
link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
UserID: correctUser.ID,
LoginType: database.LoginTypeOIDC,
})
require.NoError(t, err)
require.Equal(t, "", link.LinkedID)
// Verify the mismatched link was reset.
link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
UserID: mismatchedUser.ID,
LoginType: database.LoginTypeOIDC,
})
require.NoError(t, err)
require.Equal(t, "", link.LinkedID)
// Verify the unlinked user is still unlinked.
link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
UserID: unlinkedUser.ID,
LoginType: database.LoginTypeOIDC,
})
require.NoError(t, err)
require.Equal(t, "", link.LinkedID)
})
t.Run("NothingToReset", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitShort)
db, _ := dbtestutil.NewDB(t)
// Only an unlinked user.
user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC})
dbgen.UserLink(t, db, database.UserLink{
UserID: user.ID,
LoginType: database.LoginTypeOIDC,
LinkedID: "",
})
count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, authlink.UnmatchableIssuer)
require.NoError(t, err)
require.EqualValues(t, 0, count)
})
}
func TestResolveIssuer(t *testing.T) {
t.Parallel()