Проверить токен носителя Oauth, IIssuerSecurityTokenProvider отсутствует в Microsoft.Owin.Security.Jwt 4.x

Я пытаюсь заставить мой веб-API .Net (приложение API Azure с использованием OWIN) принять токен носителя OAuth для предоставления client_credentials, но я продолжаю получать 401 Unauthorized.

Также кажется, что все образцы Microsoft устарели (не соответствуют последним пакетам nuget для Owin).

JwtFormat выполняет IIssuerSecurityTokenProvider, но его больше не существует — вместо этого JwtFormat ожидает IIssuerSecurityKeyProvider, но я не могу понять, как его использовать.

Сервер Azure OAuth работает

Я зарегистрировал два приложения (API и клиент) в Azure Active Directory. Так как это всего лишь краткая демонстрация, я дам вам все идентификаторы и секреты ;)

Я могу получить токен из Azure AD, полный запрос см. в https://reqbin.com/817shtc2, пока все хорошо.

API

ClientId: 44cf7574-88a2-42d6-9497-bff43cc8dc09

Endpoint: https://apim-demo-mglentoft.azure-api.net/api/Values (GET)

Клиент

ClientId: 5f7ee334-b8db-46d3-972f-09f52e186d1d

Secret: ggKp94]HZHWZ.c*5wUC?ToSVfknyqLB3

Я следовал образцу в https://github.com/azureadquickstarts/appmodelv2-nativeclient-dotnet, но он ссылается на Microsoft.Owin.Security.Jwt.IIssuerSecurityTokenProvider, которого нет в nuget v4.0.1.0.

Я попытался просто закомментировать второй параметр для JwtFormat, но это не сработало. Есть идеи, как заставить это работать с помощью Microsoft.Owin.Security.Jwt.IIssuerSecurityKeyProvider?

Ниже весь файл startup.cs

Я могу заставить это работать с помощью .Net Core, но по разным причинам мне приходится придерживаться .Net Framework 4.7.2.

using System;
using System.Configuration;
using System.Threading.Tasks;
using System.Web.Http;
using DemoAPI.Middleware;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Microsoft.Owin.Security.OAuth;
using Owin;

[assembly: OwinStartup(typeof(DemoAPI.App_Start.Startup))]

namespace DemoAPI.App_Start
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();

            ConfigureAuth(app);
            app.Use(typeof(CorrelationHandlerMiddleware));


            app.UseCors(CorsOptions.AllowAll);
            WebApiConfig.Register(config);
            app.UseWebApi(config);
        }

        private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];

        public void ConfigureAuth(IAppBuilder app)
        {
            // NOTE: The usual WindowsAzureActiveDirectoryBearerAuthentication middleware uses a
            // metadata endpoint which is not supported by the v2.0 endpoint.  Instead, this 
            // OpenIdConnectSecurityTokenProvider implementation can be used to fetch & use the OpenIdConnect
            // metadata document - which for the v2 endpoint is https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration


            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
            {
                AccessTokenFormat = new JwtFormat(
                    new TokenValidationParameters
                    {
                        // Check if the audience is intended to be this application
                        ValidAudiences = new[] { clientId, $"api://{clientId}" },`enter code here`

                        // Change below to 'true' if you want this Web API to accept tokens issued to one Azure AD tenant only (single-tenant)
                        // Note that this is a simplification for the quickstart here. You should validate the issuer. For details, 
                        // see https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore
                        ValidateIssuer = false,

                    }//,
                     //new OpenIdConnectSecurityKeyProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")
                     //the OpenIdConnectSecurityKeyProvider implements IIssuerSecurityTokenProvider, which is not part of Microsoft.Owin.Security.Jwt 4.0
                ),
            });
        }
    }
}

Актуальная ошибка при использовании Microsoft.Owin.diagnostics

Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware Error: 0 : Authentication failed Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match key: kid: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Exceptions caught: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. token: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters) at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)

Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
Как установить LAMP Stack 1/2 на Azure Linux VM
Как установить LAMP Stack 1/2 на Azure Linux VM
В дополнение к нашему предыдущему сообщению о намерении Azure прекратить поддержку Azure Database для MySQL в качестве единого сервера после 16...
1
0
3 647
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

попробуйте переименовать «токен» в «ключ», и вы можете быть в порядке.

Итак, вместо

    IssuerSecurityTokenProviders = new IIssuedSecurityTokenProvider[]
                                   {
                                       new symmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                                   }

У вас должно быть что-то вроде

    IssuerSecurityKeyProviders= new IIssuerSecurityKeyProvider[]
                                   {
                                       new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
                                   }

Для получения дополнительной информации: Тема выпуска или Актуальный репозиторий Github.

Надеюсь, что это поможет вам...

Спасибо за ответ. Это звучит немного странно для меня, поскольку алгоритм RS256 (асимметричный), поэтому передача SymmetricKeyIssuerSecurityKeyProvider не имеет смысла...? Хотя я пробовал (с клиентским ключом), но он не смог найти ключ с идентификатором xxxxxxx.

Mike 07.07.2019 09:39
Ответ принят как подходящий

Я продолжу и отвечу на свой вопрос здесь, так как я заставил его работать, немного изменив образец в https://github.com/azureadquickstarts/appmodelv2-nativeclient-dotnet.

Возможно, ответ Стефа тоже работает, и я просто сделал что-то не так, но вы направили меня в правильном направлении :)

в Startup.cs:

Я перешел с реализации IIssuerSecurityTokenProvider на IIssuerSecurityKeyProvider (спасибо Стеф)

string issuerEndpoint = @"https://sts.windows.net/919e9a01-27bf-4106-9d36-48528249d0ce/";
            string metadaEndpoint = $"{issuerEndpoint}v2.0/.well-known/openid-configuration";

            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
            {
                AccessTokenFormat = new JwtFormat(
                    new TokenValidationParameters
                    {
                        // Check if the audience is intended to be this application
                        ValidAudiences = new[] { clientId, $"api://{clientId}" },

                        // Change below to 'true' if you want this Web API to accept tokens issued to one Azure AD tenant only (single-tenant)
                        // Note that this is a simplification for the quickstart here. You should validate the issuer. For details, 
                        // see https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore
                        ValidateIssuer = false,

                    },
                     new OpenIdConnectSecurityKeyProvider("https://sts.windows.net/919e9a01-27bf-4106-9d36-48528249d0ce/v2.0/.well-known/openid-configuration")
                ),
            });

OpenIDConnectSecurityKeyProvider.cs

Реализация, основанная на образце из https://github.com/azureadquickstarts/appmodelv2-nativeclient-dotnet, изменена для извлечения ключей вместо токенов.

using System.Collections.Generic;
using System.Threading;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security.Jwt;

namespace DemoAPI.App_Start
{
    //This class is necessary because the OAuthBearer Middleware does not leverage
    // the OpenID Connect metadata endpoint exposed by the STS by default.

    public class OpenIdConnectSecurityKeyProvider : IIssuerSecurityKeyProvider
    {
        public ConfigurationManager<OpenIdConnectConfiguration> ConfigManager;
        private string _issuer;
        private IEnumerable<SecurityKey> _keys;
        private readonly string _metadataEndpoint;

        private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();

        public OpenIdConnectSecurityKeyProvider(string metadataEndpoint)
        {
            _metadataEndpoint = metadataEndpoint;
            ConfigManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());

            RetrieveMetadata();
        }

        /// <summary>
        /// Gets the issuer the credentials are for.
        /// </summary>
        /// <value>
        /// The issuer the credentials are for.
        /// </value>
        public string Issuer
        {
            get
            {
                RetrieveMetadata();
                _synclock.EnterReadLock();
                try
                {
                    return _issuer;
                }
                finally
                {
                    _synclock.ExitReadLock();
                }
            }
        }

        public IEnumerable<SecurityKey> SecurityKeys
        {
            get
            {
                RetrieveMetadata();
                _synclock.EnterReadLock();
                try
                {
                    return _keys;
                }
                finally
                {
                    _synclock.ExitReadLock();
                }
            }
        }

        private void RetrieveMetadata()
        {
            _synclock.EnterWriteLock();
            try
            {
                OpenIdConnectConfiguration config = ConfigManager.GetConfigurationAsync().Result;
                _issuer = config.Issuer;
                _keys = config.SigningKeys;
            }
            finally
            {
                _synclock.ExitWriteLock();
            }
        }
    }
}

Другие вопросы по теме