Pinpoint helped me get this prototype off the launch pad - I was very close except for:
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-beta6"
}
}
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-beta6",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta6",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta6",
"Microsoft.AspNet.StaticFiles": "1.0.0-beta6",
"System.IdentityModel.Tokens": "5.0.0-beta6-207211625",
"Serilog.Framework.Logging": "1.0.0-beta-43",
"Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta6"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --config hosting.ini"
},
"frameworks": {
"dnx451": { }
},
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"publishExclude": [
"node_modules",
"bower_components",
"**.xproj",
"**.user",
"**.vspscc"
]
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseOAuthBearerAuthentication();
app.UseMvc();
}
I'm working with ASP.NET 5 and trying to implement an extremely simple proof of concept for generating and consuming JWT tokens. I have read the articles here, here and here but this one most closely matches my needs.
To this end, I very carefully read the article, re-read it, internalized all the comments and then stood up a simple example. I can now generate a JWT token, but when I attempt to call my controller action which has been decorated with an authorize attribute [Authorize("Bearer")], I receive the following message:
The following authentication scheme was not accepted: Bearer
As I have not seen a high-fidelity A-to-Z example on how to do this, consider the following steps to reproduce:
{
"projects": [ "src", "test" ],
"sdk": {
"version": "1.0.0-beta5",
"runtime": "clr",
"architecture": "x86"
}
}
{
"webroot": "wwwroot",
"version": "1.0.0-*",
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-beta6",
"Microsoft.AspNet.Server.IIS": "1.0.0-beta6",
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta6",
"System.IdentityModel.Tokens": "5.0.0-beta5-206011020",
"Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta5"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --config hosting.ini"
},
"frameworks": {
"dnx451": { }
},
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
"publishExclude": [
"node_modules",
"bower_components",
"**.xproj",
"**.user",
"**.vspscc"
]
}
public class Startup
{
const string _TokenIssuer = "contoso.com" ;
const string _TokenAudience = "contoso.com/resources" ;
RsaSecurityKey _key = null ;
SigningCredentials _signingCredentials = null ;
public Startup(IHostingEnvironment env)
{
GenerateRsaKeys();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddInstance(_signingCredentials);
services.ConfigureOAuthBearerAuthentication
(
options =>
{
options.AutomaticAuthentication = true;
options.TokenValidationParameters.IssuerSigningKey = _key ;
options.TokenValidationParameters.ValidAudience = _TokenAudience;
options.TokenValidationParameters.ValidIssuer = _TokenIssuer ;
}
);
services.ConfigureAuthorization
(
options =>
{
options.
AddPolicy
(
"Bearer",
new AuthorizationPolicyBuilder().
AddAuthenticationSchemes(OAuthBearerAuthenticationDefaults.AuthenticationScheme).
RequireAuthenticatedUser().
Build()
);
}
);
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerfactory)
{
app.UseMvc();
app.UseOAuthBearerAuthentication();
}
void GenerateRsaKeys()
{
using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
{
_key = new RsaSecurityKey(rsa.ExportParameters(true));
_signingCredentials =
new SigningCredentials
(
_key ,
SecurityAlgorithms.RsaSha256Signature ,
SecurityAlgorithms.Sha256Digest ,
"secret"
);
rsa.PersistKeyInCsp = false;
}
}
}
Credentials.cs
public class Credentials
{
public string user { set;get;}
public string password { set;get;}
}
JwtToken.cs
public class JwtToken
{
public string access_token { set; get; }
public string token_type { set; get; }
}
[ Route("[controller]") ]
public class TokenController : Controller
{
private readonly OAuthBearerAuthenticationOptions _bearerOptions ;
private readonly SigningCredentials _signingCredentials ;
public TokenController
(
IOptions<OAuthBearerAuthenticationOptions> bearerOptions ,
SigningCredentials signingCredentials
)
{
_bearerOptions = bearerOptions.Options ;
_signingCredentials = signingCredentials ;
}
// POST: /token
[HttpPost()]
public JwtToken Token([FromBody] Credentials credentials)
{
// Pretend to validate credentials...
JwtSecurityTokenHandler handler =
_bearerOptions .
SecurityTokenValidators .
OfType<JwtSecurityTokenHandler>() .
First();
JwtSecurityToken securityToken =
handler .
CreateToken
(
issuer : _bearerOptions.TokenValidationParameters.ValidIssuer ,
audience : _bearerOptions.TokenValidationParameters.ValidAudience,
signingCredentials : _signingCredentials ,
subject : new ClaimsIdentity
(
new Claim []
{
new Claim(ClaimTypes.Name,"somebody"),
new Claim(ClaimTypes.Role,"admin" ),
new Claim(ClaimTypes.Role,"teacher" ),
}
) ,
expires : DateTime.Today.AddDays(1)
);
string token = handler.WriteToken(securityToken);
return new JwtToken()
{
access_token = token ,
token_type = "bearer"
};
}
}
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[Authorize("Bearer")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
{
"user" : "user",
"password" : "secret"
}
The app responds with a token:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6bnVsbH0.eyJ1bmlxdWVfbmFtZSI6InNvbWVib2R5Iiwicm9sZSI6WyJhZG1pbiIsInRlYWNoZXIiXSwiaXNzIjoiY29udG9zby5jb20iLCJhdWQiOiJjb250b3NvLmNvbS9yZXNvdXJjZXMiLCJleHAiOjE0Mzk1MzU2MDB9.anRgL10XFG_bKDDxY3D2xQSfhPRLGMjUTreQNsP1jDA6eRKwXHf3jtpCwm_saoWyUDFFA2TMI9e_LbP6F5l7vtozCluziE_GQkPkspUSWuWIpQJLPRTTPPZHGKmPmK4MLEl1zPPrggJWbvF9RBw3mMQ0KoMfjSL0vUQ8kZ7VXAel8dnYJccd-CFdnB6aDe79x2E9Se2iLxdhr--R_qgvfz1Fa6tR1dstqLQ-UjYqPWY4SOgBjM3abtjfLLVEzeQMVyezX7Cx9ObMXAGbGvQL6GB_T5RlfAoXWME4jM8Bzhd-07wwd732bBws4OXivj1sSz-qawNTnXmnuccLRtI1uA",
"token_type": "bearer"
}
Copy the token from the previous POST, then in postman make a GET request similar to http: // localhost:22553/api/values, taking care to add an Authorization header with the value "bearer YOURTOKEN" (e.g. bearer eyJ0eXAiOiJKV1QiLCJ...)
Observe that the app responds with the error:
System.InvalidOperationException The following authentication scheme was not accepted: Bearer
Stack trace as follows:
at Microsoft.AspNet.Http.Authentication.Internal.DefaultAuthenticationManager.< AuthenticateAsync> d__9.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Http.Authentication.AuthenticationManager.< AuthenticateAsync> d__2.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter< TResult> .GetResult()
at Microsoft.AspNet.Mvc.AuthorizeFilter.< OnAuthorizationAsync> d__5.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAuthorizationFilterAsync> d__43.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAllAuthorizationFiltersAsync> d__42.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Core.FilterActionInvoker.< InvokeAsync> d__40.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.MvcRouteHandler.< InvokeActionAsync> d__4.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.MvcRouteHandler.< RouteAsync> d__3.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Mvc.Routing.InnerAttributeRoute.< RouteAsync> d__10.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Routing.RouteCollection.< RouteAsync> d__9.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Builder.RouterMiddleware.< Invoke> d__4.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Hosting.Internal.RequestServicesContainerMiddleware.< Invoke> d__3.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Hosting.Internal.HostingEngine.< > c__DisplayClass29_0.< < Start> b__0> d.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Loader.IIS.RuntimeHttpApplication.< ProcessRequestAsyncImpl> d__10.MoveNext()
--- exception rethrown ---
at Microsoft.AspNet.Loader.IIS.RuntimeHttpApplication.< ProcessRequestAsyncImpl> d__10.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.AspNet.Loader.IIS.HttpApplicationBase.< InvokeProcessRequestAsyncImpl> d__9.MoveNext()
Note that adding logging adds virtually no additional insight, as the following logs show:
2015-08-13 13:32:35.969 -07:00 [Information] Request successfully matched the route with name 'null' and template '"api/Values"'.
Exception thrown: 'System.InvalidOperationException' in Microsoft.AspNet.Http.dll
2015-08-13 13:32:36.247 -07:00 [Error] An error occurred while handling the request.
2015-08-13 13:32:36.247 -07:00 System.InvalidOperationException: The following authentication scheme was not accepted: Bearer
I am hoping someone might understand where the breakdown in this example is taking place.
You must register the OAuth2 bearer authentication middleware before MVC, or your users will be unauthenticated when reaching MVC:
public class Startup {
public void Configure(IApplicationBuilder app) {
app.UseJwtBearerAuthentication(new JwtBearerOptions {
// Your JWT bearer options.
});
app.UseMvc();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With