diff --git a/docs/multiple-accounts.md b/docs/multiple-accounts.md new file mode 100644 index 000000000..aef2cc333 --- /dev/null +++ b/docs/multiple-accounts.md @@ -0,0 +1,237 @@ +# Multiple Accounts with the CLI - v2.40.0 + +Since its creation, `gh` has enforced a mapping of one account per host. Functionally, this meant that when targeting a +single host (e.g. github.com) each `gh auth login` would replace the token being used for API requests, and for git +operations when `gh` was configured as a git credential manager. Removing this limitation has been a [long requested +feature](https://github.com/cli/cli/issues/326), with many community members offering workarounds for a variety of use cases. +A particular shoutout to @gabe565 and his long term community support for https://github.com/gabe565/gh-profile in this space. + +With the release of `v2.40.0`, `gh` has begun supporting multiple accounts for some use cases on github.com and +in GitHub Enterprise. We recognise that there are a number of missing quality of life features, and we've opted +not to address the use case of automatic account switching based on some context (e.g. `pwd`, `git remote`, etc) though +we hope many of those using these custom solutions will now find it easier to obtain and update tokens (via the standard +OAuth flow rather than as a PAT), and to store them securely in the system keyring managed by `gh`. + +We are by no means excluding these things from ever being native to `gh` but we wanted to ship this MVP and get more +feedback so that we can iterate on it with the community. + +## What is in scope for this release? + +The support for multiple accounts in this release is focused around `auth login` becoming additive in behaviour, +allowing for multiple accounts to be easily switched between using the new `auth switch` command. Switching the "active" +user for a host will swap the token used by `gh` for API requests, and for git operations when `gh` was configured as a +git credential manager. We have extended the `auth logout` command to switch the active user where possible if the +current active user has been logged out. Finally we have extended `auth token`, `auth switch`, and `auth logout` with a +`--user` flag. This new flag in combination with `--hostname` can be used to disambiguate accounts when running +non-interactively. + +Here's an example usage. First, we can see that I have a single acocunt `wilmartin_microsoft` logged in, and +`auth status` reports that this is the active account: + +``` +➜ gh auth status +github.com + ✓ Logged in to github.com account wilmartin_microsoft (keyring) + - Active account: true + - Git operations protocol: https + - Token: gho_************************************ + - Token scopes: 'gist', 'read:org', 'repo', 'workflow' +``` + +Running `auth login` and proceeding through the browser based OAuth flow as `williammartin`, we can see that +`auth status` now reports two accounts under `github.com`, and our new account is now marked as active. + +``` +➜ gh auth login +? What account do you want to log into? GitHub.com +? What is your preferred protocol for Git operations on this host? HTTPS +? How would you like to authenticate GitHub CLI? Login with a web browser + +! First copy your one-time code: A1F4-3B3C +Press Enter to open github.com in your browser... +✓ Authentication complete. +- gh config set -h github.com git_protocol https +✓ Configured git protocol +✓ Logged in as williammartin + +➜ gh auth status +github.com + ✓ Logged in to github.com account williammartin (keyring) + - Active account: true + - Git operations protocol: https + - Token: gho_************************************ + - Token scopes: 'gist', 'read:org', 'repo', 'workflow' + + ✓ Logged in to github.com account wilmartin_microsoft (keyring) + - Active account: false + - Git operations protocol: https + - Token: gho_************************************ + - Token scopes: 'gist', 'read:org', 'repo', 'workflow' +``` + +Fetching our username from the API shows that our active token correctly corresponds to `williammartin`: + +``` +➜ gh api /user | jq .login +"williammartin" +``` + +Now we can easily switch users using `gh auth switch`, and hitting the API shows that the active token has been +changed: + +``` +➜ gh auth switch +✓ Switched active account for github.com to wilmartin_microsoft + +➜ gh api /user | jq .login +"wilmartin_microsoft" +``` + +We can use `gh auth token --user` to get a specific token for a user (which should be handy for automated switching +solutions): + +``` +➜ GH_TOKEN=$(gh auth token --user williammartin) gh api /user | jq .login +"williammartin" +``` + +Finally, running `gh auth logout` presents a prompt when there are multiple choices for logout, and switches account +if there are any remaining logged into the host: + +``` +➜ gh auth logout +? What account do you want to log out of? wilmartin_microsoft (github.com) +✓ Logged out of github.com account wilmartin_microsoft +✓ Switched active account for github.com to williammartin +``` + +## What is out of scope for this release? + +As mentioned above, we know that this only addreses some of the requests around supporting multiple accounts. While +these are not out of scope forever, for this release some of the big things we have intentionally not included are: + * Automatic account switching based on some context (e.g. `pwd`, `git remote`, etc) + * Automatic configuration of git config such as `user.name` and `user.email` when switching + * User level configuration e.g. `williammartin` uses `vim` but `wilmartin_microsoft` uses `emacs` + +## What are some sharp edges in this release? + +As in any MVP there are going to be some sharp edges that need to be smoothed out over time. Here are a list of known +sharp edges in this release. + +### Data Migration + +The trickiest piece of this work was that the `hosts.yml` file only supported a mapping of one-to-one in the host to +account relationship. Having persistent data on disk that required a schema change presented a compatability challenge +both backwards for those who use [`go-gh`](https://github.com/cli/go-gh/) outside of `gh`, and forward for `gh` itself +where we try to ensure that it's possible to use older versions in case we accidentally make a breaking change for users. + +As such, from this release, running any command will attempt to migrate this data into a new format, and will +additionally add a `version` field into the `config.yml` to aid in our future maintainability. While we have tried +to maintain forward compatability (except in one edge case outlined below), and in the worst case you should be able +to remove these files and start from scratch, if you are concerned about the data in these files, we advise you to take +a backup. + +#### Forward Compatability Exclusion + +There is one case using `--insecure-storage` that we don't maintain complete forward and backward compatability. This +occurs if you `auth login --insecure-storage`, upgrade to this release (which performs the data migration), run +`auth login --insecure-storage` again on an older release, then at some time later use `auth switch` to make this +account active. The symptom here would be usage of an older token (which may for example have different scopes). + +This occurs because we will only perform the data migration once, moving the original insecure token to a place where +it would later be used by `auth switch`. + +#### Immutable Config Users + +Some of our users lean on tools to manage their application configuration in an immutable manner for example using +https://github.com/nix-community/home-manager. These users will hit an error when we attempt to persist the new +`version` field to the `config.yml`. They will need to ensure that the `home-manager` configuration scripts are updated +to add `version: 1`. + +___ + +Meta: Draft text for home-manager issue: + +## Description + +In `gh` v2.40.0, we are going to be releasing initial support for [multiple accounts](https://github.com/cli/cli/issues/326) +on a single host. As part of this process there is a migration of on-disk data and we will be adding a `version` field +with value `1` to the `config.yml` file. + +I'm not too familiar with `nix` or how people use it, so I'm not sure if the following is even an expected workflow. If +it is expected that you can update the version of `gh` in place without regenerating the `config.yml` file from +`home-manager` scripts it is likely that users will run into an error when `gh` tries to write the `config.yml` file. + +Although it's our position that `gh` manages its own configuration file, we also try not to break users that choose to +configure their systems this way. Unfortunately, in this case we value the future maintainability of having this +`version` to support future migrations should we need them. + +As such, this is just an informative issue for people to find that this new field will be required to perform any operations +with v2.40.0. Since I'm not totally sure on the workflows used in this project, here are some possible workarounds: + 1. Temporarily allow writing to `config.yml` one time so that this `version` can be written + 1. After seeing a failure, install updated `home-manager` scripts with `version: 1` declared in the config + 1. Update the `home-manager` scripts with `version: 1` declared in the config and then blow everything away and start with v2.40.0 from scratch + +To me it feels like option `3` is the easiest, since home manager enforces config to already be defined, the only consequence +will be requiring `gh auth login` to be run for any accounts that were removed when blowing it all away. + +We apologise for the inconvenience. +___ + +### Auth Refresh + +Although this has always been possible, the multi account flow increases the likelihood of doing something surprising +with `auth refresh`. This command allows for a token to be updated with additional or fewer scopes. For example, +in the following example we add the `read:project` scope to the scopes for our currently active user `williammartin`, +and proceed through the OAuth browser flow as `williammartin`: + +``` +➜ gh auth refresh -s read:project +? What account do you want to refresh auth for? github.com + +! First copy your one-time code: E79E-5FA2 +Press Enter to open github.com in your browser... +✓ Authentication complete. + +➜ gh auth status +github.com + ✓ Logged in to github.com account williammartin (keyring) + - Active account: true + - Git operations protocol: https + - Token: gho_************************************ + - Token scopes: 'gist', 'read:org', 'read:project', 'repo', 'workflow' + + ✓ Logged in to github.com account wilmartin_microsoft (keyring) + + ✓ Logged in to github.com account wilmartin_microsoft (keyring) + - Active account: false + - Git operations protocol: https + - Token: gho_************************************ + - Token scopes: 'gist', 'read:org', 'repo', 'workflow' +``` + +However, what happens if I try to remove the `workflow` scope from my active user `williammartin` but proceed through +the OAuth browser flow as `wilmartin_microsoft`? + +``` +➜ gh auth refresh -r workflow + +! First copy your one-time code: EEA3-091C +Press Enter to open github.com in your browser... +error refreshing credentials for williammartin, received credentials for wilmartin_microsoft, did you use the correct account in the browser? +``` + +When adding or removing scopes for a user, the CLI gets the scopes for the current token and then requests a new token with the requested amended scopes. Unfortunately, when we go through the account switcher flow as a different user, we end up getting a token for the wrong user with surprising scopes. We don't believe that starting and ending a `refresh` as different users is +a user case we wish to support and has the potential for misuse. As such, we have begun erroring in this case. + +Note that a token has still been minted on the platform but `gh` will refuse to store it. We are investigating +alternative approaches with the platform team to prevent this occurring earlier in the flow. + +### Account Switcher on GitHub Enterprise + +When using `auth login` with github.com, if a user has multiple accounts in the browser, they should be presented +with an interstitial page that allows for proceeding as any of their users. However, for Device Control Flow OAuth +flows, this feature has not yet made it into GHES. + +For the moment, if you have multiple accounts on GHES that you wish to log in as, you will need to ensure that you +are authenticated as the correct user in the browser before running `auth login`.