Как проверить Microsoft Graph API jwt access_token и защитить ваш API?

Сценарий:

У меня есть клиентское приложение angular5, которое использует hello.js для аутентификации пользователей с использованием их учетных данных Office 365.

Код клиента:

  hello.init({
      msft: {
        id: configuration.AppID,
        oauth: {
          version: 2,
          auth: 'https://login.microsoftonline.com/' + configuration.TenantID + '/oauth2/v2.0/authorize'
        },
        scope_delim: ' ',
        form: false
      },
    },
      { redirect_uri: configuration.redirecturl }
    );
  }


  login() {

    hello('msft').login({ scope: 'User.Read People.Read', display: 'popup' })
      .then((authData: any) => {  // console.log(authData);

        this.zone.run(() => {

          // get profile
}

Успешный ответ (манипулируется по соображениям безопасности)

{  
   "msft":{  
      "access_token":"REMOVED TOKEN HERE",
      "token_type":"Bearer",
      "expires_in":3599,
      "scope":"basic,User.Read,People.Read",
      "state":"",
      "session_state":"3b82898a-2b3f-445363f-89ae-d9696gg64ad3",
      "client_id":"672330148-2bb43-3080-9eee-1f46311f789c",
      "network":"msft",
      "display":"popup",
      "redirect_uri":"http://localhost:5653/",
      "expires":15245366.218
   }
}

Декодированный access_token имеет следующие несколько ключей:

Заголовок:

1. nonce (требует специальной обработки, я не смог найти никакой документации, касающейся специальной обработки)

2. тип: JWT

Полезная нагрузка:

"aud": " https://graph.microsoft.com/",

Как только access_token получен, я отправляю access_token в заголовке авторизации каждого вызова к моему внутреннему API. Цель состоит в том, чтобы проверить токен и отправить успешный ответ, только если access_token проверен и авторизован. В случае неудачи 401 Unauthorized является ответом.

Код API для проверки access_token, ASP .NET CORE 2, следующий ( https://auth0.com/blog/securing-asp-dot-net-core-2-applications-with-jwts/)

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

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
          options.TokenValidationParameters = new TokenValidationParameters
          {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
          };
        });

      services.AddMvc();
    }
  }
}

// other methods
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseAuthentication();

    app.UseMvc();
}

В appsettings.json у меня есть:

{   "Jwt": {
    "Key": "verySecretKey", **(I got the key from https://login.microsoftonline.com/common/discovery/keys with the kid value in access_token header)**
    "Issuer": "https://sts.windows.net/49bcf059-afa8-4bf9-8470-fad0c9cce27d/",   } }

Наконец, я получаю сообщение об ошибке: "WWW-Authenticate → Bearer error =" invalid_token ", error_description =" Ключ подписи не найден ""

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

Ключевые моменты:

  1. Я пытался проверить access_token в jwt.io ( https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx), но мне не удалось.

  2. Aud здесь https://graph.microsoft.com/, я не уверен, если мне нужно, и почему мне нужно изменить Aud на мой идентификатор клиента. как я это сделал?

  3. Что-то не так в коде или мне нужно настроить способ, которым я запрашиваю токены заголовка.

Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

3 ответа

Я пытался проверить access_token в jwt.io ( https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx), но мне не удалось.

Токены доступа Microsoft Graph API подписываются не так, как я вижу. Вам не нужно проверять токены, предназначенные для другого API, это их работа.

Aud здесь https://graph.microsoft.com/, я не уверен, если мне нужно, и почему мне нужно изменить Aud на мой идентификатор клиента. как я это сделал?

Я не знаю о HelloJS, но вы должны быть в состоянии получить токен Id после аутентификации с response_type=id_token token, Затем вам нужно приложить это к запросам. Он должен иметь ваш идентификатор клиента в качестве аудитории.

Что-то не так в коде или мне нужно настроить способ, которым я запрашиваю токены заголовка.

Единственное, что выделяется для меня - это то, что вы делаете много ненужных настроек. В основном конфигурация должна быть:

.AddJwtBearer(o =>
{
    o.Audience = "your-client-id";
    o.Authority = "https://login.microsoftonline.com/your-tenant-id/v2.0";
})

Обработчик автоматически получит открытые ключи подписи при запуске. Не очень хорошая идея жестко кодировать ключи подписи в вашем приложении, так как ваше приложение сломается, когда AAD завершит перенос ключа подписи.

Я также потратил много времени, пытаясь проверить это, но суть в том, что вы не можете:

Маркеры доступа — это непрозрачные текстовые блоки, предназначенные только для ресурса. Если вы клиент, получающий токен для Graph, предположите, что это зашифрованная строка, на которую вам никогда не следует смотреть — иногда так и будет. Мы используем специальный формат токена для Graph, который они знают, как проверять — вы не должны смотреть на токены доступа, если они не для вас. (источник: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609)

Вместо использования токена доступа вы должны создать токен ID, который является обычным токеном JWT, который может быть проверен, как и любой другой JWT:

  1. Получите открытый ключ из каталога Microsoft
  2. Проверить подпись, аудиторию, эмитента и т. д.

Чтобы получить токен идентификатора с помощью MSAL API после входа в систему, вы можете сделать (пример javascript):

      const { instance, accounts } = useMsal();
const request = {
    scopes: ["User.Read"],
    account: accounts[0]
};
const idToken = await instance.acquireTokenSilent(request).idToken;

Для получения дополнительной информации о токенах ID, пожалуйста, проверьте:

https://learn.microsoft.com/en-us/azure/active-directory/develop/id-токены

Для получения дополнительной информации о непрозрачных токенах, пожалуйста, проверьте:

https://zitadel.com/blog/jwt-vs-opaque-токены

Да, это заняло немного времени. Для всех, кто исследует это, вот мое понимание.

Вы не используете Microsoft Graph API для защиты своего веб-API. Вместо:

  1. Клиент продолжает использовать платформу Microsoft Identity Platform для аутентификации.

  2. Клиент использует полученный токен доступа JWT для вызова веб-API как обычно для потока OAuth 2.0.

  3. Веб-API использует JwtBearerAuthenticationScheme, устанавливая полномочия для платформы идентификации Microsoft. Посмотрите этот пример и выполните поиск по JwtBearerAuthenticationScheme.

  4. Веб-API использует предоставленный токен доступа для получения пользовательского токена "от имени".

  5. Веб-API вызывает API-интерфейс Graph, используя этот токен "от имени". Срок службы этого токена отличается от срока жизни токена, полученного клиентом, и его обновления должны обрабатываться отдельно.

Это очень дистиллированная версия этого примера. Отказ от ответственности: я еще не применил это на практике.