JwtFormat: почему он добавляет эмитента токена в свойство ValidIssuers?

Я смотрю на исходный код класса JwtFormat, и мне интересно, почему он добавляет эмитента, который он восстанавливает из токена, в список ValidIssuers. Означает ли это, что он будет принимать всех эмитентов как действительных, если я не укажу ключ или не предоставлю обработчик IssueValidator для используемых TokenValidationParameters?

Кстати, я смотрю на этот класс, потому что изучаю проблему, касающуюся использования токенов JWT (azure ad v2.0) в приложении веб-API, которое, похоже, игнорирует свойство ValidIssuer:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
      AccessTokenFormat = new JwtFormat(
         GetTokenValidationParameters(),
         new OpenIdConnectCachingSecurityTokenProvider(authority)),
         Provider = new OAuthBearerAuthenticationProvider {
           OnValidateIdentity = ValidateIdentity
         }
 });

private TokenValidationParameters GetTokenValidationParameters() {
    return new TokenValidationParameters {
        ValidAudience = ConfigData.ClientId,
        ValidIssuer = "nobody",
        ValidIssuers = null,
        IssuerValidator = ValidateIssuer
    };
}

Я редактирую это, чтобы дать больше информации о том, что происходит.

Согласно исходному коду ValidateIssuer по умолчанию имеет значение true, поэтому нет необходимости устанавливать его снова. На всякий случай вот исходный код:

public TokenValidationParameters()
{
  this.RequireExpirationTime = true;
  this.RequireSignedTokens = true;
  this.SaveSigninToken = false;
  this.ValidateActor = false;
  this.ValidateAudience = true;
  this.ValidateIssuer = true;
  this.ValidateIssuerSigningKey = false;
  this.ValidateLifetime = true;
}

Я настраиваю IssuerValidator, потому что хочу убедиться, что, если установлен ValidIssuer, я хочу сравнить эмитента токена с этим значением (и не хочу проверять коллекцию ValidIssuers, когда проверка ValidIssuer терпит неудачу).

Если вам интересно, где заполняется ValidIssuers (и да, даже в моем примере он заполняется автоматически, хотя я установил для него explicityl значение null), это происходит в методе Unprotect JwtFormat:

public AuthenticationTicket Unprotect(string protectedText)
{
  if (string.IsNullOrWhiteSpace(protectedText))
    throw new ArgumentNullException(nameof (protectedText));
  if (!(this.TokenHandler.ReadToken(protectedText) is JwtSecurityToken))
    throw new ArgumentOutOfRangeException(nameof (protectedText), Microsoft.Owin.Security.Jwt.Properties.Resources.Exception_InvalidJwt);
  TokenValidationParameters validationParameters = this._validationParameters;
  if (this._issuerCredentialProviders != null)
  {
    validationParameters = validationParameters.Clone();
    IEnumerable<string> second1 = this._issuerCredentialProviders.Select<IIssuerSecurityTokenProvider, string>((Func<IIssuerSecurityTokenProvider, string>) (provider => provider.Issuer));
    validationParameters.ValidIssuers = validationParameters.ValidIssuers != null ? validationParameters.ValidIssuers.Concat<string>(second1) : second1;
    IEnumerable<SecurityToken> second2 = this._issuerCredentialProviders.Select<IIssuerSecurityTokenProvider, IEnumerable<SecurityToken>>((Func<IIssuerSecurityTokenProvider, IEnumerable<SecurityToken>>) (provider => provider.SecurityTokens)).Aggregate<IEnumerable<SecurityToken>>((Func<IEnumerable<SecurityToken>, IEnumerable<SecurityToken>, IEnumerable<SecurityToken>>) ((left, right) => left.Concat<SecurityToken>(right)));
    validationParameters.IssuerSigningTokens = validationParameters.IssuerSigningTokens != null ? validationParameters.IssuerSigningTokens.Concat<SecurityToken>(second2) : second2;
  }
  SecurityToken validatedToken;
  ClaimsIdentity identity = (ClaimsIdentity) this.TokenHandler.ValidateToken(protectedText, validationParameters, out validatedToken).Identity;
  AuthenticationProperties properties = new AuthenticationProperties();
  if (this.UseTokenLifetime)
  {
    DateTime validFrom = validatedToken.ValidFrom;
    if (validFrom != DateTime.MinValue)
      properties.IssuedUtc = new DateTimeOffset?((DateTimeOffset) validFrom.ToUniversalTime());
    DateTime validTo = validatedToken.ValidTo;
    if (validTo != DateTime.MinValue)
      properties.ExpiresUtc = new DateTimeOffset?((DateTimeOffset) validTo.ToUniversalTime());
    properties.AllowRefresh = new bool?(false);
  }
  return new AuthenticationTicket(identity, properties);
}

Кстати, этот метод вызывает (косвенно) методом AuthenticateCoreAsync, когда ему нужно десериализовать токен:

protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
{
  try
  {
    string requestToken = (string) null;
    string authorization = this.Request.Headers.Get("Authorization");
    if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
      requestToken = authorization.Substring("Bearer ".Length).Trim();
    OAuthRequestTokenContext requestTokenContext = new OAuthRequestTokenContext(this.Context, requestToken);
    await this.Options.Provider.RequestToken(requestTokenContext);
    if (string.IsNullOrEmpty(requestTokenContext.Token))
      return (AuthenticationTicket) null;
    AuthenticationTokenReceiveContext tokenReceiveContext = new AuthenticationTokenReceiveContext(this.Context, this.Options.AccessTokenFormat, requestTokenContext.Token);
    await this.Options.AccessTokenProvider.ReceiveAsync(tokenReceiveContext);
    if (tokenReceiveContext.Ticket == null)
      tokenReceiveContext.DeserializeTicket(tokenReceiveContext.Token);
//remaining code removed
}

Поскольку я действительно не читал спецификации, мне было интересно, может ли кто-нибудь объяснить мне такое поведение (всегда добавлять эмитент токена в коллекцию ValidIssuers и проверять, находится ли эмитент токена в ValidIssuers - что всегда будет правдой!)

Окончательное редактирование

Ладно, плохо ... Не хватает кофе, я думаю ... На самом деле эмитент добавляется не из самого токена, а из IIssuerSecurityTokenProvider, который передается в ctor JwtFormat (получает его из конечной точки метаданных). ..

Извините ребята...

Спасибо.

Луис


person Luis Abreu    schedule 12.11.2017    source источник
comment
Вы установили для validateIssuer значение true в своих свойствах TokenValidationProperties?   -  person Nikolaus    schedule 12.11.2017
comment
Можете ли вы предоставить URL-адрес просматриваемого исходного кода?   -  person spottedmahn    schedule 13.11.2017


Ответы (1)


В настоящее время у меня нет Vs, потому что я пишу на своем мобильном телефоне, в TokenValidationParameters должно быть свойство ValidateIssuer, но похоже, что вы установили IssuerValidator в ValidateIssuer, что должно быть правдой, поэтому попробуйте это так:

private TokenValidationParameters GetTokenValidationParameters() {
    return new TokenValidationParameters {
            ValidAudience = ConfigData.ClientId,
            ValidIssuer = "nobody",
            ValidateIssuer = true
    };
}
person Nikolaus    schedule 12.11.2017
comment
Привет, Николаус. Да, есть, и это правда по умолчанию (поэтому я не устанавливаю). Я установил IssueValidator, чтобы я мог скопировать / вставить код аутентификации по умолчанию и понять, почему изменение ValidIssuer не запрещает токен. Вот как я нашел проблему, о которой упоминал в вопросе ... - person Luis Abreu; 12.11.2017
comment
@LuisAbreu Но ваш код, похоже, устанавливает IssueValidator в ValidateIssuer, что по соглашению означает true. Тогда ваш Код задает проблему, не так ли? - person Nikolaus; 14.11.2017
comment
Я обновил сообщение, добавив больше информации. Я не понимаю, почему эмитент токена всегда добавляется в коллекцию ValidIssuers и почему метод IssueValidator по умолчанию всегда проверяет эмитента на соответствие коллекции ValidIssuers, когда вы устанавливаете свойство ValidIssuer (это всегда будет возвращать true, потому что эмитент токена попадает в коллекцию ValidIssuers!). Спасибо - person Luis Abreu; 15.11.2017