-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Stop using PATs for Azure Repos (Attempt 2: Electric Boogaloo) #294
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
5b72a58
app: fix exception handling/printing
mjcheetham 34ff9df
inputargs: fix a bug in remote URI generation
mjcheetham ba7e9d4
input: better input arg checking/handling
mjcheetham f31b16f
git: replace progdata/xdg config levels with unknown
mjcheetham dc6a69c
git: drop unused git config extension methods
mjcheetham 94bfed4
git: introduce GitCfgEntry wrapper type for enumeration
mjcheetham b9dfb07
git: include gitcfg scope/level in enumeration results
mjcheetham 0bf67cc
git: expose TrySplit git cfg key splitting helper
mjcheetham 5d61bbe
git: change git config API to push level filter to methods
mjcheetham 7987850
git: add ability to list remotes and get current repo dir
mjcheetham ee64565
azrepos: add utility to extract AzDevOps org name
mjcheetham 7e493d7
azrepos: add non-PAT based auth method to AzRepos
mjcheetham 8e351df
azrepos: add Azure authority cache
mjcheetham 242073a
azrepos: add cmd to clear the authority cache
mjcheetham 606a475
azrepos: add a binding manager to store org-user maps
mjcheetham e2b7deb
azrepos: add commands to manager user bindings
mjcheetham c8a7b2b
git: add guards against malformed Git config data
mjcheetham File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
# Azure Repos: Access tokens and Accounts | ||
|
||
## Different credential types | ||
|
||
The Azure Repos host provider supports creating multiple types of credential: | ||
|
||
- Azure DevOps personal access tokens | ||
- Microsoft identity OAuth tokens (experimental) | ||
|
||
### Azure DevOps personal access tokens | ||
|
||
Historically, the only option supported by the Azure Repos host provider was | ||
Azure DevOps Personal Access Tokens (PATs). | ||
|
||
These PATs are only used by Azure DevOps, and must be [managed through the Azure | ||
DevOps user settings page](https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=preview-page). | ||
|
||
PATs have a limited lifetime and new tokens must be created once they expire. In | ||
Git Credential Manager, when a PAT expired (or was manually revoked) this | ||
resulted in a new authentication prompt. | ||
|
||
### Microsoft identity OAuth tokens (experimental) | ||
|
||
"Microsoft identity OAuth token" is the generic term for OAuth-based access | ||
tokens issued by Azure Active Directory for either Work and School Accounts | ||
(AAD tokens) or Personal Accounts (Microsoft Account/MSA tokens). | ||
|
||
Azure DevOps supports Git authentication using Microsoft identity OAuth tokens | ||
as well as PATs. Microsoft identity OAuth tokens created by Git Credential | ||
Manager are scoped to Azure DevOps only. | ||
|
||
Unlike PATs, Microsoft identity OAuth tokens get automatically refreshed and | ||
renewed as long as you are actively using them to perform Git operations. | ||
|
||
These tokens are also securely shared with other Microsoft developer tools | ||
including the Visual Studio IDE and Azure CLI. This means that as long as you're | ||
using Git or one of these tools with the same account, you'll never need to | ||
re-authenticate due to expired tokens! | ||
|
||
#### User accounts | ||
|
||
In versions of Git Credential Manager that support Microsoft identity OAuth | ||
tokens, the user account used to authenticate for a particular Azure DevOps | ||
organization will now be remembered. | ||
|
||
The first time you clone, fetch or push from/to an Azure DevOps organization you | ||
will be prompted to sign-in and select a user account. Git Credential Manager | ||
will remember which account you used and continue to use that for all future | ||
remote Git operations (clone/fetch/push). An account is said to be "bound" to | ||
an Azure DevOps organization. | ||
|
||
--- | ||
|
||
**Note:** If GCM is set to use PAT credentials, this account will **NOT** be | ||
used and you will continue to be prompted to select a user account to renew the | ||
credential. This may change in the future. | ||
|
||
--- | ||
|
||
Normally you won't need to worry about managing which user accounts Git | ||
Credential Manager is using as this is configured automatically when you first | ||
authenticate for a particular Azure DevOps organziation. | ||
|
||
In advanced scenarios (such as using multiple accounts) you can interact with | ||
and manage remembered user accounts using the 'azure-repos' provider command: | ||
|
||
```shell | ||
git-credential-manager-core azure-repos [ list | bind | unbind | ... ] <options> | ||
``` | ||
|
||
##### Listing remembered accounts | ||
|
||
You can list all bound user accounts by Git Credential Manager for each Azure | ||
DevOps organization using the `list` command: | ||
|
||
```shell | ||
$ git-credential-manager-core azure-repos list | ||
contoso: | ||
(global) -> [email protected] | ||
fabrikam: | ||
(global) -> [email protected] | ||
``` | ||
|
||
In the above example, the `contoso` Azure DevOps organization is associated with | ||
the `[email protected]` user account, while the `fabrikam` organization is | ||
associated to the `[email protected]` user account. | ||
|
||
Global "bindings" apply to all remote Git operations for the current computer | ||
user profile and are stored in `~/.gitconfig` or `%USERPROFILE%\.gitconfig`. | ||
|
||
##### Using different accounts within a repository | ||
|
||
If you generally use one account for an Azure DevOps organization, the default | ||
global bindings will be sufficient. However, if you wish to use a different | ||
user account for an organization in a particular repository you can use a local | ||
binding. | ||
|
||
Local account bindings only apply within a single repository and are stored in | ||
the `.git/config` file. If there are local bindings in a repository you can show | ||
them with the `list` command: | ||
|
||
```shell | ||
~/myrepo$ git-credential-manager-core azure-repos list | ||
contoso: | ||
(global) -> [email protected] | ||
(local) -> [email protected] | ||
``` | ||
|
||
Within the `~/myrepo` repository, the `[email protected]` account will be | ||
used by Git and GCM for the `contoso` Azure DevOps organization. | ||
|
||
To create a local binding, use the `bind` command with the `--local` option when | ||
inside a repository: | ||
|
||
```shell | ||
~/myrepo$ git-credential-manager-core azure-repos bind --local contoso [email protected] | ||
``` | ||
|
||
```diff | ||
contoso: | ||
(global) -> [email protected] | ||
+ (local) -> [email protected] | ||
``` | ||
|
||
##### Forget an account | ||
|
||
To have Git Credential Manager forget a user account, use the `unbind` command: | ||
|
||
```shell | ||
git-credential-manager-core azure-repos unbind fabrikam | ||
``` | ||
|
||
```diff | ||
contoso: | ||
(global) -> [email protected] | ||
- fabrikam: | ||
- (global) -> [email protected] | ||
``` | ||
|
||
In the above example, and global account binding for the `fabrikam` organization | ||
will be forgotten. The next time you need to renew a PAT (if using PATs) or | ||
perform any remote Git operation (is using Azure tokens) you will be prompted | ||
to authenticate again. | ||
|
||
To forget or remove a local binding, within the repository run the `unbind` | ||
command with the `--local` option: | ||
|
||
```shell | ||
~/myrepo$ git-credential-manager-core azure-repos unbind --local contoso | ||
``` | ||
|
||
```diff | ||
contoso: | ||
(global) -> [email protected] | ||
- (local) -> [email protected] | ||
``` | ||
|
||
##### Using different accounts for specific Git remotes | ||
|
||
As well as global and local user account bindings, you can instruct Git | ||
Credential Manager to use a specific user account for an individual Git remotes | ||
within the same local repository. | ||
|
||
To show which accounts are being used for each Git remote in a repository use | ||
the `list` command with the `--show-remotes` option: | ||
|
||
```shell | ||
~/myrepo$ git-credential-manager-core azure-repos list --show-remotes | ||
contoso: | ||
(global) -> [email protected] | ||
origin: | ||
(fetch) -> (inherit) | ||
(push) -> (inherit) | ||
fabrikam: | ||
(global) -> [email protected] | ||
``` | ||
|
||
In the above example, the `~/myrepo` repository has a single Git remote named | ||
`origin` that points to the `contoso` Azure DevOps organziation. There is no | ||
user account specifically associated with the `origin` remote, so the global | ||
user account binding for `contoso` will be used (the global binding is | ||
inherited). | ||
|
||
To associate a user account with a particular Git remote you must manually edit | ||
the remote URL using `git config` commands to include the username in the | ||
[user information](https://tools.ietf.org/html/rfc3986#section-3.2.1) part of | ||
the URL. | ||
|
||
```shell | ||
git config --local remote.origin.url https://alice-alt%[email protected]/project/_git/repo | ||
``` | ||
|
||
In the above example the `[email protected]` account is being set as the | ||
account to use for the `origin` Git remote. | ||
|
||
--- | ||
|
||
**Note:** All special characters must be URL encoded/escaped, for example `@` | ||
becomes `%40`. | ||
|
||
--- | ||
|
||
The `list --show-remotes` command will show the user account specified in the | ||
remote URL: | ||
|
||
```shell | ||
~/myrepo$ git-credential-manager-core azure-repos list --show-remotes | ||
contoso: | ||
(global) -> [email protected] | ||
origin: | ||
(fetch) -> [email protected] | ||
(push) -> [email protected] | ||
fabrikam: | ||
(global) -> [email protected] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -333,3 +333,26 @@ Credential: "git:https://[email protected]/example/myrepo" (user = bob) | |
|
||
https://[email protected]/example/myrepo | ||
``` | ||
|
||
--- | ||
|
||
### credential.azreposCredentialType _(experimental)_ | ||
mjcheetham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Specify the type of credential the Azure Repos host provider should return. | ||
|
||
Defaults to the value `pat`. | ||
|
||
Value|Description | ||
-|- | ||
`pat` _(default)_|Azure DevOps personal access tokens | ||
`oauth`|Microsoft identity OAuth tokens (AAD or MSA tokens) | ||
|
||
More information about Azure Access tokens can be found [here](azrepos-azuretokens.md). | ||
|
||
#### Example | ||
|
||
```shell | ||
git config --global credential.azreposCredentialType oauth | ||
``` | ||
|
||
**Also see: [GCM_AZREPOS_CREDENTIALTYPE](environment.md#GCM_AZREPOS_CREDENTIALTYPE-experimental)** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know on the Azure DevOps side, they say "identity" for the thing that represents a user. Genuine question, is that the same thing as "account" here (and if so, should we use their terminology)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can say "identity" if we want to be consistent with Azure DevOps.
In AAD there are users and accounts (an account != user.. one can span tenants and clouds, the other is in a particular cloud+tenant).
In Git there is only a "username".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess technically, you're selecting an Azure account when you sign-in, and not an Azure DevOps identity.. Just happens that Azure accounts are mapped to an AzDO identity.
Aside: there's also a FUN thing that if you've never signed-in to the web front-end of a particular Azure DevOps organisation, you cannot call any AzDevOps APIs because the "identity" doesn't exist yet - it's only materialised on first sign-in on the web. 🙄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you are seeing this - it means that someone forgot to turn on a feature flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this was the case back when GCM Core was first out. I remember hitting this exact issue and having to include support for surfacing human readable error messages (telling you do go log in via the web first) from Azure DevOps' REST API endpoints.