IdentityServer4 Quickstart issues - asp.net

I am currently at this page of the IdentityServer 4 guide http://docs.identityserver.io/en/dev/quickstarts/3_interactive_login.html and I am trying to start the MVC application.
However, I keep getting this error when I start my Client application
InvalidOperationException: Unable to resolve service for type 'IdentityServer4.Services.IIdentityServerInteractionService' while attempting to activate 'IdentityServer4.Quickstart.UI.HomeController'.
I went into the IdentityServer4 GitHub and copied the code from there but it doesn't run at all.
I am not sure how to proceed from here.
This is my Startup.cs
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace IdentityServerClient
{
public class Startup
{
// 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();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.SaveTokens = true;
});
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
I can't get to the login page shown on the documentation as well.

If you're using the quickstart UI, you should be using the guide on it, here:
https://github.com/IdentityServer/IdentityServer4.Quickstart.UI
To quote that page:
Next you need to configure the authentication handlers:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// some details omitted
services.AddIdentityServer();
services.AddAuthentication()
...
You're missing:
services.AddIdentityServer()
.AddInMemoryCaching()
.AddClientStore<InMemoryClientStore>()
.AddResourceStore<InMemoryResourcesStore>(); // <-- Add this
As a result, none of the identity server services are registered to the dependency injection container, which is why you're seeing that error.
Looks like the tutorial documentation you linked to is out of date.
--
Here is a complete set of steps to follow:
dotnet new sln -n HelloID4
dotnet new mvc -n HelloID4
dotnet sln add HelloID4/HelloID4.csproj
cd HelloID4
git clone --depth 1 https://github.com/IdentityServer/IdentityServer4.Quickstart.UI
cp -r IdentityServer4.Quickstart.UI/* .
dotnet add package IdentityServer4
rm -r Controllers/
dotnet watch run
Now modify your Startup.cs to look like this:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddIdentityServer()
.AddInMemoryCaching()
.AddClientStore<InMemoryClientStore>()
.AddResourceStore<InMemoryResourcesStore>();
}

UPDATE
*Review this code : it may help, because i am also learning on it, and its working for me. *
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.GetUsers())
//.AddInMemoryClients(Config.GetClients())
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("IdentityConnectionString"), sql =>
sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("IdentityConnectionString"),
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
});
//.AddInMemoryIdentityResources(Config.GetIdentityResources())
//.AddInMemoryApiResources(Config.GetApiResources())
services.AddAuthentication();
}
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" }
},
// resource owner password grant client
new Client
{
ClientId = "ro.client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = { "api1" }
},
// OpenID Connect hybrid flow and client credentials client (MVC)
};
}
if you are not using in-memory service
If you are getting this error : Unable to resolve service for type 'IdentityServer4.Stores.IClientStore'
Register the stores and implementation explicitly : ( give a try )
services.AddScoped<IUserStore<User>, UserService>();
services.AddScoped<IClientStore, ClientService>();
services.AddScoped<IScopeStore, ScopeService>();
services.AddScoped<IPersistedGrantStore, GrantService>();

Related

CORS policy issue when consuming ASP NET Core 3.1 Web Api by Angular 8

I have encountered issue with CORS policy when developing Angular 8, ASP NET Core Web Api web application. My angular app is running on http://localhost:4200
There is one service created for communication with Web Api. It looks as follows
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
apiUrl: string = "";
constructor(private http: HttpClient) {
this.apiUrl = 'https://localhost:44316';
}
login(Username: any, Password: any){
return this.http.post<Observable<ResultItem<AuthenticationResponse>>>(this.apiUrl + "/api/User/Authenticate", {Username: Username, Password: Password});
}
}
Services is later called within component, but it is simply injected, and used with subscribe method.
onLogin(){
this.authenticationService.login(this.loginFormValues.username.value, this.loginFormValues.password.value).subscribe(
result => {});
}
Web Api is running seperatly, on https://localhost:44316/
End point for the method called from Angular looks as follows:
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost("Authenticate")]
public async Task<IActionResult> Authenticate(AuthenticationModel model)
{
return Ok(await userService.Login(model));
}
}
What I am most concerned about is my Startup file. So far, I have tried to change the CORS setting there, but with no successful results. Code of the Startup.cs file looks as follows.
Quick note:
Two lines of code within ConfigureServices method use some of my external functions, and their purpose is to:
AddSubstracture: registers all repositories as transients and registers DbContext.
AddApplication: registers services which are one layer above repositories as transients
Startup.cs code looks as follows
public class Startup
{
private IServiceCollection _services;
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
SportFacilityUnitSettings = configuration.Get<SportFacilityUnitSettings>();
}
public IConfiguration Configuration { get; }
public IWebHostEnvironment Environment { get; }
public SportFacilityUnitSettings SportFacilityUnitSettings { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddSubstructure(Configuration, Environment, SportFacilityUnitSettings);
services.AddApplication();
services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddHttpContextAccessor();
_services = services;
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(
options => options.SetIsOriginAllowed(x => _ = true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()
);
app.UseMvc();
app.UseHsts();
app.UseMiddleware<JwtMiddleware>();
app.UseAuthentication();
app.UseRouting();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
When I hit the login button, which purpose is to send the request, I receive following error in web browser console.
Access to XMLHttpRequest at 'https://localhost:44316/api/User/Authenticate' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The weirdest thing about it, is when I debug it, and set up a breakpoint in Api layer, debugger hits it, then it enters the service layer and fails somewhere inside Authentication method .
Go to IIS where your application is hosted and check if you have set the below information right.
#Step 1 : IIS --> HTTP Response header]
#Step 2 : : Setting 4 fields in your API app hosted under IIS
#Step 3: If the above 2 steps does not work, make sure you follow the msdn information to enable cors for your application
https://learn.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1
Step 4 : : Investigate the header information you are using in your web API and if that's allowed under your IIS setting (as mentioned in Step 1)
Step 5 : : Place a breakpoint in your authenticate method to see where and why its failing. You may get more clue from this error information as well.
Step 6 : Try enabling CrossDomain to true from your front end.
Step 7 : Try enabling https for both the application (calling application and called application)

Blazor not receiving signalr created on server side

Following many tutorials, examples, this example below I call on the server side, but the client side does not receive, sometimes it works but sometimes it doesn’t (more doesn’t work than it works)
It was supposed to be very simple, but it's not, any suggest will help me so much!
Server side
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
var connection = #"data source=comandai.database.windows.net;initial catalog=HojeTaPago;persist security info=True;user id=Comandai;password=Ck#21112009;MultipleActiveResultSets=True;";
services.AddDbContext<ComandaiContext>(options => options.UseSqlServer(connection));
services.AddSignalR(options => options.KeepAliveInterval = TimeSpan.FromSeconds(5));
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "HojeTaPago API", Version = "v1" });
c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "basic",
In = ParameterLocation.Header,
Description = "Basic Authorization header using the Bearer scheme."
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "basic"
}
},
new string[] {}
}
});
});
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.AllowCredentials();
}));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "HojeTaPago API V1");
c.RoutePrefix = string.Empty;
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<AuthenticationMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<NovoPedidoHub>("/novopedidohub");
endpoints.MapControllers();
});
}
}
Where im using the signalr
await _novoPedidoContext.Clients.All.SendAsync("NovoPedido", ListaComandaItem);
Client side - Blazor
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// 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.AddRazorPages();
services.AddServerSideBlazor();
services.AddBlazoredLocalStorage();
services.AddBootstrapCss();
services.AddTransient<HubConnectionBuilder>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
Where i call..
protected override async Task OnInitializedAsync()
{
DataService dataService = new DataService();
PedidosParaAceitar = new List<Comanda>(await dataService.BuscarComandasAbertas());
connection = _hubConnectionBuilder.WithUrl(dataService.servidor + "novopedidohub",
opt =>
{
opt.Transports = HttpTransportType.WebSockets;
opt.SkipNegotiation = true;
}).Build();
connection.On<List<ComandaItem>>("NovoPedido", async lista =>
{
var idEstabelecimento = await localStorage.GetItemAsync<int>("IdEstabelecimento");
if (lista.FirstOrDefault().Comanda.IdEstabelecimento == idEstabelecimento)
{
if (PedidosParaAceitar == null)
PedidosParaAceitar = new List<Comanda>();
if (PedidosParaAceitar.Count(x => x.Id == lista.FirstOrDefault().IdComanda) > 0)
foreach (var comandaitem in lista)
{
PedidosParaAceitar.FirstOrDefault(x => x.Id == lista.FirstOrDefault().IdComanda).ComandaItem.Add(comandaitem);
}
else
PedidosParaAceitar.Add(await dataService.BuscarComandaAberta(lista.FirstOrDefault().IdComanda));
StateHasChanged();
}
});
await connection.StartAsync();
}
You didn't specify in the tags if this was client-side (WASM) or server-side Blazor.
Looking at the question I noticed this line in ConfigureServices:
services.AddServerSideBlazor();
So you're attempting to use SignalR, a client-side communication library from the Server. In server-side Blazor all the C# code runs on the server. In this respect, SignalR is redundant since it's already being used by Blazor for communicating between the clients and the server.
By a very fortunate coincidence, I actually wrote an app to test this out recently. I created a server-side Blazor app, and wrote this service:
public class TalkService
{
public TalkService()
{
history = new List<string>();
}
public Action<string> OnChange { get; set; }
// inform all users of new message
public Task SendAsync(string message)
{
// add to history
history.Add(message);
// ensure only last 10 shown
if (history.Count > 10) history.RemoveAt(0);
OnChange.Invoke(message);
return Task.FromResult(0);
}
private readonly List<string> history;
public IReadOnlyList<string> GetHistory() => history;
}
I then registered it as a Singleton on the server (all clients use the same service)
in Startup.cs in the ConfigureServices() method:
services.AddSingleton<TalkService>();
Then rewrote Index.razor as follows:
#page "/"
#inject TalkService service
<p>Talk App started</p>
<p>Send a message: <input type="text"#bind="#message" />
<button class="btn btn-sm btn-primary" #onclick="Send" >Send</button>
</p>
#foreach (var m in messages)
{
<p>#m</p>
}
#code {
string message;
async Task Send()
{
if(!string.IsNullOrWhiteSpace(message))
await service.SendAsync(message);
message = string.Empty;
}
List<string> messages;
protected override void OnParametersSet()
{
// load history
messages = service.GetHistory().ToList();
// register for updates
service.OnChange += ChangeHandler;
}
protected void ChangeHandler(string message)
{
messages.Add(message);
InvokeAsync(StateHasChanged);
}
}
The talk service is a basic "chat" example of course. It's a Singleton so all pages/clients that reference it use the same instance. The service has a simple event OnChange that clients (like the Index page) can listen to for changes elsewhere.
SignalR isn't needed for this app since it's already "there" for server-side.
Demo App
The demo app also has a background service that generates time messages as well. I've pushed this to GitHub to help as a guide:
https://github.com/conficient/BlazorServerWithSignalR

Cant access authorized routes using jwt after getting token

I can login and get a jwt
{
"resource": "resource-server",
"token_type": "Bearer",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4NzJlMTY5OS0xNGQwLTRhYmItYTU4Mi1kZDZmODkzNWU1NGEiLCJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsInRva2VuX3VzYWdlIjoiYWNjZXNzX3Rva2VuIiwianRpIjoiNzdlMDhiMGMtMGRmMy00NDJjLTgxOTItMDk4YWNiYjdiZWQyIiwiYXVkIjoicmVzb3VyY2Utc2VydmVyIiwibmJmIjoxNDk1NTY0ODI5LCJleHAiOjE0OTU1Njg0MjksImlhdCI6MTQ5NTU2NDgyOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1NTY2NC8ifQ.00X9de2jtetmWoj4BNaskvtPryElEsenpoVgisCxEoA",
"expires_in": 3600
}
But when I try and get a protected route, I get a 401.
This is my startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Authorization.Data;
using Authorization.Models;
using Authorization.Services;
using OpenIddict.Core;
using OpenIddict.Models;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace Authorization
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets<Startup>();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
options.UseJsonWebTokens();
options.AllowPasswordFlow();
options.AddSigningKey(signingKey);
options.DisableHttpsRequirement();
});
services.AddMvc();
// Add application services.
//services.AddTransient<IEmailSender, AuthMessageSender>();
//services.AddTransient<ISmsSender, AuthMessageSender>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseCors(builder =>
{
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowCredentials();
builder.AllowAnyOrigin(); // For anyone access.
//corsBuilder.WithOrigins("http://localhost:56573"); // for a specific url.
});
app.UseStaticFiles();
//app.UseOAuthValidation();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = "http://localhost:55664",
Audience = "resource-server",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
});
app.UseOpenIddict();
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvcWithDefaultRoute();
}
}
}
I've tried setting authority and audience to all sorts of different things. I've tried removing them completely and I can not get a 200 back on a route with
[Authorize]
added on.
When I try and do this in Postman, I get the error
Bearer error="invalid_token", error_description="The signature is
invalid"
It's a GET with 1 header, Authorization = bearer {token here}
I'm just at a loss. Been at this for 3 days now. I feel like it's almost right, I'm just missing some key thing. Missing a header or something.
Also note, I have an angular 2 app at localhost:4200. But my understanding is this should still work within postman?
This is the server output when I hit the authorized route
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.2832518Z","tags":{"ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.operation.id":"0HL5214V879CK","ai.application.ver":"1.0.0.0"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request starting HTTP/1.1 GET http://localhost:55664/api/values","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","Protocol":"HTTP/1.1","AspNetCoreEnvironment":"Development","DeveloperMode":"true","Scheme":"http","Host":"localhost:55664","Path":"/api/values","Method":"GET"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:55664/api/values
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.2902711Z","tags":{"ai.operation.name":"GET /api/values","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"The request path /api/values does not match a supported file type","severityLevel":"Verbose","properties":{"CategoryName":"Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware","{OriginalFormat}":"The request path {Path} does not match a supported file type","AspNetCoreEnvironment":"Development","DeveloperMode":"true","Path":"/api/values"}}}}
Exception thrown: 'Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException' in System.IdentityModel.Tokens.Jwt.dll
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3128307Z","tags":{"ai.operation.name":"GET /api/values","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4NzJlMTY5OS0xNGQwLTRhYmItYTU4Mi1kZDZmODkzNWU1NGEiLCJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsInRva2VuX3VzYWdlIjoiYWNjZXNzX3Rva2VuIiwianRpIjoiNzdlMDhiMGMtMGRmMy00NDJjLTgxOTItMDk4YWNiYjdiZWQyIiwiYXVkIjoicmVzb3VyY2Utc2VydmVyIiwibmJmIjoxNDk1NTY0ODI5LCJleHAiOjE0OTU1Njg0MjksImlhdCI6MTQ5NTU2NDgyOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1NTY2NC8ifQ.00X9de2jtetmWoj4BNaskvtPryElEsenpoVgisCxEoA.","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware","{OriginalFormat}":"Failed to validate the token {Token}.","Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4NzJlMTY5OS0xNGQwLTRhYmItYTU4Mi1kZDZmODkzNWU1NGEiLCJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsInRva2VuX3VzYWdlIjoiYWNjZXNzX3Rva2VuIiwianRpIjoiNzdlMDhiMGMtMGRmMy00NDJjLTgxOTItMDk4YWNiYjdiZWQyIiwiYXVkIjoicmVzb3VyY2Utc2VydmVyIiwibmJmIjoxNDk1NTY0ODI5LCJleHAiOjE0OTU1Njg0MjksImlhdCI6MTQ5NTU2NDgyOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1NTY2NC8ifQ.00X9de2jtetmWoj4BNaskvtPryElEsenpoVgisCxEoA","AspNetCoreEnvironment":"Development","DeveloperMode":"true","Exception":"Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)\r\n at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)\r\n at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()"}}}}
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Failed to validate the token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4NzJlMTY5OS0xNGQwLTRhYmItYTU4Mi1kZDZmODkzNWU1NGEiLCJuYW1lIjoidGVzdEB0ZXN0LmNvbSIsInRva2VuX3VzYWdlIjoiYWNjZXNzX3Rva2VuIiwianRpIjoiNzdlMDhiMGMtMGRmMy00NDJjLTgxOTItMDk4YWNiYjdiZWQyIiwiYXVkIjoicmVzb3VyY2Utc2VydmVyIiwibmJmIjoxNDk1NTY0ODI5LCJleHAiOjE0OTU1Njg0MjksImlhdCI6MTQ5NTU2NDgyOSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1NTY2NC8ifQ.00X9de2jtetmWoj4BNaskvtPryElEsenpoVgisCxEoA.
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__1.MoveNext()
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3173431Z","tags":{"ai.operation.name":"GET /api/values","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Bearer was not authenticated. Failure message: IDX10500: Signature validation failed. No security keys were provided to validate the signature.","severityLevel":"Information","properties":{"FailureMessage":"IDX10500: Signature validation failed. No security keys were provided to validate the signature.","CategoryName":"Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware","AuthenticationScheme":"Bearer","{OriginalFormat}":"{AuthenticationScheme} was not authenticated. Failure message: {FailureMessage}","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Bearer was not authenticated. Failure message: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3238602Z","tags":{"ai.operation.name":"GET /api/values","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request successfully matched the route with name '(null)' and template 'api/Values'.","severityLevel":"Verbose","properties":{"CategoryName":"Microsoft.AspNetCore.Routing.Tree.TreeRouter","{OriginalFormat}":"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.","AspNetCoreEnvironment":"Development","DeveloperMode":"true","RouteTemplate":"api/Values"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3253638Z","tags":{"ai.operation.name":"GET /api/values","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Action 'AspToken.Controllers.ValuesController.Post (Authorization)' with id 'd8fd53b2-6692-4c31-b8ce-0d7965e7e5b1' did not match the constraint 'Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint'","severityLevel":"Verbose","properties":{"CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ActionSelector","{OriginalFormat}":"Action '{ActionName}' with id '{ActionId}' did not match the constraint '{ActionConstraint}'","AspNetCoreEnvironment":"Development","ActionConstraint":"Microsoft.AspNetCore.Mvc.Internal.HttpMethodActionConstraint","ActionId":"d8fd53b2-6692-4c31-b8ce-0d7965e7e5b1","DeveloperMode":"true","ActionName":"AspToken.Controllers.ValuesController.Post (Authorization)"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3273695Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Executing action AspToken.Controllers.ValuesController.Get (Authorization)","severityLevel":"Verbose","properties":{"CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker","{OriginalFormat}":"Executing action {ActionName}","AspNetCoreEnvironment":"Development","DeveloperMode":"true","ActionName":"AspToken.Controllers.ValuesController.Get (Authorization)"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3293745Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Authorization failed for user: (null).","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Authorization.DefaultAuthorizationService","{OriginalFormat}":"Authorization failed for user: {UserName}.","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3323827Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker","{OriginalFormat}":"Authorization failed for the request at filter '{AuthorizationFilter}'.","AuthorizationFilter":"Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3348898Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Executing ChallengeResult with authentication schemes ().","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Mvc.ChallengeResult","{OriginalFormat}":"Executing ChallengeResult with authentication schemes ({Schemes}).","AspNetCoreEnvironment":"Development","DeveloperMode":"true","Schemes":"System.String[]"}}}}
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3378977Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"AuthenticationScheme: Bearer was challenged.","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware","AuthenticationScheme":"Bearer","{OriginalFormat}":"AuthenticationScheme: {AuthenticationScheme} was challenged.","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: AuthenticationScheme: Bearer was challenged.
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3409055Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Executed action AspToken.Controllers.ValuesController.Get (Authorization) in 11.408ms","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker","ElapsedMilliseconds":"11.408","{OriginalFormat}":"Executed action {ActionName} in {ElapsedMilliseconds}ms","AspNetCoreEnvironment":"Development","DeveloperMode":"true","ActionName":"AspToken.Controllers.ValuesController.Get (Authorization)"}}}}
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action AspToken.Controllers.ValuesController.Get (Authorization) in 11.408ms
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3439137Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Connection id \"0HL5214V6KQ2K\" completed keep alive response.","severityLevel":"Verbose","properties":{"CategoryName":"Microsoft.AspNetCore.Server.Kestrel","{OriginalFormat}":"Connection id \"{ConnectionId}\" completed keep alive response.","AspNetCoreEnvironment":"Development","DeveloperMode":"true","ConnectionId":"0HL5214V6KQ2K"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Message","time":"2017-05-23T18:49:45.3454177Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"MessageData","baseData":{"ver":2,"message":"Request finished in 61.7295ms 401","severityLevel":"Information","properties":{"CategoryName":"Microsoft.AspNetCore.Hosting.Internal.WebHost","ElapsedMilliseconds":"61.7295","StatusCode":"401","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 61.7295ms 401
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Request","time":"2017-05-23T18:49:45.3318446Z","tags":{"ai.operation.name":"GET Values/Get","ai.internal.nodeName":"GA-BRU-D9V2XBH2","ai.internal.sdkVersion":"aspnet5c:2.0.0","ai.cloud.roleInstance":"GA-BRU-D9V2XBH2","ai.operation.id":"0HL5214V879CL","ai.application.ver":"1.0.0.0","ai.location.ip":"::1"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"5sE5TCp7osw=","name":"GET Values/Get","duration":"00:00:00.0180848","success":false,"responseCode":"401","url":"http://localhost:55664/api/values","properties":{"httpMethod":"GET","AspNetCoreEnvironment":"Development","DeveloperMode":"true"}}}}
Sigh. I'm an idiot.
If you sign your key
options.AddSigningKey(signingKey);
Then you need to tell the
app.UseJwtBearerAuthentication
how to validate that key.
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
}
My complete startup file for anyone that also gets stuck on this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Authorization.Data;
using Authorization.Models;
using Authorization.Services;
using OpenIddict.Core;
using OpenIddict.Models;
using AspNet.Security.OpenIdConnect.Primitives;
using Microsoft.IdentityModel.Tokens;
using System.Text;
namespace Authorization
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets<Startup>();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
options.UseOpenIddict();
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
options.UseJsonWebTokens();
options.AllowPasswordFlow();
options.AddSigningKey(signingKey);
options.DisableHttpsRequirement();
});
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseCors(builder =>
{
builder.AllowAnyHeader();
builder.AllowAnyMethod();
builder.AllowCredentials();
builder.AllowAnyOrigin(); // For anyone access.
//corsBuilder.WithOrigins("http://localhost:56573"); // for a specific url.
});
app.UseStaticFiles();
var audience = "resource-server";
var authority = "http://localhost:55664";
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
//app.UseOAuthValidation();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = authority,
Audience = audience,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
// makes no difference seemingly being ignored
//ValidIssuer = Configuration.Get<AppOptions>().Jwt.Authority,
ValidateAudience = true,
ValidAudience = audience,
ValidateLifetime = true,
}
});
app.UseOpenIddict();
// Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvcWithDefaultRoute();
}
}
}

OpenIddict - hosting auth server and web api resource in same project

I want to implement an OpenIdConnect/Oauth2 server using OpenIddict in order to secure a .NET core API app. Most examples I have seen implement these as separate projects.
The client app is a SPA and we are using implicit flow.
I have based my solution on the code shown in the OpenIddict samples here:
https://github.com/openiddict/openiddict-samples
For the project I am working on it would ideally have the Auth server and API to use the same port and be in the same project. ( One of the customer's requirements is that they don't want another server to configure since they own the API resource and it will be on the same server)
I have configured OpenIddict and combined it with our API project. Almost everything works correctly - the API endpoints are protected with the [Authorize] attribute and prevent access to protected API end points. However, when the API resource is protected, instead of returning a 401 Unauthorized HTTP status code, the returned result is the HTML Login page of the Auth server itself.
Here is the relevant setup code in my Startup.cs file:
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseIdentity();
app.UseCors("AllowAll");
//app.UseCors(builder =>
//{
// builder.AllowAnyOrigin();//)WithOrigins("http://localhost:9000");
// builder.WithMethods("GET","POST", "PUT", "DELETE", "OPTIONS");
// builder.WithHeaders("Authorization");
//});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseIdentity();
});
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), branch =>
{
branch.UseOAuthValidation();
});
app.UseOpenIddict();
#region Adding resource config here (api)
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseOAuthIntrospection(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.Authority = "http://localhost:5000";
options.Audiences.Add("resource-server-1");
options.ClientId = "resource-server-1";
options.ClientSecret = "846B62D0-DEF9-4215-A99D-86E6B8DAB342";
});
//app.UseCors(builder => {
// builder.WithOrigins("http://localhost:9000");
// builder.WithMethods("GET");
// builder.WithHeaders("Authorization");
//});
#endregion
app.UseMvcWithDefaultRoute();
// Seed the database with the sample applications.
// Note: in a real world application, this step should be part of a setup script.
InitializeAsync(app.ApplicationServices, CancellationToken.None).GetAwaiter().GetResult();
}
private async Task InitializeAsync(IServiceProvider services, CancellationToken cancellationToken)
{
// Create a new service scope to ensure the database context is correctly disposed when this methods returns.
using (var scope = services.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
//await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<OpenIddictApplicationManager<OpenIddictApplication>>();
if (await manager.FindByClientIdAsync("MySPA", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "MySPA",
DisplayName = "MySPA",
LogoutRedirectUri = "http://localhost:9000/signout-oidc",
RedirectUri = "http://localhost:9000/signin-oidc"
};
await manager.CreateAsync(application, cancellationToken);
}
if (await manager.FindByClientIdAsync("resource-server-1", cancellationToken) == null)
{
var application = new OpenIddictApplication
{
ClientId = "resource-server-1"
};
await manager.CreateAsync(application, "846B62D0-DEF9-4215-A99D-86E6B8DAB342", cancellationToken);
}
}
}
Not sure how to implement these both side by side in the same project. As mentioned it all "works" except the API is returning the HTML login page and not a desired HTTP status
app.UseIdentity(); is present twice in your pipeline, which defeats the whole purpose of using branch.UseIdentity() in a app.UseWhen() branching builder (i.e making sure the cookies middleware registered by Identity are not invoked for your API endpoints).
Remove the first occurrence and it should work.
You set the AutomaticChallenge to true. According to the documentation
this flag indicates that the middleware should redirect the browser to the LoginPath or AccessDeniedPath when authorization fails.
So by setting this to false it will not redirect to the login.

No authentication handler is configured to handle the scheme: Automatic

I updated ASP.NET 5 framework beta-8 packages with RC ones on previously working application. After I got it running next error occured in the startup process:
InvalidOperationException: No authentication handler is configured to handle the scheme: Automatic
Microsoft.AspNet.Http.Authentication.Internal.DefaultAuthenticationManager.d__12.MoveNext()
var defaultPolicy =
new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
services.AddMvc(setup =>
{
setup.Filters.Add(new AuthorizeFilter(defaultPolicy)); // Error occurs here
});
If anyone had similar problem, I'd appreciate your idea or solution on what might have gone wrong. Explanation of this exception is also appreciated.
Startup.cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
using SuperUserMVC.Configuration;
using SuperUserMVC.Extensions;
using SuperUserMVC.GlobalModules;
using System;
namespace SuperUserMVC
{
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettingsBase>(Configuration.GetSection("AppSettingsBase"));
services.Configure<ConnectionString>(Configuration.GetSection("ConnectionString"));
services.AddSqlServerCache(cache =>
{
cache.ConnectionString = Configuration.Get<string>("ASPState:ConnectionString");
cache.SchemaName = Configuration.Get<string>("ASPState:Schema");
cache.TableName = Configuration.Get<string>("ASPState:Table");
});
services.AddSession(session =>
{
session.IdleTimeout = TimeSpan.FromMinutes(120);
});
// Only allow authenticated users.
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
// Add MVC services to the services container.
services.AddMvc(setup =>
{
setup.Filters.Add(new AuthorizeFilter(defaultPolicy));
});
var builder = new ContainerBuilder();
builder.RegisterModule(new AutofacModule());
builder.Populate(services);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
public void Configure(IApplicationBuilder app, IHttpContextAccessor httpContextAccessor)
{
// Catch unhandled exception in pipeline.
bool isProductionEnvironment = Configuration.Get<bool>("environmentVariables:isProductionEnvironment");
app.UseCustomUnhandledException(isProductionEnvironment, Configuration.Get<string>("defaultErrorPagePath"));
// Log requests.
app.UseVisitLogger(isProductionEnvironment);
// Session must be used before MVC routes.
app.UseSession();
// Configure the HTTP request pipeline.
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = "Cookies";
options.LoginPath = new PathString("/Account/Login/");
options.AccessDeniedPath = new PathString("/Account/Forbidden/");
options.CookieName = "MyCookie";
options.AutomaticAuthenticate = true;
options.SessionStore = new MemoryCacheSessionStore();
});
AutoMapperInitializer.Init();
app.UseStaticFiles();
// Route configuration.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "AreaDefault",
template: "{area:exists=Demo}/{controller=Home}/{action=Index}/{id?}"
);
routes.MapRoute(
name: "Default",
template: "{controller=Home}/{action=Index}/{id?}"
);
});
}
}
}
Hopefully this will help somebody else because I just spent a lot of time dealing with this error even though I had set AutomaticChallenge = true.
Turns out you will get the same error if you put app.UseIdentity(); after app.UseMvc(routes => ...). Now that I know the answer it's obvious. It's because all this middleware happens in the order you add it.
This causes the "No authentication handler is configured" error:
public void Configure(...)
{
app.UseMvc(routes => { routes.MapRoute(...) }; );
app.UseIdentity();
}
This does not cause the error:
public void Configure(...)
{
app.UseIdentity();
app.UseMvc(routes => { routes.MapRoute(...); });
}
Try setting options.AutomaticChallenge = true; in your cookies options and it should work.
options.AutomaticAuthentication was split into options.AutomaticAuthenticate and options.AutomaticChallenge. If the last one is left to false, an exception is thrown because no authentication middleware handles the challenge applied by the authorization filter.
Put this on Configure method.
app.UseIdentity();
The problem was solved for me by making sure the cookies scheme was consistently named wherever it was referenced. e.g.:
public void ConfigureServices(IServiceCollection services)
{
// if using IdentityServer4
var builder = services.AddIdentityServer(options =>
{
options.AuthenticationOptions.AuthenticationScheme = Constants.DefaultCookieAuthenticationScheme;
...
})
services.AddIdentity<MyUser, IdentityRole>(options =>
{
options.Cookies.ApplicationCookie.AuthenticationScheme = Constants.DefaultCookieAuthenticationScheme;
...
}
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = Constants.DefaultCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = true
});
}
And when interacting with the authentication middleware. e.g.:
await HttpContext.Authentication.SignInAsync(Constants.DefaultCookieAuthenticationScheme, cp);
If you use app.UseIdentity(); and some other login middleware such as UseFacebookAuthentication make sure app.UseFacebookAuthentication() is AFTER app.UseIdentity();.
another possibility is missing the following setting in Configure
app.UseCookieAuthentication();
While it's tempting to place much of our configuration settings within the startup.cs file, it seems that the preferred way of doing things is to set your app.UseCookieAuthentication() - sans options - within the startup.cs file, and then place all of the 'options' and other details within a separate file.
Sort of like what we were doing with how the Global.asax file had pointers to the App_Start folder files in Asp.Net vBefore.
I suffered similar pain while trying to configure EF/Sql in the startup.cs, and by moving all 'options' outside of startup.cs things worked much better.
ALSO: take note of the Fredy Wenger comment to your question that points out the 'renaming' of many of the namespaces from v -8beta to v -RC1-final.

Resources