Bearer Token Authentication and Password Change - oauth-2.0

Now i am in learning stage of owin bearer token authentication in Web API. The code is implemented with token and cookie based authentication. The code is
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
try
{
using (UserManager<ApplicationUser> userManager = userManagerFactory())
{
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null || user.IsDeleted)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
CookieAuthenticationDefaults.AuthenticationType);
var roleName = await GetRoleName(user.Roles.First().RoleId);
AuthenticationProperties properties = CreateProperties(user.UserName, roleName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
}
catch (Exception ex)
{
throw ex;
}
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
The code is implemented by colleague and i have some doubts.
Token authentication is based on the generated token. I generated a token for my user, whose role is 'Admin'. Now i can access restricted action as the user has 'Admin' role. But after that i changed the role to 'User' for the same old user. Now with the same old token i can access the resource even he is not in 'Admin' now. Actually i read some articles that this is implemented with extra custom logic. its ok
Now i changed the user password to some other password. Now itself, i can access the resource with same old token. I think this is not good even i create short lived tokens also.
Anyone please guide to arrest this or i missed anything? Which method actually call when i call an action with 'Authorization' header

Well there is no “direct” way to revoke granted access tokens or do “logoff”. if the user has the token then he can access the secured server resources until the token is expired. The indirectway is to store token_id for each token granted to the user in a database and do DB checks with each call which is something I do not recommend.
So in some situations it is better to use the refresh tokens along with the access token. So you issue short lived access token (15) mins and you use the refresh token to obtain new access tokens. The nice thing here that refresh tokens can be revoked from the backend system so there is control on them.
Check my post on how to enable OAuth refresh tokens in ASP.NET Web API

Related

Bearer token ignores asp.net user roles

I have a asp.net mvc application that runs a hangfire task. This hangfire task retrieves client data from another asp.net mvc webserver application using a bearer token. The hangfire task sends a username and password to the webserver and gets a new bearer token in return every time its run using the code below.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
List<Claim> roles = oAuthIdentity.Claims.Where(c => c.Type == ClaimTypes.Role).ToList();
AuthenticationProperties properties = CreateProperties(user.UserName, Newtonsoft.Json.JsonConvert.SerializeObject(roles.Select(x => x.Value)));
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public static AuthenticationProperties CreateProperties(string userName, string roles)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName },
{ "roles", roles }
};
return new AuthenticationProperties(data);
}
This bearer token is then stored in a database. Once a new bearer token has been retrieved the application makes a http call to the webserver using the bearer token to retrieve the clients information.
The username and password / bearer token that is being sent is associated with a user on the webserver that has a role of 'WebserverUser'.
The problem is, Authorise(UserRole.WebserverUser)
is not currently doing anything. The http call is still being allowed even if I remove the 'WebserverUser' role from the user.
Is there a way I can make my application only allow the http call when the user is in the designated role?
[Route("GetClientsTable")]
[HttpGet, Authorise(UserRole.WebserverUser)]
[ResponseType(typeof(Byte[]))]
public IHttpActionResult GetClientsTable()
{
//Getting clients here..
return Ok(Clients);
}

Authorization with .net core 3.1 with JWT Token response from API

Im new to C# and im struggling with authorization in ASP.Net Core 3.1 MVC web application.I know that there is a lot of instruction on google, i've been reading and watching for 3 days but can not work this out because every instruction i found, it use another way and im really confused.
The idea of my system is:
Step 1. I POST username and password to my API and it'll response with JWT Token (if account is correct)
Step 2. I decode the token and get the username, email, role for my website, set HttpClient header for another requests.
My problems:
How and where to set HttpClient header (with my token) only one time when user login
How to force users stay at the Login page if they aren't login yet
Here's my Login method
[HttpPost, AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel account)
{
string url = "accounts/signin";
var response = await new HttpClientHelper<LoginViewModel>().PostRequest(url, account);
var userToken = JsonConvert.DeserializeObject<UserToken>(response);
Console.Out.WriteLine(userToken.Token);
if (userToken.Token != null)
{
var token = new JwtSecurityToken(jwtEncodedString: userToken.Token);
var userId = token.Claims.First(c => c.Type == "userId").Value;
var username = token.Claims.First(c => c.Type == "unique_name").Value;
var role = token.Claims.First(c => c.Type == "role").Value;
HttpContext.Session.SetString("token", token.ToString());
HttpContext.Session.SetString("userId", userId);
HttpContext.Session.SetString("username", username);
HttpContext.Session.SetString("role", role);
return RedirectToAction("Home", "Index");
}
return RedirectToAction("Login", "Login");
}
My model to receive response from api
public class UserToken
{
public string Token { get; set; }
public string ValidFrom { get; set; }
public string ValidTo { get; set; }
}
FYI: Ive already recived the response from api and got the Token, but ive to set HttpClient header every time i make a request..
How and where to set HttpClient header (with my token) only one time when user login
As far as I know, we couldn't set the httpclient header only one time when user login. Normally, we could store the token into session or cookie and then read it from cookie or session when you want to send request to web api.
How to force users stay at the Login page if they aren't login yet
For this requirement, I suggest you could consider using the authentication middleware to achieve your requirement.
You could check the user's session inside this middleware, if this user doesn't contains the session then you could modify the request path to login page.
More details, you could refer to below example:
//Below cods should add after app.usesession in startup.cs Configure method
app.Use((context, next) =>
{
string token = context.Session.GetString("token");
if (token == null)
{
context.Request.Path = "/account/login";
}
return next.Invoke();
});

.Net Web API 2, OWIN, and OAuth: Scopes and roles. What are the differences?

I would like to create a clearer picture in my mind as to what the differences are between roles and scopes in .NET Web API projects. This is more of a best-approach question than anything else, and I am finding myself to be a little confused as to how best authorize users that want to access my API. I come from a .NET MVC background, so I am familiar with roles and I am wondering if the same approaches apply to the web API framework. I am having difficulties putting scopes in the picture and how I should use them to allow access for a user using a particular client ID. Are scopes similar to access permissions? To illustrate my confusion, let's use this example:
Client A
Native app: displays event calendar
Role: Event
User login required? No
Allowed scopes: Read events
Client B
Web app: shows next upcoming event, displays registrant names
Role: Event
User login required? Yes
Allowed scopes: Read events, read registrants
Client C
Native app: registers a person for an event
Role: Registrant
User login required? Yes
Allowed scopes: Read events, read registrants, write registrants
Basically I would like to know if my above use of scopes is correct and what the best approach would be to grant resource owner credentials. I am using the token based authentication as outlined in Taiseers tutorial. Below is my current incomplete code snippet that will take care of validating requested client and scope:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
ApiClient client = null;
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
context.TryGetFormCredentials(out clientId, out clientSecret);
if (context.ClientId == null)
{
context.Validated();
context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (ApiClientRepo _clientRepo = context.OwinContext.GetUserManager<ApiClientRepo>())
{
client = _clientRepo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
// Validate client secret
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_secret", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
WPasswordHasher passwordHasher = new WPasswordHasher();
PasswordVerificationResult passwordResult = passwordHasher.VerifyHashedPassword(client.SecretHash, clientSecret);
if (passwordResult == PasswordVerificationResult.Failed)
{
context.SetError("invalid_secret", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<int>("as:clientRepoId", client.Id);
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
IApiUser user = null;
string scope = null;
// Get parameters sent in body
Dictionary<string, string> body = context.Request.GetBodyParameters();
// Get API scope
body.TryGetValue("scope", out scope);
if (scope == null)
{
context.Validated();
context.SetError("invalid_scope", "Invalid requested scope.");
return;
}
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
// At this point I got the requested scope.
// What should I do with it?
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
// create claims identity based on user info
ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Username));
identity.AddClaim(new Claim(ClaimTypes.Role, scope));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
Thanks ahead for all thoughts, suggestions and ideas!
In my perspective scopes define the resources.
Basically the request challenge is "may client (=application) access resource x on your behalf"?
Where x is a any resource your API serves.
I've used a conveniention in a project where a scope can be specific for a CRUD action on a resource. For example scope = tweets.read or tweets.create.
Having a token for a scope doesn't give a client the permission. The permission is based on the fact that the user has permission to preform the action and has the client the correct resource scope in its token. Of course a users permission can be based on a role like guest or admin etc.
So in theory the user can grant access to scopes (resources) it has no permissions at.
A token has a lifetime of let's say 20 min, if you base a permission on any value in the access token, the permission cannot be revoked or changed within the token lifetime.

Dissecting ASP.NET MVC Identity for OAuth Bearer Authentication

I'm learning how to put the Asp.Net MVC Identity 2.0 to work.
I have this code that works for OAuth Bearer
[HttpGet]
[ActionName("Authenticate")]
[AllowAnonymous]
public String Authenticate(string user, string password)
{
if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password))
{
return "Failed";
}
var userIdentity = UserManager.FindAsync(user, password).Result;
if (userIdentity != null)
{
if (User.Identity.IsAuthenticated)
{
return "Already authenticated!";
}
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userIdentity.Id));
AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(1));
string AccessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
return AccessToken;
}
return "Failed in the end";
}
Here is the code for Startup.Auth.cs
//This will used the HTTP header Authorization: "Bearer 1234123412341234asdfasdfasdfasdf"
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
I have looked at the source code for ClaimsIdentity and AuthenticationTicket and I don't see how the ticket is registered for the identity.
My question is how did this ticket get registered with the Owin pipeline?
My aim is to revoke this ticket if possible.
Thanks in advance.
First off, here is a great tutorial on ASP.NET Identity 2 by Taiseer Joudeh.
This is the line that adds Bearer token processing to an OWIN application pipeline.
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
Also, did you write the authorization provider yourself? My startup code looks more like this:
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
PublicClientId = "self";
OAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1440), //TODO: change to smaller value in production, 15 minutes maybe
Provider = new SimpleAuthorizationServerProvider(PublicClientId),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
My SimpleAuthorizationServerProvider then has a Grant method like this:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin") ?? "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
foreach (var role in userManager.GetRoles(user.Id))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{"as:client_id", context.ClientId ?? string.Empty}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
Just about all of this was based on the tutorial mentioned above. Hope it helps.
Update
There is no standard way to revoke a token according to Taiseer on this page.
Revoking access from authenticated users: Once the user obtains long lived access token he’ll be able to access the server resources
as long as his access token is not expired, there is no standard way
to revoke access tokens unless the Authorization Server implements
custom logic which forces you to store generated access token in
database and do database checks with each request. But with refresh
tokens, a system admin can revoke access by simply deleting the
refresh token identifier from the database so once the system requests
new access token using the deleted refresh token, the Authorization
Server will reject this request because the refresh token is no longer
available (we’ll come into this with more details).
However, here is an interesting approach that may accomplish what you need. It will just take a bit of custom implementation.

ASP.NET Web API 2: Login with external provider via native mobile (iOS) app

I have done much searching and have not been able to find an ideal solution for this issue. I know that there is an alleged solution (WebApi ASP.NET Identity Facebook login) however, some elements of the solution are a (in my mind) seriously hacky (e.g. registering the user with a regular account and then adding the external login, rather than registering them with the external login).
I would like to be able to register and authenticate against an ASP.NET Web API 2 application, after already having used the Facebook SDK login on a iOS mobile app, i.e. I have already authenticated against Facebook using their SDK, and now want to seamlessly register/authenticate with the ASP.NET Web API. I do not want to use the process where I have to use the web calls (/api/Account/ExternalLogin) as this, well, is not a great user experience on a native mobile app.
I have tried learning about OWIN, but the .NET framework is complex and I am struggling in how to solve this issue.
I needed to do this today for my Ionic app. The Web API Account controller has its own opinion on how to do things and the best way to understand it is reading this pretty amazing 3 part blog post by Dominick Baier. https://leastprivilege.com/2013/11/26/dissecting-the-web-api-individual-accounts-templatepart-3-external-accounts/.
The way I worked around it was to forget the out-of-the-box flow, but instead use the accessToken from the native Facebook login and then call into the following server code to 1) call the Facebook API to validate the access token, 2) from that Facebook call, get the email and id, 3) either get the user or create it (and login) which is already code that's in the Account controller in other places, 4) Create the local authority JWT for subsequent Web API calls.
public class ProviderAndAccessToken {
public string Token { get; set; }
public string Provider { get; set; }
}
[AllowAnonymous]
[HttpPost]
[Route("JwtFromProviderAccessToken")]
public async Task<IHttpActionResult> JwtFromProviderAccessToken(ProviderAndAccessToken model) {
string id = null;
string userName = null;
if (model.Provider == "Facebook") {
var fbclient = new Facebook.FacebookClient(model.Token);
dynamic fb = fbclient.Get("/me?locale=en_US&fields=name,email");
id = fb.id;
userName = fb.email;
}
//TODO: Google, LinkedIn
ApplicationUser user = await UserManager.FindAsync(new UserLoginInfo(model.Provider, id));
bool hasRegistered = user != null;
string accessToken = null;
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
var props = new AuthenticationProperties() {
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(Startup.OAuthOptions.AccessTokenExpireTimeSpan),
};
if (hasRegistered) {
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(UserManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await user.GenerateUserIdentityAsync(UserManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
identity.AddClaim(new Claim("role", "user"));
}
else {
user = new ApplicationUser() { UserName = userName, Email = userName };
IdentityResult result = await UserManager.CreateAsync(user);
if (!result.Succeeded) {
return GetErrorResult(result);
}
result = await UserManager.AddLoginAsync(user.Id, new UserLoginInfo(model.Provider, id));
if (!result.Succeeded) {
return GetErrorResult(result);
}
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
}
identity.AddClaim(new Claim("role", "user"));
var ticket = new AuthenticationTicket(identity, props);
accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
return Ok(accessToken);
}
The code I'm using in Ionic basically does this to get the access token from Facebook, then call the Web API to get a local authority JWT to use as the Bearer token.
Facebook.login(['public_profile', 'email']).then((result) => {
return this.http.post("<URL>/api/Account/JwtFromProviderAccessToken", { provider: "Facebook", token: result.authResponse.accessToken })
.map((res: Response) => res.json())
.catch(this.handleError)
.subscribe((res: Response) => {
// use the result as the Bearer token
});
})...
Seems pretty safe, but understand that I'm not security expert so this code comes without warranty and please let me know if you see anything glaring and I'll update the code.

Resources