Skip to content

Sample project for authorization using on-behalf-of flow with .NET Core applications and Entra ID

License

Notifications You must be signed in to change notification settings

Jandev/auth-with-obo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sample project for on-behalf-of flow flow

This repository is used as a sample to create the OAuth2.0 On-Behalf-Of flow using two different .NET Core application and Entra ID.
The flow and detals are descibed in the MS Learn pages regarding this topic.

Set up

This solution contains multiple projects:

  • BackendService
    This service has authorization in place for all endpoints.
  • IntegratingService This service also has authorization in place, but also needs to interact with the BackendService using the OAuth2.0 OBO-flow to retrieve data for the user.

The Bicep file will take care of deploying all resources in Azure. This is automated with the GitHub Action responsible for this.

Do make sure to enable the necessary secrets in Azure Key Vault and fill them with the correct details.

Note

If you run into errors about a Managed Identity not being able to access Azure Key Vault, please refer to the error section below.

Assigning Application Roles

To assign application roles to a user, group, or service principal, you can use the Azure CLI. Below is an example of how to assign a role to a user-managed identity.

# The object id from the User Managed Identity in need of the role assignment.
$managedIdentityObjectId = "90adeed6-5fd2-4a3a-bc40-5495870d66a4"
# The enterprise application object id of the that you want the identity assigned a role to.
$enterpriseApplicationObjectId = "a38fce62-6da2-45b0-8204-b7bcf7cc4613"
# The role id that you want to assign to the identity.
$appRoleId = "93ef304e-c5f2-4b9a-b93c-64b5daf61afb"
# The identity type for the identity. This should always be ServicePrincipal, when not assigning Users or Groups.
$IdentityType = "ServicePrincipal"

Write-Verbose -Message "Assigning role $($appRoleId) to identity $($managedIdentityObjectId) for service principal $($enterpriseApplicationObjectId)."
az rest `
    --method post `
    --uri https://graph.microsoft.com/beta/servicePrincipals/$($enterpriseApplicationObjectId)/appRoleAssignments `
    --headers "{'content-type': 'application/json'}" `
    --body "{'appRoleId': '$($appRoleId)', 'principalId': '$($managedIdentityObjectId)', 'principalType': '$($IdentityType)', 'resourceId': '$($enterpriseApplicationObjectId)'}"

Write-Output "Assigned role $($appRoleId) to identity $($managedIdentityObjectId) for service principal $($enterpriseApplicationObjectId)."

Sample usage

Please refer the the different HTTP files included in this repository to see how the endpoints can be invoked.

Tests

While implementing the solution, continuous testing is in place. Results are shared in this section.

BackendService

Admin role and WeatherUser scope

With the contents in the bearer token, we see functionality (authorization) is working as expected.

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "H9nj5AOSswMphg1SFx7jaV-lB9w"
}.{
  "aud": "8ebbea06-f01e-4f94-8254-32da2e94c240",
  "iss": "https://login.microsoftonline.com/4b1fa0f3-862b-4951-a3a8-df1c72935c79/v2.0",
  "iat": 1725622279,
  "nbf": 1725622279,
  "exp": 1725627509,
  "aio": "AXQAi/8XAAAA/bEntSNEIXjGJrqs9nbb6HjiuPAp/p+FYSIJDgUShpt52nHCvU3opUwMe2MjvRjGK970a3eWWMxQPcK6merohVFPhbFsu72mGRouKE1+QGIWkxW5xjivCnJTkENx2HSoEJh9NsQI+HpJaTpN3/65Tg==",
  "azp": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
  "azpacr": "0",
  "name": "Jan de Vries",
  "oid": "bf6c3c10-5aad-4cd8-b54c-f9083925e7e3",
  "preferred_username": "jan@jan-v.nl",
  "rh": "0.ATAA86AfSyuGUUmjqN8ccpNceQbqu44e8JRPglQy2i6UwkAwAGo.",
  "roles": [
    "Admin"
  ],
  "scp": "BackendDefault WeatherUser",
  "sub": "MiVyAiU0Q9B7caSpyCvvge63H6Ohve2hED15NmFfx1s",
  "tid": "4b1fa0f3-862b-4951-a3a8-df1c72935c79",
  "uti": "zqIiAA_oSEC3T1wZvLoKAA",
  "ver": "2.0"
}.[Signature]

Results of the HTTP calls to the endpoins using an Admin Application Role and a WeatherUser scope. The endpoints show a green checkmark for those who succeeded and a red cross who didn't.

IntegratingService

The goal for this solution is to have the on-behalf-of flow working from the IntegratingService towards the BackendService.

The HTTP tests that go along with this are successful at this moment.

Results of HTTP calls to the endpoints using the user scopes, as those are validated on the BackendService endpoints

The details of the token towards the IntegratingService is the following:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "H9nj5AOSswMphg1SFx7jaV-lB9w",
  "kid": "H9nj5AOSswMphg1SFx7jaV-lB9w"
}.{
  "aud": "api://afad1932-49c8-4e3c-a3a1-cf9543c84d9e",
  "iss": "https://sts.windows.net/4b1fa0f3-862b-4951-a3a8-df1c72935c79/",
  "iat": 1727187997,
  "nbf": 1727187997,
  "exp": 1727192955,
  "acr": "1",
  "aio": "AVQAq/8XAAAARDTadTqbY0d9N30tk3gsUURsoUvlUxtdl4ow7qE/x1a9MEUWdgnwWWbt/pMHBbmUnZXxh8x2oADc71grFgs9KpluEsbFNOPaRV3jg4qr0bU=",
  "amr": [
    "pwd",
    "rsa",
    "mfa"
  ],
  "appid": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
  "appidacr": "0",
  "deviceid": "adc2fde5-e2ad-4bb5-a432-4b342eae1e5d",
  "family_name": "de Vries",
  "given_name": "Jan",
  "ipaddr": "84.84.170.136",
  "name": "Jan de Vries",
  "oid": "bf6c3c10-5aad-4cd8-b54c-f9083925e7e3",
  "pwd_url": "https://portal.microsoftonline.com/ChangePassword.aspx",
  "rh": "0.ATAA86AfSyuGUUmjqN8ccpNceTIZra_ISTxOo6HPlUPITZ4wAGo.",
  "scp": "CustomDefault",
  "sub": "eaAin-N5Y1B0hUYIdj2vNCXRsYxLmr0caHxNuNRPLxQ",
  "tid": "4b1fa0f3-862b-4951-a3a8-df1c72935c79",
  "unique_name": "jan@jan-v.nl",
  "upn": "jan@jan-v.nl",
  "uti": "ZzxNOSfCe0-ylOknR9pXAA",
  "ver": "1.0"
}.[Signature]

The token sent towards the backend is the following:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "H9nj5AOSswMphg1SFx7jaV-lB9w"
}.{
  "aud": "8ebbea06-f01e-4f94-8254-32da2e94c240",
  "iss": "https://login.microsoftonline.com/4b1fa0f3-862b-4951-a3a8-df1c72935c79/v2.0",
  "iat": 1727188097,
  "nbf": 1727188097,
  "exp": 1727192953,
  "aio": "AXQAi/8XAAAA1+ns3RW8+ljrfjo6G6WtVSjvbd0xtDPm/eGhHStWH7ZagBwj2nuw25+9CmO8iLDzcMDryih+HK/A2YoVQxtjiDNKCxcb0uQRX6Fp4c4W7nxWKb9I6ybT6JcDN1fecwx3rQ9kAvtUvDGeqLaJz6JY8g==",
  "azp": "afad1932-49c8-4e3c-a3a1-cf9543c84d9e",
  "azpacr": "1",
  "name": "Jan de Vries",
  "oid": "bf6c3c10-5aad-4cd8-b54c-f9083925e7e3",
  "preferred_username": "jan@jan-v.nl",
  "rh": "0.ATAA86AfSyuGUUmjqN8ccpNceQbqu44e8JRPglQy2i6UwkAwAGo.",
  "roles": [
    "Admin"
  ],
  "scp": "user_impersonation WeatherAdmin WeatherUser",
  "sub": "MiVyAiU0Q9B7caSpyCvvge63H6Ohve2hED15NmFfx1s",
  "tid": "4b1fa0f3-862b-4951-a3a8-df1c72935c79",
  "uti": "xhunNTrb2US8oEV8a05HAA",
  "ver": "2.0"
}.[Signature]

Note

The scope property has all the scopes that are assigned to the IntegratingService App Registration. Screenshot of Entra ID where the IntegratingService App Registration is shown with assigned (delegated) scopes towards the BackendServiceApp. This needs to be fixed!

Errors seen

Get a token for a specific scope

When trying to get a token with a scope defined for the first time, you can an error.

 az account get-access-token --resource=api://afad1932-49c8-4e3c-a3a1-cf9543c84d9e --scope=api://8ebbea06-f01e-4f94-8254-32da2e94c240/WeatherUser --query accessToken --output tsv
(pii). Status: Response_Status.Status_InteractionRequired, Error code: 3399614476, Tag: 557973645
Please explicitly log in with:
az login --scope api://8ebbea06-f01e-4f94-8254-32da2e94c240/WeatherUser

This is because you need to provide Consent for the scope first. When you run the login command with the proper scope, you will be prompted to provide consent.

az login --scope api://8ebbea06-f01e-4f94-8254-32da2e94c240/WeatherUser

This will open a browser window where you can provide consent for the scope.

Provide consent for the Azure CLI to use the WeatherUser permission.

Provide consent for the BackendService, to read the user information.

This should yield in a token looking similar to the sample below.

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "H9nj5AOSswMphg1SFx7jaV-lB9w"
}.{
  "aud": "8ebbea06-f01e-4f94-8254-32da2e94c240",
  "iss": "https://login.microsoftonline.com/4b1fa0f3-862b-4951-a3a8-df1c72935c79/v2.0",
  "iat": 1725547137,
  "nbf": 1725547137,
  "exp": 1725551937,
  "aio": "AWQAm/8XAAAAPG9SheyqcZONBaWFrBLTrYCZvTRTV8gRh3Bklxy7HQYw0PDedJgYh0Zj+Fqqk04gy3j8yWrBtG4aR+2ddup99MhkQHmJaqWoK+o/XxYShKdrcc6mZivLzvgRO4QDf/6C",
  "azp": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
  "azpacr": "0",
  "name": "Jan de Vries",
  "oid": "bf6c3c10-5aad-4cd8-b54c-f9083925e7e3",
  "preferred_username": "jan@jan-v.nl",
  "rh": "0.ATAA86AfSyuGUUmjqN8ccpNceQbqu44e8JRPglQy2i6UwkAwAGo.",
  "roles": [
    "Admin"
  ],
  "scp": "BackendDefault WeatherUser",
  "sub": "MiVyAiU0Q9B7caSpyCvvge63H6Ohve2hED15NmFfx1s",
  "tid": "4b1fa0f3-862b-4951-a3a8-df1c72935c79",
  "uti": "Ats0cwbTrk6Dfs2WbHwNAA",
  "ver": "2.0"
}.[Signature]

After providing consent, you can login in the Azure CLI with the regular flow again. Creating tokens should now work, even with the scope.

az login --tenant 4b1fa0f3-862b-4951-a3a8-df1c72935c79

Key Vault references not working

If you get the following error when using Key Vault references, you need to do an additional step.

Status: MSINotEnabled Error details: Reference was not able to be resolved because site Managed Identity not enabled.

According to the MS Learn pages on this topic, you need to invoke the following commands:

$resourceGroup = "auth-with-obo"
$backendIdentityName = "backendServiceApp-identity"
$backendAppName = "backendServiceApp"
$backendIdentityResourceId =$(az identity show --resource-group $resourceGroup --name $backendIdentityName --query id -o tsv)
az webapp update --resource-group $resourceGroup --name $backendAppName --set keyVaultReferenceIdentity=$backendIdentityResourceId

$integratingIdentityName = "integratingServiceApp-identity"
$integratingAppName = "integratingServiceApp"
$integratingIdentityResourceId =$(az identity show --resource-group $resourceGroup --name $integratingIdentityName --query id -o tsv)
az webapp update --resource-group $resourceGroup --name $integratingAppName --set keyVaultReferenceIdentity=$integratingIdentityResourceId

The App Service now is using the user-managed identity to connect with the Azure Key Vault. By default the System Managed Identity is used.

About

Sample project for authorization using on-behalf-of flow with .NET Core applications and Entra ID

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published