ASP.NET Core:无法从 openid connect 获取 id_token 和访问令牌

问题描述 投票:0回答:1

我有 ASP.NET Core Web 应用程序,需要将 Azure AD 访问令牌发送到 PHP 脚本。但我无法使用 GetTokenAsync 从 http 上下文获取令牌 - 它总是返回 null。

在我的 Startup.cs 中,我按照 Microsoft Learn 的记录配置 openid connect:

public void ConfigureServices(IServiceCollection services)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    services.AddControllersWithViews();
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddOpenIdConnect(options =>
    {
        IConfigurationSection sectionOptions = AppConfiguration?.GetSection("OpenIdConnectOptions");
        if(sectionOptions?.GetChildren()?.Any() == true)
        {
            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.Authority = sectionOptions["Authority"];
            options.ClientId = sectionOptions["ClientId"];
            options.ClientSecret = sectionOptions["ClientSecret"];
            options.CallbackPath = new PathString(sectionOptions["CallbackPath"]);
            options.SignedOutCallbackPath = new PathString(sectionOptions["SignedOutCallbackPath"]);
            options.RemoteSignOutPath = new PathString(sectionOptions["RemoteSignOutPath"]);
            options.ResponseType = OpenIdConnectResponseType.Code;
            options.ResponseMode = OpenIdConnectResponseMode.Query;
            options.GetClaimsFromUserInfoEndpoint = true;
            List<string> scopes =
                (from curSubsectionScope in sectionOptions.GetSection("Scope")?.GetChildren()
                where !string.IsNullOrWhiteSpace(curSubsectionScope.Value)
                select curSubsectionScope.Value.Trim().ToLower())?.ToList();
            options.Scope?.Clear();
            scopes?.ForEach(options.Scope.Add);
            options.MapInboundClaims = false;
            options.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
            options.TokenValidationParameters.RoleClaimType = ClaimTypes.Role;
        }
    }
}

appsettings.json:

    "OpenIdConnectOptions": {
        "Authority": "https://login.microsoftonline.com/{my-tenant-id}/v2.0/",
        "ClientId": "{my-client-id}",
        "ClientSecret": "{my-client-secret}",
        "CallbackPath": "/signin-oidc",
        "SignedOutCallbackPath": "/signout-callback-oidc",
        "RemoteSignOutPath": "/signout-oidc",
        "RedirectUri": "http://localhost:80/azure",
        "SaveTokens": true,
        "Scope": [ "openid", "offline_access" ]
    },

我的 Azure AD 应用程序设置:

az ad app show --id $clientId --query "web.redirectUris"
[
  "https://localhost/signin-oidc",
  "https://localhost/signout-callback-oidc",
  "https://localhost/signout-oidc",
  "http://localhost/azure"
]
az ad app permission list --id $clientId
[
  {
    "resourceAccess": [
      {
        "id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",    # User.Read
        "type": "Scope"
      },
      {
        "id": "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9",    # Application.ReadWrite.All
        "type": "Role"
      },
      {
        "id": "37f7f235-527c-4136-accd-4a02d197296e",    # openid
        "type": "Scope"
      },
      {
        "id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",    # offline_access
        "type": "Scope"
      }
    ],
    "resourceAppId": "00000003-0000-0000-c000-000000000000"    # https://graph.microsoft.com
  }
]

但是当我尝试从 http 上下文获取访问令牌时,适当的方法总是返回 null:

string access_token = await httpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
string id_token = await httpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);

在调试时,我进入 GetTokenAsync 并发现该进程被 CookieAuthenticationHandler.HandleAuthenticateOnceAsync() 函数中止,该函数返回 AuthenticateResult.Failure。

我的设置有什么问题?

c# asp.net-core azure-active-directory openid-connect access-token
1个回答
0
投票

我将以下代码行添加到 ASP.net core 6 Web 应用程序中的 Program.cs 中,并获取了访问令牌和 ID 令牌。

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect(options =>
        {
            IConfigurationSection sectionOptions = builder.Configuration.GetSection("OpenIdConnectOptions");
            if (sectionOptions.GetChildren().Any())
            {
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority = sectionOptions["Authority"];
                options.ClientId = sectionOptions["ClientId"];
                options.ClientSecret = sectionOptions["ClientSecret"];
                options.CallbackPath = sectionOptions["CallbackPath"];
                options.SignedOutCallbackPath = sectionOptions["SignedOutCallbackPath"];
                options.RemoteSignOutPath = sectionOptions["RemoteSignOutPath"];
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.ResponseMode = OpenIdConnectResponseMode.Query;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.SaveTokens = true;

                var scopes = sectionOptions.GetSection("Scope").GetChildren()
                    .Select(scope => scope.Value.Trim().ToLower())
                    .ToList();
                options.Scope.Clear();
                scopes.ForEach(scope => options.Scope.Add(scope));

                options.MapInboundClaims = false;
                options.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
                options.TokenValidationParameters.RoleClaimType = ClaimTypes.Role;
            }
        });

下面是完整的 Program.cs 代码。

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddOpenIdConnect(options =>
        {
            IConfigurationSection sectionOptions = builder.Configuration.GetSection("OpenIdConnectOptions");
            if (sectionOptions.GetChildren().Any())
            {
                options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.Authority = sectionOptions["Authority"];
                options.ClientId = sectionOptions["ClientId"];
                options.ClientSecret = sectionOptions["ClientSecret"];
                options.CallbackPath = sectionOptions["CallbackPath"];
                options.SignedOutCallbackPath = sectionOptions["SignedOutCallbackPath"];
                options.RemoteSignOutPath = sectionOptions["RemoteSignOutPath"];
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.ResponseMode = OpenIdConnectResponseMode.Query;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.SaveTokens = true;

                var scopes = sectionOptions.GetSection("Scope").GetChildren()
                    .Select(scope => scope.Value.Trim().ToLower())
                    .ToList();
                options.Scope.Clear();
                scopes.ForEach(scope => options.Scope.Add(scope));

                options.MapInboundClaims = false;
                options.TokenValidationParameters.NameClaimType = JwtRegisteredClaimNames.Name;
                options.TokenValidationParameters.RoleClaimType = ClaimTypes.Role;
            }
        });
builder.Services.AddControllersWithViews(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
});
builder.Services.AddRazorPages()
    .AddMicrosoftIdentityUI();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();

HomeController.cs:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace WebApplication3
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        [Authorize]
        public async Task<IActionResult> Secure()
        {
            var authenticateResult = await HttpContext.AuthenticateAsync();
            if (authenticateResult.Succeeded)
            {
                string? accessToken = await HttpContext.GetTokenAsync("access_token");
                string? idToken = await HttpContext.GetTokenAsync("id_token");

                ViewData["AccessToken"] = accessToken;
                ViewData["IdToken"] = idToken;
            }
            else
            {
                ViewData["Error"] = "Authentication failed.";
            }
            return View();
        }
    }
}

appsettings.json:

"OpenIdConnectOptions": {
  "Authority": "https://login.microsoftonline.com/<tenant_ID>/v2.0/",
  "Instance": "https://login.microsoftonline.com/",
  "Domain": "xxxxxxxxx.onmicrosoft.com",
  "TenantId": "<tenant_ID>",
  "ClientSecret": "<client_ID>",
  "ClientId": "<client_secret>",
  "CallbackPath": "/signin-oidc",
  "SignedOutCallbackPath": "/signout-callback-oidc",
  "RemoteSignOutPath": "/signout-oidc",
  "RedirectUri": "http://localhost:7116/signin-oidc",
  "SaveTokens": true,
  "Scope": [ "openid", "profile", "offline_access" ]
}

.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UserSecretsId>aspnet-WebApplication3-a98bec0e-1eab-4fe3-bf2a-0f269133384b</UserSecretsId>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.31" NoWarn="NU1605" />
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.31" NoWarn="NU1605" />
    <PackageReference Include="Microsoft.Identity.Web" Version="2.16.0" />
    <PackageReference Include="Microsoft.Identity.Web.UI" Version="2.16.0" />
  </ItemGroup>
</Project>

我在 Azure AD 应用程序中授予了 openidprofileoffline_access 的权限,如下所示,

enter image description here

浏览器输出:

成功登录并点击转到安全页面后,我得到了以下页面,

enter image description here

我获得了访问令牌ID令牌,如下所示。

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.