aspnetcore/src/Microsoft.AspNet.Authentica.../JwtBearerHandler.cs

183 lines
7.3 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.Framework.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNet.Authentication.JwtBearer
{
internal class JwtBearerHandler : AuthenticationHandler<JwtBearerOptions>
{
private OpenIdConnectConfiguration _configuration;
/// <summary>
/// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options.
/// </summary>
/// <returns></returns>
protected override async Task<AuthenticationTicket> HandleAuthenticateAsync()
{
string token = null;
try
{
// Give application opportunity to find from a different location, adjust, or reject token
var messageReceivedContext = new MessageReceivedContext(Context, Options);
// event can set the token
await Options.Events.MessageReceived(messageReceivedContext);
if (messageReceivedContext.HandledResponse)
{
return messageReceivedContext.AuthenticationTicket;
}
if (messageReceivedContext.Skipped)
{
return null;
}
// If application retrieved token from somewhere else, use that.
token = messageReceivedContext.Token;
if (string.IsNullOrEmpty(token))
{
string authorization = Request.Headers["Authorization"];
// If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization))
{
return null;
}
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
// If no token found, no further work possible
if (string.IsNullOrEmpty(token))
{
return null;
}
}
// notify user token was received
var securityTokenReceivedContext = new SecurityTokenReceivedContext(Context, Options)
{
SecurityToken = token,
};
await Options.Events.SecurityTokenReceived(securityTokenReceivedContext);
if (securityTokenReceivedContext.HandledResponse)
{
return securityTokenReceivedContext.AuthenticationTicket;
}
if (securityTokenReceivedContext.Skipped)
{
return null;
}
if (_configuration == null && Options.ConfigurationManager != null)
{
_configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
}
var validationParameters = Options.TokenValidationParameters.Clone();
if (_configuration != null)
{
if (validationParameters.ValidIssuer == null && !string.IsNullOrEmpty(_configuration.Issuer))
{
validationParameters.ValidIssuer = _configuration.Issuer;
}
else
{
var issuers = new[] { _configuration.Issuer };
validationParameters.ValidIssuers = (validationParameters.ValidIssuers == null ? issuers : validationParameters.ValidIssuers.Concat(issuers));
}
validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
}
SecurityToken validatedToken;
foreach (var validator in Options.SecurityTokenValidators)
{
if (validator.CanReadToken(token))
{
var principal = validator.ValidateToken(token, validationParameters, out validatedToken);
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme);
var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Options)
{
AuthenticationTicket = ticket
};
await Options.Events.SecurityTokenValidated(securityTokenValidatedContext);
if (securityTokenValidatedContext.HandledResponse)
{
return securityTokenValidatedContext.AuthenticationTicket;
}
if (securityTokenValidatedContext.Skipped)
{
return null;
}
return ticket;
}
}
throw new InvalidOperationException("No SecurityTokenValidator available for token: " + token ?? "null");
}
catch (Exception ex)
{
Logger.LogError("Exception occurred while processing message", ex);
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
if (Options.RefreshOnIssuerKeyNotFound && ex.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
{
Options.ConfigurationManager.RequestRefresh();
}
var authenticationFailedContext = new AuthenticationFailedContext(Context, Options)
{
Exception = ex
};
await Options.Events.AuthenticationFailed(authenticationFailedContext);
if (authenticationFailedContext.HandledResponse)
{
return authenticationFailedContext.AuthenticationTicket;
}
if (authenticationFailedContext.Skipped)
{
return null;
}
throw;
}
}
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
{
Response.StatusCode = 401;
await Options.Events.ApplyChallenge(new AuthenticationChallengeContext(Context, Options));
return false;
}
protected override Task HandleSignOutAsync(SignOutContext context)
{
throw new NotSupportedException();
}
protected override Task HandleSignInAsync(SignInContext context)
{
throw new NotSupportedException();
}
}
}