Проверка JWT — sts.windows.net и login.windows.net: почему эмитенты не согласованы?

avatar
Kzryzstof
8 августа 2021 в 17:36
1046
1
1

Я разрабатываю приложение, которое распространяется на несколько Function App, работающих на .net5.

Мне нужно аутентифицировать вызовы HTTP между функциями. Для этого я использую Azure Active Directory. Я создал зарегистрированное приложение в своем клиенте и создал новый секрет. Всякий раз, когда Function1 необходимо связаться с Function2, я получаю маркер доступа из AAD, например:

var stringContent = new StringContent($"grant_type=client_credentials&client_id={Uri.EscapeUriString(clientId)}&client_secret={Uri.EscapeUriString(clientSecret)}&scope={Uri.EscapeUriString(scope)}", Encoding.UTF8, "application/x-www-form-urlencoded");
             
string tokenUrl = "https://login.microsoftonline.com/57cc008d-ba7c-4887-acfd-93089c705640/oauth2/v2.0/token";
           
HttpResponseMessage result = await _httpClient.PostAsync(tokenUrl, stringContent);
                    
string content = await result.Content.ReadAsStringAsync();

С помощью этого вызова я получаю токен со следующей информацией:

{
  "typ": "JWT",
  "alg": "RS256",
  "x5t": "XXXXXXXXXXXXX_KXEg",
  "kid": "XXXXXXXXXXXXX_KXEg"
}.{
  "aud": "api://f87cc6ac-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "iss": "https://sts.windows.net/57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX/",
  "iat": 1628443017,
  "nbf": 1628443017,
  "exp": 1628446917,
  "aio": "E2ZgYOg4qv7qZsTRKv5v+XXXXXXXXXXX",
  "appid": "f87cc6ac-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "appidacr": "1",
  "idp": "https://sts.windows.net/57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX/",
  "oid": "39b2e6b8-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "rh": "0.AVsAjQDMV3y6h0is_ZMInHBWQKzGfPhOtBZEj3l003jzIFFbAAA.",
  "sub": "39b2e6b8-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "tid": "57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "uti": "U7pMFAzw_XXXXXXXXXXXXX",
  "ver": "1.0"
}.[Signature]

Теперь маркер доступа используется в качестве маркера-носителя при вызове Function2. Function2 получает токен из заголовка Authorization и пытается проверить его, например:

ConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ("https://login.microsoftonline.com/57cc008d-ba7c-4887-acfd-93089c705640/v2.0/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
    
OpenIdConnectConfiguration openIdConnectConfiguration = await configurationManager.GetConfigurationAsync();
    
TokenValidationParameters validationParameters = new TokenValidationParameters
{
    ValidateAudience = false,
    ValidateIssuer = true,
    ValidIssuers = new[]
    {
        openIdConnectConfiguration.Issuer
    },
    ValidateIssuerSigningKey = true,
    IssuerSigningKeys = openIdConnectConfiguration.SigningKeys,
    RequireExpirationTime = true,
    ValidateLifetime = true,
    RequireSignedTokens = true,
};
    
JwtSecurityTokenHandler securityTokenHandler = new();
    
if (!securityTokenHandler.CanReadToken(cleanedBearerToken))
    throw new ArgumentException("Unable to read the token. It is malformed.");
    
try
{
    ClaimsPrincipal claimsPrincipal = securityTokenHandler.ValidateToken(cleanedBearerToken, validationParameters, out SecurityToken _);
    
    return claimsPrincipal;
}
catch (Exception unhandledException)
{
    throw new AuthenticationException("The token could not be validated.", unhandledException);
}

Кроме эмитента, проверка работает. При такой настройке эмитент не может быть проверен. Маркер указывает, что эмитент принадлежит sts.windows.net. Однако конфигурация OpenID указывает, что эмитент должен быть login.microsoft.com.

.

Чтобы заставить его работать, я откатился к чему-то подобному, что менее идеально, так как я должен игнорировать параметр, возвращаемый конечной точкой openid-configuration (которая должна знать лучше меня):

TokenValidationParameters validationParameters = new TokenValidationParameters
{
    ValidateAudience = false,
    ValidateIssuer = true,
    ValidIssuers = new[]
    {
        // --> Force the use of the following issuer!!
        "https://sts.windows.net/57cc008d-ba7c-4887-acfd-93089c705640/"
    },
    ValidateIssuerSigningKey = true,
    IssuerSigningKeys = openIdConnectConfiguration.SigningKeys,
    RequireExpirationTime = true,
    ValidateLifetime = true,
    RequireSignedTokens = true,
};

Вопрос

Является ли переопределение эмитента хорошей практикой? Если нет, что я могу сделать, чтобы получить согласованный эмитент из Azure Active Directory и не указывать эмитента самостоятельно?

Источник

Ответы (1)

avatar
AmanpreetSingh_msft
31 августа 2021 в 14:35
4

Значение издателя зависит от версии токена доступа. Если вы просмотрите Образцы токенов в jwt.ms, вы увидите, что эмитентом токена доступа V1 является https://sts.windows.net/..., а для токена доступа V2 — https://login.microsoft.com/...

.

Кроме того, если вы проверите конечную точку метаданных OIDC v1 (https://login.microsoftonline.com/common/.well-known/openid-configuration), издателем будет sts.windows.net и для OIDC конечная точка метаданных v2 (https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration), издателем будет login.microsoft.com.

Обновление

В манифесте зарегистрированного приложения найдите значение accessTokenAcceptedVersion:

enter image description here

Измените его на 2, и эмитент станет login.microsoft.com вместо sts.windows.net.

enter image description here

Kzryzstof
9 сентября 2021 в 18:06
0

Токен запрашивается у https://login.microsoftonline.com/<TenantId>/oauth2/v2.0/authorize?nonce={{$randomUUID}}&response_mode=query и https://login.microsoftonline.com/57cc008d-ba7c-4887-acfd-93089c705640/oauth2/v2.0/token, поэтому я не уверен, почему он все еще возвращает sts.windows.net в качестве эмитента...

AmanpreetSingh_msft
10 сентября 2021 в 07:49
1

Я имел в виду версию токена доступа (утверждение ver в токене доступа), а не конечную точку oauth2/v2.0. Вы можете получить токен с «ver»: «1.0» из конечной точки oauth2/v2.0.

Kzryzstof
10 сентября 2021 в 11:36
0

Понял. Есть ли способ запросить версию 2 токена доступа (который, я полагаю, будет выдан login.microsoft.com) или он контролируется Microsoft?

AmanpreetSingh_msft
10 сентября 2021 в 15:20
1

Это зависит от значения параметра «accessTokenAcceptedVersion» в манифесте API/ресурса, для которого вы запрашиваете токен. Например. если вы запросите токен доступа для API1, для которого accessTokenAcceptedVersion имеет значение null или 1, вы получите токен доступа v1.0, если вы измените accessTokenAcceptedVersion на 2, версия токена будет v2.0. Таким образом, его можно настроить только тогда, когда у вас есть доступ к манифесту API/ресурса, для которого должен быть получен токен. Подробнее: docs.microsoft.com/en-us/azure/active-directory/develop/…

Kzryzstof
10 сентября 2021 в 16:54
1

Точно :) В манифесте я увидел нулевой набор для accessTokenAcceptedVersion. Я изменил его на 2, и это сработало! Я обновил ответ, включив в него эти шаги, которые, по моему мнению, имеют решающее значение. Спасибо!!