ASP.NET web api "Remember Me" functionality using cookies - c#

I am trying to implement a "Remember Me" functionality in my Web Api project.
I would like to :
have the Remember Me functionality when the user Sign In.
save a cookies for to keep the user always logged in, so that the user no need type the username and password every single time when they visit the websites.
Sign the user in by reading the cookies that saved on the last login.
One more question that I am thinking about is... I am trying to generate the cookies by using JavaScript when the user checked the Remember Me Checkbox. Is it possible to do this?
OR
I should implement the RememberMe() in the AccountController??
Addition:
Here's my code in ApplicationOAuthProvider.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindByNameAsync(context.UserName);
if (user == null) {...}
if (userManager.IsLockedOut(user.Id)) {...}
if (!(await userManager.CheckPasswordAsync(user, context.Password)))
{ ... }
if (!user.EmailConfirmed) {...}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
In my JavaScript.
$('#checkbox').click(function () {
if ($('#checkbox').is(':checked')) {
// save username and password
username = $('#txtLoginEmail').val();
password = $('#pass').val();
checkbox = $('#chkRememberMe').val();
} else {
username = '';
password = '';
checkbox = '';
}
});

You need to implement refresh tokens in you app to be able to offer this functionality.
Basically, you need to create a RefreshTokenOAuthProvider that will generate refresh tokens. You can use 2 types of client_id to make a difference between clients who need to be remembered or not.
It is explained in this excellent series of blog posts (though it might start to become a little bit outdated, the information regarding owin setup is gold).

Related

How to implement a new policy in Razor Pages with addAuthorization?

Objective:
Once logged in, be authorized to reroute to Contact page which is authorized with the
Must Belong To HR Department policy.
(Note: Using razor pages)
Expected:
Log in, see the claims in user for HR department, have the policy requirements be read, allow the user access to the contact page.
Actual:
Log in successfully, see claims in user for HR department, policy requirements not read or met or something, denied the user access to the contact page.
Program.cs file:
//Authorization
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("MustBelongToHRDepartment",
policy => policy.RequireClaim("Department", "HR"));
});
////RazorPage Options
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/contact", "MustBelongToHRDepartment");
});
Login page:
public async Task<IActionResult> OnPostAsync(User user)
{
var result = await _signInManager.PasswordSignInAsync(user.username,
user.password, user.rememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
//Create the security context
var claims = new List<Claim> {
new Claim(ClaimTypes.Name, "admin"),
new Claim(ClaimTypes.Email, "admin#mywebsite.com"),
new Claim("Department", "HR")
};
var identity = new ClaimsIdentity(claims, "MyCookieAuth");
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("MyCookieAuth", claimsPrincipal);
return RedirectToPage("contact");
}
else
{
return Page();
}
}
Is there anything I'm missing? Format? Silly error?
I have tried docs, YouTube videos, website, asking friends, and searching forums.
I have tried different methods of authorization and such, but nothing seems to work.
This method I am using now should work according to documentation, but I know something is wrong with it.
Resources I have been using:
This is a YouTube video on register and login
docs
docs
var IdeUser = await _userManager.FindByNameAsync(user.username);
await _userManager.AddClaimsAsync(IdeUser, claims);
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(IdeUser);
await _signInManager.RefreshSignInAsync(IdeUser);
I added this and it worked. I think claims weren't going to userManager.

manually check if claims token is ok

I have a custom attribute where I manually wanna check if a claims token is valid. How do I do that?
public class AuthorizeClaimsAttribute : AuthorizeAttribute {
protected override bool UserAuthorized(IPrincipal user) {
var cookie = HttpContext.Current.Request.Cookies.Get("bearerToken");
if (cookie != null) {
//Check if token is valid, how?
}
return false;
}
}
The token is created as follow:
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim("Username", model.Username));
identity.AddClaim(new Claim("IsAdmin", isAdmin.ToString()));
var properties = new AuthenticationProperties() {
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthOptions.AccessTokenExpireTimeSpan)
};
var ticket = new AuthenticationTicket(identity, properties);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
Note: I cannot use the existing Authorize attribute. That´s why I need to check it manually.
One method is to store the token alongside its username somewhere in a persistant data structure
For example, when you create the Identity store the model.UserName & the accessToken in a a database of your choice.
Then, when you want to check your cookie you can re-open your database and query for it and take the appropriate action.
Also, adding that date in the database will also help you keep the size of it down resulting in faster searches, i.e. if your token only lasts for 3 months, delete the old ones as part of maintenance

Return JWT Token generated by OAuthAuthorizatioServer from controller in Web API

Following #Taiseer Joudeh I was able to create simple POC of Web API. I'm able to create new account, then log-in and call secure Web API when I add JWT token to header.
I'd like to modify method that is responsible for creating accounts.
Right now I'm returning Create (201) code with new user object, but instead I'd like to return access token.
I've found similar question but it requires creating HttpClient and doing request to OAuthAuthorizatioServer TokenEndpointPath.
Second question I found requires generating temporary token that is returned to front-end, but then front-end must do additional request to server to get "real" token.
What I'd like to do is to return login response (access_token, token_type and expires_in) when I create user account.
I want user to be authenticated when his account is created.
I'm using just Web API and JWT without any cookies.
EDIT: My temporary solution:
after creating user I'm doing this:
var validTime = new TimeSpan(0, 0, 0, 10);
var identity = await UserManager.CreateIdentityAsync(user, "JWT");
var jwtFormat = new CustomJwtFormat(ApplicationConfiguration.Issuer);
var authenticationProperties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, ExpiresUtc = DateTimeOffset.UtcNow.Add(validTime) };
var authenticationTicket = new AuthenticationTicket(identity, authenticationProperties);
var token = jwtFormat.Protect(authenticationTicket);
var response = new
{
access_token = token,
token_type = "bearer",
expires_in = validTime.TotalSeconds.ToInt()
};
return Ok(response);
where CustomJwtFormat comes from this awesome article.
Below is some code similar to what I'm doing in my application, which is using Asp.Net Core 1.0. Your signin and user registration will differ if you're not using Core 1.0.
public async Task<string> CreateUser(string username, string password)
{
string jwt = String.Empty;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
var user = await _userManager.FindByNameAsync(username);
if (user == null) // user doesn't exist, create user
{
var newUser = await _userManager.CreateAsync(new ApplicationUser() { UserName = username }, password);
if (newUser.Succeeded) //user was successfully created, sign in user
{
user = await _userManager.FindByNameAsync(username);
var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, true);
if (signInResult.Succeeded) //user signed in, create a JWT
{
var tokenHandler = new JwtSecurityTokenHandler();
List<Claim> userClaims = new List<Claim>();
//add any claims to the userClaims collection that you want to be part of the JWT
//...
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.UserName, "TokenAuth"), userClaims);
DateTime expires = DateTime.Now.AddMinutes(30); //or whatever
var securityToken = tokenHandler.CreateToken(
issuer: _tokenOptions.Issuer, //_tokenAuthOptions is a class that holds the issuer, audience, and RSA security key
audience: _tokenOptions.Audience,
subject: identity,
notBefore: DateTime.Now,
expires: expires,
signingCredentials: _tokenOptions.SigningCredentials
);
jwt = tokenHandler.WriteToken(securityToken);
Response.StatusCode = (int)HttpStatusCode.Created;
await _signInManager.SignOutAsync(); //sign the user out, which deletes the cookie that gets added if you are using Identity. It's not needed as security is based on the JWT
}
}
//handle other cases...
}
}
Basically, the user is created and then signed in automatically. I then build a JWT (add in any claims you want) and return it in the response body. On the client side (MVC and Angular JS) I get the JWT out of the response body and store it. It is then passed back to the server in the Authorization header of each subsequent request. Authorization policies for all server actions are based on the set of claims supplied by the JWT. No cookies, no state on the server.
EDIT: I suppose you don't even need to call the signIn method in this process as you can just create the user, create a JWT, and return the JWT. When a user logs in on a future request you would use the Sign-In, create token, Sign-Out approach.
The idea of sending a response with access_token, token_type and expires_in, when the user is created is a great idea. But, I would stick to calling the "/token" end point with HttpClient to achieve this task. There are quite a few security checks that needs to be performed before token is generated. Since this is security I would not take any risk. I feel comfortable using the libraries/ code provided by industry experts.
That said, I tried to come up with a class you can call to create the token once the user is created. This code has been taken from the Microsoft's implementation of OAuth Authorization Server in their Katana project. You can access the source here. As you can see there is quite a lot happening when creating the token.
Here is the modified version of that middleware class for generating the token. You have to provide the proper OAuthAuthorizationServerOptions, Context, username, password, Scopes and clientid to get an access token. Please note that this is a sample implementation to guide you in the right direction. Please test it thoroughly if you want to use this.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNetIdentity.WebApi.Providers;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Microsoft.Owin.Security.OAuth;
namespace WebApi.AccessToken
{
public class TokenGenerator
{
public string ClientId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public IList<string> Scope { get; set; }
private OAuthAuthorizationServerOptions Options { get; } =
new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("http://localhost:59822")
};
public async Task<IList<KeyValuePair<string, string>>> InvokeTokenEndpointAsync(IOwinContext owinContext)
{
var result = new List<KeyValuePair<string, string>>();
DateTimeOffset currentUtc = Options.SystemClock.UtcNow;
// remove milliseconds in case they don't round-trip
currentUtc = currentUtc.Subtract(TimeSpan.FromMilliseconds(currentUtc.Millisecond));
AuthenticationTicket ticket = await InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(owinContext, Options, currentUtc);
if (ticket == null)
{
result.Add(new KeyValuePair<string, string>("ERROR", "Failed to create acess_token"));
return result;
}
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(Options.AccessTokenExpireTimeSpan);
ticket = new AuthenticationTicket(ticket.Identity, ticket.Properties);
var accessTokenContext = new AuthenticationTokenCreateContext(
owinContext,
Options.AccessTokenFormat,
ticket);
await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
string accessToken = accessTokenContext.Token;
if (string.IsNullOrEmpty(accessToken))
{
accessToken = accessTokenContext.SerializeTicket();
}
DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;
result.Add(new KeyValuePair<string, string>("access_token", accessToken));
result.Add(new KeyValuePair<string, string>("token_type", "bearer"));
TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
if (expiresIn > 0)
{
result.Add(new KeyValuePair<string, string>("expires_in", "bearer"));
}
return result;
}
private async Task<AuthenticationTicket> InvokeTokenEndpointResourceOwnerPasswordCredentialsGrantAsync(IOwinContext owinContext, OAuthAuthorizationServerOptions options, DateTimeOffset currentUtc)
{
var grantContext = new OAuthGrantResourceOwnerCredentialsContext(
owinContext,
options,
ClientId,
UserName,
Password,
Scope);
await options.Provider.GrantResourceOwnerCredentials(grantContext);
return grantContext.Ticket;
}
}
}
Please let me know if you have any questions.
Thank you,
Soma.
I assume you are referring to the following article: http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
The general approach to authenticating a user in this case is
Make an HTTP call to the token endpoint
User Enters the credentials at the UI that is rendered
IdP verifies the credentials and issues a token
User makes another call to an authorized endpoint and OWIN will validate this (JWT) token (as configured in the ConfigureOAuthTokenConsumption method) and if successfull will set a user session with expiry same as the token expiry. The session is set using session cookies.
Now try to understand that in general, this whole process of authentication is required because your server does not trust the user logging in to be the user the person is claiming to be. However, in your case, you (or your server code) just created a user and you know for sure that the person accessing your website is the user you have just created. In this case you don't need to validate a token to create a session for this user. Just create a session with an expiry that suits your use-case.
The next time the user will have to log-in and prove him/herself to the server using a token but this time the user does not need to prove him/her self.
Note: If you absolutely are adamant about requiring a token to log in a user who you yourself have just created using their credentials here are a couple of issues.
You are taking on the responsibility of storing (having access to) the user credentials, which might not be with you over the lifetime of the application (in most cases you might want to act as a relying party rather than an IdP).
Even if you want to then doing it is not trivial. You will have to make the calls to the token end point in code (server or client side) on behalf of the user, enter their credentials for them, retrieve the token, call an authenticated endpoint on your site and retrieve the session cookie all while hiding all this from the user, which probably is something you will either do if you hate yourself :) but also isn't very secure way of doing things especially when you are taking all the trouble to implement OAuth in the first place.
Also, take a look at Windows Server 2016 (technical preview 5 at this time) which supports implicit grants and might take writing all this custom code off your plate if you can wait a bit for RTM.
In an OAuth solution you as a developer are are not required to handle the cookie setting yourself. The cookie handling is done for you automatically by the framework.
Also the only way to set a session is a. Using session cookies or b. Use cookie-less (in the url) methods. Look at http://www.cloudidentity.com/blog/2015/02/19/introducing-adal-js-v1/ for more details on token validation and session establishment (also search the term cookie and you will know what all it's used for).
If you start thinking about not using cookies at all not only will you have to figure out how to maintain session and do it securely without cookies but also have to re-write the token refresh code that detects and refreshes the token for you based on the presence of a session cookie. (i.e. not a smart idea)
I am using the exact technology stack and recently implemented token based authorization successfully. The link I took reference from had very neatly defined the token-based auth in Web APIs. A must bookmark page I must say. Here is the link: TOKEN BASED AUTHENTICATION IN WEB APIs.

How to use two external OAuth 2 OWIN providers (for separate login and fetching of LinkedIn profile data)

We wish to allow login to an MVC Razor website, with the usual assortment of OAuth providers:
e.g.
Google/Google+
Microsoft Account
Facebook
LinkedIn
These all work perfectly fine by themselves, but we need to have the additional option to import the user's profile data, specifically from LinkedIn regardless of how they login.
As a test of fetching a LinkedIn profile, we successfully stored the access token from LinkedIn in the ApplicationUser object like this:
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Manage");
}
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
//check claims here
if (info.Login.LoginProvider.ToLower()=="linkedin")
{
var claim = info.ExternalIdentity.Claims.First(p => p.Type == "urn:linkedin:accesstoken");
if (claim != null)
{
user.OAuthAccessToken = claim.Value;
}
}
IdentityResult result = await UserManager.CreateAsync(user);
And fetch that token back later with:
var usertoken = UserManager.FindById(User.Identity.GetUserId()).OAuthAccessToken;
But of course this only works if they signed in with LinkedIn.
What we really want is to login via OAuth, with say Google, and then optionally have the user login to their the LinkedIn account to get the access token and fetch the profile data.
Does OWIN support making an additional OAuth login that does not change the current login? There is so much framework code provided by the latest Razor templates, I am not sure of the best way to do this.

Custom the AccessTokenExpires of OAuthAuthorizationServerOptions

I want to custom my login scenario in my MVC 5 project.
I followed the SPA template to use oauth bearer token for authentication. But I want to add some logic to control the AccessTokenExpires time.
From this topic and A de Baugh's comment, I can change the expire time of the ticket properties. But I still can't find where to add my logic to my custom OAuthAuthorizationServerProvider class.
Basically I want to add something like Remember Me by passing more parameter when sending the login request.
At the moment I must create another controller to handle login
The login method of the controller looks like
// POST api/Account/Login
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
ApplicationUser user = await userManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
// my check for remember me
if (model.RememberMe)
properties.ExpiresUtc = properties.IssuedUtc + TimeSpan.FromDays(7);
else properties.ExpiresUtc = properties.IssuedUtc + TimeSpan.FromHours(3);
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
return Ok(new
{
user = model.UserName,
access_token = accessTokenFormat.Protect(ticket),
expire = properties.ExpiresUtc
});
}
ModelState.AddModelError("error", "Invalid username or password");
}
return BadRequest(ModelState);
}
Authentication
private IAuthenticationManager Authentication
{
get { return Request.GetOwinContext().Authentication; }
}
And the accessTokenFormat to protect ticket is Startup.OAuthOptions.AccessTokenFormat
Am I doing this in an unusual way? I believe there is a better approach to archive this.
Any suggestion would be appreciate. Thanks!
I am not totally understanding your questions but by rolling my one OAuthAuthenicationServerProvider I found a very good example from #leastprivilege on github: The SimpleRefreshTokenProvider ... this describes (with comments) how to handle the refresh cycle of a token. Maybe this is a good starting point to abstract your own example.
https://github.com/thinktecture/Thinktecture.IdentityModel/blob/master/samples/OAuth2/EmbeddedResourceOwnerFlowWithRefreshTokens/EmbeddedAuthorizationServer/Provider/SimpleRefreshTokenProvider.cs
HTH
P.S.: It is also worth it to spend some time reading his blog about security OAuth, ... http://leastprivilege.com/

Categories