I have followed everything I know from posts regarding how to implement AspNet.Security.OpenIdConnect.Server.
Pinpoint, do you hear me? ;)
I've managed to separate token issuing and token consumption. I won't show the "auth server side" because I think that part is all set, but I'll show how I built the authentication ticket inside my custom AuthorizationProvider:
public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
// The other overrides are not show. I've relaxed them to always validate.
public override async Task GrantResourceOwnerCredentials(GrantResourceOwnerCredentialsContext context)
{
// I'm using Microsoft.AspNet.Identity to validate user/password.
// So, let's say that I already have MyUser user from
//UserManager<MyUser> UM:
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
//identity.AddClaims(await UM.GetClaimsAsync(user));
identity.AddClaim(ClaimTypes.Name, user.UserName);
(await UM.GetRolesAsync(user)).ToList().ForEach(role => {
identity.AddClaim(ClaimTypes.Role, role);
});
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
// Some new stuff, per my latest research
ticket.SetResources(new[] { "my_resource_server" });
ticket.SetAudiences(new[] { "my_resource_server" });
ticket.SetScopes(new[] { "defaultscope" });
context.Validated(ticket);
}
}
And startup at the auth server:
using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;
using MyAuthServer.Providers;
namespace My.AuthServer
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
services.AddCaching();
services.AddMvc();
string connectionString = "there is actually one";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options => {
options.UseSqlServer(connectionString).UseRowNumberForPaging();
});
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app)
{
app.UseIISPlatformHandler();
app.UseOpenIdConnectServer(options => {
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.Provider = new AuthorizationProvider();
options.TokenEndpointPath = "/token";
options.AccessTokenLifetime = new TimeSpan(1, 0, 0, 0);
options.Issuer = new Uri("http://localhost:60556/");
});
app.UseMvc();
app.UseWelcomePage();
}
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
}
Sure enough, when I have this HTTP request, I do get an access token, but I'm not sure if that access token has all the data that the resource server expects.
POST /token HTTP/1.1
Host: localhost:60556
Content-Type: application/x-www-form-urlencoded
username=admin&password=pw&grant_type=password
Now, At the resource server side, I'm using JWT Bearer Authentication. On startup, I've got:
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;
namespace MyResourceServer
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
string connectionString = "there is actually one";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options => {
options.UseSqlServer(connectionString).UseRowNumberForPaging();
});
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
}
public void Configure(IApplicationBuilder app)
{
app.UseIISPlatformHandler();
app.UseMvc();
app.UseWelcomePage();
app.UseJwtBearerAuthentication(options => {
options.Audience = "my_resource_server";
options.Authority = "http://localhost:60556/";
options.AutomaticAuthenticate = true;
options.RequireHttpsMetadata = false;
});
}
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
}
When I make this HTTP request to the resource server, I get a 401 Unauthorized:
GET /api/user/myroles HTTP/1.1
Host: localhost:64539
Authorization: Bearer eyJhbGciOiJS...
Content-Type: application/json;charset=utf-8
The controller who has a route to /api/user/myroles
is decorated with a plain [Authorize]
with no parameters.
I feel like I'm missing something in both auth and resource servers, but don't know what they are.
The other questions that ask "how to validate token issued by AspNet.Security.OpenIdConnect.Server" don't have an answer. I would appreciate some help in this.
Also, I've noticed that there is OAuth Introspection commented out in the sample provider, and have read somewhere that Jwt is not going to be supported soon. I can't find the dependency that gives me the OAuth Instrospection.
UPDATE I've included both of my startup.cs, from each of auth and resource servers. Could there be anything wrong that would cause the resource server to always return a 401 for every request?
One thing I didn't really touch throughout this whole endeavor is signing. It seems to generate a signature for the JWT at the auth server, but the resource server (I guess) doesn't know the signing keys. Back in the OWIN projects, I had to create a machine key and put on the two servers.