Unable to retrieve extended attributes (groups) with Azure AD connection

I have setup an working Enterprise connection to our companys Azure AD, when I try the connection from the Auth0 portal it works great, and when I check the login in the dashboard, I can see all Identity Provider Attributes, including the groups from Azure AD. But the issue is that I dont get the extended attributes (groups etc) when I try to authenticate to that connection from our application.

It is a .NET application, using OIDC. Here is the code from the startup.cs file:

private void UseAzureAd(IAppBuilder app)
{
// Enable Kentor Cookie Saver middleware
app.UseKentorOwinCookieSaver();

        // Set Cookies as default authentication type
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        //Enable cookie authentication, used to store the claims between requests
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
        });

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        {                
            AuthenticationType = "Auth0",
            Authority = ConfigurationManager.AppSettings["OpenIdAuthority"],
            ClientId = ConfigurationManager.AppSettings["OpenIdClientIdInternal"],
            ClientSecret = AppSettings.GetSetting<string>("ClientSecretInternal"),
            ResponseType = OpenIdConnectResponseType.IdToken,
            RedirectUri = ConfigurationManager.AppSettings["OpenIdDomainUrlInternal"],
            PostLogoutRedirectUri = LogOutOpenIdUrl,
            Scope = OpenIdConnectScope.OpenIdProfile,

            TokenValidationParameters = new TokenValidationParameters
            {                    
                NameClaimType = "name"
            },

            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = context =>
                {
                    _logger.Info("*** AuthenticationFailed with following exception: " + context.Exception.Message + "redirecting to startpage, user will not be authenticated");
                    context.Response.Redirect("/");
                    context.HandleResponse();

                    return Task.FromResult(0);
                },
                RedirectToIdentityProvider = (ctx) =>
                {
                    //To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
                    if (ctx.OwinContext.Response.StatusCode == 401 && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated)
                    {
                        ctx.OwinContext.Response.StatusCode = 403;
                        //ctx.Response.Redirect(LogoutUrl.Value); 
                        ctx.HandleResponse();
                    }

                    return Task.FromResult(0);
                },
                SecurityTokenValidated = async (ctx) =>
                {
                    //Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
                    var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
                    if (redirectUri.IsAbsoluteUri)
                    {
                        ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
                    }

                    // Make sure the GroupMembershipClaims is set in the manifest in Azure AD for the application. 
                    var adGroupClaims = ctx.AuthenticationTicket?.Identity?.Claims?.Where(c => c.Type == "groups");

                    if (adGroupClaims != null)
                    {
                        foreach (Claim claim in adGroupClaims)
                        {
                            // use the OID and get a friendly name to use as the role (if it exists)
                            var groupStringValue = ConfigurationManager.AppSettings[claim.Value];
                            if (groupStringValue != null)
                            {
                                ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, groupStringValue));

                                if (groupStringValue.Equals(Constants.AdGroups.WebSupport) || groupStringValue.Equals(Constants.AdGroups.WebIntraTest))
                                {
                                    // Access to EPiServer
                                    ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "Administrators"));
                                    ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "CmsEditors"));
                                    ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "CmsAdmins"));
                                    ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "WebEditors"));
                                    ctx.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "WebAdmins"));
                                }
                            }
                        }
                    }

                    await ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);

                    return;
                },
                AuthorizationCodeReceived = ctx =>
                {
                    ctx.AuthenticationTicket.Properties.IsPersistent = true;
                    ctx.AuthenticationTicket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60);
                    return Task.FromResult(0);
                }
            }
        });

        //Add stage marker to make sure WsFederation runs on Authenticate (before URL Authorization and virtual roles)
        app.UseStageMarker(PipelineStage.Authenticate);

        //Remap logout to a federated logout
        app.Map(LogoutUrl, map =>
        {
            map.Run(ctx =>
            {
                Logout(ctx);
                return Task.FromResult(0);
            });
        });

        //Tell antiforgery to use the name claim
        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
    }

The right configurations are made in the manifest in Azure AD, and as mentioned it works from the Auth0 portal, so I suspect there is something I’m missing in my setup above? I’m using an company associated email to login with as well, so there should be no problem there.

All feedback/suggestions is much appreciated.

Kind regards //Johannes

It seems I have missed to set the correct access rights in Azure AD, regarding to the documentation, Directory.AccessAsUser.All and Directory.Read.All. Waiting for confirmation since I dont have admin rights.

I have now added the required access rights in Azure AD, still no attributes, don’t know what I’m missing here.

Hey @johannes.lind, Thanks for posting to Auth0 Community.

As I have already provided the answer on your ticket. Just posting here as well for visibility.

Since groups does not fall under Standard OIDC claims the ID token does not return it by default.
There is a method you can add groups in the ID token its outlined below:

Basically you have write a rule something like:

Sample code:

function (user, context, callback) {
const namespace = ‘https://myapp.example.com/’;
context.idToken[namespace + ‘Groups’] = user.groups; // this will add groups to the token
callback(null, user, context);
}

This rule will groups as custom name spaced claims in the ID token, which can be accessed from the .NET application code

2 Likes

Thanks for sharing that Sidharth!

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.