MSAL.Net 没有帐户或登录提示传递到 AcquireTokenSilent 调用

2024-05-08

我见过很多相同或类似的问题,并尝试了他们所有的答案(如果有的话),但这些都不适合我。

我在用着这个例子 https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi来自微软的Github帐户作为我的项目库。

它非常适合仅登录的用户。

该项目有 1 个 WebApi、1 个 Angular App。

然后我按照这个微软的例子 https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/2-WebApp-graph-user/2-1-Call-MSGraph添加调用 Graph API 的代码。 这是控制器代码:

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class BillsController : ControllerBase
{
    static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
    readonly ITokenAcquisition tokenAcquisition;
    readonly WebOptions webOptions;

    public BillsController(ITokenAcquisition tokenAcquisition,
                          IOptions<WebOptions> webOptionValue)
    {
        this.tokenAcquisition = tokenAcquisition;
        this.webOptions = webOptionValue.Value;
    }

    [HttpGet]
    [AuthorizeForScopes(Scopes = new[] { Constants.ScopeUserRead, Constants.ScopeMailRead })]
    public async Task<IActionResult> Profile()
    {
        HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);

        var subject = string.Empty;
        try
        {
            // Initialize the GraphServiceClient. 
            Graph::GraphServiceClient graphClient = GetGraphServiceClient(new[] { Constants.ScopeUserRead, Constants.ScopeMailRead });

            var me = await graphClient.Me.Request().GetAsync();
            // Get user photo
            var messages = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
            subject = messages.First().Subject;
            return Ok(subject);
        }
        catch (System.Exception ex)
        {
            throw ex;
        }
    }

    private Graph::GraphServiceClient GetGraphServiceClient(string[] scopes)
    {
        return GraphServiceClientFactory.GetAuthenticatedGraphClient(async () =>
        {
            string result = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
            return result;
        }, webOptions.GraphApiUrl);
    }
}

对于 Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        // Setting configuration for protected web api
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddProtectedWebApi(Configuration);

        services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { Constants.ScopeUserRead, Constants.ScopeMailRead })
            .AddInMemoryTokenCaches();

        services.AddOptions();
        services.AddGraphService(Configuration);

        // Creating policies that wraps the authorization requirements
        services.AddAuthorization();

        services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));

        services.AddControllers();

        // Allowing CORS for all domains and methods for the purpose of sample
        services.AddCors(o => o.AddPolicy("default", builder =>
        {
            builder.AllowAnyOrigin()
                   .AllowAnyMethod()
                   .AllowAnyHeader();
        }));
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            // Since IdentityModel version 5.2.1 (or since Microsoft.AspNetCore.Authentication.JwtBearer version 2.2.0),
            // Personal Identifiable Information is not written to the logs by default, to be compliant with GDPR.
            // For debugging/development purposes, one can enable additional detail in exceptions by setting IdentityModelEventSource.ShowPII to true.
            // Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseExceptionHandler("/error");

        app.UseCors("default");
        app.UseHttpsRedirection();
        app.UseCookiePolicy();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

在 Angular 应用程序上,我添加了一个按钮来调用此 Profile() 控制器操作。

todo-view.component.ts

  getEmails(): void {
    this.service.getEmails().subscribe({
      next: (emails: any) => {
        alert(emails);
      },
      error: (err: any) => {
        console.log("error happened~!");
        console.log(err);
      }
    });
  }

todo-view.component.html

<button (click)="getEmails();">Get Emails</button>

我将以下代码添加到 Startup.cs 中并删除了 AddWebAppCallsProtectedWebApi。 services.AddProtectedWebApiCallsProtectedWebApi(配置).AddInMemoryTokenCaches();

现在它向我抛出了不同的错误消息:


我在使用 React 应用程序时遇到了同样的问题。由于 AuthorizeForScopes 用于返回视图,因此它不适用于 API 解决方案。我能够添加一些配置选项以使其正常工作。

我做的第一件事就是使用 SQL 缓存。这有助于在站点重新启动时阻止“无登录提示”错误。之后,令牌将正常工作,直到超时,之后令牌将从缓存中删除,并且错误将再次出现。

为此,我开始查看配置设置。我将配置更改为以下内容。

services
    .AddWebAppCallsProtectedWebApi(new string[] { "User.Read" }, idOps =>
    {
        Configuration.Bind("AzureAd", idOps);
        idOps.SaveTokens = true;
        idOps.RefreshOnIssuerKeyNotFound = true;
        idOps.SingletonTokenAcquisition = true;
        idOps.UseTokenLifetime = true;
    },
    ops => Configuration.Bind("AzureAd", ops))
    .AddDistributedTokenCaches()
    .AddDistributedSqlServerCache(options =>
    {
        options.ConnectionString = Configuration.GetConnectionString("Site_DbContext");
        options.SchemaName = "dbo";
        options.TableName = "_TokenCache";
    });

我还没有对它进行太多测试来找出神奇的组合,但最佳点似乎是SingletonTokenAcquisition。有了这个集合,它的行为似乎就像一个混合缓存。首次设置时,它将令牌拉入内存并保存它,因此,如果将其从数据库缓存中删除,它仍然可以访问它。

其他设置可能是刷新所必需的,但我还没有测试过。

我注意到的一件事是,令牌在刷新之前不会被添加回 SQL 缓存,因此,如果发生令牌被删除并且站点出现故障清除内存的情况,错误可能会再次出现,但这是最好的到目前为止我找到的解决方案。我的 SPA 能够运行 24 小时,并且仍然能够提取新数据。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MSAL.Net 没有帐户或登录提示传递到 AcquireTokenSilent 调用 的相关文章

随机推荐