MCPcopy Index your code
hub / github.com/coder/coder / findLinkedUser

Function findLinkedUser

coderd/userauth.go:2191–2252  ·  view source on GitHub ↗

findLinkedUser tries to find a user by their unique OAuth-linked ID. If it does not find a match, it falls back to email-based lookup. The email fallback is restricted to first-time account linking and legacy links (empty linked_id) only. If the user found by email already has a link with a differen

(ctx context.Context, db database.Store, linkedID string, loginType database.LoginType, allowInsecureLinkedIDMismatch bool, emails ...string)

Source from the content-addressed store, hash-verified

2189//
2190//nolint:revive // allowInsecureLinkedIDMismatch is intentionally a control flag; it gates an INSECURE opt-in.
2191func findLinkedUser(ctx context.Context, db database.Store, linkedID string, loginType database.LoginType, allowInsecureLinkedIDMismatch bool, emails ...string) (database.User, database.UserLink, error) {
2192 var (
2193 user database.User
2194 link database.UserLink
2195 )
2196 link, err := db.GetUserLinkByLinkedID(ctx, linkedID)
2197 if err != nil && !errors.Is(err, sql.ErrNoRows) {
2198 return user, link, xerrors.Errorf("get user auth by linked ID: %w", err)
2199 }
2200
2201 if err == nil {
2202 user, err = db.GetUserByID(ctx, link.UserID)
2203 if err != nil {
2204 return database.User{}, database.UserLink{}, xerrors.Errorf("get user by id: %w", err)
2205 }
2206 if !user.Deleted {
2207 return user, link, nil
2208 }
2209 // If the user was deleted, act as if no account link exists.
2210 user = database.User{}
2211 }
2212
2213 for _, email := range emails {
2214 user, err = db.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
2215 Email: email,
2216 })
2217 if err != nil && !errors.Is(err, sql.ErrNoRows) {
2218 return user, link, xerrors.Errorf("get user by email: %w", err)
2219 }
2220 if errors.Is(err, sql.ErrNoRows) {
2221 continue
2222 }
2223 break
2224 }
2225
2226 if user.ID == uuid.Nil {
2227 // No user found.
2228 return database.User{}, database.UserLink{}, nil
2229 }
2230
2231 // LEGACY: This is annoying but we have to search for the user_link
2232 // again except this time we search by user_id and login_type. It's
2233 // possible that a user_link exists without a populated 'linked_id'.
2234 link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{
2235 UserID: user.ID,
2236 LoginType: loginType,
2237 })
2238 if err != nil && !errors.Is(err, sql.ErrNoRows) {
2239 return database.User{}, database.UserLink{}, xerrors.Errorf("get user link by user id and login type: %w", err)
2240 }
2241
2242 // Block email fallback when an existing link has a different linked_id.
2243 // Prevents account takeover via IdP email reuse; first-time and legacy
2244 // (empty linked_id) links pass through. The INSECURE
2245 // allowInsecureLinkedIDMismatch escape hatch keeps the existing link
2246 // (and its original linked_id) and lets the login proceed.
2247 if err == nil && link.LinkedID != "" && link.LinkedID != linkedID && !allowInsecureLinkedIDMismatch {
2248 return database.User{}, database.UserLink{}, errLinkedIDAlreadyBound

Callers 2

userOAuth2GithubMethod · 0.85
userOIDCMethod · 0.85

Calls 6

GetUserLinkByLinkedIDMethod · 0.65
GetUserByIDMethod · 0.65
IsMethod · 0.45
ErrorfMethod · 0.45

Tested by

no test coverage detected