So, ive made a simple basic auth middleware for owin. The middleware returns an AuthenticationTicket with the user principal claims, authentication type ("Basic") yada yada.
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
....
var claimsIdentity = TryGetPrincipalFromBasicCredentials(token, Options.CredentialValidationFunction);
....
var ticket = new AuthenticationTicket(claimsIdentity, new AuthenticationProperties(){userName = claimsIdentity.name});
return Task.FromResult(ticket);
}
To get the claims, im using a hardcoded value for testing:
var claims = new List<Claim>();
claims.Add(new Claim(KneatClaimTypes.Permission, Permission.UserAdministrationView.ToString()));
var claimsIdentity = new ClaimsIdentity(
new GenericIdentity("Administrator", "Basic"),
claims
);
return claimsIdentity;
I`m hooking into owin using a simple extension using the same architecture IdentityModel has
https://github.com/IdentityModel/IdentityModel.Owin.BasicAuthentication
app.UseBasicAuthentication(new BasicAuthOptions("Realm",
(username, password, context) => BasicAuthCredentialCheck.Authenticate(username, password, context))
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = "Basic"
});
Im creating the correct principal for the ticket, but after the owin resolves the authentication the Thread.currentPrincipal is not populated. If i add a simple cookie auth , whenever i call Owin.Authentication.SignIn(...) it creates the Principal for me. If i set manually the Thread.currentPrincipal, something is UNSETTING that.
Im not very deep on how owin works under the hood, and i was tryng to get the source from it and begin reading it maybe ill figure it out. But in case i dont, any ideas on who could be resetting this Thread.currentPrincipal ? (Searched my own app code, no one does that, only reads no setting)
Best Regards.
Related
I am having some issue to make work 2 auth provider at the same time for servicestack.
I am using the : JWT Tokens - Allowing users to authenticate with JWT Tokens I am my users get authenticate fine.
Still Now I would like to use the API Keys - Allowing users to authenticate with API Keys for a few external 3rd Parties user access.
Still when I Configure both my users allready authenticate by JWT Tokens doesnt work anymore.
Here is my configuration AuthProvider configuration :
IAuthProvider[] providers = new IAuthProvider[]
{
new JwtAuthProviderReader(this.AppSettings)
{
HashAlgorithm = "RS256",
PrivateKeyXml = this.AppSettings.GetString("TokenPrivateKeyXml"),
PublicKeyXml = this.AppSettings.GetString("TokenPublicKeyXml"),
RequireSecureConnection = this.AppSettings.Get<bool>("TokenUseHttps"),
EncryptPayload = this.AppSettings.Get<bool>("TokenEncryptPayload"),
PopulateSessionFilter = (session, obj, req) =>
{
ApplicationUserSession customSession = session as ApplicationUserSession;
if (customSession != null)
{
customSession.TimeZoneName = obj["TimeZoneName"];
customSession.Type = (FbEnums.UserType) (obj["UserType"].ToInt());
if (Guid.TryParse(obj["RefIdGuid"], out Guid result))
{
customSession.RefIdGuid = result;
}
}
},
},
new ApiKeyAuthProvider(AppSettings)
{
RequireSecureConnection = false
}
};
I am genereting fine the token with JwtAuth. Still It look like servicestack is not accepting my token as a valid session, because now whenever I do :
var session = httpReq.GetSession();
session.IsAuthenticated --> is always FALSE
If my remove ApiKeyAuthProvider from the configuration, token from JwtAuth working fine again.
How do I make both provider works together and tell servicestack tham some users will use JwtAuth and others ApiKeyAuth ?
You need to call a Service that requires Authentication, e.g. has the [Authenticate] attribute in order to trigger pre-Authentication for the IAuthWithRequest providers like JWT and API Key AuthProviders.
Here is my scenario. I have an MVC 5 application that uses Owin as an authentication mechanism. The default template calls the SignInManager.PasswordSignInAsync in the Login action which I would like to overwrite to use LDAP to validate the user instead of looking into the database.
I am able to do the validation via:
PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain.com", "DC=domain,DC=com", "user_name", "password");
bool authenticated = dc.ValidateCredentials(userName, password);
Then I can retrieve the UserPrincipal using:
UserPrincipal user = UserPrincipal.FindByIdentity(dc, IdentityType.SamAccountName, userName);
However, I am stuck here and I am not sure how to continue with signing in the user. The goal is that after I sign in the user, I would have access to User.Identity including all the roles the user is in. Essentially, the app should behave as if it uses Windows Authentication, but the credentials are provided by the user on the Login page.
You would probably ask why not user Windows Authentication directly. The app will be accessed from the outside of the network, but the requirements are to use AD authentication and authorization. Hence my predicament.
Any suggestions are highly appreciated.
Thank you.
After many hours of research and trial and error, here is what I ended up doing:
AccountController.cs - Create the application user and sign in
ApplicationUser usr = new ApplicationUser() { UserName = model.Email };
bool auth = await UserManager.CheckPasswordAsync(usr, model.Password);
if (auth)
{
List claims = new List();
foreach (var group in Request.LogonUserIdentity.Groups)
{
string role = new SecurityIdentifier(group.Value).Translate(typeof(NTAccount)).Value;
string clean = role.Substring(role.IndexOf("\\") + 1, role.Length - (role.IndexOf("\\") + 1));
claims.Add(new Claim(ClaimTypes.Role, clean));
}
claims.Add(new Claim(ClaimTypes.NameIdentifier, model.Email));
claims.Add(new Claim(ClaimTypes.Name, model.Email));
ClaimsIdentity ci = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = false,
ExpiresUtc = DateTime.UtcNow.AddDays(7),
}, ci);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid login credentials.");
return View(model);
}
IdentityConfig.cs (CheckPasswordAsync) - Authenticate against LDAP
public override async Task CheckPasswordAsync(ApplicationUser user, string password)
{
PrincipalContext dc = new PrincipalContext(ContextType.Domain, "domain", "DC=domain,DC=com", [user_name], [password]);
bool authenticated = dc.ValidateCredentials(user.UserName, password);
return authenticated;
}
Global.asax - if you are using the Anti Forgery Token in your login form
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
At this point, you will are logged in and can access the User.Identity object. You can also mark controllers and actions with [Authorize(Roles = "some_role"]
It turned out that it was easier than I thought, it is just that not much is really written on the topic (at least I could not find anything).
Also, this code presumes that you are running the app from a server which has access to the Domain Controller on your network. If you are on a DMZ server, you need to discuss this strategy with your network admin for other options.
I hope this saves you some time. I am also eager to hear what the community thinks of this. Maybe there is a better way of handling this situation. If so, please share it here.
Thanks.
Daniel D.
Our Mvc/WebAPI solution currently has four trusted identity providers which we have registered in ADFS3. Each of these identity providers can be used by our users by direct links, effectively working around any home-realm-cookies that ADFS may have created (eg: www.ourportal.com/accounts/facebook or www.ourportal.com/accounts/twitter). Currently we are migrating from WIF to OWIN but will keep using WS-Federation protocol for the time being by implementing wsfederation and cookie authentication middleware. When using WIF, we did the following in order to go directly to a known identity provider:
var signInRequest = new SignInRequestMessage(stsUrl, realm) { HomeRealm = homeRealm };
return new RedirectResult(signInRequest.WriteQueryString());
This seems to have two concerning behaviors, it does not pass the WsFedOwinState parameter, and on the return back to the Relying Party, the Home.cshtml is built (with a windows principal) before the the Owin authentication middleware is fired. The Home.cshtml being fired before the Owin middleware is the most concering as this view relies on Claims that would is provided in the transformation done by the authentication pipeline, which is fired afterwards and thus our view does not work. It works in the correct order when going to the portal in the normal way (eg www.ourportal.com)
I understand that in order to provide the Whr parameter, you do the following when configuring the ws-federation middleware:
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "SomeUrnOfAnIdentityProvider";
return Task.FromResult(0);
}
but this sets a single identity provider for the whole solution and does not allow our users to go directly to one of a list of identity providers.
The non-working method which builds the sign-in-request is currently:
private RedirectResult FederatedSignInWithHomeRealm(string homeRealm)
{
var stsUrl = new Uri(ConfigurationManager.AppSettings["ida:Issuer"]);
string realm = ConfigurationManager.AppSettings["ida:Audience"];
var signInRequest = new SignInRequestMessage(stsUrl, realm)
{
HomeRealm = homeRealm
};
HttpContext.Request.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return new RedirectResult(signInRequest.WriteQueryString());
}
The ws-federation and cookie middleware are configured as the first middleware in OWIN startup and the default authentication is set to
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
I think I found a solution. The new method for skipping the home realm screen would be like this :
private void FederatedSignInWithHomeRealm(string homeRealm)
{
HttpContext.Request
.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
var authenticationProperties = new AuthenticationProperties { RedirectUri = "/" };
authenticationProperties.Dictionary.Add("DirectlyToIdentityProvider", homeRealm);
HttpContext.GetOwinContext().Authentication.Challenge(authenticationProperties);
}
And the OWIN WS-Federation middleware would be configured like this :
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = notification =>
{
string homeRealmId = null;
var authenticationResponseChallenge = notification.OwinContext
.Authentication
.AuthenticationResponseChallenge;
var setIdentityProvider = authenticationResponseChallenge != null
&& authenticationResponseChallenge.Properties
.Dictionary
.TryGetValue("DirectlyToIdentityProvider", out homeRealmId);
if (setIdentityProvider)
{
notification.ProtocolMessage.Whr = homeRealmId;
}
return Task.FromResult(0);
}
},
MetadataAddress = wsFedMetadata,
Wtrealm = realm,
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = realm
}
});
I'm trying to implement OAuth using OWIN for a Web API v2 endpoint on my local intranet. The API is hosted in IIS using built-in Windows Authentication. In short, this is what I want to happen.
When I ask for my Token at /token
Pull the WindowsPrincipal out of the OWIN context
Use the SID from the WindowsPrincipal to look up some roles for this
user in a SQL table.
Create a new ClaimsIdentity that stores the username and roles
Turn that into a Json Web Token (JWT) that I sent bak
When I request a resource from my API using my token
Convert the JWT Bearer token back to the ClaimsIdentity
Use that ClaimsIdentity for authorizing requests to the resource by
role
This way I don't have to do a database lookup for user roles on each
request. It's just baked into the JWT.
I think I'm setting everything up correctly. My Startup.Configuration method looks like this.
public void Configuration(IAppBuilder app)
{
// token generation
// This is what drives the action when a client connects to the /token route
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
// for demo purposes
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(8),
AccessTokenFormat = GetMyJwtTokenFormat(),
Provider = new MyAuthorizationServerProvider()
});
//// token consumption
app.UseOAuthBearerAuthentication(
new OAuthBearerAuthenticationOptions()
{
Realm = "http://www.ccl.org",
Provider = new OAuthBearerAuthenticationProvider(),
AccessTokenFormat = GetMyJwtTokenFormat()
}
);
app.UseWebApi(WebApiConfig.Register());
}
MyAuthorizationServerProvider looks like this...
public class MyAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Since I'm hosting in IIS with Windows Auth enabled
// I'm expecting my WindowsPrincipal to be here, but it's null :(
var windowsPrincipal = context.OwinContext.Request.User.Identity;
// windowsPrincipal is null here. Why?
// Call SQL to get roles for this user
// create the identity with the roles
var id = new ClaimsIdentity(stuff, more stuff);
context.Validated(id);
}
}
My problem is that context.Request.User is null here. I can't get to my WindowsPrincipal. If I create some other dummy middleware, I can get to the WindowsPrincipal without issue. Why is it null in this context? Am I doing something wrong?
Swap the order of UseOAuthAuthorizationServer and UseOAuthBearerAuthentication. UseOAuthBearerAuthentication calls UseStageMarker(PipelineStage.Authenticate); to make it (and everything before it) run earlier in the ASP.NET pipeline. User is null when you run during the Authenticate stage.
Using asp.net identity version 1.0.0-rc1 with Entity Framework 6.0.0-rc1 (the ones that come with Visual Studio 2013 RC).
Trying to give users an opportunity to change their UserName.
There seems to be no function for that under AuthenticationIdentityManager, so I change the data using EF (get User object for current user, change UserName and save changes).
The problem is that authentication cookies remain unchanged, and the next request fails as there is no such user.
With forms authentication in the past I used the following code to solve this.
var formsAuthCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
var isPersistent = FormsAuthentication.Decrypt(formsAuthCookie.Value).IsPersistent;
FormsAuthentication.SetAuthCookie(newUserName, isPersistent);
What should I do with asp.net identity to update the cookies?
UPDATE
The following code seems to update the authentication cookie.
var identity = new ClaimsIdentity(User.Identity);
identity.RemoveClaim(identity.FindFirst(identity.NameClaimType));
identity.AddClaim(new Claim(identity.NameClaimType, newUserName));
AuthenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(identity), new AuthenticationProperties {IsPersistent = false});
The remaining problem is: how to extract IsPersistent value from current authentication cookie?
How do you login/authenticate a user with Asp.Net MVC5 RTM bits using AspNet.Identity?
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
For the RC1, You can use the similar code.
AuthenticationManager.SignOut();
IdentityManager.Authentication.SignIn(AuthenticationManager, user.UserId, isPersistent:false);
For persistent value, you need to access the authentication cookie and retrieve the status.
Updated:
Use appropriate AuthenticationType used in place of "Bearer". Also make sure while issuing the signin ticket, you are setting the AuthenticationProperties.IsPersistent.
bool isPersistent=false;
var authContext = await Authentication.AuthenticateAsync("Bearer");
if (authContext != null)
{
var aProperties = authContext.Properties;
isPersistent = aProperties.IsPersistent;
}