MSAL.Net没有帐户或登录提示传递给AcquireTokenSilent调用 [英] MSAL.Net No account or login hint was passed to the AcquireTokenSilent call

查看:109
本文介绍了MSAL.Net没有帐户或登录提示传递给AcquireTokenSilent调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到了许多相同或相似的问题,并且尝试了所有答案,如果有一个,但对我来说都没有用.

I have seen many same or similar questions, and tried all their answers if there was one, but none of those works for me.

我正在使用此示例以Microsoft的Github帐户作为我的项目基础.

I'm using this example from Microsoft's Github account as my project base.

它仅适用于登录用户.

该项目有1个WebApi,1个Angular应用程序.

The project has 1 WebApi, 1 Angular App.

然后,我按照以下

Then I followed this Microsoft example to add code to call Graph API. Here is the controller code:

[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

For 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 App上,我添加了一个按钮来调用此Profile()控制器动作.

On the Angular App, I added one button to call this Profile() controller action.

todo-view.component.ts

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

todo-view.component.html

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

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

I added the below code into my Startup.cs and removed the AddWebAppCallsProtectedWebApi. services.AddProtectedWebApiCallsProtectedWebApi(Configuration).AddInMemoryTokenCaches();

现在,它向我抛出了另一条错误消息:

Now it throws me a different error message:

推荐答案

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

I was having the same issue with a react app. Since the AuthorizeForScopes is for returning views, it does not work for API solutions. I was able to add some configuration options to get it working.

我做的第一件事是使用SQL缓存.这有助于停止无登录提示".网站重新启动时发生错误.此后,令牌将可以正常工作,直到超时为止,此后令牌将从高速缓存中删除,并且错误会再次出现.

The first thing I did was use a SQL cache. This helped stop the "No login hint" error when the site restarted. After that, the token would work fine until timeout, after which the token would get removed from the cache and the error would reappear.

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

For that, I started looking at the configuration settings. I changed my configuration to the following.

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.有了这个设置,它看起来就像一个混合缓存.第一次设置时,它将令牌存储到内存中并保存它,因此,如果将其从数据库缓存中删除,它仍然可以访问它.

I haven't done much testing on it to find out the magic combination, but the sweet spot seems to be SingletonTokenAcquisition. With that set, it seems to be behaving like a hybrid cache. When first set, it pulls the token into memory and holds it so if it is removed from the database cache, it still has access to it.

刷新可能需要其他设置,但我尚未测试过.

The other settings may be necessary for the refreshing but I haven't tested that yet.

我注意到的一件事是令牌直到刷新后才重新添加到SQL缓存中,因此,如果在删除令牌的情况下发生了某些事情,并且该站点关闭了清除内存的操作,则错误可能会再次出现,但这是到目前为止找到的最好的解决方案.我能够使SPA运行24小时,但仍然能够提取新数据.

The one thing I did notice is the token doesn't get added back to the SQL cache until it refreshes so if something happens where the token is removed and the site goes down clearing the memory, the error may reappear, but this is the best solution I found so far. I was able to have my SPA running for 24 hours and was still able to pull new data.

这篇关于MSAL.Net没有帐户或登录提示传递给AcquireTokenSilent调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆