I have been battling with security on our MVC apps for two weeks. I have set up an Authorization Server to generate tokens to be used by two client MVC applications. However, I am now generating the token and it returns a bearer token to the client, however where I check for a claim, it returns false.
This the code generating the token from Auth server:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
User user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The email address or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(context, user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
This generates the token to be sent to a client and in the 'GenerateUserIdentityAsync' method, I add the roles as required on the Claim Identity.
However, when I receive this on the client, the bearer token and do not know how to transform this into a local ClaimIdentity I can interogate to gain access to the roles. This is the code to get an access_token to the Resource Server (Client) from Auth Server:
[HttpPost]
[AllowAnonymous]
//[ValidateAntiForgeryToken]
public ActionResult SignIn(AccountViewModel account)
{
var getTokenUrl = $"{_settings.AuthServiceUrl}oauth2/token";
HttpContent content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", account.Login.Email),
new KeyValuePair<string, string>("password", account.Login.Password),
new KeyValuePair<string, string>("client_id", _settings.AuthClientId)
});
using (var client = new HttpClient())
{
HttpResponseMessage result = client.PostAsync(getTokenUrl, content).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;
var token = JsonConvert.DeserializeObject<Token>(resultContent);
if (string.IsNullOrEmpty(token.access_token))
{
ViewBag.Error = "Incorrect Username or Password, Please try again!";
return View("Login");
}
var options = new AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddSeconds(int.Parse(token.expires_in))
};
var claims = new[]
{
new Claim(ClaimTypes.Email, account.Login.Email),
new Claim("AccessToken", $"Bearer {token.access_token}"),
};
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
Request.GetOwinContext().Authentication.SignIn(options, identity);
if (identity.HasClaim(ClaimTypes.Role, "Admin"))
{
return RedirectToAction("Index", "Admin");
}
return RedirectToAction("Index", "Home");
}
}
I have check the bearer token on JWT.io and it has the role:
{ "nameid": "1", "unique_name": [ "[email protected]", "Admin Admin" ], "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider": "ASP.NET Identity", "AspNet.Identity.SecurityStamp": "de9090f4-bddb-4baf-a62b-38ed0d6528fe", "role": [ "Admin", "Admin" ], "UserId": "1", "sub": "[email protected]", "email": "[email protected]", "Verified": "False", "iss": "https://localhost:44318/", "aud": "b77f169bd7bf4787b1aed11599861768", "exp": 1540293963, "nbf": 1540292163 }
The question I have is, how do you on the client work with the bearer token and authorize users? How do you request a refresh token?