What is OAuth?
An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications.
NuGet Packages
Microsoft.Owin.Security.OAuth
Microsoft.AspNet.WebApi.Cors
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
BCrypt-Official (Used for hashing passwords)
Adding OAuth Provider
public class OAuthProvider : OAuthAuthorizationServerProvider
{
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
return Task.Factory.StartNew(() =>
{
// context object contains user's crendentials when user is requesting token
// Make user validation here
var user = UserUtil.Login(context.UserName, context.Password);
if (user != null)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, user.Role));
// Custom claims can be set from here
// identity.AddClaim(new Claim(ClaimTypes.Country, user.Country));
// identity.AddClaim(new Claim(ClaimTypes.Gender, user.Gender));
//
context.Validated(identity);
}
else
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
});
}
}
Enabling Oauth in Startup
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration httpConfiguration = new HttpConfiguration();
ConfigureOAuth(appBuilder);
WebApiConfig.Register(httpConfiguration);
appBuilder.UseWebApi(httpConfiguration);
}
private void ConfigureOAuth(IAppBuilder appBuilder)
{
OAuthAuthorizationServerOptions oAuthAuthorizationServerOptions = new OAuthAuthorizationServerOptions()
{
// Set url for token requests
TokenEndpointPath = new PathString("/token"),
// Set token's lifetime
AccessTokenExpireTimeSpan = TimeSpan.FromDays(7),
// HTTPS is highly recommended.
AllowInsecureHttp = true,
Provider = new OAuthProvider()
};
appBuilder.UseOAuthAuthorizationServer(oAuthAuthorizationServerOptions);
appBuilder.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
Register
We have register method below with anonymous access.
[Route("api/Register")]
[HttpPost]
[AllowAnonymous]
public bool Register(User user)
{
using (var db = new NotesModel())
{
var result = UserUtil.Register(user.Username, user.Password);
return result;
}
}
How to get token
With registered credentials we can send post request to “/token” path.
We have 7 day valid token which has “Name” and “Role” claims encrypted inside it.
Adding token to request
In request’s header we should add “Authorization” key with “bearer token” value
[Route("api/AddNote")]
[HttpPost]
[Authorize]
public bool AddNote([FromBody]string pNote)
{
// Get user's claim informations according to token
var username = User?.Identity?.Name;
Role based Authorization
We can restrict authorization with roles. In this example, only admin role can call the method below.
[Route("api/GetAllNotes")]
[HttpGet]
[Authorize(Roles = "Admin")]
public List<Note> GetAllNotes()
{
using (var db = new NotesModel())
{
var noteList = db.Note.ToList();
return noteList;
}
}
Hashing password and verify
public class CryptoUtil
{
public static string HashPassword(string password)
{
var salt = BCrypt.Net.BCrypt.GenerateSalt();
var hashedPassword = BCrypt.Net.BCrypt.HashPassword(password, salt);
return hashedPassword;
}
public static bool VerifyPassword(string password, string hashedPassword)
{
var isVerified = BCrypt.Net.BCrypt.Verify(password, hashedPassword);
return isVerified;
}
}
You can download source code, db script and postman requests from here –> Download