I have been trying to find an explanation, but are coming rather short, as to how exactly identity works with mvc core. I have found numerous guides on how to implement it and I have and it all works great, but I want to understand it under the hood at the very least on a high level.
My current understanding is that when a user passes their credentials, identity library will issue a token and that token will be written to the browser's cookies. Now all further requests will have the user identified based on that browser cookie. However, I can't seem to find explanations past that. Why is this safe, why can't I steal that cookie and use it as my own? I know there is more to this and potentially I have understood the above part wrong.
In any case, I'm either looking for a high-level explanation on how this works or a good reference that can delve more into details.
how exactly identity works with mvc core.
As #Chris Pratt says, what you're talking about is the security subsystem. Since you're talking about cookie, I'll take the authentication scheme of cookie as an example.
The built-in security could be mainly found in 4 projects:
HttpAbstractions: core interfaces and classes, such as authentication scheme, authentication handler, authentication ticket and so on.
Security: authentication middleware, cookie authentication, JWT Bearer authentication, OAuth2.0 authentication(Google/Facebook/Microsoft/...) and so on.
Identity : a scaffold project named as "Identity" that helps to manage user/roles/claims/etc.
DataProtection : Data Protection APIs for protecting and unprotecting data. You can treat it as an API to encrypt and decrypt.
The entry point to understand how authentication works is the AuthenticationMiddleware. This middleware will try to authenticate every request if possible:
public async Task Invoke(HttpContext context)
{
// ...
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
// Use the default scheme to authenticate request
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await _next(context);
}
Usually, this middleware runs before other middlewares/mvc thus you can intercept requests as you need.
When you want to access an url protected by [Authorize] without login, it will ask you to sign in through some scheme. You can configure your services to use different schemes as you like, such as Jwt Bearer, cookies, and so on.
If you're using the cookie scheme,
CookieAuthenticationHandler will do the heavy lifting :
Signin: will issue a new cookie when you think you have validated the user principal.
Authenticate: validate the cookie sent by client
Signout : delete the cookie
Note all these are done by Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler, i.e. a handler defined in aspnet/Security, not the aspnet/Identity library.
why can't I steal that cookie and use it as my own?
Of course you can steal someone's cookie and use it as your own. Actually, if Alice's cookie is stolen by Bob (let's say through XSS or sniffering), Bob will be treated as Alice. ASP.NET Core (and other technologies such as PHP/Python/Java) cannot prevent this and there're quite a lot to do to prevent the stealing :
The website should use HTTPS rather than HTTP
encode characters like <,>,<img onclick='javascript:' and so on to prevent XSS
...
Also, you don't need to steal someone's cookie sometimes. By CSRF, you simply "borrow" his cookie.
Why is this safe
Typically, even if it's possible to steal someone's cookie or borrow someone's cookie in theory, it will only happen when you're developing your app in a wrong way or deploy them in an insecure way.
Another thing is that you could hardly fake a cookie on client side.
Related
I'm going to get so many "okay grandpa" comments for this one.
I've read a dozen articles and every SO question I could find on this subject.
I must have been away too long or missed something completely, because I swear that user authentication used to be very simple. I seem to recall built-in methods and a session on the server simply knowing who the user was via a cookie or similar, with the ability to store information "in session". I don't recall even setting up authentication in years past, it was just built-in to new applications.
Instead, the most succinct guide I could find is very involved. I think I need a token authorization/authentication setup because there may be consumers (like apps) who don't have a typical cookie pattern these days. In my head, the token works like a cookie except it's manually held on the user end and passed via header with each request?
To its credit, the guide worked, at least for logging in and correctly utilizing the simple Authorize attribute in controllers. However, User.Identity.Name is always empty, even when User.Identity.IsAuthenticated is true, which is perplexing.
How I think auth is working:
User request hits API with username/password
Service checks the combination, and returns an encrypted JWT to the user
The user sends the JWT back with every request
The server decrypts this JWT to identify the user - this is probably where I'm wrong
So here is where my question comes in:
I need more data about the user, like access to the entire UserModel with every request, but I don't want to go to the database to find it every time. This is where I think there should just be a session object in memory, but that doesn't appear to be the case with token authentication.
TL;DR:
Where do I put user-specific, short-term ("session") information for consumption in future requests where a user is identified with a JWT in the Authorization header instead of a cookie?
Session state isn't right, because it's hard-wired to a cookie
HttpContext.Items aren't right, because it's just for the one request
Cache storage isn't right, because it's not user/session specific. I could potentially create a session-like user-keyed storage here but that seems way, way over-engineered for this.
Basically anything where I'm passing all the data (not just a user identifier) to the client then relying on the client to pass it back seems wrong? But feel free to correct me.
The server decrypts this JWT to identify the user This is probably
where I'm wrong
The JWT token is not encrypted, its signed so you can't alter it. You can open it if you look at jwt.io for example.
Where do I put user-specific, short-term ("session") information for
consumption in future requests where a user is identified with a JWT
in the Authorization header instead of a cookie?
You put it in the principle claims of the token. In the guide you linked it wrote:
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
};
So you add whatever you want to the claims to store it on the token and later you can access this data via:
var claim = _contextAccessor.HttpContext.User?.Claims.FirstOrDefault(d =>
d.Type == ClaimTypes.NameIdentifier);
You also can't use any of these other examples that you listed like HttpContext.Items because those are not signed. If the token is altered in any way the system identifies this and returns a 401
I have been trying to wrap my head around this concept but have many questions and unfortunately, all official documents and tutorials are based on Visual Studio templates with individual user accounts.
My goal is pretty straightforward I believe. I have a web application which will only support external provider logins (namely: Facebook, Twitter, and LinkedIn). I do not want to support cookie authentication since there won't be a support for custom username/password.
My first problem is to define a default AuthenticationScheme. Below is my startup.cs:
services.AddAuthentication()
.AddFacebook(/* options */)
.AddTwitter(/* options */)
If I define a controller action with Authorize attribute I get no default authentication scheme defined error when I hit that route. However, I want users to be redirected to my login route if they are unauthorized. If I modify startup.cs like below it all works but then I think I support cookie (old forms authentication?) authentication which I don't want to.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
.AddFacebook(/* options */)
My other issue is that I don't know what happens under the hood of AddFacebook() call. If I set up my middleware this way and log in with Facebook I magically get all the necessary tokens, claims and suddenly I have an application cookie set and my fb_login callback route can access to Facebook's token! When I check the network requests I see there is a hit to the signin-facebook route -which I didn't define- and I guess under the hood it calls HttpContext.SignInAsync() etc... but if I refresh my fb-login callback and check if
HttpContext.AuthenticateAsync(FacebookDefaults.AuthenticationScheme)
returns Success = true no! it returns false! But it was true just one second ago?
Also, when should I be using methods like AuthenticateAsync() and SignInAsync()?
Long story short I need a tutorial or documentation that explains this middleware without asp.net Identity framework, EntityFramework and templates.
I want to understand how a simple AddFacebook() call binds everything, and if I want to manually do that (say with AddOauth) how can I achieve the same functionality?
I'm not a fan of "automagically working" code so if someone can explain what's going on here I'd be very appreciated.
Cookie auth is used to persist the authenticated state between the requests. There is no substitute for this, and no it's not the same as forms auth, though cookies are used in both cases. The reason for that is simply that cookies are what makes state work over the HTTP protocol, which is itself stateless. If exclude the use of cookies, then there is no other mechanism to maintain state.
Using something like the Facebook auth scheme directly authorizes the initial request, but again, because there is no state, the next request is no longer authenticated without going through the Facebook OAuth flow again.
Long and short, the other auth schemes are there for things like APIs, where each request is typically authenticated individually via something like the Authorization header. A web browser doesn't work this way, and instead relies on cookies handle subsequent authorization. No cookies, no auth.
I've been following the Thinktecture Identity Server example of OAuth2 Resource Owner Password Flow found at http://leastprivilege.com/2012/11/01/oauth2-in-thinktecture-identityserver-v2-resource-owner-password-flow/
I have the example working and returning JWT tokens successfully via the following process
Use the Thinktecture OAuth2Client to retrieve the access token
Retrieve the signing certificate from the "Trusted People" store on the client machine
Using the certificate and creating a new JwtSecurityTokenHandler and TokenValidationParameters and calling tokenHandler.ValidateToken to get a ClaimsPrincipal
From here I am authorized, but I am uncertain of the best way to persist the token for further requests. I tried using
var sessionToken = new SessionSecurityToken(principal, TimeSpan.FromHour(8));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
But I do not have a SessionAuthenticationModule registered. I tried using the Identity and Access wizard to get this in place, but it makes many changes to config and tries to set things up for passive authentication.
I could use a traditional FormsAuthentication cookie (.aspnetAuth) but I remember discussion that an advantage of the .FedAuth cookie was that it was naturally split into several cookies if the size grew too big.
I'm struggling to find an article that completes the picture for me. I need the bearer token for accessing various APIs further down the stack. I have working examples of this for SSO/passive authentication, because most of the work is done for you. I'm just not sure of the best pattern for use when using the Resource Owner Password flow.
So
Have I missed a more straightforward way to achieve this with Thinktecture Identity Model and Server?
Should I try to create a FedAuth cookie so that I can reuse the various Messagehandler/filter components that are already setup for WIF?
Otherwise - is there anything particularly wrong with simply putting the access token in the UserData section of the FormsAuthentication cookie?
Try to look at this question: WIF Security Token Caching.
I believe this code might do
var sessionSecurityToken = new SessionSecurityToken(principal, TimeSpan.FromHours(Convert.ToInt32(System.Web.Configuration.WebConfigurationManager.AppSettings["SessionSecurityTokenLifeTime"])))
{
IsPersistent = true, // Make persistent
IsReferenceMode = true // Cache on server
};
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
I'm creating a web service to expose some data via publicly accessible APIs. At a high level, what mechanisms are people using to secure their APIs to ensure that a valid, authenticated user is making the call?
The service will be C#, the consumer could be anything (Facebook or iPhone app as well as a website) so Microsoft only solutions are out.
It's not a new problem so I assume there are some standard practices in place to deal with it but my google-fu is failing me on this one. Can the collective point me to any resources? Thanks.
You can still use Membership authentication: have a web service method Login(username, password), inside that method validate user:
[WebMethod]
public bool Login( string username, string password)
{
bool isValid = Membership.ValidateUser(username, password);
if (isValid)
{
FormsAuthentication.SetAuthCookie(username, true);
return true;
}
return false;
}
And that should do it - it will create a cookie that travels with requests and in each method you can check HttpContext.Current.User.IsAuthenticated.
void SomeWebMethodThatRequiresAuthentication(someparameter)
{
if (HttpContect.Current.User.IsAuthenticated)
{
... do whatever you need - user is logged in ...
}
else
{
.... optionally let user know he is not logged in ...
}
}
I believe it can work with different consumers that support cookies because all it needs to work is for consumer to send the auth cookie along with the request to your web server.
I see that ferequently in SaaS web services is used authentication by token key over SSL - we choose this simple method in our last project over OAuth and SAML protocols. Maybe this can be usefull - sometimes simple solutions make things more scalable and over control.
Try the answers in this similar question:
What is the best way to handle authentication in ASP.NET MVC with a Universe database?
We use the WS-Security. It's a published standard so any client (in theory) can use it to send authentication credentials.
Here's another SO question that covers using WS-Security with C#.
How to use WS-Security in C#?
I’ve inherited a rather convoluted project. The original designer created a “cookie” that appears to be server side rather than client based (though I could be very wrong on that part). He is using it for what he called “Least Privileges, Single Sign On”. I have the following code in all of the Web Service Proxies he set up:
[WebServiceBinding(Name = "ISecurityManager", Namespace = "urn:riv:apis:security:forms:ver1")]
public partial class SecurityManager : SoapHttpClientProtocol, ISecurityManager
{
public SecurityManager()
{
//Url = CookieManager.WebServiceUrl(String.Empty, ref CookieContainer);
// I’d like to replace the following code with a call like this...
CookieContainer = new System.Net.CookieContainer();
string urlSetting = ConfigurationManager.AppSettings["SecurityManager"];
if (urlSetting != null)
Url = urlSetting;
else
Trace.TraceWarning("No URL was found in application configuration file");
string cookieName = FormsAuthentication.FormsCookieName;
string cookiePath = FormsAuthentication.FormsCookiePath;
string cookieDomain = Properties.Settings.Default.CookieDomain;
HttpCookie authCookie = HttpContext.Current.Request.Cookies[cookieName];
if (null != authCookie)
CookieContainer.Add(new Uri(urlSetting), new System.Net.Cookie(cookieName, authCookie.Value, cookiePath, cookieDomain));
}
….
I also have this code pretty much everywhere:
string cookieName = FormsAuthentication.FormsCookieName;
string SecurityContext.ApplicationName = HttpContext.Current.Request.Cookies[cookieName].Path;
string SecurityContext.UserName = HttpContext.Current.User.Identity.Name;
if (!string.IsNullOrEmpty(SecurityContext.UserName))
….
In all instances, when it goes to get the authCookie, it comes up null or the SecurityContext.UserName is blank. I’m not a cookie guru and a lot of this guy’s code is obfuscated – and zero documentation.
Can anyone make heads or tails out of the intent of the code blocks?
TIA
FormsAuthentication for a web service method? Storing authentication credentials in a cookie? There are so many things wrong with this story. (Note: heavy obfuscation of code should be taken as a sign.)
The intent of the code blocks, as it appears, is using the cookie framework for user identification during a method call. It assumes the user has already been authenticated and that the authentication cookie is present in all requests.
EDIT: a bit more information on "server-side cookies" -- the references you see to System.Net.Cookie and such are .Net Framework classes for handling cookies. Cookies are client-side pieces of data that reside either in-memory for the client (usually a web browser), and/or saved as text files somewhere on the local file system of the client. Most web applications that set client-side cookies assume they are dealing with a web browser, as all the major browser providers support cookies.
When a web browser is used to make a request to a URL, lots of information is sent in the background that is hidden from the user: IP address, the type of browser and OS, etc. Included in this list are cookies for that given URL domain (there are HTTP rules that browsers agree to). The code you're looking at are specific .Net Framework classes for dealing with those cookie values in a structured way.
Most applications that consume web services are completely stateless -- no cookies, no sessions, nothing. While it's possible that a client to a web service may implement cookie support, assuming or requiring cookie support for a web service is folly.
In the code scenario you've debugged to detect null values, most likely the calling application does not support cookies, effectively rendering the entire code block invalid. This is broken-by-design.
I cannot find a sensible way of improving this code block that doesn't involve a teardown of the entire structure. Given your suggested level of familiarity, spend a little time on web security 101. Get familiar with the concepts of authentication, sessions, (and cookies, too.) You'll know you're ready to proceed as soon as you realize that security is something you don't invent yourself.
Well obviously he's attempting to create a security/authorization provider that apparently doesn't work well or at all. I recommend you look at Enterprise Library for this functionality. Security Application Block QuickStart. Then look at caching the security token.