WebApi serialize object from HttpActionContext - c#

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.

Related

Populate AuthContext in gRPC C# from JWT Authentication

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

What is 'Xsrf check when linking' in ASP.Net Identity/OWIN [duplicate]

In my MVC 5 web app I have this (in AccountController.cs):
// Used for XSRF protection when adding external sign ins
private const string XsrfKey = "XsrfId";
and
public string SocialAccountProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, SocialAccountProvider);
}
How exactly is it being used for protection?
Should I set the value of XsrfKey to something more random?
Take a look at ManageController methods LinkLogin and LinkLoginCallback:
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
These are the methods that handle linking of external accounts (i.e. Google, Facebook, etc.). The flow goes like this:
User clicks "Link Account" button, which calls a POST to LinkLogin method.
LinkLogin returns ChallengeResult object, with callback url set to LinkLoginCallback method.
ChallengeResult.ExecuteResult is called by MVC framework, calls IAuthenticationManager.Challenge, which causes a redirect to the specific external login provider (let's say: google).
User authenticates with google, then google redirects to callback url.
The callback is handled with LinkLoginCallback. Here, we want to prevent XSRF and verify that the call was initiated by a user, from a page served by our server (and not by some malicious site).
Normally, if it was a simple GET-POST sequence, you would add a hidden <input> field with an anti-forgery token and compare it with a corresponding cookie value (that's how Asp.Net Anti-Forgery Tokens work).
Here, the request comes from external auth provider (google in our example). So we need to give the anti-forgery token to google and google should include it in the callback request. That's exactly what state parameter in OAuth2 was designed for.
Back to our XsrfKey: everything you put in AuthenticationProperties.Dictionary will be serialized and included in the state parameter of OAuth2 request - and consequentially, OAuth2 callback. Now, GetExternalLoginInfoAsync(this IAuthenticationManager manager, string xsrfKey, string expectedValue) will look for the XsrfKey in the received state Dictionary and compare it to the expectedValue. It will return an ExternalLoginInfo only if the values are equal.
So, answering your original question: you can set XsrfKey to anything you want, as long as the same key is used when setting and reading it. It doesn't make much sense to set it to anything random - the state parameter is encrypted, so no one expect you will be able to read it anyway.
Just leave it as is:
As the name of the member states it is a key:
private const string XsrfKey = "XsrfId";
It is defined in this manner to avoid "magic numbers" and then is used a little down in the scaffold code:
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
The value of the dictionary item is then set to the UserId property in the above code by using the XsrfKey member as the key.
IOW the code is already setting the XSRF dictionary item to the value of the user ID in the snippet. If you change the XsrfKey members value to anything else you will cause problems down the line, since the expected key "XsrfId" will have no value set.
If by changing it to something more random you are implying to change the value and not they key of the dictionary, or in other words, not set it to the user id then please see the following for an explanation of the anti forgery token inner workings.
http://www.asp.net/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages

Get reference to requested Controller and Action in custom middleware in ASP.NET Core

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() { /* ... */ }

How to get whether user is authenticated by custom Action Filter in ASP.NET MVC View?

I have an action method that uses my authentication filter:
public class TutorAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"];
if (!string.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
var user = new { Name = cred[0], Password = cred[1] };
if (userService.AuthorizeTutor(user.Name, user.Password))
{
return;
}
}
filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", $"Basic realm= {BasicRealm}");
filterContext.Result = new HttpUnauthorizedResult();
}
}
I would like to then display on main page something for user that have been authenticated this way, but this does not work in my View :(
#if (Request.IsAuthenticated)
{
<h1>Hello</h1>
}
I know it does not work because I don't use Identity, but is there any way that I can do this?
Thank you for answers :)
I suppose, that sending login and password in header is not secure. Better solution is one time when user is verified. And after checking, you can check all request.
For example, if you use FormsAuthentication and authCookie it's very simple:
Set auth mentod in web.config: <authentication mode="Forms" />
When login and password is valid, use FormsAuthentication.SetAuthCookie(userName, createPersistentCookie = true); This step is performed only once, when user login to application.
Then you can use property this.Request.IsAuthenticated in view or HttpContext.Current.Request.IsAuthenticated in controller (or filter).
And it works attribute [Authorize] on conntrolers or actions (public methods in conntrollers). When request is not authenticated, request is redirected to default (or set in web.config) login page.
Create a new extension method for the request object say (IsUserAuthenticated()) & in that method check if the user is valid.
Once this is done, you can use this new extension method the same way you are using Request.IsAuthenticated Property.
Below is the sample code, which you will need to tweak as per your needs. (specifically for
userservice
initialization)
public class RequestValidator
{
public bool IsValid(HttpRequest request)
{
bool isValid = false;
//TODO: Intitialize your userService here, may be using DI or a concrete object creation depending on your implementation
var auth = request.Headers["Authorization"];
if (!string.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
var user = new { Name = cred[0], Password = cred[1] };
isValid = userService.AuthorizeTutor(user.Name, user.Password))
}
return isValid;
}
}
Your attribute will change like this
public class TutorAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
RequestValidator validator = new RequestValidator();
if(validator.IsValid(request))
{
return;
}
filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", $"Basic realm= {BasicRealm}");
filterContext.Result = new HttpUnauthorizedResult();
}
}
And the extension method to be used on view will be
public static class Extensions
{
public static bool IsUserAuthenticated(this HttpRequest request)
{
RequestValidator validator = new RequestValidator();
return validator.IsValid(request);
}
}
Use it like this
#if(Request.IsUserAuthenticated())
{
<p>Hello</p>
}
If you want to pass the boolean value indicating if the user is authenticated, maybe it makes sense to just use the model object and pass it to the view.
Or maybe you should review your Form Authentication to make Request.IsAuthenticated working properly. This thread will help to start digging.
Another option would be to consider using the IAuthorizationFilter instead of the custom action filter. This thread will be a starting point.
Hope that helps!
To meet your purpose, you would need to set HttpContext.User to some valid IPrincipal.
So, if according to your criteria, the user is valid you just need to create a GenericPrinicpal and set HttpContext.User with the instance you have just created.
Something like this:
var genericIdentity=new GenericIdentity(user.Name, "CustomAuthType");
var genericPrincipal=new GenericPrincipal(genericIdentity, null);
HttpContext.User = genericPrincipal;
With GenericIdentity, the value of IsAuthenticated is dependent on the Name property, so as soon as the GenericIdentity has a Name, it is considered to be authenticated.
In this example, I'm setting the HttpContext.User and not the Thread.CurrentPrincipal so that you can get the IsAuthenticated from the Request.IsAuthenticated property.
Some extra and related information:
GenericIdentity Class
Principal and Identity Objects
Create GenericPrincipal and GenericIdentity Objects
Replacing a Principal Object
in your startup.cs file add this
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(40)
});

ClaimsPrincipalPermission vs ClaimsAuthorization Attributes use different information, which is best to use

I'm trying to implement Claims Base Authorization on individual webapi's... However I don't want the user claims to be tightly coupled to the webapi. Using ClaimsPrincipalPermissions I get the desired decoupling as I can assign users to resources in the database. For example
Webapi Method
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Manage", Resource = "Roles")]
public async Task<IHttpActionResult> GetRole(string Id)
{
var role = await AppRoleManager.FindByIdAsync(Id);
if (role != null)
{
return Ok(ModelFactory.Create(role));
}
return NotFound();
}
Using Custom AuthorizationManager
public class AuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
//return base.CheckAccess(context);
string resource = context.Resource.First().Value;
string action = context.Action.First().Value;
if (action == "Manage" && resource == "Roles")
{
// check for roles
//context.Principal.IsInRole("Admin");
bool hasRolesManagement = context.Principal.HasClaim("ManageRoles", "True");
return hasRolesManagement;
}
return false;
}
}
I can do the appropriate checks for a particular logged in user checking their claims against the resource they are hitting. However I can't kick back the appropriate unauthorized response back to the user in this fashion. Another example I found is below where the webapi is as follows
[ClaimsAuthorization(ClaimType="ManageRoles", ClaimValue="True")]
[Route("")]
public IHttpActionResult Get()
{
return Ok();
}
Then using the custom claims authorization attribute
public class ClaimsAuthorizationAttribute : AuthorizationFilterAttribute
{
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
public override Task OnAuthorizationAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var principal = actionContext.RequestContext.Principal as ClaimsPrincipal;
if (!principal.Identity.IsAuthenticated)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult<object>(null);
}
if (!(principal.HasClaim(x => x.Type == ClaimType && x.Value == ClaimValue)))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult<object>(null);
}
//User is Authorized, complete execution
return Task.FromResult<object>(null);
}
}
This gives me access to the actionContext to send back an appropriately statusCoded response.
THE BIG Caveat to the second approach is to introduce accessability to the api method for a new claim I have to recompile the code.
So my question is how can I get the flexibilty of the first approach where each api is designated as a resource and the claims are assigned in the database to a resource, but still get the flexibility of the second approach where I have access to the action context and be able to return a properly status coded response? We don't want to have to recompile the code to allow additional roles/claims access to a webapi resource.
I'm new to claims so I don't have a thorough understanding of all the concepts yet so please tell me if I'm missing something.
According to the documentation for ClaimsPrincipalPermission.Demand
"If the current principal is not authorized for the specified action
on the specified resource, a SecurityException is thrown; otherwise,
execution proceeds."
The solution would then be to create an exception handler in your solution and return an UnAuthorized Http response. (I really thought ASP.Net did that by default, but I have never checked/reflected over that :)

Categories

Resources