I have created a brand new WebService using ASP.Net Web Api 2.0 and am trying to use authorization based on an OAuth JWT token. I have all of the basic wiring done with OWIN, but when I use the [Authorize] attribute, it fails, even for a valid token. Can you help.
Edit: I narrowed down the issue. It seems that the key is RS256 and SymmetricKeyIssuerSecurityKeyProvider doesn't handle that... Either that or I am passing the Key Provider the wrong information. Any ideas how to fix that?
Here is the code of my startup.cs
using System.Configuration;
using System.Web.Http;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Jwt;
using Owin;
using Microsoft.AspNet.Identity;
namespace SecuredApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
var config = ConfigureWebApi();
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
private HttpConfiguration ConfigureWebApi()
{
var config = new HttpConfiguration();
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
config.MapHttpAttributeRoutes();
return config;
}
private void ConfigureOAuth(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings.Get("Issuer");
var audience = ConfigurationManager.AppSettings.Get("Audience");
var secret = ConfigurationManager.AppSettings.Get("Secret");
// Api controllers with an [Authorize] attribute will be validated with JWT
var jwtBearerAuthenticationOptions = new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AllowedAudiences = new[] { audience },
IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[]
{
new SymmetricKeyIssuerSecurityKeyProvider(issuer, secret)
}
};
app.UseJwtBearerAuthentication(
jwtBearerAuthenticationOptions);
}
}
}
Related
I have the below code on my webservice to authenticate bearer token which works fine for the scope defined in azure app. But in one of my application token is generated from "https://graph.microsoft.com". How can i authenticate graph token using the below method.
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["Audience"]
},
});
}
}
I have an ASP.NET Web API project in (running on .NET 4.8 - not .NET Core). I have setup 'Bearer token' authentication by adding:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType))
}
}
And in Startup.cs:
public class Startup1
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "https://www.example.com", //some string, normally web url,
}
});
}
}
And in my controller method, I add [Authorize] in my controller API.
But when I call the endpoint in my browser, I checked that it has a bearer token in the http header. The body of the http response is
"Message":"Authorization has been denied for this request."
How can I debug my issue? as I don't see any exception or any message in log.
Could you add these parameters in your code and try:
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "Any" },
IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[] {
new SymmetricKeyIssuerSecurityKeyProvider(issuer, secret)
}
});
var issuer = ConfigurationManager.AppSettings["issuer"];
var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
Add these parameters in App config file:
<appSettings>
<add key="issuer" value="http://localhost:xxx/"/>
<add key="secret" value="YourToken"/>
</appSettings>
I have gone through a lot of docs but it seems my problem is strange.
I have configured Oauth but I am not able to get the bearer token back. whenever I hit api to get the token, I get 200 but nothing back in response(I am expecting bearer token). Below is the config:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
Provider = new ApplicationOAuthProvider()
};
app.UseOAuthAuthorizationServer(oAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider()
});
HttpConfiguration config = new HttpConfiguration();
//config.Filters.Add(new );
//config.MapHttpAttributeRoutes();
// There can be multiple exception loggers. (By default, no exception loggers are registered.)
//config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
WebApiConfig.Register(config);
//enable cors origin requests
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
}
public static class WebApiConfig
{
/// <summary>
///
/// </summary>
/// <param name="config"></param>
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.Filters.Add(new HostAuthenticationAttribute("bearer")); //added this
config.Filters.Add(new AuthorizeAttribute());
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var form = await context.Request.ReadFormAsync();
if (myvalidationexpression)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Role, "AuthorizedUser"));
context.Validated(identity);
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
}
}
}
Now when I launch the APi and hit /token, I get this as below:
API Request
I think that code you have written in WebApiConfig.cs to suppress host authentication and some other code is creating the issue.
I have a working example for bearer token generation in web API, which is working properly and generating token.
WebApiConfig.cs file code:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Startup.cs Code:
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions
OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider=new ApplicationOAuthProvider(),
//AuthenticationMode = AuthenticationMode.Active
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
Provider = new OAuthBearerAuthenticationProvider()
}
);
}
}
}
Controller to check authorization call after adding bearer token in the request.
public class TokenTestController : ApiController
{
[Authorize]
public IHttpActionResult Authorize()
{
return Ok("Authorized");
}
}
install the following package
Microsoft.Owin.Host.SystemWeb
I am facing an issue with Jwt Authentication.
I have an ASP.NET Core 2 WepApi which also serves my SPA App (Its a Vue-App) The SPA App gets the Token from Azure B2C via the MSAL.js library from Microsoft.
When i hit the WebApi where i need to Authorize i get the following error:
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
Failed to validate the token [MyTokenHere]
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
Bearer was not authenticated. Failure message: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
In the browser i get a 401
GET http://localhost:51420/api/values 401 (Unauthorized)
I face the very same issue with the sample Application provided here An ASP.NET Core web API with Azure AD B2C (with their tanant)
Here is my Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using VueTemplate.SignalR;
namespace VueTemplate
{
public class Startup
{
public string Authority { get; set; } = "https://login.microsoftonline.com/tfp/[MyB2CTenant]/[MyPolicy]/v2.0/";
public string ClientId { get; set; } = [MyApplicationId];
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSignalR();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => new JwtBearerOptions() {
Authority = Authority,
Audience = ClientId,
Events = new JwtBearerEvents() { OnAuthenticationFailed = AuthenticationFailed, }
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions() {
HotModuleReplacement = true
});
}
else {
app.UseExceptionHandler("/Home/Error"); // TODO Create Error page
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseSignalR(routes => {
routes.MapHub<ChatHub>("Hub/Chat");
});
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}"
);
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" }
);
});
}
private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationFailed: {arg.Exception.Message}";
arg.Response.ContentLength = s.Length;
arg.Response.Body.Write(Encoding.UTF8.GetBytes(s), 0, s.Length);
return Task.FromResult(0);
}
}
}
ValuesController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace VueTemplate.Controllers
{
[Authorize]
[Route("api/[controller]/")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get() {
return Ok(new int[] { 1, 2, 3, 4 });
}
}
}
Any thoughts? Do i have to provide a security key? Where do i find it in Azure B2C?
I have found a solution to my problem.
The correct way to configure the AddJwtBearer() method is to use the options object which is already provided and not to create a new one.
Bad:
.AddJwtBearer(option => new JwtBearerOptions // <--- Evil
{
Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/",
Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]),
Audience = Configuration["Authentication:AzureAd:ClientId"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
},
});
Good:
.AddJwtBearer(options => {
options.Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/", Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]);
options.Audience = Configuration["Authentication:AzureAd:ClientId"];
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = AuthenticationFailed
};
});
I have decoupled my Resourse server from the Authorization server and I can get access token from my authorization but when am trying to access a protected resource on my resource server i always get "Authorization has been denied for this request.". I have followed some posible solution and suggestion to make sure that my owin midleware references are same for my resource and authozation but still there is no win;
Here Is My StartUp(Resousource server)
[assembly: OwinStartupAttribute(typeof(NetAppWeb.Startup))]
namespace NetAppWeb
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config); // wire the web api to the owin server
}
private void ConfigureOAuth(IAppBuilder app)
{
//Token Consumption
//we are configuring the Resource Server to accept (consume only) tokens with bearer scheme
app.UseOAuthBearerAuthentication(new
{
});
}
}
}
StartUp.cs (Authorization Server)
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();//configure API routes
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config); //wires up Asp.Net Web Api to owin Server
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), //1 day
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}