简体   繁体   中英

AAD OpenId and Bearer Authentication on the same Application

I'm trying to create auth using Azure Active Directory Authentication, my application consists of WebApis and WEb Pages, what I'm trying to achieve is to redirect the user to Microsoft login page if the user requested the the application "controller" pages, and to authorize the client application when calling API controller by the token.

I managed to do this by setting up the Startup.Auth.cs as below

public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());
        app.UseOpenIdConnectAuthentication(
        new OpenIdConnectAuthenticationOptions
        {
            
            ClientId = clientId,
            Authority = authority,
            RedirectUri = redirectUri,
            PostLogoutRedirectUri = redirectUri,
            Scope = OpenIdConnectScope.OpenIdProfile,
            ResponseType = OpenIdConnectResponseType.IdToken,
            TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = true
            },
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                AuthenticationFailed = OnAuthenticationFailed
            }
        });

                    app.UseWindowsAzureActiveDirectoryBearerAuthentication(
               new WindowsAzureActiveDirectoryBearerAuthenticationOptions
               {
                   Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
                   TokenValidationParameters = new TokenValidationParameters { SaveSigninToken = true, ValidAudience = $"api://{clientId}" }
               });
    }

Decorated my controllers with [Authorize] attribute

However, when an unauthorized client application calls the API it is returning the below

  <html>

<head>
    <title>Working...</title>
</head>

<body>
    <form method="POST" name="hiddenform" action="https://local:44341/">
        <input type="hidden" name="id_token" value="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Im5PbzNaRHJPRFhFSzFqS1doWHNsSFJfS1hFZyJ9.eyJhdWQiOiI3NjIzMjcyMC1hNmViLTRkOTctOTI3Yy1lMWFjNzRiODIyMjAiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vYjFmZTVlMmEtZjc1Yy00NDQxLTlmYjItNWFjYzAxYmEwMGQwL3YyLjAiLCJpYXQiOjE2MTA3MzAJub25jZSI6IjYzNzQ2MzI3OTEwNDAxMzk3Ni5aR0k1TmpCaU1XWXRPRGsxT0MwMFpEWmlMV0kxWXprdE9ESTFZMlZrTWpjeU0yWTJNRFZqTWpnNU9EY3RNak0yWkMwME1qWmpMV0UwTTJZdE5XSXpNVFZsTlRKa1pHRXgiLCJvaWQiOiJiN2YzMjg1Mi1hMDRhLTRlOWUtYjFjOS02NTI5NDk3YTgyZDEiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiIyMjQxN0Byai5jb20iLCJyaCI6IjAuQUFBQUtsNy1zVnozUVVWoeHm6x2ad02ExHlw48lfRVZhEoHw8cZ5SWRFOGZfi3TJ3aOFKdaDZZ8Yt1no30Hku8pL3Vf7Lrq-BuKX8PNtkU45aVO1fZf8PFdM4UbpYnHOwvuG1NlPG8mTa1KheQTza9j3PTaiLq8e15jtSoFoHfIJbMZJoNTfvAF40kt9XvYee-rga80Oj1tJX78g_80MmRYORwArr1rq1n6EyPgFHaDDF5vD-zWOLDrXKyj2rwW-7LLpbtojtbsyCPdM5QPkLKnZZFanpvwRAFRTHaLdHINGAlHhvhFP9kvRhVtaTUgwYzrFqlN8k3zSZrvnMOec7A" /><input type="hidden" name="state" value="OpenIdConnect.AuthenticationProperties=7_QsmCbQZ3vBxo1tTvZXcYRLeNQMBZfanq5zpZvqNjuudSAu52-UZnVGkkWMXeBh_rIHE3i_j8g_B751WFqQHR1CXJrdjBi6PZy-" /><input type="hidden" name="session_state" value="5938bc609" /><noscript>
            <p>Script is disabled. Click Submit to continue.</p><input type="submit" value="Submit" /></noscript></form>
    <script language="javascript">
        document.forms[0].submit();
    </script>
</body>

</html>

But when removing the call of UseOpenIdConnectAuthentication from my function, it is returning the correct response message with status code which is 401

{    "Message": "Authorization has been denied for this request."}

Having said that, can anyone help me on how to set the bearer token auth for web api, and the openIdConnect for the webapplication pages.

Thank you, and please tell me if my approach is wrong, and recommend the correct one for my requirement.

Don't use UseOpenIdConnectAuthentication here, it's for customization of OpenID support which isn't really applicable here, with out-of-the box Azure AD specific helpers like the one you used next.

Here's a good starting point for server side: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-overview

And for client side, generic caller (console app or arbitrary service): https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-overview

For client side, an interactive web application which needs to protect itself: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-web-app-sign-user-overview

In a nutshell, your API client must ask Azure AD for a token to access your API endpoint. For Azure AD to know which resource this token is for, create an Application Registration for your API, and configure the API app with its client ID and any other necessary params.

When your application receives the token, it will have to contact Azure AD to validate it. For this call, it should use the client id you generated for it in Azure AD.

You may want to run the client while impersonating a real AD login, or you might run the client as some AD principal (Application Registration). See if the example of a .NET Core 3.1 client application below can get you going. It impersonates some AD principal with its id and secret, so it can run and connect to an Azure AD-protected service from outside Azure cloud, without a managed identity.

For the sample below to work, make sure to properly configure the principals (Application Registration / Enterprise Application) of the client app and server API. This is quite a topic of its own, well described in the official documentation linked above.

If your client runs as Azure AppService or on an Azure VM, better use Azure Managed Identity. This eliminates need for secrets and simplifies constructor call to AzureServiceTokenProvider (no args = use ambient context = pick up managed identity). Unfortunately local development isn't that easy to do via Azure.

using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace N
{
    public class Test
    {
        public async Task Main(IConfigurationRoot config, ILoggerFactory loggerFactory)
        {
            using var httpClient = new HttpClient(new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) =>
                {
                    return true;
                }
            });

            var url = "https://localhost:5000";
            var clientId = "guid of your caller's identity App Registration";
            var clientSecret = "secret you produced for your caller's managed identity App Registration";
            var tenantId = "guid (Azure tenant id)";
            var runAs = $"RunAs=App;AppId={clientId};TenantId={tenantId};AppKey={clientSecret}";

            var tokenProvider = new AzureServiceTokenProvider(runAs);
            var accessToken = await tokenProvider.GetAccessTokenAsync("https://my-test-app-identity.azurewebsites.net");

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, new Uri(uri));
            requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

            var response = await httpClient.SendAsync(requestMessage);
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsStringAsync();

            foreach (var header in response.Headers)
            {
                Console.WriteLine(header.Key + ": " + string.Join(",", header.Value));
            }
            Console.WriteLine(content);
        }
    }
}

Dependencies in.csproj:

<PackageReference Include="Azure.Core" Version="1.4.1" />
<PackageReference Include="Azure.Identity" Version="1.2.2" />
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.1.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.17.1" />

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