Skip to content

chore: ensure github uids are unique #11826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 29, 2024
Merged

Conversation

Emyrk
Copy link
Member

@Emyrk Emyrk commented Jan 25, 2024

What this does

If a github uid is nil, it is returned as 0. If we link a user with this uid, then multiple users could potentially be considered the same user. We should reject this.

I also added a unit test to test changing primary emails.

This was found when trying to debug #10972.

Github Oauth login matches on github user id, not their email
@Emyrk Emyrk marked this pull request as draft January 25, 2024 18:49
@Emyrk Emyrk changed the title fix: github changing emails should update coder emails on login chore: ensure github uids are unique Jan 26, 2024
Comment on lines 607 to 625
// If we have a nil GitHub ID, that is a big problem. That would mean we link
// this user and all other users with this bug to the same uuid.
// We should instead throw an error. This should never occur in production.
//
// Verified that the lowest ID on GitHub is "1", so 0 should never occur.
if ghUser.GetID() == 0 {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "The GitHub user returned an ID of 0, this should never happen. Please report this error.",
// If this happens, the User could either be:
// - Empty, in which case all these fields would also be empty.
// - Not a user, in which case the "Type" would be something other than "User"
Detail: fmt.Sprintf("Other user fields: name=%q, email=%q, type=%q",
ghUser.GetName(),
ghUser.GetEmail(),
ghUser.GetType(),
),
})
return
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main change.

@Emyrk Emyrk marked this pull request as ready for review January 26, 2024 18:25
Comment on lines +606 to +686
t.Run("ChangedEmail", func(t *testing.T) {
t.Parallel()

fake := oidctest.NewFakeIDP(t,
oidctest.WithServing(),
oidctest.WithCallbackPath("/api/v2/users/oauth2/github/callback"),
)
const ghID = int64(7777)
auditor := audit.NewMock()
coderEmail := &github.UserEmail{
Email: github.String("alice@coder.com"),
Verified: github.Bool(true),
Primary: github.Bool(true),
}
gmailEmail := &github.UserEmail{
Email: github.String("alice@gmail.com"),
Verified: github.Bool(true),
Primary: github.Bool(false),
}
emails := []*github.UserEmail{
gmailEmail,
coderEmail,
}

client := coderdtest.New(t, &coderdtest.Options{
Auditor: auditor,
GithubOAuth2Config: &coderd.GithubOAuth2Config{
AllowSignups: true,
AllowEveryone: true,
OAuth2Config: promoauth.NewFactory(prometheus.NewRegistry()).NewGithub("test-github", fake.OIDCConfig(t, []string{})),
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
return []*github.Membership{}, nil
},
TeamMembership: func(ctx context.Context, client *http.Client, org, team, username string) (*github.Membership, error) {
return nil, xerrors.New("no teams")
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
Login: github.String("alice"),
ID: github.Int64(ghID),
}, nil
},
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
return emails, nil
},
},
})

ctx := testutil.Context(t, testutil.WaitMedium)
// This should register the user
client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err := client.User(ctx, "me")
require.NoError(t, err)
userID := user.ID
require.Equal(t, user.Email, *coderEmail.Email)

// Now the user is registered, let's change their primary email.
coderEmail.Primary = github.Bool(false)
gmailEmail.Primary = github.Bool(true)

client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err = client.User(ctx, "me")
require.NoError(t, err)
require.Equal(t, user.ID, userID)
require.Equal(t, user.Email, *gmailEmail.Email)

// Entirely change emails.
newEmail := "alice@newdomain.com"
emails = []*github.UserEmail{
{
Email: github.String("alice@newdomain.com"),
Primary: github.Bool(true),
Verified: github.Bool(true),
},
}
client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err = client.User(ctx, "me")
require.NoError(t, err)
require.Equal(t, user.ID, userID)
require.Equal(t, user.Email, newEmail)
})
Copy link
Member Author

@Emyrk Emyrk Jan 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a change email unit test using the fake idp.

@Emyrk Emyrk requested a review from johnstcn January 26, 2024 19:16
Comment on lines +618 to +621
Detail: fmt.Sprintf("Other user fields: name=%q, email=%q, type=%q",
ghUser.GetName(),
ghUser.GetEmail(),
ghUser.GetType(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this potentially leak another user's email? Would it be safer to log this instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it cannot. This comes from the authenticated request to github using your oauth token.

So this payload comes from:

  • User does github oauth flow with coder
  • Oauth flow succeeds
  • Coder uses their oauth token to determine to make a new user or link to an existing
    • In this flow, the AuthentictedUser request fails from an id=0.

To leak someone else email, you would need to authenticate as that user via github.

Copy link
Member

@mafredri mafredri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Emyrk Emyrk merged commit 04a2326 into main Jan 29, 2024
@Emyrk Emyrk deleted the stevenmasley/github_oauth_email branch January 29, 2024 15:13
@github-actions github-actions bot locked and limited conversation to collaborators Jan 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants