【问题标题】:MSAL .NET Device Code Authentication in ASP.NET API not working when app is running in docker container当应用程序在 docker 容器中运行时,ASP.NET API 中的 MSAL .NET 设备代码身份验证不起作用
【发布时间】:2021-11-12 12:45:54
【问题描述】:

我正在尝试在我的容器化 ASP.NET WebAPI 项目中使用设备代码身份验证。当我在 docker 之外运行应用程序时一切正常,但是当应用程序在 docker 容器中运行时出现以下错误:

Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HMBPTCKK2U5R", Request id "0HMBPTCKK2U5R:00000002": An unhandled exception was thrown by the application.
      MSAL.NetCore.4.36.0.0.MsalServiceException:
        ErrorCode: invalid_client
      Microsoft.Identity.Client.MsalServiceException: A configuration issue is preventing authentication - check the error message from the server for details. You can modify the configuration in the application registration portal. See https://aka.ms/msal-net-invalid-client for details.  Original exception: AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.
      Trace ID: 79b92211-930d-41a2-9bf7-af89711cbe00
      Correlation ID: 7e73cb95-1e9c-4a28-ab66-0672aa3e9e1c
      Timestamp: 2021-09-17 14:56:50Z
         at Microsoft.Identity.Client.OAuth2.OAuth2Client.ThrowServerException(HttpResponse response, RequestContext requestContext)
         at Microsoft.Identity.Client.OAuth2.OAuth2Client.CreateResponse[T](HttpResponse response, RequestContext requestContext)
         at Microsoft.Identity.Client.OAuth2.OAuth2Client.ExecuteRequestAsync[T](Uri endPoint, HttpMethod method, RequestContext requestContext, Boolean expectErrorsOn200OK, Boolean addCommonHeaders)
         at Microsoft.Identity.Client.OAuth2.OAuth2Client.GetTokenAsync(Uri endPoint, RequestContext requestContext, Boolean addCommonHeaders)
         at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ICoreLogger logger)
         at Microsoft.Identity.Client.OAuth2.TokenClient.SendHttpAndClearTelemetryAsync(String tokenEndpoint, ICoreLogger logger)
         at Microsoft.Identity.Client.OAuth2.TokenClient.SendTokenRequestAsync(IDictionary`2 additionalBodyParameters, String scopeOverride, String tokenEndpointOverride, CancellationToken cancellationToken)
         at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.WaitForTokenResponseAsync(DeviceCodeResult deviceCodeResult, CancellationToken cancellationToken)
         at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.WaitForTokenResponseAsync(DeviceCodeResult deviceCodeResult, CancellationToken cancellationToken)
         at Microsoft.Identity.Client.Internal.Requests.DeviceCodeRequest.ExecuteAsync(CancellationToken cancellationToken)
         at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
         at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenWithDeviceCodeParameters deviceCodeParameters, CancellationToken cancellationToken)
         at Elicity.Ampere.Logic.Authentication.DeviceCodeAuthenticationLogic.GetTokenForWebApiUsingDeviceCodeFlowAsync(IEnumerable`1 scopes, Action`1 deviceCodeCallback) in /app/Logic/Logic.Authentication/DeviceCodeAuthenticationLogic.cs:line 109
         at Elicity.Ampere.Logic.Authentication.DeviceCodeAuthenticationLogic.AcquireATokenFromCacheOrDeviceCodeFlowAsync(IEnumerable`1 scopes, Boolean interactive, Action`1 deviceCodeCallback) in /app/Logic/Logic.Authentication/DeviceCodeAuthenticationLogic.cs:line 78
         at Elicity.Ampere.Service.Authentication.AuthenticationController.Get() in /app/Services/Service.Authentication/Controllers/AuthenticationController.cs:line 65
         at lambda_method7(Closure , Object )
         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
        StatusCode: 401
        ResponseBody: {"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 79b92211-930d-41a2-9bf7-af89711cbe00\r\nCorrelation ID: 7e73cb95-1e9c-4a28-ab66-0672aa3e9e1c\r\nTimestamp: 2021-09-17 14:56:50Z","error_codes":[7000218],"timestamp":"2021-09-17 14:56:50Z","trace_id":"79b92211-930d-41a2-9bf7-af89711cbe00","correlation_id":"7e73cb95-1e9c-4a28-ab66-0672aa3e9e1c","error_uri":"https://login.microsoftonline.com/error?code=7000218"}
        Headers: Cache-Control: no-store, no-cache
      Pragma: no-cache
      Strict-Transport-Security: max-age=31536000; includeSubDomains
      X-Content-Type-Options: nosniff
      P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
      client-request-id: 7e73cb95-1e9c-4a28-ab66-0672aa3e9e1c
      x-ms-request-id: 79b92211-930d-41a2-9bf7-af89711cbe00
      x-ms-ests-server: 2.1.12025.15 - WEULR2 ProdSlices
      x-ms-clitelem: 1,7000218,0,,
      Set-Cookie: fpc=Ah_T82wZi7hJgqcpQ9HohJiJEGHQAQAAAFaj1tgOAAAAUSo9cgEAAACyo9bYDgAAAA; expires=Sun, 17-Oct-2021 14:56:50 GMT; path=/; secure; HttpOnly; SameSite=None, x-ms-gateway-slice=estsfd; path=/; secure; httponly, stsservicecookie=estsfd; path=/; secure; samesite=none; httponly
      Date: Fri, 17 Sep 2021 14:56:50 GMT

我在 MacOS 和 Linux 环境中测试了代码,两次都按预期工作。

public DeviceCodeAuthenticationLogic(PublicClientApplicationOptions msalOptions, IPublishEndpoint publishEndpoint)
        {
            _publishEndpoint = publishEndpoint;
            _app = PublicClientApplicationBuilder.CreateWithApplicationOptions(msalOptions).Build();
            var storageProperties = new StorageCreationPropertiesBuilder("user", "~/cache", msalOptions.ClientId)
                .WithLinuxUnprotectedFile()
                // This makes development on mac easier.
                .WithMacKeyChain("app_context", "user_token")
                .Build();
            var cacheHelper = MsalCacheHelper.CreateAsync(storageProperties).GetAwaiter().GetResult();
            cacheHelper.RegisterCache(_app.UserTokenCache);
        }

public async Task<AuthenticationResult> AcquireATokenFromCacheOrDeviceCodeFlowAsync(IEnumerable<string> scopes, bool interactive = true, Action<DeviceCodeResult> deviceCodeCallback = null)
        {
            AuthenticationResult result = null;
            var accounts = await _app.GetAccountsAsync();

            if (accounts.Any())
                try
                {
                    // Attempt to get a token from the cache (or refresh it silently if needed)
                    result = await _app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                        .ExecuteAsync();
                }
                catch (MsalUiRequiredException)
                {
                }

            // No token in cache, attempt device code flow
            if (result == null && interactive)
                result = await GetTokenForWebApiUsingDeviceCodeFlowAsync(scopes, deviceCodeCallback);

            return result;
        }

private async Task<AuthenticationResult> GetTokenForWebApiUsingDeviceCodeFlowAsync(IEnumerable<string> scopes,
            Action<DeviceCodeResult> deviceCodeCallback = null)
        {
            AuthenticationResult result;
            try
            {
                result = await _app.AcquireTokenWithDeviceCode(scopes,
                    callback =>
                    {
                        if (deviceCodeCallback is not null) deviceCodeCallback(callback);
                        return Task.FromResult(0);
                    }).ExecuteAsync();
            }
            catch (MsalServiceException ex)
            {
                throw;
            }
            catch (OperationCanceledException)
            {
                result = null;
            }
            catch (MsalClientException ex)
            {

                result = null;
            }

            return result;
        }

该错误向我表明这是我的配置错误,但它为什么会在 docker 环境之外工作。

我对此有点迷茫,非常感谢任何帮助。

【问题讨论】:

    标签: c# docker asp.net-web-api msal


    【解决方案1】:

    我发现了问题。我的 b2c 中的应用注册未配置为允许公共客户端身份验证。

    The switch controlling that setting

    验证码在更改该值后有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-20
      • 2021-10-31
      • 1970-01-01
      • 2020-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多