I have a Winform client that we are slowing changing inline SQL data calls into ASP.NET Web API calls. We currently use the WindowsPrincipal.IsInRole check in the Winform client to determine if the user can run the SQL data calls. We would like to move into a Claims type setup where both the Winform client and the Web API can check the roles "claims" of a user.
I can't seem to find any "good" articles on how to get a Winform client to (1. Pass the claim to the service) and (2. Use a claim check inside the Winform client like the IsInRole). Any help or push in the right direction would be great.
--EDIT
So I used this article http://zamd.net/2012/05/04/claim-based-security-for-asp-net-web-apis-using-dotnetopenauth/ as a sample on getting a token back from the server but the article does not show how to get the claims identity out of the http client. Any idea how to get the claims identity out of the http client?
While I haven't tested this code, hopefully it will get you moving in the right direction.
I believe to answer your question you do this in your ClaimsAuthenticationManager where upon validating the token received from the server you set the Thread.CurrentPrincipal -- the same way you do on the web side without setting the HttpContext.Current.User principal.
Again this isn't tested but I think it would look something like this...
In my Token Validator I have the following code:
public static ClaimsPrincipal ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(new JwtSecurityToken(token),
Constants.TokenValidationParameters);
return FederatedAuthentication.FederationConfiguration
.IdentityConfiguration
.ClaimsAuthenticationManager.Authenticate(token, claimsPrincipal);
}
public static string GetToken(string username, string password)
{
OAuth2Client client = Constants.OAuth2Client;
AccessTokenResponse response = client.RequestAccessTokenUserName(username.ToLower(), password,
Constants.AllowedAudience);
return response.AccessToken;
}
Within my ClaimsAuthenticationManager I have modified the following code as you don't want to set the HttpContext in a non web environment:
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
/* HttpContext.Current.User = */ Thread.CurrentPrincipal = incomingPrincipal;
return incomingPrincipal;
}
I believe you then just have to set the appropriate keys in the app.config, specifically the system.identityModel => identityConfiguration => claimsAuthenticationManager
Once the thread you are running on has the "Authenticated Principal" you should be able to call the ClaimsPrincipal.Current.HasClaim() or your higher level Authorization.CheckAccess() function to validate sections of your WinForm logic.
Hope this helps :)
Related
I have a simple requirement to read the token in asp.net core web api for every request and read the sub part of token which has UserID. Token validation will be done by a third party rest api call which will tell if token is valid or not. No validation logic should exist in my api.
Unfortunately all the articles I have read, they are validating the token, which I don't want in my case.
Any help would be much appreciated.
You can use the following extension method to get userID from HttpContext. Use ClaimType which contains userID value.
public static int GetUserID(this Microsoft.AspNetCore.Http.HttpContext context){
var claimsIdentity = context.User;
var claim = claimsIdentity.Claims.FirstOrDefault(cl => cl.Type == ClaimTypes.YOUR_CLAIM_TYPE);
return Convert.ToInt32(claim.Value);
}
Usage in your controller methods:
var userID = HttpContext.GetUserID();
Here is my code, I need to setMandatory to 'false' because the resource provider does not return the 'state' parameter as the Oauth2 specification recommends.
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2RestOperations restTemplate() {
OAuth2RestTemplate template = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(accessTokenRequest));
AuthorizationCodeAccessTokenProvider authorizationCodeAccessTokenProvider = new AuthorizationCodeAccessTokenProvider();
authorizationCodeAccessTokenProvider.setStateMandatory(false);
AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(authorizationCodeAccessTokenProvider));
provider.setClientTokenServices(clientTokenServices());
template.setAccessTokenProvider(provider);
return template;
}
Here is the code from the github example that works with H2:
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2RestOperations restTemplate() {
OAuth2RestTemplate template = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(accessTokenRequest));
AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(new AuthorizationCodeAccessTokenProvider()));
provider.setClientTokenServices(clientTokenServices());
return template;
}
I changed the LONGVARBINARY to BLOB for all cases in schema.sql, in order to get the scripts to work with MySQL. I can verify that my database is created.
template.setAccessTokenProvider(provider); without this line, I still get the CSRF problems because the resource provider isn't returning the "state" param.
I am using an AuthorizationCodeResourceDetails just like the example as well. I also setup my accessTokenRequest and clientTokenServices identical to the example.
After the user authorizes the client and is redirected back, the code is exchanged for a token and things are working. I can see that my client does have an access token. I expect to see that access token stored in the database as well. I am creating the database schema on startup and the datasource appears to be setup properly. I don't get any errors during runtime that point to configuration issues either. Everything else is working as expected, I just don't get any data in any of the tables where I expect to see information about the access and refresh tokens sent back from the resource server.
I am building an intranet application using ASP.NET MVC 4 with Windows authentication. In the global.asax file, I have implemented this method:
protected void WindowsAuthentication_OnAuthenticate(object sender, WindowsAuthenticationEventArgs args)
In this method, I create a new ClaimsIdentity and set args.User to it, just like the example on MSDN. Later on in the application, in one of the Controllers, I need to get some data from the database. Since I already had an API action that does this, I call that API (synchronously) from my Controller.
The API gets the claims for the current user using the ApiController.User property. Here though, the claims are not the ones I set in global.asax. In fact, they are the claims that were in place on the user before this request.
The strange thing (to me) is that the next time I make a call to the application, the new claims are in place. So in my case, I change the claims that later on decide which buttons should be visible to a user, but only after the user makes another request to the application, these buttons are updated.
How can I make sure that the claims that I set in global.asax immediately take effect?
Extra info:
I don't set the claims on every request. When this method executes, I check a number of things to see if the user is still valid: cookie, user isn't anonymous, and user is still "valid". The latter is decided by cache - I keep a list of users that are still valid and if someone updates their permissions through a user interface, they become invalidated and will receive new claims in their next request.
I've attached a debugger and I see my code getting executed, the principal gets all the claims I want it to have while still in this method. When I reach a controller action, ApiController.User has the claims it had on the request before this one. When I make another request, the authentication method is skipped (because the user name is now in the cache), and in the controller the ApiController.User has the correct claims.
You need to set both the members to make it work.
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
I don't think you can access your claims in the same request that you set them. Try to redirect after setting your claims.
I'm doing something similar. Here is my code, i hope it would be helpful.
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var clamisIdentityBuilder = DependencyResolver.Current.GetService<IClaimsIdentityBuilder>();
var transformer = new ClaimsTransformer(clamisIdentityBuilder);
var principal = transformer.Authenticate(string.Empty, ClaimsPrincipal.Current);
// user if authenticated but Claims could not be created (they are not available in cache nor DB)
if (principal == null)
{
var cacheProvider = DependencyResolver.Current.GetService<ICacheProvider>();
cacheProvider.Clear();
FormsAuthentication.SignOut();
Response.Clear();
string redirectUrl = FormsAuthentication.LoginUrl;
Response.Redirect(redirectUrl);
}
else
{
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
}
}
I'm using asp.net and trying to assign roles for a user with forms authentication like this:
public ActionResult AdminLogin(string password, string username)
{
User _user = _us.GetUsers(username, password).FirstOrDefault();
if (_user != null)
{
string _username = _user.Username;
FormsAuthentication.SetAuthCookie(_username, false);
string[] _roles = _us.GetUserRoles(_username);
HttpContext.User = new GenericPrincipal(HttpContext.User.Identity, _roles);
return RedirectToAction("Index", "Admin");
When I debug HttpContext.User.Identity always is null, but _username and _roles contains the proper data. Howto fix this?
/M
Your action is setting the User IPrincipal for the current context. As soon as you redirect to your other action (and all subsequent requests) a new HttpContext is created with a null User IPrincipal.
What you could do is persist the information in the authentication cookie and then extract that data in the Application_AuthenticateRequest method in your Global.asax file and set the User property of the HttpContext there.
This answer contains more details and example code
I believe the issue is that you are just setting the user as authenticated, and therefore, the HttpContext is not updated yet since the auth cookie has not yet been set on the users side of the request.
I was struggling too.
I was trying to carryout my authentication and authorization inside a WCF service using standard ASP.Net Membership and Role providers.
I wanted to pass in credentials and a 'requested app' to determine if the user 'authenticated' for that app. (not the ASP.Net APP, but an app in my own database).
To do this, I wanted access to the roles, but didn't want to 'redirect' or have a second call to my WCF service.
Here is some code that works for me:
First I determine if the user is valid as follows:
if (Membership.ValidateUser(CompanyCn, CompanyPwd))
{
sbLogText.AppendFormat("\r\n\r\n\tValid User UID/PWD: '{0}'/'{1}'", CompanyCn, CompanyPwd);
FormsAuthentication.SetAuthCookie(CompanyCn, false);
}
Then the following code workes nicely for getting the list of roles:
List<string> roleList = new List<string>(Roles.GetRolesForUser(CompanyCn));
sbLogText.AppendFormat("\r\n\r\n\tUser ('{0}'): Roles ({1}):", CompanyCn, roleList.Count);
foreach (string s in roleList)
sbLogText.AppendFormat("\r\n\t\tRole: {0}", s);
Is there any good way of combining ASP.NET Windows Authentication with a custom IPrincipal/IIdentity object? I need to store the user's email address and have done so for Forms Authentication using a custom IIdentity/IPrincipal pair that I added to the Context.CurrentUser during the AuthenticateRequest event.
How would I best go by to accomplish this using WindowsAuthentication?
Maybe you could create your "ExtendedWindowsPrincipal" as a derived class based on WindowsPrincipal, and just add your extra data to the derived class?
That way, your ExtendedWindowsPrincipal would still be recognized anywhere where a WindowsPricinpal is needed.
OR: since you're talking about using Windows Authentication, you're probably in a Windows network - is there an Active Directory or a user database somewhere, where you could look up your e-mail address that you're interested in instead of storing it in the principal?
Marc
I ended up refactoring my initial solution into replacing the Principal instead of the Identity as I originally thought. Replacing the Identity proved troublesome, since i ran into security problems when creating an instance of a new extended WindowsPrincipal.
public class ExtendedWindowsPrincipal : WindowsPrincipal
{
private readonly string _email;
public ExtendedWindowsPrincipal(WindowsIdentity ntIdentity,
string email) : base(ntIdentity)
{
_email = email;
}
public string Email
{
get { return _email; }
}
}
In my Authentication module i replaced the principal on the HttpContext like this:
var currentUser = (WindowsIdentity)HttpContext.Current.User.Identity;
HttpContext.Current.User =
new ExtendedWindowsPrincipal(currentUser, userEmail);