Basically what I want to do:
Client-Side (get a token, attach is as a metadata-token and send it to the different services) -- DONE
Server-side (get the token, verify issuer, date and audience) -- DONE
Server-side (After verifying the token, I would like to populate the fields of the AuthContext, so that I can use them in the my GrpcServices) -- Need help here
So Far I manage to return a ClaimsPrinciple from my tokenChallenger.GetClaimsPrincipal(token) method, however I am unsure how to populate the AuthContext.
I was reading the documentation , and I basically need an interceptor on the server side.
Here is my code so far
public class AuthInterceptor: Interceptor
{
private readonly JwtTokenChallenger _tokenChallenger;
public AuthInterceptor(JwtTokenChallenger tokenChallenger)
{
_tokenChallenger = tokenChallenger;
}
public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
Task<TResponse> response;
var isThisProtectedMethodAttribute = IsThisProtectedMethod(continuation.Target.GetType(), context);
if (isThisProtectedMethodAttribute == null) //only protected methods have attributes.
{
response = continuation(request, context);
return response;
}
//jwt token validation;
//populate auth context with claims principle?
var token = context.RequestHeaders.FirstOrDefault(h => h.Key == "authorization").Value.Split(" ").Last();
if (token == null)
{
context.Status = new Status(StatusCode.Unauthenticated, "Invalid token");
return default(Task<TResponse>);
}
if (ValidateToken(token))
{
PopulateAuthContext(token, context);
return continuation(request, context);
}
context.Status = new Status(StatusCode.Unauthenticated, "Invalid token");
return default(Task<TResponse>);
//test
}
private ProtectedMethod IsThisProtectedMethod(Type t, ServerCallContext context)
{
List<ProtectedMethod> returnAttributes = new List<ProtectedMethod>();
Attribute[] attrs = Attribute.GetCustomAttributes(t);
foreach (Attribute attr in attrs)
{
if (attr is ProtectedMethod a && (a.ProtectedResourceAcceessMethodName == context.Method.Split("/").Last()))
{
return a;
}
}
return null;
}
private bool ValidateToken(String tokenToValidate)
{
return _tokenChallenger.isValidToken(tokenToValidate);
}
private void PopulateAuthContext(String token, ServerCallContext context)
{
//help needed?
}
}
Client-side I use Java (Android), Server-side I use C#
Edit: token has 2 things that I want, nameidentifier and roles
The gRPC C# API doesn't allow you to populate AuthContext, the AuthContext can only be populated by gRPC internally (if you use TLS certificate-based authentication).
You basically have two options here:
you can populate the request metadata of the request with additional entries you need to pass to the actual method handlers (server-side interceptor can modify the metadata). Note: if you decide to populate the metadata, you need to be very careful about making sure that a malicious client can't send corresponding metadata entries along with her request and thus fake that she's been authenticated. You can do that e.g. by adding one more interceptor that strips all the sensitive headers from all incoming requests.
your interceptor can set the auth-related values into an execution context that's compatible with async/await. That way, the values will be accessible from the async methods that implement the server-side behavior. See e.g. https://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html for more info on C# contexts.
There is a third option as well, populate a new derived class of the ServerCallContext, with a new AuthContext populated with your claims. You can cache the transformation from the token
Related
I have several Asp.Net Core Web APIs that use Bearer authentication and IdentityServer4.AccessTokenValidation middleware to introspect tokens, authenticate the user and create claims. This works fine for HTTP requests.
I am in the process of configuring these APIs to also be MassTransit endpoints (for both Publishing and Consuming messages) using RabbitMQ as transport. I followed the instructions here for adding MassTransit to the API and for setting up message consumers. A typical workflow will be something like:
HTTP Request to API > Publish message on MassTransit > RabbitMQ > Message consumed in another API
What I'm struggling to understand is how I can create a ClaimsPrincipal when consuming messages off the bus so that I know which user to perform actions on behalf of? Where it's not an HTTP request there is no AuthenticationHandler being invoked.
What I've tried so far:
I thought I'd approach this by passing a token (and/or individual claim values) in message headers. The publish part seemed easily enough as MassTransit allows adding any number of custom headers when publishing messages using MassTransit.PublishContextExecuteExtensions.Publish. This allowed me to get messages onto the transport with information identifying a user and this info can be viewed in a consumer by manually viewing the headers e.g.
public class SomeEventConsumer : IConsumer<SomeEventData>
{
public async Task Consume(ConsumeContext<SomeEventData> context)
{
var token = context.Headers["token"];
}
}
At this point I could take the token and call the Introspection endpoint in Identity Server manually but then I'd need to:
Do this in every consumer every time and then ...
... pass that information down to logic classes etc manually instead of making use of IHttpContextAccessor.HttpContext.User.Claims or by wrapping the claims and using Dependency Injection.
To address point 1 I created a new custom middleware ...
public class AuthenticationFilter<T> : IFilter<ConsumeContext<T>> where T : class
{
public void Probe(ProbeContext context)
{
var scope = context.CreateFilterScope("authenticationFilter");
}
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
var token = context.Headers.Where(x => x.Key == "token").Select(x => x.Value.ToString()).Single();
// TODO: Call token introspection
await next.Send(context);
}
}
public class AuthenticationFilterSpecification<T> : IPipeSpecification<ConsumeContext<T>> where T : class
{
public void Apply(IPipeBuilder<ConsumeContext<T>> builder)
{
var filter = new AuthenticationFilter<T>();
builder.AddFilter(filter);
}
public IEnumerable<ValidationResult> Validate()
{
return Enumerable.Empty<ValidationResult>();
}
}
public class AuthenticationFilterConfigurationObserver : ConfigurationObserver, IMessageConfigurationObserver
{
public AuthenticationFilterConfigurationObserver(IConsumePipeConfigurator receiveEndpointConfigurator) : base(receiveEndpointConfigurator)
{
Connect(this);
}
public void MessageConfigured<TMessage>(IConsumePipeConfigurator configurator)
where TMessage : class
{
var specification = new AuthenticationFilterSpecification<TMessage>();
configurator.AddPipeSpecification(specification);
}
}
public static class AuthenticationExtensions
{
public static void UseAuthenticationFilter(this IConsumePipeConfigurator configurator)
{
if (configurator == null)
{
throw new ArgumentNullException(nameof(configurator));
}
_ = new AuthenticationFilterConfigurationObserver(configurator);
}
}
... and then added that into the pipeline ...
IBusControl CreateBus(IServiceProvider serviceProvider)
{
return Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host("rabbitmq://localhost");
cfg.UseAuthenticationFilter();
// etc ...
});
}
And this is where I'm stuck. I don't know how to authenticate the user for the scope of the request. Where it's not an HTTP request I'm not sure what best practice is here. Any suggestions or pointers would be gratefully received. Thanks...
I've just been watching a Kevin Dockx course on Pluralsight that covers this scenario on Azure Service Bus, but the same principal would apply to Mass Transit or any other asynchronous communication between services using a message bus. Here's a link to the section: Securing Microservices in ASP.NET Core
Kevin's technique is to include the access token (JWT) as a property on the bus message and to then validate this in the consumer using IdentityModel.
To summarise:
In the Producer:
Get the Access Token from the request (e.g. HttpContext.GetUserAccessTokenAsync()).
Set this as a property in the message before sending.
In the Consumer:
Use IdentityModel to get the IdP Discovery Document
Extract the public signing keys from the discovery response (these must be converted to RsaSecurityKey)
Call JwtSecurityTokenHandler.ValidateToken() to validate the JWT from the message. This returns a ClaimsPrincipal if successful.
If you're concerned about Access Token expiration, you can make use of the datetime that the message was enqueued as part of the token validation logic in the consumer.
Here's how the validator works (simplified):
var discoveryDocumentResponse = await httpClient.GetDiscoveryDocumentAsync("https://my.authority.com");
var issuerSigningKeys = new List<SecurityKey>();
foreach (var webKey in discoveryDocumentResponse.KeySet.Keys)
{
var e = Base64Url.Decode(webKey.E);
var n = Base64Url.Decode(webKey.N);
var key = new RsaSecurityKey(new RSAParameters
{ Exponent = e, Modulus = n })
{
KeyId = webKey.Kid
};
issuerSigningKeys.Add(key);
}
var tokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = "my-api-audience",
ValidIssuer = "https://my.authority.com",
IssuerSigningKeys = issuerSigningKeys
};
var claimsPrincipal = new JwtSecurityTokenHandler().ValidateToken(tokenToValidate,
tokenValidationParameters, out var rawValidatedToken);
return claimsPrincipal;
I have an intranet site, hosted locally within my organisation. The same site also exposes some data through various web services. It's written using ASP.NET MVC 5 and WebAPI 2, and it's .NET 4.5, not Core.
At the moment users can login to the website using Windows Authentication, and once authenticated they can access the APIs. However, I need to also allow access to the APIs using tokens so that they can be interrogated by automated processes, so I've created a page where authenticated users can go and request a token.
It's my intention that this token can be used as a Bearer token, included in the header of HTTP requests to the Web API, to allow access to the APIs. As I understand it, a Bearer token intrinsically represents the User's right to access the data and doesn't require any other information (even a username).
However, I've struggled to find a complete, end-to-end tutorial for authenticating and authorizing the requests. There are questions on this site and Microsoft aritcles which give some great pointers but I feel that they're perhaps hinting at something much too complicated for my requirements. I don't need to return any kind of Identity with Claims or anything like that, and I'm not concerned with OAuth at all.
I'm using Microsoft's Web API framework so it seems reasonable to assume that it should be fairly straightforward to do something as basic as extract and check a token from the request header!
Would somebody be able to outline the components and the process I need to put in place within my application to allow it to extract the Bearer token from the HTTP request, use my own code to check its validity and then support the Authorize attribute on Web API Methods if the token is valid?
Looks like we have the same need, I also just needed a quick bearer token verification to not leave the API completely wide open.
I copied most parts from here and tweaked it so it just checks the Bearer token https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/authentication-filters
Add filter in WebApiConfig.cs
public class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Add authentication
config.Filters.Add(new SimpleAuthenticationFilter()):
foo
}
}
SimpleAuthenticationFilter.cs
public class SimpleAuthenticationFilter : IAuthenticationFilter
{
private readonly string _bearerToken = ConfigurationManager.AppSettings["simpleToken"];
public bool AllowMultiple { get; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// 1. Look for credentials in the request.
var request = context.Request;
var authorization = request.Headers.Authorization;
// 2. If there are no credentials, do nothing.
if (authorization == null)
{
context.ErrorResult = new AuthenticationFailureResult("Authorization header is 'null''", request);
return;
}
// 3. If there are credentials but the filter does not recognize the
// authentication scheme, do nothing.
if (!authorization.Scheme.Equals("Bearer"))
{
context.ErrorResult = new AuthenticationFailureResult("Authentication type must be 'Bearer'", request);
return;
}
// 4. If there are credentials that the filter understands, try to validate them.
// 5. If the credentials are bad, set the error result.
if (string.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Bearer token is null or empty", request);
return;
}
if (!authorization.Parameter.Equals(_bearerToken))
{
context.ErrorResult = new AuthenticationFailureResult("Bearer token invalid", request);
}
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
}
AuthenticationFailureResponse.cs
public class AuthenticationFailureResult : IHttpActionResult
{
public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
{
ReasonPhrase = reasonPhrase;
Request = request;
}
private string ReasonPhrase { get; }
private HttpRequestMessage Request { get; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
private HttpResponseMessage Execute()
{
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{
RequestMessage = Request, ReasonPhrase = ReasonPhrase
};
return response;
}
}
Expanding on Min's answer above:
string token = Request.Headers.Authorization.ToString().Split(' ')[1];
I'm developing a custom middleware for authenticating clients that invokes an API.
I use an attribute to define if an Action requires authentication, but I can't figure out how to get a reference to the requested Controller and Action inside the Invoke method.
Below is my code so far
AuthenticateClient.cs:
public class AuthenticateClient
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly GenericUnitOfWork _worker;
public AuthenticateClient(RequestDelegate next, ApiDbContext db, IHttpContextAccessor httpContext, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<Utility.LCLog.Settings> settings)
{
_next = next;
_logger = loggerFactory.CreateLogger(settings.Value.ApplicationName);
_worker = new GenericUnitOfWork(new AppHelper(httpContext, db, env));
}
public async Task Invoke(HttpContext context)
{
if (!context.Request.Headers.Keys.Contains("ClientAuth"))
{
_logger.LogWarning("ClientAuth missing in request", new string[] { "Host: " + context.Request.Host, "IP: " + context.Request.HttpContext.Connection.RemoteIpAddress });
context.Response.StatusCode = 400;
await context.Response.WriteAsync("ClientAuth missing from request header values");
return;
}
else
{
string[] tmp = context.Request.Headers["ClientAuth"].ToString().Split("/");
if (tmp.Length != 2)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("The format of the ClientAuth value is wrong");
return;
}
else
{
Client client;
string key, pass;
key = tmp[0];
pass = tmp[1];
client = await _worker.GetRepo<Client>().SingleOrDefault(clnt => clnt.Active && clnt.Key.Equals(key) && clnt.Password.Equals(pass));
if (client == null)
{
_logger.LogWarning("Client authentication failed", new string[] { "Key: " + key, "Password: " + pass, "Host: " + context.Request.Host, "IP: " + context.Request.HttpContext.Connection.RemoteIpAddress });
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Authentication failed");
return;
}
}
}
await _next.Invoke(context);
}
}
ClientAuthenticationAttribute.cs:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ClientAuthenticationAttribute : Attribute
{
private readonly bool _authRequired;
public ClientAuthenticationAttribute(bool authRequired = true)
{
_authRequired = authRequired;
}
public bool AuthRequired { get { return _authRequired; } }
}
I'd recommend you to split your logic for authentication and authorization and keep them in different places.
To recap from here:
Authentication is the process of verifying who you are.
Authorization is the process of verifying that, given that we know who you are, you have access to the specific resource.
What you're currently trying to do, is to both authenticate and authorize your user in the middleware component. Although you could probably get it to work by moving all such logic into filters which you register with the api framework (be it ASP.NET Core MVC, Web API 2 or something else), that would mean that none of your other middleware components have access to the user data (which, I'm guessing, is one of the reasons you chose to implement it in a middleware in the first place).
Given your new knowledge of the separation of authentication and authorization, a possible solution would be to do the following:
Middleware for authentication only
In your middleware, concern yourself only with authentication, and leave authorization up to components later in the pipeline. In practice, this means that your middleware should do the following:
Look for user tokens, cookies or whatever you use for the users to authenticate their request
If not present, treat the request as anonymous, and call the next pipeline component without attaching a user to the request context.
If a valid token is present, resolve the user data from it (e.g. parse the user's claims from a JWT, look up roles in a database, etc...) and store it on the request context. I've found it useful both to create an IPrincipal and set context.Request.User to it, as well as adding information to the context dictionary directly.
With the user registered in the request context, call the next pipeline component.
Authorization assuming an authenticated user
You can now re-write your authorization logic to assume that there's already an authenticated user registered on the request context.
In an ASP.NET Web API 2 application, you'd implement a custom filter attribute inheriting from AuthorizationFilterAttribute, to make sure it runs first of the filters. In my current application, for example, we have the following attribute to authorize that a user has a specific claim. Note that it doesn't do any work to figure out who the user is; if a user is not attached to the context, the response is simply Unauthorized. You could be more sophisticated here, and treat anonymous requests differently from authenticated requests for users who lack access, and, for example, redirect anonymous requests to the login form, while redirecting users lacking access to an error page stating as much.
[AttributeUsage(validOn: AttributeTargets.Method)]
public class AuthorizeClaimsFilterAttribute : AuthorizationFilterAttribute
{
public AuthorizeClaimsFilterAttribute(string claimType, string claimValue)
{
ClaimType = claimType;
ClaimValue = claimValue;
}
public string ClaimType { get; }
public string ClaimValue { get; }
public override void OnAuthorization(HttpActionContext actionContext)
{
if (!(actionContext.RequestContext.Principal is ClaimsPrincipal principal)
|| !principal.HasClaim(x => x.Type == ClaimType && x.Value == ClaimValue))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
To use it, we just decorate the action method with it:
[AuthorizeClaimsFilter("urn:ourapp:claims:admin", true)]
public IHttpActionResults OnlyAdminsCanAccess() { /* ... */ }
In custom CredentialsAuthProvider, on successfull authenticate I want to send additional data in Meta property. Where can I get current response and add info to meta? I want to do this in OnAuthenticated method because I have some more logic for authenticated users and then send meta. This is not session data, only one time thing for logged in users.
This is an Existing Question, but it is approx 4 years old and not sure if it will have any impact on already authenticated users like mentioned in the comments. Also this SO suggests to override Authenticate method which doesn't apply for me as I want to perform only for authenticated users in OnAuthenticated Event.
You can modify the AuthenticateResponse by having your AuthProvider implement IAuthResponseFilter which gets called with an AuthFilterContext on successful Authentication that lets you modify the response with:
public void Execute(AuthFilterContext authContext)
{
authContext.AuthResponse.Meta = new Dictionary<string,string> { ... };
}
Another option is to override Authenticate in your AuthProvider, e.g:
public override object Authenticate(
IServiceBase authService, IAuthSession session, Authenticate request)
{
var response = base.Authenticate(authService, session, request);
if (response is AuthenticateResponse authDto)
{
authDto.Meta = new Dictionary<string,string> { ... }
}
return response;
}
Alternatively as it's just a normal Service you could also register a Global Response Filter to modify the Response DTO, e.g:
GlobalResponseFilters.Add((req, res, responseDto) =>
{
if (responseDto is AuthenticateResponse authDto)
{
authDto.Meta = new Dictionary<string,string> { ... }
}
});
EDIT: I changed my approach. Now I am using MessageHandlers. Thanks to Raciel, who pointed the direction where to dig.
These links where very useful for me:
MessageHandlers overview
Using own Principal classes
I have a WebApi project and need to provide custom authorization to it. Special Token object is added to each request from the frontend. Something like that:
SendApiRequest: function (controller, action, data, successCallback, failureCallback) {
var url = ROUTER.GetApiUrl(controller, action);
data.Token = TOKEN;
jQuery.ajax({
type: 'POST',
dataType: 'json',
url: url,
data: data,
success: function (result) {
if (typeof successCallback == 'function') {
successCallback(result);
}
},
error: function (result) {
if (typeof failureCallback == 'function') {
failureCallback(result);
}
}
});}
I have an AuthorizationAttribute and I somehow need to serialize Token object from the request. But cannot find any automatic way to do that.
public class CustomAuthorizationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext context)
{
UserToken token = null;
//HOW TO SERIALIZE token object from context?
}
}
UserToken class looks like that:
public class UserToken
{
public Int64 TokenID;
public int UserID;
public string TokenValue;
public string IP;
public string Hash;
public DateTime CreatedOn;
public DateTime ActionOn;
}
So the question is: how to serialize custom Object from HttpActionContext?
Thank you.
This is what I have done when I'm dealing with cases similar to yours.
Instead of creating your own Authorize attribute, you can create a MessageHandler which checks for the token and validates it on every request. This message handler is the responsible for populating the Principal in the current thread, so the Authorize attribute can work as expected allowing authorized clients to access the controller/action in question.
This is how my Authorization message handler looks like:
public class AuthMessageHandler : DelegatingHandler
{
protected ITokenProvider TokenProvider { get; private set; }
protected IPrincipalProvider PrincipalProvider { get; private set; }
public AuthMessageHandler(ITokenProvider tokenProvider, IPrincipalProvider principalProvider)
{
TokenProvider = tokenProvider;
PrincipalProvider = principalProvider;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Identity identity = null;
string token = ExtractToken(request);
if (token != null && TokenProvider.Verify(token, out identity))
{
request.Properties.Add(Constants.IdentityKey, identity);
var principal = PrincipalProvider.CreatePrincipal(identity);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
return base.SendAsync(request, cancellationToken);
}
private string ExtractToken(HttpRequestMessage request)
{
IEnumerable<string> tokenValues = null;
if (request.Headers.TryGetValues(Constants.TokenHeaderKey, out tokenValues))
return tokenValues.First();
return null;
}
}
Please note:
Both TokenProvider and PrincipalProvider are injected here. The first is in charge of validating the token and if valid, return the identity data so it is available across the request.
The IPrincipal provider just creates a GenericPrincipal that is then assigned to the Thread.CurrentPrincipal and to the context user (HttpContext.Current.User). This ensures that the Authorize attribute works later, when it checks for IsAuthenticated in the current Principal.
In this case, I'm passing the token information in the headers, which I prefer. (You might need to authorize GET requests too)
If you need to pass data from a message handler to a controller, you use the request.Properties, this is why I'm putting the Identity information there with request.Properties.Add(Constants.IdentityKey, identity); so it is available to the controller. There are several ways you can achieve this, but this dictionary is pretty much a cheap transport for this type of data.
Please let me know if you have any questions. This is actually simpler that it looks.