I'm working with a .net framework 4.7 app hosted in IIS. The api needs to be secured with JWT token. Identity is provided by another server and clients will send the JWT as bearer token in the header. I like to use OWIN pipeline for authorization. Currently the app uses Global.asax for startup and I like to keep it as is. I just want OWIN for authorization using JWT. I will use the [Authorize] attribute on the controllers needing jwt authorization. IIS doesn't do any authorization at the moment.
I have this in the Startup.cs for Owin.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
}
How do I call the Startup.Configure() from Global.asax so Owin pipeline handles the authorization for incoming requests.
Thanks
You can have both global.asax and OWIN startup in the same project. ASP.NET will first call the global.asax and hand the control over to OWIN's startup. Make sure you have the Microsoft.Owin.Host.SystemWeb package installed in the project. And you have a class with the name Startup and a method Configuration(IAppBuilder app). There are other ways to let OWIN know where it should start.
You should also be aware of the fact that in .NET framework, there is a manual process to retrieve the signing keys from the authority that issued the JWT token. Otherwise, you will get the mismatched key error. Once you get the keys, you will assign them to ValidSigningKeys property in TokenValidationParameters. Search SO for examples.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = ConfigHelper.GetAudience(),
ValidIssuer = ConfigHelper.GetIssuer(),
ValidateLifetime = true,
ValidateIssuerSigningKey = true
}
});
}
Related
I need to implement role based authorization for asp .net web api
from native client application the access token will be sent in the header ,
so when i give [Authorize] attribute to my web api it is working fine,but when i give [Authorize(Role="Admin")] it is giving unauthorized error,
then whenever check for role in the claims it is always null.
My startup.cs is
public void Configuration(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
}
and i have applied authorize attribute for a method
[Authorize]
[HttpGet]
public async Task<UserDetails> TestAuthorization()
{
string upn = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Name).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
var role = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Role);
return null;
}
here the role value is always null
Please help me on this
I have an Asp.net webapi with JWT authentication using OWIN middle ware.
My resource server and the authorization server are same. I am able to get the token from the token endpoint. ValidateClientAuthentication and GrantResourceOwnerCredentials methods are hit successfully.
However when I try to access a protected(with [Authorize]) api (with authorization header set to bearer token) I only get "Authorization has been denied for this request".
I have overridden ValidateAuthorizeRequest method just to see if it gets hit when the api call is made via Postman. However it is never hit.
I am trying to figure out a way to see if at all OWIN is intercepting calls to the api other than the calls to the token endpoint.
Is there any way or methods to override so that I can debug and see where in the pipeline the request is being rejected and why.
As of now I make the call via Postman and get an unauthorized response.
Any help would be greatly appreciated.
this is difficult to answer without seeing what you've done.
I am wondering if you have wired things up correctly. Startup class is where you define your Provider and Token format and then you set your application to use those settings. Here is an example:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
int accessTokenExpiresInSeconds = ConfigurationHelper.GetAppSetting("AccessTokenExpirationInSeconds").ToInt();
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(ConfigurationHelper.GetAppSetting("TokenEndPoint")),
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(accessTokenExpiresInSeconds),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(ConfigurationHelper.GetAppSetting("TokenIssuer"))
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
}
}
If that's not the issue then you can use my own article on OAuth2 and JWT, I've got a full example on how to set everything up and the code is on GitHub. Hopefully it will guide you in the right direction:
https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/
Im getting an infinite redirect after i logged in with ADFS 2.0.
My ConfigureAuth.cs is like
//defines default authentication to WSFederation
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
//Defines the MetadataAddress and realm
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
MetadataAddress = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"]
});
//Defines WSFederation cookie as default authentication type
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
});
i can get to the ADFS login page, but when it returns to my app it keeps asking ADFS for a valid authentication, after 6 requests i get blocked by ADFS.
UPDATE 1
It turns out i needed to specify the Issuer, TokenEndpoint and the certificate key, for some reason owin didnt get these values from the metadata, so i ended up copying the values of the metadata and using them in the webconfig under appsettings.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
Configuration = getWsFederationConfiguration()
}
);
}
private static WsFederationConfiguration getWsFederationConfiguration()
{
WsFederationConfiguration configuration = new WsFederationConfiguration
{
Issuer = ConfigurationManager.AppSettings["wsFederation:trustedIssuer"],
TokenEndpoint = ConfigurationManager.AppSettings["wsFederation:issuer"],
};
configuration.SigningKeys.Add(new X509SecurityKey(new X509Certificate2(Convert.FromBase64String(ConfigurationManager.AppSettings["wsFederation:trustedIssuerSigningKey"]))));
return configuration;
}
How do you trigger authentication? If it is through an [Authorize], do you happen to request special user or roles? If you request a role that the signed in user does not have, you'll end up bouncing around.
Also, you should change the order of your calls: first set the cookie middleware, then the protocol one.
It turns out i needed to specify the Issuer, TokenEndpoint and the certificate key, for some reason owin didnt get these values from the metadata, so i ended up copying the values of the metadata and using them in the webconfig under appsettings.
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
Configuration = getWsFederationConfiguration()
}
);
}
private static WsFederationConfiguration getWsFederationConfiguration()
{
WsFederationConfiguration configuration = new WsFederationConfiguration
{
Issuer = ConfigurationManager.AppSettings["wsFederation:trustedIssuer"],
TokenEndpoint = ConfigurationManager.AppSettings["wsFederation:issuer"],
};
configuration.SigningKeys.Add(new X509SecurityKey(new X509Certificate2(Convert.FromBase64String(ConfigurationManager.AppSettings["wsFederation:trustedIssuerSigningKey"]))));
return configuration;
}
I am creating a simple authentication server using the default owin oauth server. After supplying the correct credentials a bearer token is generated and returned to the client. I used among others this tutorial by Taiseer
I would like to store the token in a database before the token is send to the client.
Maybe I completely overlooked it, but where can I get the token before it is send? As far as I know the token is generated after the ticket is validated in the GrantResourceOwnerCredentials method.
I am guessing the token is stored in the context. How can I get it out?
Startup.cs
private void ConfigureAuthServer(IAppBuilder app) {
// Configure the application for OAuth based flow
var oAuthServerOptions = new OAuthAuthorizationServerOptions {
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
ApplicationOAuthProvider
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) {
//Dummy check here
if (context.UserName != context.Password) {
context.SetError("invalid_grant", "The user name or password is incorrect");
return Task.FromResult<object>(null);
}
var claims = new List<Claim> {
new Claim(ClaimTypes.NameIdentifier, context.UserName),
new Claim(ClaimTypes.Name, context.UserName)
};
var oAuthIdentity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
return Task.FromResult<object>(null);
}
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);
}
Note: for those who wonder why I want to store the tokens.. it is a requirement I have to fulfill.
To fetch the token before it is sent to the client you must override TokenEndpointResponse:
public override Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
return base.TokenEndpointResponse(context);
}
the context object has a property AccessToken which will contains the representation of the token as a string.
OAuthTokenEndpointResponseContext contains a dictionary of objects
IDictionary<string, object> in AdditionalResponseParameters which allows us to find all the claims for the indentity.
If we wanted to fetch the expiration of the token we would find the claim .expires in the dictionary:
context.AdditionalResponseParameters[".expires"]
There's a github repository if someone is interested to play with a simple integration of client and server interaction.
I have a console application SERVER that hosts WebApi controllers using OWIN self-hosting, and runs under a custom account named "ServiceTest1".
In the same machine I have another console application CLIENT that runs under the account "ServiceTest2", and I want to capture in SERVER that "ServiceTest2" invoked a controller action. However:
WindowsIdentity.GetCurrent() is always "ServiceTest1".
Thread.CurrentPrincipal is an unauthenticated GenericIdentity.
RequestContext.Principal is null.
User is null.
What do I need to make this WebApi OWIN self-hosted to grab the Windows identity of the caller?
Your question is a little unclear on exactly how you've implemented the Windows authentication.
Enable Windows authentication:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// ...
}
}
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;