Updated:
Pinpoint helped me get this prototype from the launch pad - I was very close, except:
{ "projects": [ "src", "test" ], "sdk": { "version": "1.0.0-beta6" } }
- I updated the links in project.json:
{ "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" ] }
- The order of the middleware at startup. UseOAuthBearerAuthentication should appear before UseMvc. The Configure method in Startup.cs now looks like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseOAuthBearerAuthentication(); app.UseMvc(); }
I work with ASP.NET 5 and try to implement an extremely simple proof of the concept of generating and consuming JWT tokens. Here I read articles, here and here , but this one most closely matches my needs.
To this end, I read the article very carefully, re-read it, learned all the comments, and then stood by a simple example. Now I can create a JWT token, but when I try to call my controller action, which was decorated with the authorize [Authorize ("Bearer")] attribute, I get the following message:
The following authentication scheme was not accepted: Media
Since I have not seen a high-precision A-to-Z example on how to do this, consider the following steps to reproduce:
- Create a new web API project in Visual Studio 2015 (I use Enterprise) by selecting "New Project ... Web ... ASP.NET Web Application" and then the "Web API" option under "ASP.NET 5 Preview Templates "
- Using beta 5 of the SDK, global.json looks like this:
{ "projects": [ "src", "test" ], "sdk": { "version": "1.0.0-beta5", "runtime": "clr", "architecture": "x86" } }
- When entering the dependencies required for JWT tokens, project.json looks like this:
{ "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" ] }
- Startup.cs (this is an example not intended for production)
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; } }
- Token controller for receiving a token (this is an example not intended for production), TokenController.cs:
[ 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 ; }
- The value controller demonstrating the passage of the token, ValuesController.cs:
[Route("api/[controller]")] public class ValuesController : Controller {
- Run a copy of postman (or your favorite REST client), run the sample application in Visual Studio and make a POST request, similar to http: // localhost: 22553 / token / with JSON body:
{ "user" : "user", "password" : "secret" }
The application responds with a token:
{ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6bnVsbH0.eyJ1bmlxdWVfbmFtZSI6InNvbWVib2R5Iiwicm9sZSI6WyJhZG1pbiIsInRlYWNoZXIiXSwiaXNzIjoiY29udG9zby5jb20iLCJhdWQiOiJjb250b3NvLmNvbS9yZXNvdXJjZXMiLCJleHAiOjE0Mzk1MzU2MDB9.anRgL10XFG_bKDDxY3D2xQSfhPRLGMjUTreQNsP1jDA6eRKwXHf3jtpCwm_saoWyUDFFA2TMI9e_LbP6F5l7vtozCluziE_GQkPkspUSWuWIpQJLPRTTPPZHGKmPmK4MLEl1zPPrggJWbvF9RBw3mMQ0KoMfjSL0vUQ8kZ7VXAel8dnYJccd-CFdnB6aDe79x2E9Se2iLxdhr--R_qgvfz1Fa6tR1dstqLQ-UjYqPWY4SOgBjM3abtjfLLVEzeQMVyezX7Cx9ObMXAGbGvQL6GB_T5RlfAoXWME4jM8Bzhd-07wwd732bBws4OXivj1sSz-qawNTnXmnuccLRtI1uA", "token_type": "bearer" } - R_qgvfz1Fa6tR1dstqLQ-UjYqPWY4SOgBjM3abtjfLLVEzeQMVyezX7Cx9ObMXAGbGvQL6GB_T5RlfAoXWME4jM8Bzhd-07wwd732bBws4OXivj1sSz-qawNTnXmnuccLRtI1uA", { "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 the postman make a GET request similar to http: // localhost: 22553 / api / values, trying to add an authorization header with the value "bearer YOURTOKEN" (for example, bearer eyJ0eXAiOiJKV1QiLCJ ...)
Please note that the application responds to an error:
System.InvalidOperationException The following authentication scheme was not accepted: Bearer
The stack trace is 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 little to no additional understanding, 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 hope someone can figure out where the breakdown in this example occurs.