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);
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
So, I'm trying to implement an OIDC client application using ASP.NET Core 3.1. I am trying to leverage the .AddOpenIdConnect() and .AddJswtBearer() middleware. However, I need some clarification on what this middleware is doing.
Here is what I currently have for the middleware configuration:
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["auth:oidc:authority"];
options.ClientId = Configuration["auth:oidc:clientid"];
options.ClientSecret = Configuration["auth:oidc:clientsecret"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
})
.AddJwtBearer(options =>
{
options.Authority = Configuration["auth:oidc:authority"];
options.Audience = Configuration["auth:oidc:clientid"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = Configuration["auth:oidc:authority"],
ValidAudience = Configuration["auth:oidc:clientid"],
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero
};
}
I notice that requests to the Authorization server's /.well-known/oidc-configuration and /.well-known/keys endpoints are requested when the application is first started, based on my Fiddler capture below
Where does it do that?
I'm trying to also validate that the JWT received from the authorization server is valid (that it hasn't been tampered with between the time the server sent it, and the time that the client has received it). I understood this to happen when I added the TokenValidationParameters object in the .AddJwtBearer() middleware. To test this, I tried changing Valid Audience in the TokenValidationParameters to something like asdkwewrj which I know is not the valid audience for my token. But, I never got an error from the client saying that the audience was invalid. The authentication still worked, and I was able to access my secure dashboard still.
Another thing I'm trying to implement is refresh_token grant_type with this OIDC client. I thought that the options.saveTokens in the .AddOpenIdConnect() middleware would allow me to save the tokens. It looks like they're save as cookies, but these cookies look nothing like my token values (my access token is a JWT, but out of the cookies I see, none of them begin with ey).
In a nutshell, I'm trying to understand the following:
Does this .AddJwtBearer() middleware validate the ID Token for me if I have the correct JwtBearerOptions defined (like I do above)? Or do I need to manually validate the ID token against the JWKs from the JWKs URI?
If I have to manually validate the ID token using the JWKs from the JWKs URI, how do I store these JWKs when the middleware makes the request to the /.well-known/keys endpoint?
How do I get the cookies that correspond to the access token and refresh token, and then send the refresh token to my authorization server?
I noticed that I can utilize options.Events in both of these middlewares. Would any of those solve any of the items I'm trying to accomplish?
Overall, what do these two middlewares handle for me, that I shouldn't need to manually do (i.e token validation and/or token renewal)?
Thank you! I am still fairly new to in-depth ASP.NET development like this, so I appreciate any responses.
First of all, the OIDC authentication scheme and the JWT bearer authentication scheme are independent of each other. OIDC is mostly used for server-side authentication and will pretty much never be used on its own but always with the cookie scheme. The reason for this is that the OIDC scheme will just be used for the authentication but is not able to persist the information on its own. I’ve went into more details in a different answer of mine that also explains how the authentication flow works with OIDC.
As for JWT bearer, this authentication scheme will run on every request since it is completely stateless and expects clients to authenticate themselves using the Authorization header all the time. This makes it primarily used for protecting APIs since browsers wouldn’t be able to provide a JWT for normal browser requests.
So you should first ask yourself whether you are protecting your server-side web application (e.g. using Razor views or Razor pages) in which case you want to use OIDC and the cookies authentication scheme, or if you are protecting your API. Of course, the answer could be “both” in which case you want all of those three schemes but ASP.NET Core will not support this without further configuration.
With that being clarified, let’s get into your questions:
The requests to /.well-known/oidc-configuration and /.well-known/keys are done by both the OIDC and the JWT bearer scheme in order to retrieve information from your identity provider. They will do that regularly to update their data, including information about the signining keys which they will use to validate the tokens. This happens within the scheme handler and is usually not visible to you.
Correctly set up, the JWT bearer authentication will validate the token for you. It will do that by verifying the signatures using the retrieved signing keys, and then it may check additional properties like the specified audience or its lifetime.
You shouldn’t ever need to validate tokens manually. That’s the job of the authentication scheme. You are using the authentication stack so you can just access the user principal within your app without doing anything.
Cookies are protected using data protection so that they are safe against forgery. In order to retrieve the tokens stored with SaveTokens = true, you can use the GetTokenAsync method on your HTTP context.
You can use the authentication events to add to the default behavior of the authentication schemes. For validating your tokens using the standard mechanisms, you shouldn’t need to though.
There is just one middleware: The authentication middleware. It uses the configured authentication schemes to perform the authentication of users so that—once set up correctly—the authenticated user is available throughout the application, e.g. in controllers, MVC filters, Razor views, etc.
Does this .AddJwtBearer() middleware validate the ID Token for me if I
have the correct JwtBearerOptions defined (like I do above)? Or do I
need to manually validate the ID token against the JWKs from the JWKs
URI?
AddJwtBearer is only used by APIs to validate the access token and create a user (ClaimsPrincipal) out of it. It's all it does. It does not deal with id token.
In general its easier to put the API on a separate service, to make it more clear who is doing what. when you mix both the client and API in the same service, it can be harder to reason about it.
If I have to manually validate the ID token using the JWKs from the
JWKs URI, how do I store these JWKs when the middleware makes the
request to the /.well-known/keys endpoint? How do I get the cookies
that correspond to the access token and refresh token, and then send
the refresh token to my authorization server?
The ID token is validated and handled for you by AddOpenIdConnect. You dont need to validate the ID-token by yourself. AddOpenIdConnect will create the cookie and optionally store the tokens as well in the cookie.
Overall, what do these two middlewares handle for me, that I shouldn't
need to manually do (i.e token validation and/or token renewal)?
To summarize:
Ues .AddOpenIdConnect() for the client, that allows the user to login.
Use .AddJswtBearer() for the backend APIs.
Token renewal is a different story that none of them handles out of the box. For that, you can consider to use IdentityModel.AspNetCore or do something by yourself.
My sincere apologies for wrong details, The other application was developed using VB.Net and MySql NOT clssic asp.
I have developed a document management system application using ASP.Net Core 3.1 framework and JWT token authentication. Now I have a requirement to grant current authenticated user an access to another web application which was developed in VB.Net 2 and MySqlP and redirect back to login page (of the Core application) if the user is not authenticated or token has expired.
Note: Both of these applications are hosted on private network(Intranet).
After some research I came across the Single-Sing-On concept, passing sessionId in query string etc but not certain to make decision. May I ask whether it is possible to achieve and ways to implement this please?
The code to generate token looks like this
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret_key"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var expires = DateTime.Now.AddDays(Convert.ToDouble("expiryTimeSpan"));
var token = new JwtSecurityToken(
issuer: "myIssuer",
audience: "myIssuer",
claims : GetUserClaims(user,roles),
notBefore: new DateTimeOffset(DateTime.Now).DateTime,
expires: expires,
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
What is the flow of obtaining the jwt token? I will assume that you are using OAuth 2.0 or open id connect.
There is a perfectly good guide on the implementation here:https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Bear in mind that the authorization code is not a session id. It is something to be used once and be exchanged with the access token.
Session ids in url, are very dangerous as they are logged in logs and can be potentially used by others, just by accessing said logs.
Storing the jwt token that openid connect creates in your cookies, is probably the best way to go, if the applications exist in the same domain. Since the idp is the same for all applications, you won't need to authenticate again.
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.
I want to do token based mechanism where I would be having either SPA or mobile apps supporting multiple clients.
Use case of my web service engine and my application:
My web application: Client will do registration of their application either SPA or mobile apps.They will get client id on registration.Only client id as secret key would be compromised in case of SPA or mobile apps hence I am just providing clientid.
Web service engine: Support multiple client with managing session of each user after login in to respective application of clients.
So let's say there are 2 client who have register their application in to my web application :
Client 1 : MyApp1
Client 2 : MyApp2
Now if MyApp1 have 2 users with John and Stephen and if they login in MyApp1 then i want to manage session for those users with token based mechanism. Now if John and Stephen wants to access protected resource then they can access only through valid accesstoken.
Same goes for MyApp2.
For token based mechanism I have seen lots of question referring to this below article only:
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
But the only confusion part in above tutorial and in most of the tutorial is after validating user name and password and generating access token. Does above tutorial is storing access token in server side cookie for validating accesstoken when request comes to access protected resource?
I am really confused here. I know accesstoken validation happens inside [Authorize attribute] but I am not getting without storing accesstoken how above tutorial is validating accesstoken.
My thought is like may be when request comes for accessing protected resources access token is encrypted or decrypted based on machine key attribute in webconfig and this is how access token is validated inside [Authorize] attribute but I am just not sure about this.
You can control what information goes inside a token. Look at the SimpleAuthorizationServerProvider class in the article:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
Use the Claims to store anything you need regarding to the user, their username or roles and this is what happens in the article you referred to.
The token generated already contains that information about the user.
This is taken from the article :
The second method “GrantResourceOwnerCredentials” is responsible to
validate the username and password sent to the authorization server’s
token endpoint, so we’ll use the “AuthRepository” class we created
earlier and call the method “FindUser” to check if the username and
password are valid.
If the credentials are valid we’ll create “ClaimsIdentity” class and
pass the authentication type to it, in our case “bearer token”, then
we’ll add two claims (“sub”,”role”) and those will be included in the
signed token. You can add different claims here but the token size
will increase for sure.
This is why you do not need to store the token anywhere,the token is self contained and everything is stored inside it in an encrypted form. Don't forget that before you add a claim containing the username you have already validated the username and password, so you can guarantee that the token is created correctly for a valid user / password combination. Of course you do not want to store the password inside the token, the whole point of tokens is to avoid doing that. Passing passwords to an API all the time does increase the risk of them being stolen, tokens are much better for this.
Finally, the tokens expire after a time you control, usually they are short lived so even if someone does get their hands on one they will not last long.
If you take care of how you pass the tokens, meaning in the Authorisation Header over an https call then you are as protected as you can be and the headers will be encrypted. The point here is to never issue calls like this over basic http.
The author of the article you referenced is a well respected authority in this particular area and currently a Microsoft MVP and you are basically in good hands. Keep reading his articles, but pay attention to the details.
----------- Clarification related to JWT format --------------
yes the JWT token will contain information related to its issue date and expiry date as well. I have an article of my own on this : https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/
Look at the calls which create the token and look at the information returned in the screenshots.
In my example the token contains the actual encrypted token, the token type, seconds it expires in, the audience which is the ClientID, when it was issued and when it expires.
This is just an example of a token, yours will look probably a bit differently but you get the idea I hope. Use Postman to see what's coming back in the token
There are a number of concepts to be understood when it comes to OAuth2, it does require a bit of research and practice.
In short, you request a token with A Basic Authorisation Header, you get the token back and it's telling you what type it is, in my case it's Bearer so that's my next Authorisation Header for any call to a protected resource.
My suggestion is to start small, one step at a time, use Postman to build your calls and understand what's going on. Once you have that knowledge it's much easier to progress. Took me about 6 weeks to wrap my head around all concepts and get something working first time around, but now it takes a couple hours at most. Good luck
The application does not need to store the access token server side, it will only read the user from the token which is passed along.
When the request hits the authentication server, which is attach to the Owin pipeline in the ConfigureOAuth() method,
the HTTP header token is decrypted and the user data from the token is sat to the current user of the context.
This is one of the things that bugged me for a long time
I'm not sure I understand why did you give an example for 2 applications, but the token mechanism is actually simple, but it's kinda black boxed when you use owin and identity
the token is not stored anywhere on the server or the database, authenticating the user on login is done using your logic or usually again black boxed in identity, this involves validating a secured password etc
after this the token is generated (usually using identity) or if you did it manually this will involve securing the token with whatever info you want to store in it
when the user sends a request next time he should pass the token and you will need to decrypt it and validate what's necessary (like expiration time for example), all of this is done behind the scene usually
just a fun note: even if you changed the DB completely the token will still be valid with the user id that doesn't even exist in your new DB! but of course identity automatically invalidates this token when it compares with the securityStamp