I'm using Azure Active Directory to provide authentication to the Backoffice on my website running Umbraco version 11.0.
This is working nicely and I can log in but I want to improve the experience by using app roles within Azure to manage the user's group within Umbraco.
My Azure setup
I've created an App Registration within Azure with the following configuration:
- Added a Redirection URI:
- URI: https://localhost:44391/umbraco-signin-microsoft/
- Enabled
Access tokens (used for implicit flows)
- Enabled
ID tokens (used for implicit and hybrid flows)
- Supported account types:
Accounts in this organizational directory only (Example only - Single tenant)
- Added App Roles
- Administrator
- Editor
In Enterprise Applications, I've also added the App Roles above to my users:
My code
Login Provider
namespace Example.Api.Features.Authentication.Extensions;
public static class UmbracoBuilderExtensions
{
public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder)
{
builder.Services.ConfigureOptions<OpenIdConnectBackOfficeExternalLoginProviderOptions>();
builder.AddBackOfficeExternalLogins(logins =>
{
const string schema = MicrosoftAccountDefaults.AuthenticationScheme;
logins.AddBackOfficeLogin(
backOfficeAuthenticationBuilder =>
{
backOfficeAuthenticationBuilder.AddMicrosoftAccount(
// the scheme must be set with this method to work for the back office
backOfficeAuthenticationBuilder.SchemeForBackOffice(OpenIdConnectBackOfficeExternalLoginProviderOptions.SchemeName) ?? string.Empty,
options =>
{
//By default this is '/signin-microsoft' but it needs to be changed to this
options.CallbackPath = "/umbraco-signin-microsoft/";
//Obtained from the AZURE AD B2C WEB APP
options.ClientId = "CLIENT_ID";
//Obtained from the AZURE AD B2C WEB APP
options.ClientSecret = "CLIENT_SECRET";
options.TokenEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/token";
options.AuthorizationEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/authorize";
});
});
});
return builder;
}
}
Auto-linking accounts
namespace Example.Api.Features.Configuration;
public class OpenIdConnectBackOfficeExternalLoginProviderOptions : IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
public const string SchemeName = "OpenIdConnect";
public void Configure(string name, BackOfficeExternalLoginProviderOptions options)
{
if (name != "Umbraco." + SchemeName)
{
return;
}
Configure(options);
}
public void Configure(BackOfficeExternalLoginProviderOptions options)
{
options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
// must be true for auto-linking to be enabled
autoLinkExternalAccount: true,
// Optionally specify default user group, else
// assign in the OnAutoLinking callback
// (default is editor)
defaultUserGroups: new[] { Constants.Security.EditorGroupAlias },
// Optionally you can disable the ability to link/unlink
// manually from within the back office. Set this to false
// if you don't want the user to unlink from this external
// provider.
allowManualLinking: false
)
{
// Optional callback
OnAutoLinking = (autoLinkUser, loginInfo) =>
{
// You can customize the user before it's linked.
// i.e. Modify the user's groups based on the Claims returned
// in the externalLogin info
autoLinkUser.IsApproved = true;
},
OnExternalLogin = (user, loginInfo) =>
{
// You can customize the user before it's saved whenever they have
// logged in with the external provider.
// i.e. Sync the user's name based on the Claims returned
// in the externalLogin info
return true; //returns a boolean indicating if sign in should continue or not.
}
};
// Optionally you can disable the ability for users
// to login with a username/password. If this is set
// to true, it will disable username/password login
// even if there are other external login providers installed.
options.DenyLocalLogin = true;
// Optionally choose to automatically redirect to the
// external login provider so the user doesn't have
// to click the login button. This is
options.AutoRedirectLoginToExternalProvider = true;
}
}
In this file, I'd ideally do as the comment says and i.e. Modify the user's groups based on the Claims returned in the externalLogin info
.
Also registered in my Startup file
services.AddUmbraco(_env, _config)
.AddBackOffice()
.AddWebsite()
.AddComposers()
.ConfigureAuthentication()
.Build();
I've attempted to give the following permissions to the application, with no luck:
Current state of play is that I can login just fine but if I debug externalInfo
, there's nothing in there about the users having either the Administrator
or Editor
App Role as configured above.
My gut feeling is that I'm missing something with the Azure Active Directory setup but I've tried a few different configurations and can't seem to get the App Roles to come back.
Thanks,
Ben
EDIT - 15.02.2023:
I can see that the roles come back when I hit the https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
endpoint using client_credentials
as the grant_type
. It looks like the .NET application using authorization_code
instead. I've decoded the token retrieved from this and it doesn't contain the roles.
I wonder if there's some kind of configuration on the .NET application that allows me to add the roles.
App roles
in your Azure AD application? – GarridoApp roles
. I've updated my question. – IntimaUser/Group + Applications
. I checked the token coming back and it doesn't contain roles. Though if I hit the token endpoint manually using theclient_credentials
grant_type
, it does contain the roles. The code usesauthorisation_code
instead which doesn't contain the roles. – Intima