What is OAuth
oAuth stands for Open Authorization. It helps to access the resource
in secured way , when the resource owner want it to share to any third party
provider.
Like you logged in to the twitter and now you want to send a
friend request to all your google address book user. So you can login securely
to the google and than google will authenticate and share the entire address
book to twitter and that you can use to send the friend request.
How it Work
User has logged into as is user credential into seeker Resource (SR) now he want to get
the details from third party resource(RO) where he already have account. He has to
provide the credentials than third party resource owner (RO) will allocate the security
token. Then Seeker Resource (SR) will send the Security token to the resource
Owner(RO), and it will give the details of resource what Seeker Resource want.
Implementation
1.
Create a sample WebApi using the visual studio as a default api.
2.
Now browse your WebApi. Just to test that your
API is working fine or not.
Install below Nuget packages in your applications.
a. Microsoft.owin.security.oauth:
Implemantaion
of Oauth Services. This is the iddleware that will handle token generation.
b. Microsoft.Owin.Cors:
Allow
you to manage Cors security for client side requests and validation of proper
domains. This package contains the components to enable Cross-Origin Resource
Sharing (CORS) in OWIN middleware.
c. Microsoft.AspNet.WebApi.Core
This
package contains the core runtime assemblies for ASP.NET Web API. This package
is used by hosts of the ASP.NET Web API runtime. To host a Web API in IIS use
the Microsoft.AspNet.WebApi.WebHost package. To host a Web API in your own
process use the Microsoft.AspNet.WebApi.SelfHost package. [ Its core package of Web Api]
It Might be
already installed.
d. Microsoft.AspNet.WebApi.Owin
This
package allows you to host ASP.NET Web API within an OWIN server and provides
access to additional OWIN features.
e. Microsoft.Owin.Host.SystemWeb
OWIN
server that enables OWIN-based applications to run on IIS using the ASP.NET
request pipeline.
f.
Microsoft.AspNet.Identity.Owin
g. Microsoft.AspNet.Identity.EntityFramework
It
helps to maintain the identity into entity frameworks and code first approach.
Configuration in web API.
Open
WebApiConfig.cs and add the below code. In WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer
token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
/// It is using only if you are sending the
Form type data
config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new
System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data"));
}
|
Ø
first line of code removes any default authentication
which might be defined at the host level
Ø
Second line of code adds an authentication
filter for OAuth that it will be the only one applied.
Startup.cs
Add
a startup.cs class to your application.
a.
Rt click on App_Start à Add à Class
b.
Now search for OWIN at right search box
(Follow Below Link For more detail)
c.
Added below code in startup.cs page it is partial class.
[assembly: OwinStartup(typeof(WebApiOAuth_1.App_Start.Startup))]
namespace
WebApiOAuth_1.App_Start
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
ConfigureAuth(app);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
}
}
|
Configuration
of authentication ConfigureAuth it
is defined in Startup.auth.cs. Just
a configuration of OAuth. Configuring the CORS for application. By
calling app.UseWebApi you configure OWIN/Katana to send web requests
through ASP.NET Web Api that in OWIN terminology is considered a middleware.
Startup.Auth.cs
Add file Startup.Auth.cs for maintaining the
authorization.
Content
for Startup.Auth.cs
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring
authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user
manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a
cookie to store information for the signed in user
// and to use a cookie to temporarily
store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for
OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production
mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use
bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Uncomment the following lines to
enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
//
clientId: "",
//
clientSecret: "");
//app.UseTwitterAuthentication(
//
consumerKey: "",
//
consumerSecret: "");
//app.UseFacebookAuthentication(
//
appId: "",
//
appSecret: "");
//app.UseGoogleAuthentication(new
GoogleOAuth2AuthenticationOptions()
//{
//
ClientId = "",
//
ClientSecret = ""
//});
}
}
|
IdentityConfig.cs
It uses to manage the user configuration. Configure
the application user manager used in this application. UserManager is defined
in ASP.NET Identity and is used by the application.
namespace WebApiOAuth_1
{
// Configure the application user manager
used in this application. UserManager is defined in ASP.NET Identity and is
used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for
usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for
passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
var dataProtectionProvider =
options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET
Identity"));
}
return manager;
}
}
}
|
IdentityModels.cs
You can add profile data for the user by adding more
properties to your ApplicationUser class.
namespace
WebApiOAuth_1.Models
{
// You can add profile data for the user by
adding more properties to your ApplicationUser class, please visit
https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must
match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await
manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await
GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
private const string LocalLoginProvider = "Local";
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public async void CreateUsers()
{
ApplicationDbContext
applicationDbContext = new ApplicationDbContext();
ApplicationUser applicationUser = new ApplicationUser();
//IdentityUserClaim identityUserClaim
= new IdentityUserClaim();
//identityUserClaim.UserId =
"satya";
applicationUser.PasswordHash = "satya12345";
applicationUser.UserName = "satya";
//applicationUser.Claims.Add(identityUserClaim);
//UserManager o = new
UserManager<ApplicationUser, string>();
//UserManager<ApplicationUser>
o = new UserManager(;
//await
applicationUser.GenerateUserIdentityAsync(applicationUser).Wait();
//
applicationUser.GenerateUserIdentityAsync()
applicationDbContext.Users.Add(applicationUser);
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
|
ChallengeResult.cs
It is default implementation for OAuth Provider. The file
model structure is
namespace
WebApiOAuth_1.oAuthConfiguration
{
public class ChallengeResult : IHttpActionResult
{
public ChallengeResult(string loginProvider, ApiController controller)
{
LoginProvider = loginProvider;
Request = controller.Request;
}
public string LoginProvider { get; set; }
public HttpRequestMessage Request { get; set; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage =
Request;
return Task.FromResult(response);
}
}
}
|
ApplicationOAuthProvider
This is the actual implementation of your Oauth provider,
here only it will validate your username and password which is added into
Identity.
namespace
WebApiOAuth_1.oAuthConfiguration
{
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
//TODO: Pull from configuration
if (publicClientId == null)
{
throw new ArgumentNullException(nameof(publicClientId));
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await userManager.FindAsync(context.UserName,
context.Password); // .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);
AuthenticationProperties properties =
CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity,
properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
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)
{
// 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);
}
public static AuthenticationProperties CreateProperties(string userName)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", userName }
};
return new AuthenticationProperties(data);
}
}
}
|
API Controller Code
Code to get the values. Just need to add the [Authorize] on
top of the action method.
Get Method.
[Authorize]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
|
Register New Identity.
It use to register new identity to application and only for
that user, you can grant the access token.
private ApplicationUserManager _userManager;
public void AddUsers(string userName, string password)
{
//var user = new ApplicationUser() {
UserName = userName, Email = "satya@gmail.com" };
//IdentityResult result = await
UserManager.CreateAsync(user, password);
var userStore = new UserStore<IdentityUser>();
var manager = new UserManager<IdentityUser>(userStore);
var user = new IdentityUser() { UserName =
userName };
IdentityResult result =
manager.Create(user, password);
if (result.Succeeded)
{
var
authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
var userIdentity =
manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ??
Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody]string value)
{
try
{
var UserName =
value.Split('|')[0];
var Password =
value.Split('|')[1];
AddUsers(UserName, Password);
return new HttpResponseMessage(HttpStatusCode.Accepted);
}
catch
{
return new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
|
WebApi Testing with OAuth Security (Postman)
Secure URL without Token
Now
if you try to access the Application without token. Same URL As we seen above
you can get message as "Authorization has been denied"
Fetch token for Unregistered User
Trying
to request for token if not registered.
- Use Post
- Access Toke URL as http://localhost:[12345]/token/
- select content type as application/x-www-form-urlencoded
- Pass as header: grant_type, username, password,
You will get message as "Unsupported grant_type" as below
Access the services à register new User
You
need to add the Identity for application by which only you can validate the
Request.
- Use Post
- Add identity URL http://localhost:[12345]/api/values/post
- select content type as JSON(application/JSON)
- Pass as Body: plan text satya|satya12345,
Adding Identity as satya|satya12345
Fetch Token for Registered User
You need to get the
token by using the token URL.
By Passing the UserName and Password.
Use same above Setup for postman for fetching tokens.
Access URL with Authorization Key
Once
you get the Access token than access the URL With the Authorization Token as
like below:
pass the token you got in last postman fetch result as like below where key is Authorization
and value as
Bearer
DBu8L7mwTKEyhgAkO1m3o604RCok6gHiEUrbSZNk5BcWHkZJTc0KqgriyEjnHMXtH_w_tKAF_RvVGBK3GuwPeJwy7ZLHjmHn-ny4Aeopy2GUEqNOl_BG1wh02b6phsW2-yvXJgY8WQqhRkO28Mjk5bvfO26usbSG5ukEcs0bNTAkJBfd8r1jDC_k17krX5w622Kq5lneNXlnNIwwMO8wByEhFqbWWCGh1ne_Fc3oXVAT-zUMtbQiJ--E9dd5w66ERyn_UweOC2D_dpx2shnMNEJpMzdvyFnBHjImvmkdb8ugv_vapvLVt8uhWIAbPob-0pcQrJn4JRVnNTb3Fv6rqbfY_zWfnAl8miBdkgE0zJrO4qxJ7BTAReuZ7LfU9DYyBUDcZzt3BIwI1_UHUiftBfrSz5bQVBkgtwztBr4iblniN5U6TaiTMzdPa6Z3K2uoOCI4YNL9bhhJlDdaHY--aNOzdMcGAi2QWXPo9RJ6JCY
|
Full
Post man screen request:
No comments:
Post a Comment