Атрибут Authorize всегда возвращает 401 - PullRequest
1 голос
/ 20 марта 2019

Я использую поток учетных данных клиента / идентификацию приложения (OAuth 2.0), где API может аутентифицировать веб-приложение по его идентификатору приложения.Есть две вещи, которые мне нужны, чтобы убедиться, что аутентификация прошла успешно:

  1. Токен доступа, переданный из веб-приложения для доступа к API, должен быть действительным токеном-носителем (например: не истек,действительный формат и т. д.)

  2. Идентификатор приложения из маркера доступа должен быть указанным веб-приложением

Когда я помещаюАтрибут [authorize] в классе контроллера продолжал возвращать 401.

Вот класс startup.cs

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseMvc();
        }

AzureAdAuthenticationBuilderExtentsions класс

public static class AzureAdAuthenticationBuilderExtentsions
    {
        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder)
        => builder.AddAzureAdBearer(_ => { });

        public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
        {
            builder.Services.Configure(configureOptions);
            builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureAzureOptions>();
            builder.AddJwtBearer();
            return builder;
        }

        private class ConfigureAzureOptions : IConfigureNamedOptions<JwtBearerOptions>
        {
            private readonly AzureAdOptions _azureOptions;

            public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }

            public void Configure(string name, JwtBearerOptions options)
            {
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidAudiences = new string[] {
                        _azureOptions.ClientId,
                        _azureOptions.ClientIdUrl
                    },
                    ValidateAudience = true,
                    ValidateIssuer = true,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                    RequireExpirationTime = true
                };
                options.Audience = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
            }

            public void Configure(JwtBearerOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }

ВотКласс AzureAdOptions

 public class AzureAdOptions
    {
        internal static readonly object Settings;

        public string ClientId { get; set; }

        public string ClientIdUrl { get; set; }

        public string ClientSecret { get; set; }

        public string Instance { get; set; }

        public string Domain { get; set; }

        public string TenantId { get; set; }
    }

И класс контроллера

  [Route("api")]
    [ApiController]
public class FindController : ControllerBase
{
    private IConfiguration _configuration;
    HttpClient _client;
    public ContentController( IConfiguration configuration)
    {
        _configuration = configuration;
    }

    private bool ValidateRequest()
    {
        var authHeader = Request.Headers["Authorization"];
        if (StringValues.IsNullOrEmpty(authHeader) || authHeader.Count == 0)
        {
            throw new UnauthorizedAccessException(Messages.AuthHeaderIsRequired);
        }
        var tokenWithBearer = authHeader.Single();
        var token = tokenWithBearer.Substring(7); //remove bearer in the token
        var jwtHandler = new JwtSecurityTokenHandler();
        if (!jwtHandler.CanReadToken(token))
        {
            throw new FormatException("Invalid JWT Token");
        }

        var tokenS = jwtHandler.ReadToken(token) as JwtSecurityToken;
        var appId = tokenS.Audiences.First();
        if (string.IsNullOrEmpty(appId))
        {
            throw new UnauthorizedAccessException(Messages.AppIdIsMissing);
        }
        var registeredAppId = _configuration.GetSection("AzureAd:AuthorizedApplicationIdList")?.Get<List<string>>();
        return (registeredAppId.Contains(appId)) ? true : false;
    }
    [HttpPost("Find")]
    [Produces("application/json")]
    [Authorize]
    public async Task<IActionResult> Find()
    {
        try
        {
            if (!ValidateRequest())
            {
                return Unauthorized();
            }
         return new ObjectResult("hello world!");
        }
        catch (InvalidOperationException)
        {
            return null;
        }
    }
}

Кто-нибудь знает, почему он продолжает возвращать ошибку 401?Одна вещь, которую я хотел бы упомянуть, это то, что между моментом, когда я начинаю вызывать API, пока он не возвращает ошибку 401, точки останова внутри класса контроллера никогда не были достигнуты ...

Ответы [ 2 ]

1 голос
/ 20 марта 2019

Если ресурс App ID URI приложения API при получении токена доступа для доступа к приложению API. В приложении API разрешенная аудитория должна также включать App ID URI приложения API.

1 голос
/ 20 марта 2019

Я бы сделал 2 очка для проверки:

1.Проверьте соответствие appID AuthorizedApplicationIdList в вашем коде

Я думаю, что способ, которым вы описали условия для проверки в словах, подходит, но есть проблема с тем, как вы реализовали второе условие в коде.

Идентификатор приложения из маркера доступа должен быть указанным веб-приложением

При реализации этого условия кажется, что вы устанавливаете appId в качестве значения из aud, то есть утверждения аудиториив знакЭто неверно, потому что аудитория всегда будет вашим собственным API, для которого предназначен этот токен.

Вместо этого вы хотите проверить значение для утверждения appid в токене, которое будет идентификатором приложения для клиента.который приобрел этот токен.Это должен быть идентификатор приложения внешнего веб-приложения, который вы хотите проверить по списку авторизованных приложений.

Взгляните на справочник Microsoft Docs по токенам доступа

enter image description here

enter image description here

Кроме того, это можно легко проверить, расшифровав токен с помощью https://jwt.ms

* 1036.* Соответствующий код из вашего поста, где я вижу выпуск :
    var appId = tokenS.Audiences.First();
    if (string.IsNullOrEmpty(appId))
    {
        throw new UnauthorizedAccessException(Messages.AppIdIsMissing);
    }
    var registeredAppId = _configuration.GetSection("AzureAd:AuthorizedApplicationIdList")?.Get<List<string>>();
    return (registeredAppId.Contains(appId)) ? true : false;

2.Общий журнал / отладка

Кроме того, вы можете отладить или поместить операторы log / trace в свой код API, чтобы точно определить, где происходит сбой в вашем коде ... или неожиданнотерпит неудачу где-то еще до того, как ваша пользовательская логика называется.Возможно, пока выполняются некоторые из начальных проверок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...