This may have been correct as of 2021 but in 2023 we have the absolute magic that is OAuth tokens for AAD identities.
One important caveat - this only works for AAD-backed Azure DevOps.
In essence to clone an Azure Repository using only a service principal we need to follow the below steps
- Create a Service Principal in the Azure Portal. Note that you can use either client secret or certificate authentication here as you'll use the credentials later on in the Azure CLI
- Add the Service Principal to your Azure Devops organisation and assign it permissions on the Azure Repositories you care about, just as you would any other user. This excellent Microsoft page contains a video on how to do this. Effectively you can now see Service Principals in the list of users and they function identically to any other "user account".
- Use the following code to authenticate using either a client secret (1) or client certificate (2)
az login --service-principal --allow-no-subscriptions --username
[YOUR_APP_ID] --password [YOUR_CLIENT_SECRET] --tenant [YOUR_TENANT_ID]
az login --service-principal --allow-no-subscriptions --username [YOUR_APP_ID] --password [PATH_TO_YOUR_PEM_FILE] --tenant [YOUR_TENANT_ID]
- The magic. Obtain an OAuth token using the following command:
az account get-access-token --scope "499b84ac-1321-427f-aa17-267ca6975798/.default"
Note that the scope here is hard coded as it's a fixed value used by Microsoft
- This will return a JSON object similar to the below (which is a classic OAuth access token structure):
{
"accessToken": "eyJ0eXAiOiJKV1...",
"expiresOn": "2023-06-13 22:15:28.000000",
"subscription": "[YOUR SUBSCRIPTION]",
"tenant": "[YOUR TENANT]",
"tokenType": "Bearer"
}
- The slam dunk. Use this token as your password in the git clone URL (example below):
git clone https://[ACCESS_TOKEN]@dev.azure.com/[YOUR_ORG]/[YOUR_PROJECT]/_git/[YOUR_REPOSITORY]
This will, incredibly, allow you to clone a repository purely using a service principal. I can barely believe I'm writing this, but welcome to the future.
Edit Another SO user requested a pure cURL answer to this which is documented in a different answer I provided here