👩🏻‍💻 Amy K
categories tags about rss

Naming things is one of the hard problems in Computer Science. Users want to be able to change their usernames for a variety of reasons. If your GitHub app uses the username information from GitHub, you should ensure you properly handle when this changes.

Usernames have properties that generally make them good identifiers. They are guaranteed to be unique and they are often human-readable. GitHub makes use of this property by including usernames as part of their URL structure (e.g. my GitHub profile is located https://github.com/amy-keibler).

However, GitHub usernames are not permanently reserved. In their documentation on changing your username, they state

After changing your username, your old username becomes available for anyone else to claim.

Because of this, some guides will recommend users to create a new account or to immediately create an account after they changes their username if there is a concern of impersonation. GitHub does have a name squatting policy that forbids this, but it is still the best option available until they develop sufficient anti-impersonation tools.

Why is this important to my app?

When creating an app that allows user login via GitHub OAuth, it can be tempting to reuse the username that you receive from GitHub as a username for your app guaranteed to be unique across GitHub and most users would appreciate the simplicity of not needing to choose an additional username.

I think it is helpful to think of the username like a display name rather than a user identifier.

  • Uniqueness is only guaranteed within GitHub, so you lose that property if you want to support multiple OAuth providers.
  • The value can change without you being notified, since there is no webhook for username changes.
  • Its main purpose is to allow users to identify other users.

Guidelines

Use the authenticated user

If you can avoid storing a user's GitHub username and display name information, you should consider fetching it every time you perform an authentication check with GitHub. You won't have to build any user management features, don't have to worry about compliance with the GDPR, and don't have to worry about your users' integrations with your app breaking.

This approach works best for apps that do not need to store user accounts server-side. You can use the authentication token to perform actions on behalf of the user and then structure the data for presentation before sending it to the client. An example of an application that might fit this pattern would be one that performs data visualization on the associations between a user's repositories.

Use the ID to get the username

If you can't avoid storing a user's GitHub username and display information, you should sync it frequently (every time the user logs in to your app, for example). This can be done by checking the https://api/github.com/user endpoint, which will return the id and login of the user, as well as useful fields like name and avatar_url.

To implement this pattern, you should store the user ID that you receive from GitHub on their account and use that as a value that is guaranteed to be unique and immutable. A users table might look something like:

CREATE TABLE users (
   user_id UUID PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(),
   github_id TEXT UNIQUE NOT NULL,
   username TEXT NOT NULL,
   display_name TEXT NOT NULL
);

An update to this table when a username changes might look like:

UPDATE users SET username = 'new_username' WHERE github_id = '2';

It's important to also consider storing a history of prior usernames if your app uses them in places such as URLs, since those links to your site would be broken when the username changed. This can be a nuanced feature to get right, because GitHub will happily hand out usernames that have been previously owned, so be sure that you handle any security implications specific to your app. You might consider either revoking the username history once it has been reassigned to a different user or invalidating any existing URLs with usernames that would point to the wrong user's data.

Be thoughtful about how you use usernames in your app's features

  • If you allow users to reference other users in a manner similar to GitHub's @<username> mention in comments feature, consider how you will handle username changes. This is something that GitHub does not handle particularly well. According to their documentation, @- mentions of users lose their association when users rename their accounts. Bitbucket handles this more gracefully. They store comments in a processed form that contains the UUID and manage the username display in the user interface. This allows them to ensure comments are kept accurate as usernames change.
  • If you ban users via username, it will be easy for users to evade bans by changing their usernames. You might have an internal tool that allows the entry of usernames to ban, but you should store it either in your main users table, or you should store your internally unique identifier.

So what's the takeaway?

Hopefully, this has convinced you that supporting your users' ability to update their usernames is an easy and worthwhile feature. This can be thought of as a "Curb cut effect", where a feature that is implemented for one demographic has positive impact on a wider range of people and does not negatively affect the rest. This will be unnoticed by the majority of your users, but will earn you goodwill with the rest when they don't have to reach out to you for manual support for their accounts.

References and Further Reading

  1. A Quick Guide to Changing Your GitHub Username
  2. Changing your name is a hard unsolved problem in Computer Science
  3. The Curb-Cut Effect

Basically, your app should behave like Kor from Star Trek: Deep Space Nine.

A scene from Star Trek: Deep Space Nine, where a Klingon named Kor greets Jadzia Dax. He says "Curzon, my beloved old friend". She responds "I'm Jadzia now". He responds "Jadzia, my beloved old friend".