简体   繁体   中英

.Net Core MVC app with IdentityServer4, login with Azure Active Directory. Problem with admin consent and permissions

I'm trying to have integration with AAD for the MVC web app made with IdentityServer4. I wanted to have a code authentication flow [https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow]. After my changes in existing code (current code is show later in the post), all seemed to work properly. However, after some testing I found out that when I'm trying to sign in with an account that does not have any administrator role assigned to it, the popup below is shown. Popup shown for users without admin role

And when I'm trying to sign in to the app with account that has administrator role this popup is shown Popup shown for users with admin role

It seems to be proper behavior to me, because one of the permissions requires the admin consent.

But the problem start when I consent the permissions on behalf of my organization (sing in as user with admin Global admin role). My expected behavior is that after granting consent by one of the admins, regular users can access the app.

The actual result is that the popup number 1 is still presented to users and they can't enter my application.

What is going in my code when user signed in successfully?

Basically, when user sign in with AAD to my app for the very first time, I want to add him to my users table in the DB. If it's not the first time he sign in, I want to update some info based on the profile and the group user is assigned to. To do so I need to read the basic user information and groups names using Microsoft Graph API.

The permissions (type=delegated) I configured for the app:

  • GroupMember.Read.All
  • User.Read

GroupMember.Read.All requires admin consent. What I found out is that I need this 'GroupMember.Read.All' to retrieve groups with properties such as DisplayName filled. And it seems this permission is causing the problem with signing in. (only with User.Read signing in works fine, but obviously I don't have access to groups names etc, there are only groups ids)

The code I use for getting signed in user group info:

        var accessToken = authResult.Properties.GetTokenValue("access_token");

        GraphServiceClient graphClient = GetClient(accessToken);

        var groupsList = (await graphClient.Me.MemberOf.Request().GetAsync()).ToList();

As you can see, the access token is retrieved from properties of 'AuthenticateResult'.

Logic for getting this access_token to use graph resource is here below in Startup.cs file. Having authentication code returned I get access_token for Microsoft Graph and then I pass it to authentication result that I can use further in my services code like show above (getting groups the user is member of).

if (aadSettings.IsValid)
        {
            services.AddAuthentication()
            .AddOpenIdConnect("aad", "Active Directory", options =>
            {
                options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
                options.SignOutScheme = IdentityServerConstants.SignoutScheme;

                options.Authority = aadSettings.ADAuthority;
                options.ClientId = aadSettings.ADClientId;
                options.ClientSecret = aadSettings.ApplicationSecret;

                options.CallbackPath = aadSettings.CallbackPath;
                options.SignedOutCallbackPath = aadSettings.SignedOutCallbackPath;
                options.RemoteSignOutPath = aadSettings.RemoteSignOutPath;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                };
                options.Prompt = "consent";
                options.Scope.Add("https://graph.microsoft.com/User.Read");
                options.Scope.Add("https://graph.microsoft.com/GroupMember.Read.All");

                options.ResponseType = "code id_token";
                options.Resource = "https://graph.microsoft.com/";
                options.SaveTokens = true;

                options.Events.OnAuthorizationCodeReceived = async contex => {
                    var authCode = contex.ProtocolMessage.Code;
                    var credential = new ClientCredential(aadSettings.ADClientId, aadSettings.ApplicationSecret);
                    var currentUri = UriHelper.BuildAbsolute(contex.Request.Scheme, contex.Request.Host, contex.Request.PathBase, contex.Request.Path);
                    var authContext = new AuthenticationContext(aadSettings.ADAuthority);

                    AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(authCode, new Uri(currentUri), credential, options.Resource);
                    contex.HandleCodeRedemption(result.AccessToken, result.IdToken);
                };
            });

URL when sending request for auth code: https://login.microsoftonline.com/{tenant_id}/oauth2/authorize?client_id={client_id}&redirect_uri=http%3A%2F%2Flocalhost%3A5000%2Fsignin-oidc&resource=https%3A%2F%2Fgraph.microsoft.com%2F&response_type=code%20id_token&prompt=consent&scope=openid%20profile%20https%3A%2F%2Fgraph.microsoft.com%2FUser.Read%20https%3A%2F%2Fgraph.microsoft.com%2FGroupMember.Read.All&response_mode=form_post&nonce=...&state=.. .

So the question is: How can I solve the issue with signing in for users without admin roles? and how can i get groups the user is assigned to? Am I missing some configuration in AAD? Or maybe I'm using wrong permissions?

You have specified options.Prompt = "consent"; , which triggers the consent dialog every time regardless if the user has consented or not. Since you are using the "v1" authorization endpoint, you don't need to specify the Graph API scope URIs in the scope parameter (it looks at the permissions in your app registration). Removing the prompt parameter should allow users to login without consenting to permissions when they have already been consented.

One downside of the v1 endpoint is that if there are any permissions that have been consented to, consent is not triggered again, even if you add new permissions to your app registration. This works better with the v2 endpoint, where you can specify the permissions needed in the scope parameter (and leave out the resource parameter). In v2, consent is triggered if not all the requested scopes have been consented to.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM