I am implemented basic authentication and authorization in web api using AuthenticationHandler : DelegatingHandler.
So before calling any api, the code in this handler gets executed which basically checks whether the user is authenticated or not.
This handler gets executed for each api call. Now my problem is that for few api like login or registration etc. where user is not logged in and I don't need to check the user authentication, how can I bypass this?
You should not get confused between authentication and authorization.
Basically, your AuthenticationHandler should only authenticate the user and sets the user identity.
The point of authentication is to say who this user is (a manager, a teller, an anonymous user,...). You should not reject the request here, it's for authorization. Example code:
public class AuthHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//authenticate with your data storage (user,password), or decrypt the information from request's token (I don't know what approach you're doing)
// here I hardcode just for demo
//If the user is authenticated (not an anonymous user)
//create a identity for that user and set the roles for
//the user. The roles could come from your db or your decrypted token depending on how you implement your code.
GenericIdentity MyIdentity = new ClaimsIdentity("MyUser");
String[] MyStringArray = {"Manager", "Teller"};
GenericPrincipal MyPrincipal = new GenericPrincipal(MyIdentity, MyStringArray);
//Set the authenticated principal here so that we can do authorization later.
Thread.CurrentPrincipal = MyPrincipal;
if (HttpContext.Current != null)
HttpContext.Current.User = MyPrincipal;
return await base.SendAsync(request, cancellationToken);
}
}
Authorization happens after authentication to verify whether the user has the rights to access a function. That could be accomplished by applying AuthorizeAttribute:
On the action method.
On the controller. All the action methods require the user to be not anonymous. You can override this in each action method by applying AllowAnonymousAttribute
Globally by adding the AuthorizeAttribute to the application filters collection. You can use the same technique with AllowAnonymousAttribute for specific action methods. Example code extracted from the link:
In your case, you can:
Add the AuthorizeAttribute globally to your application filters collection.
Set an identity in your AuthHandler based on the authenticated user.
Apply AllowAnonymousAttribute on your login, registration action methods.
Side note: The most prominent authorization approach today is claims based security. If you have time, you should spend some time to investigate that. Basically, the idea is similar, just that we use claims instead of roles for authorization.
With claims-based in web api, you could subclass ClaimsAuthorizationManager to implement your authorization rules by overriding the CheckAccess method.
Related
I have this case where I have an anonymous endpoint can work on it's own but if a user have already done authentication with the cookie I need to get from that authenticated cookie session the data about the authenticated user, but as I searched online I haven't found a solution to this and the User.Identity.IsAuthenticated is always false despite the user has the authenticated session cookie but on a Anonymous endpoint it's like being ignored so I can't figure out if the user data in the cookie.
Is there any way to accomplish this?
Example of the needed behaviour:
[HttpGet,AllowAnonymous]
public async Task<IActionResult> GetSomething()
{
if(User.Identity.IsAuthenticated){
//Get data from User.Claims and act upon it
} else {
//Get data without user logic
}
}
You still must use the Authorize attribute. The AllowAnonymous attribute serves to still allow access if the user isn't authorized.
[HttpGet,Authorize,AllowAnonymous]
public async Task<IActionResult> GetSomething()
My ASP.Net Web API app created a JWT Token upon successful login.
public IHttpActionResult LogOn([FromBody] LoginRequest request)
{
var result = _service.LogOn(request);
if (result.Success)
{
var token = CreateToken(request.UserName);
return Ok(OpResult<string>.SuccessResult(token));
}
return Ok(result);
}
I have all controller methods decorated with an "Authorize" attribute which delegates to my TokenValidationHandler (inherits from DelegatingHandler) to validate the token in subsequent requests.
[HttpGet]
[Authorize]
public IHttpActionResult GetAccount(){ // get user details here}
Now I have a requirement to not let the user in unless they have created an account and verified their eMail address. So my idea is that in the first method (user login), instead of just checking result.success and issuing a token, I'd check also check if the retrieved account is eMail verified. If not I'd issue a jwt token with an additional claim "emailverified" set to false. So users hwo haven't activated their eMail can still login and get this jwt token, but the only operation they are allowed is VerifyEmail.
How do I go about implementing this VerifyEmail controller method? Ideally I want it to look like below
[HttpGet]
[AuthorizeEvenIfEmailNotVerified]
public IHttpActionResult GetAccount()
How do I implement AuthorizeEvenIfEmailNotVerified ? Is it another handler that inherits from DelegatingHandler ? But if I have two such handlers (my existing handelr for the regular authorize and this new one), then how does the ASP.Net engine know which handler to send [Authorize] attribute to and which to send [AuthorizeEvenIfEmailNotVerified] to ?
Or should I be using an AuthenticationFilter?
But in that case, it seems weird that I have two attributes doing pretty much the same thing (one authenticating a verified user and the other authenticating a non verified user). yet one of those is implemented via [Authorize] backed by handler inheriting DelegatingHandler whereas the other is implemented via an attribute backed by an AuthenticationFilter?
Or am I going about this the wrong way? For the record I'd prefer to keep the project free of any MVC related libraries unless absolutely needed. Also this is .Net Framework 4.7 project.
Probably Roles will be easiest solution. Generate token with relevant claim:
identity.AddClaim(new Claim(ClaimTypes.Role, "Verified")); //verified email
identity.AddClaim(new Claim(ClaimTypes.Role, "NotVerified")); //not verified email
Next add attribute to controller:
[Authorize(Roles="NotVerified")]
Using Owin + Oauth2 + Identity2.
I have a web Api with default basic authentication setup that i have modified.
my startup.cs partial class
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);//TODO: prob wont need this
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),//TODO: prob wont need this
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true //TODO: set debug mode
};
// Token Generation
app.UseOAuthBearerTokens(OAuthOptions);
}
my startup.cs class partial at the root
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
my applicationOAuthProvider.cs
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//get user
var service = new CarrierApi.CarrierManagementClient();
var result = service.LoginAsync(context.UserName, context.Password);
var user = result.Result.Identity;
//TODO: log stuff here? i.e lastlogged etc?
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = user;
ClaimsIdentity cookiesIdentity = user;
AuthenticationProperties properties = CreateProperties(user.GetUserName());
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
As you can see i actually go and get the identity via a wcf call to our existing DB. when using postman, i get the /token url and obtain my bearer token, on the next request i pass it into the header and call my controller method.
[Authorize(Roles = "Templates_Access")]
public string Post([FromBody]string value)
{
return "woo";
}
This works great, if the user has the permission it wont allow access, if they do it does.
However if i go to our website that uses the same wcf and DB and change a users permission, if i send the same request on postman it still allows access even though ive removed that permission on the role the user is assigned too.
How do i make sure that permissions are "refreshed" or checked again on each request?
Every roles of a user logged in are stored in the bearer token at login time as claim, in the GrantResourceOwnerCredentials method. If a request have to be authorized, the role is searched on the list stored in the bearer token by the default implementation of AuthorizationFilter; so if you change the user's permissions, you need a new login.
This behavior respects the Stateless contraint of a Restfull architecture, as Fielding wrote in his dissertation, and also this is a good balance between performance and security
If you need a different behavior there is more than one possibility.
Refresh Token
You Can use Refresh Token, implementing the GrantRefreshToken method of applicationOAuthProvider class; you can retrieves the refreshed user's permissions and create a new access token; this is a good article to learn how.
Keep in mind:
more complexity on client
no real time effect; you have to wait the access Token expiring
If the Access Token has a short life, you have to update it often (even when the user permissions are not changed) otherwise a long life does not solve the problem
Check Permissions each request
You can implement a custom AuthorizationFilter and check in the database the permissions of the user, but it is a slow solution.
Cache and Login Session
You can generate a user session's key (like a guid) for each login in the GrantResourceOwnerCredentials method, and store it in the bearer token as a claim. You have to store it also in a cache system (like Redis), using two index: the user session's key and the userId. The official documentation of Redis explains how.
When the permessions of a user are changed, you can invalidate every sessions of that user in the cache system, searching by userId
You can implement a custom AuthorizationFilter and check for each request in cache if the session is valid, searching by user session's key.
Be careful: this will violate the stateless constraint and your architecture will not restfull
Here you can find the standard implementation of AuthorizaAttribute filter.
You can create your custom filter extending AuthorizeAttribute and overriding the IsAuthorized method.
Most likely there are other ways, but how often are changed the permissions of a user? In many systems, also in systems where the security is the first requirement, if the permissions's configuration of a user is changed during an active session, it is necessary a new login to active the new one.
Are you sure you needs to modify this standard behavior?
If you are, I suggest the solution with a cache system.
I am using OAuth token based authentication in my web api based project.
If user is authenticated,an access token is generated as below.
{"access_token":"FFz_DC6zzEDD4mGOCk9172ijj3sGxCUWnk-tGanm9wGk76hMB8sHI8ImeWtdUKHHGNXv465ZSlbb-3fr_hr9DqUHc9Dm9OBI7XjJhdjdOpAGAGSFOpE0Y17LCEWTjCmEZotuf42Mpgl81ewoS7OlnH4b5w4PrtzJbIBpSAMoWObziL_U3mTkeFKvWrcWOfvlSCvhhBA9Dc3UTXv3HiHKWQk0T3-pvVy7ZuW2oac-IIuaq_GYaVkIZh7s9-YjX9KAL2Z9yfrPrVOQXZe_5OcNd7nS3tdT5odchEAiuWRYQ6t7Tfb2si4T6VdAe73OYefE0se1FeQsxbOiNaLyF8OwBqymEUzEG8tEHJ-cejVbhPw","token_type":"bearer","expires_in":1799,"as:client_id":"","user":"1","role":"1",".issued":"Thu, 16 Feb 2017 09:37:44 GMT",".expires":"Thu, 16 Feb 2017 10:07:44 GMT"}
Below is one of the api method.
[Authorize]
[HttpGet]
[Route("{userId}/{type}/")]
public IHttpResponse GetCustomerDetails(int userId, string type)
{
//my api stuff
}
I am using Postman for testing api. When I pass parameters as
http://localhost:50684/api/customer/1/gold
--along the access token in token in header--
It returns the desired json.
But if I use the same token & pass the customer id = 2,still it allows the access to the other customer(with id=2).
http://localhost:50684/api/customer/2/gold
--Access token in header--
It should NOT allow to access the resource to user with id=2 since the generated access token is valid for user with id =1.
How do I prevent this security breach?
Any help/suggestion highly appreciated.
Thanks
The problem is that you send the userId as a parameter which by itself is bad design.
The simple solution is to get the current user from the context instead
[Authorize]
[HttpGet]
[Route("{type}/")]
public IHttpResponse GetCustomerDetails(string type)
{
var user = RequestContext.Principal.Identity.Name;
//my api stuff
}
You can store user id and token in some storage(session, db).
And write own MVC authorization fileter like Authorize filter, which compare token with user id stored in storage.
Currently WebApi doesn't match the concept of user id and authenticated user's id. And it shouldn't, because the only thing you specify is the route of the controller's method with some parameter. You only require the user to be authenticated, by using "Authorize" attribute, but once the access is granted, no further validation is run. To make this method available to only specific subset of your users you could either write your own generic validation (i.e. in this case check against the claims of the user which can be accessed by "User" property within controller scope, or use some out-of-the-box external implementations of handling authentication.
I managed to implement this token based authentication system in my application, but I have a little question. How can I check if a user is signed it (eg if the there is a valid token in the request) within the method? So with the [Authorize] ?
So I have controller, and in that controller I want to check if the user is signed in. I thought of using this:
if (_signInManager.IsSignedIn(ClaimsPrincipal.Current))
{
...
}
but it does not work since ClaimsPrincipal.Current is always null
You don't need to use the SigninManager or something similar. The user is injected on the pipeline (on the User property of the base controller) and it's info is filled automatically by the authentication middleware (cookie or token). So, on your controller:
bool isAuthenticated = User.Identity.IsAuthenticated;
yes . put [Authorize] attribute above your class or methods to check if user is authenticate or not. You can get user by this code:
var principal = User as ClaimsPrincipal;
var check = User.Identity.IsAuthenticated;