Reputation: 22556
I have set-up a customer Authorization Attribute in my asp.net web api
global file:
FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters);
FilterConfig.cs
public static void RegisterHttpFilters(System.Web.Http.Filters.HttpFilterCollection filters)
{
filters.Add(new TokenAuthentication(""));
}
Authentication Class:
public class TokenAuthentication : Attribute, IAuthenticationFilter
{
private readonly string realm;
public bool AllowMultiple { get { return false; } }
public TokenAuthentication(string realm)
{
this.realm = "realm=" + realm;
}
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var request = context.Request;
// Receive token from the client. Here is the example when token is in header:
string token = null;
if (request.Headers.Contains("Token"))
{
token = request.Headers.GetValues("Token").FirstOrDefault();
}
if (token != null && token != "")
{
// Get your secret key from the configuration
var secretKey = ConfigurationManager.AppSettings["JWTSecurityKey"];
try
{
//Get the Payload from the token
string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
int separatorIndex = jsonPayload.IndexOf(';');
string userId = "";
DateTime timeIssued = DateTime.MinValue;
if (separatorIndex >= 0)
{
//userId = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(jsonPayload.Substring(0, separatorIndex)));
userId = jsonPayload.Substring(1, separatorIndex - 1);
string tmpTime = jsonPayload.Substring(separatorIndex + 1, (jsonPayload.Length - separatorIndex) - 2);
timeIssued = DateTime.Parse(tmpTime);
}
short TokenTTL = 10; //minuets
//Int16.TryParse(ConfigurationManager.AppSettings["TokenTTL"],TokenTTL);
// if ((DateTime.Now.Subtract(timeIssued).TotalMinutes >= TokenTTL))
if ((DateTime.Now.Subtract(timeIssued).TotalMinutes < 0))
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
else
{
//Save user in context
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, userId)
};
var id = new ClaimsIdentity(claims, "Basic");
var principal = new ClaimsPrincipal(new[] { id });
// Set the user name to the user id in the httpcontext which is passed to the controller
context.Principal = principal;
}
}
catch (JWT.SignatureVerificationException)
{
context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[0], context.Request);
}
}
else
{
return Task.FromResult(0);
}
return Task.FromResult(0);
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
context.Result = new ResultWithChallenge(context.Result, realm);
return Task.FromResult(0);
}
}
/// <summary>
///
/// </summary>
public class ResultWithChallenge : IHttpActionResult
{
private readonly IHttpActionResult next;
private readonly string realm;
/// <summary>
/// Constructor
/// </summary>
/// <param name="next"></param>
/// <param name="realm"></param>
public ResultWithChallenge(IHttpActionResult next, string realm)
{
this.next = next;
this.realm = realm;
}
/// <summary>
///
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var res = await next.ExecuteAsync(cancellationToken);
if (res.StatusCode == HttpStatusCode.Unauthorized)
{
res.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Basic", this.realm));
}
return res;
}
}
Now my issue is this class is being called even when the [AllowAnonymous]
attribute is applied to my Controllers Action.
This is giving me trouble specifically with the login action as the user does not have a token yet.
How can I rectify this?
Upvotes: 3
Views: 4078
Reputation: 19311
AllowAnonymous
will work only with authorization filter. What you have here is an authentication filter. Use OverrideAuthentication
from System.Web.Http instead.
UPDATE
Where in my original answer did I say you must implement authentication? All I have said is, if you do not want the AuthenticateAsync
method of the TokenAuthentication
to be called, apply OverrideAuthentication
, instead of AllowAnonymous
. AllowAnonymous
will work only with authroization filters and not authentication filters. So, in your Login
action, you will need to have OverrideAuthentication
applied like this.
public class SomeController : ApiController
{
[OverrideAuthentication]
public HttpResponeMessage Login(Dto dto)
{ }
}
Upvotes: 5