Identity Server 4 request validation failed because of invalid scopes - authentication

first time seeking help here so I hope i will explain my problem on proper way.
I have a .NET Core 3.0 MVC application up and running and I was trying to add a Identity Server 4 authentication to it. I'm struggling for hours with the following problem.
Sorry, there was an error : invalid_scope
Invalid scope
In the log I got this message
[ERR] Invalid scope: profile
[ERR] Request validation failed
Line bellow that In the info log when checking for scopes I found this JSON objects, and can't see whats problem here.
"scope":"openid profile",
"RequestedScopes":"openid profile"
Here is the config.cs file of the Identity Server project
public static IEnumerable<IdentityResource> Ids =>
new IdentityResource[]
{
new IdentityResources.OpenId()
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{ };
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "mvc",
ClientName = "Festival MVC",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = { "https://127.0.0.1:44330/signin-oidc" },
PostLogoutRedirectUris = { "https://127.0.0.1:44330/signout-callback-oidc" },
AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId }
}
};
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// uncomment, if you want to add an MVC-based UI
services.AddControllersWithViews();
var builder = services.AddIdentityServer()
.AddTestUsers(TestUsers.Users)
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients);
// not recommended for production - you need to store your key material somewhere secure
builder.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
// uncomment, if you want to add MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
On the .NET Core MVC project I configured the follow in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
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"; // Auth Server
options.RequireHttpsMetadata = false; // only for development
options.ClientId = "mvc"; // client setup in Auth Server
options.SaveTokens = true;
});
services.AddControllersWithViews();
services.AddDbContext<FestivalContext>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
If you need any other info or code snippet feel free to post.
Thanks in advance and greetings,
Anes !

Related

ASP.NET Core 3.1 - routing Issue

Since upgrading to 3.1, I seemed to have lost my routing. My endpoints are now just returning a 404 and I have ran out of ideas of what the issue could be. Can anyone see if I'm doing something stupid in my below setup code? All that really changed in my setup code was that I stopped using
app.UseMvc
And now use the endpoint instead
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Code below
[ApiController]
[Authorize]
[Route("api/guild")]
public class GuildController : Controller
{
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]CreateGuildRequest request)
{
return Ok();
}
}
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)
{
var domain = Configuration["Auth0:Domain"];
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = Configuration["Auth0:Audience"];
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyMethod()
.AllowAnyHeader()
.SetIsOriginAllowed(origin => true)
.AllowCredentials()
.Build());
});
services.AddControllers();
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "clientapp/dist"; });
}
// 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.UseSpaStaticFiles();
app.UseRouting();
app.UseSpa(spa =>
{
spa.Options.SourcePath = "clientApp";
if (env.IsDevelopment())
{
spa.UseVueCli(npmScript: "serve", port: 8080);
}
});
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Middleware order matters. Place your UseSpa() after UseEndpoints()
// this should be the last two middlewares, in this order
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "clientApp";
if (env.IsDevelopment())
{
spa.UseVueCli(npmScript: "serve", port: 8080);
}
});

ASP.NET Core returns InternalServerError while using Identity server

I trying to add identity server for my web API as its identity server4 documentation. when I was trying to call API from my console application it's every time returns InternalServerError.
Here is My Identity server Config.cs
public static class Config
{
// register api
public static IEnumerable<ApiScope> ApiScopes => new List<ApiScope>
{
// in here add your api name
new ApiScope("api1", "My API")
};
// register client which is going to access api. eg: front-end application, mobile apps etc. can add multiple client.
public static IEnumerable<Client> Clients => new List<Client>
{
new Client
{
// which is going to access
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
}
and here the identity server startup file configuration service and configure functions
public void ConfigureServices(IServiceCollection services)
{
// uncomment, if you want to add an MVC-based UI
services.AddControllersWithViews();
var builder = services.AddIdentityServer()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
builder.AddDeveloperSigningCredential();
builder.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
// uncomment, if you want to add MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
and here is my API startup file's congurationService and configure functions
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030/";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
}
);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
here is my API controller
[Route("identity")]
public class IdentityController : ControllerBase
{
[HttpGet]
[Authorize]
public IActionResult Get() => Ok(new JsonResult(from c in User.Claims select new { c.Type, c.Value }));
}
and here is my console application client request a api
static async System.Threading.Tasks.Task Main(string[] args)
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:14030");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("https://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
what are the mistakes should I have to fix. Im really appreciates your valuable answers and efforts.
Thank
I got the code working, I would do the following:
use HTTPS here, not HTTP:
var disco = await
client.GetDiscoveryDocumentAsync("http://localhost:14030");
Remove the duplicate lines of in IdentityServer startup class:
builder.AddDeveloperSigningCredential();
I would add in your API startup.cs
services.AddAuthorization();
Remove the trailing / at the end of the URL here:
options.Authority = "https://localhost:14030/";
To get more debugging output from your API, you can add the following two trace lines to your appsettings.Development.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Trace",
"Microsoft.AspNetCore.Authorization": "Trace"
}
}
}
If you want to validate the Audience (and using IdentityServer4 v4.00) you can add:
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new[] {"https://localhost:14030/resources"},
ValidateAudience = true
};
}
);

Asp.net core 3.1 backend does not get the Identity from cookie

I have vue.js frontend and asp.net core 3.1 backend. Backend uses SignInManager and Identity. I am trying to use cookie authentication. Api requests work from Postman(!!) roles are applied, everything, but do not from vue app (httpContext.User.Identity.IsAuthenticated is false). Indentity is empty. Cookie is present in the HttpContext
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:Default"]));
services.AddCors();
services.AddControllers()
services.AddIdentity<AppUser, IdentityRole>(
opts =>
{
opts.SignIn.RequireConfirmedEmail = true;
}
)
.AddSignInManager<SignInManager<AppUser>>()
.AddEntityFrameworkStores<MyDBContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.SameSite = SameSiteMode.None;
options.SlidingExpiration = true;
});
//some DI
...
//
}
bit more Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<AppUser> userManager)
{
app.UseRouting();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I was trying to proxy requests to api in vue app. Did not help
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'https://localhost:44376',
ws: true,
changeOrigin: true
}
}
}
}
What can be wrong?
Maybe you are missing AddRoles<IdentityRoles>()?
services.AddIdentity<AppUser, IdentityRole>(
opts =>
{
opts.SignIn.RequireConfirmedEmail = true;
}
)
.AddRoles<IdentityRoles>() .// <== this line
.AddSignInManager<SignInManager<AppUser>>()
.AddEntityFrameworkStores<MyDBContext>()
.AddDefaultTokenProviders();

Identity Server 4 - Audience validation failed error

I encounter this error in my API IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'
I've been trying to solve this for 2 days already and can't figure out yet about how to solve it.
I have the following applications:
Client App (Angular 7)
Identity Server
API
I was able to login successfully to Identity Server in my Client app and was able to get the token but when I used the token to connect to the API method it throws this error IDX10214: Audience validation failed. Audiences: 'https://localhost:44337/resources'. Did not match: validationParameters.ValidAudience: 'joborderingapi' or validationParameters.ValidAudiences: 'null'.
I followed the answer from Identity Server 4 with EF Identity DB Issue and checked the three tables (ApiResources, ApiScopes, ClientScopes), the values are correct, joborderingapiis enabled in ApiResources, in ApiScopes it is linked to ApiResource and in ClientScopes it is linked to the Client
Here is my API Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var apiIdpAuthority = Configuration["AppSettings:IdpAuthority"];
var apiName = Configuration["AppSettings:ApiName"];
var apiSecret = Configuration["AppSettings:ApiSecret"];
var requireHttps = Convert.ToBoolean(Configuration["AppSettings:RequireHttps"]);
var httpsPort = Configuration["AppSettings:HttpsPort"];
var applicationUrl = Configuration["AppSettings:ApplicationUrl"];
services.Configure<ClientAppSettings>(Configuration.GetSection("ClientAppSettings"));
services.AddDbContext<JobOrderingDataContext>(options => options.UseSqlServer(Configuration.GetConnectionString("JobOrderingDB")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
IdentityModelEventSource.ShowPII = true;
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = apiIdpAuthority;
options.RequireHttpsMetadata = requireHttps;
options.ApiName = apiName;
options.ApiSecret = apiSecret;
});
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins(apiIdpAuthority, applicationUrl)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials();
});
});
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(60);
});
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
options.HttpsPort = Convert.ToInt32(httpsPort);
});
}
// 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.UseHsts();
}
app.UseCors("default");
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseHttpsRedirection();
app.UseCookiePolicy();
var locale = Configuration["SiteLocale"];
var supportedCultures = new List<CultureInfo> { new CultureInfo("en-US") };
if (supportedCultures.Where(x => x.Name == locale).Count() == 0)
{
supportedCultures.Add(new CultureInfo(locale));
}
RequestLocalizationOptions localizationOptions = new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
DefaultRequestCulture = new RequestCulture(locale)
};
app.UseRequestLocalization(localizationOptions);
app.UseAuthentication();
app.UseMvc();
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Identity Server Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//var microsoftClientId = Configuration["MicrosoftClientId"];
// var microsoftClientSecret = Configuration["MircosoftClientSecret"];
var azureADClientId = Configuration["AzureADClientId"];
var azureADClientSecret = Configuration["AzureADClientSecret"];
var azureADEndPoint = Configuration["AzureADEndPoint"];
var issuerUri = Configuration["IssuerUri"];
string connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<IdentityServerDataContext>(options => options.UseSqlServer(connectionString));
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication()
// .AddCookie()
.AddOpenIdConnect("AAD", "Azure AD", options =>
{
options.Authority = string.Format("https://login.microsoftonline.com/{0}", azureADEndPoint);
options.ClientId = azureADClientId;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
});
IdentityModelEventSource.ShowPII = true;
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddIdentityServer() .AddSigninCredentialFromConfig(Configuration.GetSection("SigninKeyCredentials"), _logger)
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
})
.AddAspNetIdentity<ApplicationUser>()
.AddProfileService<IdentityWithAdditionalClaimsProfileService>();
}
// 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)
{
// this will do the initial DB population
// InitializeDatabase(app);
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Note: I only encounter this issue when I use the local login. It is working fine if I use the Azure AD login, I was able to connect to the API using the authorization token from the client app

How to access Session in OpenIdConnect TokenValidated even handler

I have an ASP.NET Core 2.1 MVC application in which I have configured OpenIdConnect provider for authentication. The Startup class looks like below:
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 => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(1200);
options.Cookie.HttpOnly = true;
});
services.AddHttpContextAccessor();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IClientDataHandler, ClientDataHandler>();
services.AddAuthentication(options => .AddOpenIdConnect("oidc", options =>
{
...
options.Events.OnTokenValidated = async x =>
{
var serviceScopeFactory = services.BuildServiceProvider().GetRequiredService<IServiceScopeFactory>();
...
await x.HttpContext.Session.LoadAsync(new CancellationToken()); --does NOT work
x.HttpContext.Session.Set("clients", Utils.ObjectToByteArray(someData)); --does NOT work
};}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Though this lets me use HttpContext.Session (by injecting IHttpContextAccessor) in any controller or service, I can't use the Session in TokenValidated event handler. Any help?
Thanks in advance.
You should not be building the service provider in your event handler. This is not executed during startup. It's executed on each request by your authentication handler long after the service provider has been built.
options.Events.OnTokenValidated = async context =>
{
// don't do this...service provider is already built
var serviceScopeFactory = services.BuildServiceProvider().GetRequiredService<IServiceScopeFactory>();
};
Instead, you can access the built service provider from the HttpContext.RequestServices.
options.Events.OnTokenValidated = async context =>
{
var serviceScopeFactory = context.HttpContext.RequestServices.GetRequiredService<IServiceScopeFactory>();
};