I am trying to undesratnd owin and OAuthAuthorizationServer.
I know that Outh2 has 4 parts:
1- Resource Owner
2- Resource Server:
3- Client Applications:
4- Authorization Server:
I have implemnted owin and Authorization Server in a simple application.
The application is working fine.
I just want to learn more regading the Outh stuffs.
so I have in my srartp class :
public class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);//this is very important line cross orgin source(CORS)it is used to enable cross-site HTTP requests //For security reasons, browsers restrict cross-origin HTTP requests
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),//token expiration time
Provider = new OauthProvider()
};
app.UseOAuthBearerTokens(OAuthOptions);
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);//register the request
}
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
Then I created my OauthProvider
here is my class
public class OauthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//First request will come here, this method will validate the request wheather it has crendtials(UserName and Password) if the request not contain username and
//password the request will reject from here not proceded any further
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//If the request has valid and it contain username and password than this method will check correct crenstials and than generate a valid token
var identity = new ClaimsIdentity(context.Options.AuthenticationType); //it will check the authenticate type
using (var db = new DataContext())
{
if (db != null)
{
var user = db.Users.Where(o => o.UserName == context.UserName && o.Password == context.Password).FirstOrDefault();
if (user != null)
{
//Store information againest the request
identity.AddClaim(new Claim("UserName", context.UserName));
identity.AddClaim(new Claim("LoggedOn", DateTime.Now.ToString()));
context.Validated(identity);
}
else
{
context.SetError("Wrong Crendtials", "Provided username and password is incorrect");
context.Rejected();
}
}
else
{
context.SetError("Wrong Crendtials", "Provided username and password is incorrect");
context.Rejected();
}
return;
}
}
}
So if I want to undersatnd the OAuth parts.
How can I define what I have done to each part ?
Notice That this is a web api project ?
Any useful informatuin is helpful.
Thanks
Related
We have an MVC 5 web app that uses ADFS 4 authentication. I'm trying to find the best place where I can add additional claims into the ClaimsPrincipal, after authentication has been completed.
Are there any events I can access, like OnAuthenticated? How do I access this kind of event?
This is what I intend to use once I can access the event:
IOwinContext context = Request.GetOwinContext();
if (appRoles != null)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(System.Web.HttpContext.Current.User.Identity);
foreach (var role in appRoles)
{
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
}
context.Authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant
(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties { IsPersistent = true });
}
EDIT:
This is what my App_Data\Startup.Auth.cs file looks like:
public partial class Startup
{
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieManager = new SystemWebCookieManager()
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata
});
}
}
I encountered similar problem and managed to find a way to add additional claims after ADFS login in my MVC 5 app. More info can be found on msdn link.
Here is code from that link.
Firstly create the new ClaimsAuthenticationManager class and inside set additional claims:
class SimpleClaimsAuthenticatonManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "User"));
}
return incomingPrincipal;
}
}
Afterwards specify this class in web.config file, under identityConfiguration element:
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="ENTER YOUR NAMESPACE HERE.SimpleClaimsAuthenticatonManager, ENTER PROJECT NAME HERE" />
...
</identityConfiguration>
</system.identityModel>
I am new to WEB API ,here I need to implement a token based authentication for a login page .
As of now I have completed the token generate functions by providing static values for learning purpose .
Now What I want to do is I want to validate each request with the table which I have created in my SQL SERVER (LOGIN_TABLE) and generate Token instead of validate with Identity tables in web API.
Columns of LOGIN_TABLE
FirstName,LastName,UserName,Email,Password,CreationDate
I don't know is it possible or not ,if possible please help me to do that .
I have created my project as Web API template without MVC and choosed No Authentication then I have added all the necessary packages and classes.
Here I have added the code I have used to generate token based on the static values.
My_Controller.cs
[Authorize]
[HttpGet]
[Route("api/data/authenticate")]
public IHttpActionResult GetForAuthenticate()
{
var identity = (ClaimsIdentity)User.Identity;
return Ok("Hello" + identity.Name);
}
MyAutorizationServerProvider.cs
public class MyAuthorizationServerProvider :OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
if (context.UserName == "user" && context.Password == "123")
{
identity.AddClaim(new Claim(ClaimTypes.Role, "USER"));
identity.AddClaim(new Claim("username", "user"));
identity.AddClaim(new Claim(ClaimTypes.Name, "Sakthi"));
context.Validated(identity);
}else
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
//This enable cors orgin requests
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var myProvider = new MyAuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
}
}
AuthorizeAttribute.cs
public class AuthorizeAttribute :System.Web.Http.AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actioncontext)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
base.HandleUnauthorizedRequest(actioncontext);
}else
{
actioncontext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
}
}
I am new to web API if I did any mistakes here please guide me to correct it .
Thanks .
I created pone webapi and implemented authentication. I have the token method to get the user token. All working fine.
Scenario:
I tested the token method with the postman. Here I noted that I can use any type of HTTP method to request for the token. I think /token method should support POST method only. But when I use DELETE method also I got token. Same as, I can also use PUT, PATH etc.
Is this expected? I assume that it should return Method Not Supported other than POST requests.
You can write your custom OAuthAuthorizationServerOptions.Provider. And use the context to accept Http Post request only
OAuthAuthorizationServerOptions is asp.net identity core class. which you can find under this namespace Microsoft.Owin.Security.OAuth.
Sample Code:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
if (context.Request.Method != "POST")
{
context.SetError("invalid_request", "Invalid request");
return;
}
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(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("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
I added Token based authentication to my OWIN middleware and can generate the token. But while using, the token for an API call with Authorize attribute I always get "Authorization has been denied for this request." It works fine though without Authorize attribute. Here is my startup.cs and controller method. Any thoughts , what is wrong?
startup.cs
public void Configuration(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["issuer"];
var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthProvider(),
AccessTokenFormat = new JwtFormat(issuer)
});
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "*" },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
container = BuildDI();
var config = new HttpConfiguration();
config.Formatters.XmlFormatter.UseXmlSerializer = true;
config.MapHttpAttributeRoutes();
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ExternalBearer));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseCors(CorsOptions.AllowAll);
app.UseSerilogRequestContext("RequestId");
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
RegisterShutdownCallback(app, container);
}
public class SimpleAuthProvider: OAuthAuthorizationServerProvider
{
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
context.Rejected();
return Task.FromResult<object>(null);
}
var ticket = new AuthenticationTicket(SetClaimsIdentity(context), new AuthenticationProperties());
context.Validated(ticket);
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ExternalBearer);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
return identity;
}
}
API Controller Method:
[HttpGet]
[Route("sampleroute")]
[Authorize]
public async Task<HttpResponseMessage> GetSamples(string search)
{
try
{
HttpResponseMessage response;
using (HttpClient client = new HttpClient(Common.CreateHttpClientHandler()))
{
response = await client.GetAsync("test url");
}
var result = response.Content.ReadAsStringAsync().Result;
Samples[] sampleArray = JsonConvert.DeserializeObject<Samples[]>(result);
var filteredSamples = sampleArray .ToList().Where(y => y.NY_SampleName.ToUpper().Contains(search.ToUpper())).Select(n=>n);
log.Information("<==========Ended==========>");
return Request.CreateResponse(HttpStatusCode.OK,filteredSamples);
}
catch (Exception ex)
{
log.Error($"Error occured while pulling the Samples: {ex.ToString()}");
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.ToString());
}
}
It's probably a problem with the allowed audiences.
Here
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
...
AllowedAudiences = new[] { "*" },
...
}
you set the allowed audiences. The tokens audclaim will be checked against the list of AllowedAudiences. But you never add any audience to the token.
In our project I used a CustomJwtFormat based on the code shown in http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The token will be generated with a call to
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
the second parameter is responsible for the aud claim in the JWT:
From https://msdn.microsoft.com/en-us/library/dn451037(v=vs.114).aspx :
audience
Type: System.String
If this value is not null, a { aud, 'audience' } claim will be added.
After setting the aud claim in the token authorization should work fine.
From what I understood, you need to add the header: Authorization: Bearer "token".
If you have not modified the default implementation of the authorization request the steps are these:
Register user at the endpoint:
/api/Account/Register
Post to /token the following items:
grant_type: password
username: "the username you registered"
password: "the password you registered for the user"
You will receive a token in the Response
Copy that token and create a Request to the method you secured with the [Authorize] filter of type:
Authorization: Bearer "the_token_you_copied_earlier"
Needless to say, it could be pretty easy for you if you used Postman or Fiddler to make and receive Requests because it shows you how everything works.
I am working with ASP.NET Identity 2 and trying to enable Token Authentication. From the Identity 2 sample there is nothing about Token Authentication so I setup myself by looking at different post. I am following the last answer of this post app.UseOAuthBearerTokens with ASP.NET Identity 2.0's DbContext middleware?
and able to setup each and everything but now when I try to reach this end point on browser http://localhost:6722/token getting 404 error. Any idea?
Update:
Here is my ApplicationOAuthProvider.cs
public class ApplicationOAuthProvider<TUserManager, TUser, TKey> : OAuthAuthorizationServerProvider
where TUserManager : UserManager<TUser, TKey>
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.Get<TUserManager>(_publicClientId);
TUser 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 userManager.CreateIdentityAsync(user,
context.Options.AuthenticationType);
ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
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);
}
}
Here is my Startup.Oauth.cs
public partial class Startup
{
static Startup()
{
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider<ApplicationUserManager, User, int>(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = false
};
}
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and role manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDb.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.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
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// 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();
}
}
And I am trying to access /Token end point directly from the browser and it throws 404 error.
I had a similar issue today, what fixed the problem for me was installing the following Nuget package.
I have no idea why it wasn't installed with all of the other packages, but give it a shot and let us know.
The package name is Microsoft.Owin.Host.SystemWeb
Do you have the other part of Startup class? Because you should have a Startup.cs file in your web project that is calling your ConfigureAuth method. If not you will get a 404 exception. The startup.cs file should look like this:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(YOUR_NAME_SPACE_TO_THE_STARTUP_FILE.Startup))]
namespace YOUR_NAME_SPACE_TO_THE_STARTUP_FILE
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
If you then trying to access the page through your browser you should get this error:
{
error: "unsupported_grant_type"
}
In my case I wrapped the AllowInsecureHttp in an #if DEBUG processor directive, and ended up publishing it to the server (as Release) and my customer didn't afford SSL.
After removing the DEBUG condition it all works fine now.
I've just had a similar (if not same) issue where I was getting 404s when accessing /token. Eventually worked out that it was due to the order I was calling things in the Startup.Configuration method. I was calling ConfigureAuth(app) at the very bottom of this method, after I had added Ninject config (ie. app.UseNinjectMiddleware(CreateKernel).UseNinjectWebApi(config);. Moving the ConfigureAuth above the Ninject setup fixed this issue and /token now works.