I am developing a web application on ASP.Net MVC 4 with razor syntax. I have to deploy it on cloud, probably on Azure.
I am quiet confused about the login scheme of MVC. We have to work on multiple schemas so thats why we aren't using the membership provided by ASP.Net.
I know session maintenance and i used it in web forms but session have some serious issues with cloud.
What would be the best method to save usernames and session data?
I would avoid using the Session State to store user information or even session data, because this makes your application less scalable.
If you want to store a username, displayname, email address, ... I would suggest Claims Based authentication. Brock Allen wrote a great introduction article to get you started: Replacing forms authentication with WIF’s session authentication module (SAM) to enable claims aware identity.
The main idea is that you hand out a cookie (just like with Forms Authentication):
Claim[] claims = LoadClaimsForUser(username);
var id = new ClaimsIdentity(claims, "Forms");
var cp = new ClaimsPrincipal(id);
var token = new SessionSecurityToken(cp);
var sam = FederatedAuthentication.SessionAuthenticationModule;
sam.WriteSessionTokenToCookie(token);
And this cookie represents a ClaimIdentity which can contain one or more claims like email address etc...
private Claim[] LoadClaimsForUser(string username) {
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(ClaimTypes.Email, "username#company.com"),
new Claim(ClaimTypes.Role, "RoleA"),
new Claim(ClaimTypes.Role, "RoleB"),
new Claim(OfficeLocationClaimType, "5W-A1"),
};
return claims; }
In terms of session data you might want to consider Windows Azure In-Role Caching or the Windows Azure Caching Service. There's even a Session State Provider which can store the session state in cache: http://msdn.microsoft.com/en-us/library/windowsazure/gg185668.aspx.
But you can easily do this yourself without using the session state by playing with the cache keys, like this:
myCache.Put(user.Id + "_Friends", friendsList);
Related
I have an existing ASP.NET app deployed on Azure. It has its own auth system, essentially a u/p database table and creation of a forms auth cookie:
public void LogIn(LoginDetails userLogin, bool createPersistentCookie)
{
var info = (CustomPrincipalInfo) userLogin;
var timeout = 30;
if (createPersistentCookie)
timeout = 60*24;
var cookie = info.CreateAuthenticationCookie(DateTime.Now, timeout, createPersistentCookie);
HttpContext.Current.Response.Cookies.Add(cookie);
}
public static HttpCookie CreateAuthenticationCookie(this CustomPrincipalInfo info, DateTime current, int timeout, bool remember)
{
var serializer = new JavaScriptSerializer();
var userData = serializer.Serialize(info);
var ticket = new FormsAuthenticationTicket(
1, info.Email, current, current.AddMinutes(timeout), remember, userData);
var secureTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, secureTicket) { Domain = FormsAuthentication.CookieDomain };
if (remember)
cookie.Expires = current.AddMinutes(timeout);
return cookie;
}
Most of the customers just register details, log in using their details with us etc, but we've been asked to integrate with a customer that uses ADFS for SSO. My reading around the topic suggests that we need to run an ADFS server, integrate our app with it, and then engage in a process of establishing trust between their ADFS server and ours
I'm curious to know if, given that it's hosted on Azure already, there is anything we can do with Azure/AAD to skip the "run an ADFS server" part, because we don't really have any infrastructure for doing so. The app startup routine is older school Global.asax style, using castle windsor for DI/IoC:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
DependencyRegistrar.Register();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
InitializeInfrastructure();
MigrateDatabaseSchema();
DataAnnotationConfig.RegisterValidationAttributes();
BundleConfig.RegisterBundles();
}
The resources I'm turning up are generally older stuff:
ADFS with existing ASP.Net MVC App - this mentions a separate blog discussing server 2012r2 in depth - we do have such a sevrer, but are looking to retire it rather than add another reason to carry on using it/have to replace it with a newer one
Azure Websites SSO using ADFS brought up an azure sample that seems to imply Azure can help us avoid having to run a dedicated ADFS server, but again the sample is ~9 years old
The most recent one I found seemed to be a reasonable description of the problem I face, but I couldn't understand the answer (which basically seemed to be "read this link, scroll down a bit" - reading the link didn't smack me in the face with anything obviously "this is what you have to do")
Resources surrounding using ADFS on Azure seem massively involved; I don't feel like I need/want/to pay for load balancers, multiple storage accounts, DMZs and proxies etc.. This is literally so that one user at one customer can sign in a handful of times a month
What set of steps do I take to create a really basic ADFS SSO auth mechanism in this existing app, hook in to how it IDs users so that when joe.bloggs#customer.com comes and signs in via ADFS I can become aware of the email address that is signed in and give him his forms auth cookie like anyone else, and leverages some Azure based facility so that I don't have to run an ADFS server?
There are two ways to do this.
The basic problem is that your credentials are in a DB and neither AAD nor ADFS supports this.
You could run identityserver4 in Azure and configure idsrv4 to authenticate against the DB. Then you could federate idsrv4 with the partner ADFS.
Or you could move the users into Azure AD via the Graph API and then federate AAD with ADFS.
In my current asp.net MVC core application we use OpenId Connect to authenticate with our corporation's identity provider.
However during local development we cannot reach the the provider.
Also we would like to easily change claim values for development and unit testing purposes.
I tried swapping my service binding for IHttpContextAccessor to a DevelopmentHttpContextAccessor that fills up the HttpContext's Identity with the desired claims.
This is a bit of a roundabout way and it also doesn't work as the claims are emptied when I check them in my Service.
What is the standard way of handling this during development and unit testing? What am I missing?
The answer was not faking IHttpContextAccessor, but setting a custom ClaimsPrincipal to the identity property.
In your Startup.Configure you can add a middleware step:
app.Use((httpContext, nextMiddleware) => {
var claims = new[] {
// Your claims here
};
var claimsIdentity = new ClaimsIdentity(claims);
var user = new ClaimsPrincipal(claimsIdentity);
httpContext.User = user;
return nextMiddleware();
});
I have my own tables for Authentication:
Users
Roles
UserRoles
I am am trying to figure out what the best way to implement custom authentication with ASP.NET Core MVC would be. I do not want to use the built in UserManager, RoleManager, etc. I prefer creating my own. Can I somehow still tap into the cookie based authentication and use all of the ASP.NET Authorization helper tags without using asp.net identity?
Correct me if I am wrong, but I believe I want something like this:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie
I have been trying to figure out ASP.NET Identity for years and I am
sick of not understanding every single part of it. I would rather
implement my own auth just like the good old days. It is most likely
my problem for not being able to read the documentation and pick up on
it, but I cant stand Entity Framework / ASP.NET Identity. I am aware
that ASP.NET Identity can be used without EF but just seems like a
pain.
Well, if you decided to go that route, you can use Cookie Authentication Middleware.
There are too many moving pieces, so I created a sample project in GitHub.
You can replace this LDAP Authentication with your own Authentication mechanism. Here is actual implementation.
The main reason I did not use ASP.NET Identity in some of my projects is we already have Active Directory in our organization.
Startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
Events = new CookieAuthenticationEvents
{
OnRedirectToAccessDenied = context =>
{
context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
return TaskCache.CompletedTask;
}
},
ExpireTimeSpan = TimeSpan.FromMinutes(Int32.Parse(Configuration.GetSection(
"AppSettings:CookieAuthentication:ExpireMinutes").Value)),
AuthenticationScheme = Constants.AuthenticationScheme,
LoginPath = new PathString("/Account/Login"),
AccessDeniedPath = new PathString("/Common/AccessDenied"),
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
I'm working on a SaaS application built around ASP.net MVC & WebAPI and want to make it easy for enterprises to use my service. Example would be Office 365 Basic Authentication (Active Profile) where the user enters his username/password on microsoft's site (or desktop app) and he is authenticated against his employer's Active Directory. My understanding so far is that I would need to create a RP-STS which will accept credentials and then forward those to AD FS Proxy running on the client company's AD server. Is this correct?
If yes, then how do I implement this? Setting up AD server adding a Relying Party and AD FS Proxy Role is easy, so that's really not an issue. I just need to figure out how to create/setup RP-STS service and any other steps involved in this process. There just isn't an example/tutorial of this in .net
I believe this msdn blog post describes exactly what you're asking for. It has a complete walkthrough of the entire process, including creating an RP by creating a normal WCF service, and then use the provided utility to configure the service to trust your ADFS.
http://blogs.msdn.com/b/mcsuksoldev/archive/2011/08/17/federated-security-how-to-setup-and-call-a-wcf-service-secured-by-adfs-2-0.aspx
Edit:
This code, taken from the linked article (comments are mine), is a demonstration of active federation. The client application is manually retrieving a security token from the ADFS. Passive Federation would involve forwarding the user to a secure web page in which they could send their credentials directly to the ADFS. The major benefit of Passive Federation is that the end user's secret credentials are provided directly to the ADFS, and the RP's client side code never has access to it.
var requestTokenResponse = new RequestSecurityTokenResponse();
//The line below is the 'Active' federation
var token = Token.GetToken(#"mydomain\testuser", "p#ssw0rd", "http://services.testdomain.dev/wcfservice/Service.svc", out requestTokenResponse);
var wcfClient = new FederatedWCFClient<MyTestService.IService>(token, "WS2007FederationHttpBinding_IService"); // This must match the app.config
var client = wcfClient.Client as MyTestService.IService;
var result = client.GetData();
Console.WriteLine(result);
wcfClient.Close();
Take a look at these links:
https://github.com/OfficeDev/O365-WebApp-SingleTenant
https://github.com/OfficeDev/O365-WebApp-MultiTenant
It shows how to make an application using the office 365 api to authenticate and authorize the users.
Be aware about Single Tenant and Mult Tentant application, and choose the right one.
It's really easy to do that, I've done it couple months ago.
I found the answer on the blog: http://leandrob.com/2012/04/requesting-a-token-from-adfs-2-0-using-ws-trust-with-username-and-password/
What this code essentially does is that it directly authenticates with the tenant's ADFS endpoint and gets a token as well. That's what I was looking for.
var stsEndpoint = "https://[server]/adfs/services/trust/13/UsernameMixed";
var relayPartyUri = "https://localhost:8080/WebApp";
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(stsEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
// Username and Password here...
factory.Credentials.UserName.UserName = user;
factory.Credentials.UserName.Password = password;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(relayPartyUri),
KeyType = KeyTypes.Bearer,
};
var channel = factory.CreateChannel();
SecurityToken token = channel.Issue(rst);
Another good article on that blog is: http://leandrob.com/2012/02/request-a-token-from-adfs-using-ws-trust-from-ios-objective-c-iphone-ipad-android-java-node-js-or-any-platform-or-language/ - which covers other similar scenarios.
I have a question regarding the claims in MVC 5.
So basically imagine I have a registered user in DB, now the user is going to log in, like so:
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Add more custom claims here if you want. Eg HomeTown can be a claim for the User
var homeclaim = new Claim(ClaimTypes.Country, user.HomeTown);
identity.AddClaim(homeclaim);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
So in this case i add a new claim to the identity and then i sign in this identity.
Now my questions are:
What is the use of setting this claim? (because i can also get this from the db if i need it, what is the point in this case of claim)
And how do i use it later on in the code?
Setting the Claim against the identity makes your application security more efficient and saves hitting your database each time.
The method above can be known as a Claims Transformation which often involves reading in data that is transformed into claims after authentication succeeds.
In order to read it later you can do this:
//Get the current claims principal
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
//Get the country from the claims
var country = identity.Claims.Where(c => c.Type == ClaimTypes.Country).Select(c => c.Value);
Update
Just to provide some further information to the answer as discussed in the comments below.
With a Claims based approach you also benefit from being able to use a claims authorization manager which can provide a centralized/finely grained access control to resources and actions.
If you've not used claims before it's best to think of actions against resources rather than role based permissions. That way you can drill right down and control access to each resource/action individually rather than having a multitude of roles for each one.
I personally like to use a mixture but store the roles as claims too.
That way I can use the standard authorization tags in mvc with roles, which read the claims and use thinktecture's attributes/ClaimsAuthorization to make the claims authorization manager pickup the more complicated rules.
A good link on implementing claims based authentication in MVC 4 is available here:
http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/