WSFederation ADFS login loop .Net Core 2.1 - redirect

While logging into ADFS(on Premise), I am being redirected after I enter in my credentials and eventually it erros out with the error Exception details: Microsoft.IdentityServer.Web.InvalidRequestException: MSIS7042: The same client browser session has made '6' requests in the last '7' seconds. Contact your administrator for details in Event Viewer. I followed couple of stack overflow suggestion(link) but couldn't resolve the issue. I am running on https and made sure the certificate are right.
Here is my code
namespace TestApp
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
private static string HtmlEncode(string content) =>
string.IsNullOrEmpty(content) ? string.Empty : HtmlEncoder.Default.Encode(content);
// 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 =>
{
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.Secure = CookieSecurePolicy.SameAsRequest;
});
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.Cookie.Name = ".AspNet.SharedCookie";
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.None;
})
.AddWsFederation(options =>
{
options.MetadataAddress =
$"https://adfsdomain/FederationMetadata/2007-06/FederationMetadata.xml";
options.Wtrealm = "urn:apptest";
options.Wreply = "https://appdomain/apptest";
options.CallbackPath = "/apptest";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://adfsdomain/adfs/services/trust/"
};
options.SaveTokens = true;
options.RequireHttpsMetadata = false;
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvc();//.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// 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.UseHsts();
}
app.UseCors(policy => policy.SetIsOriginAllowed(origin => origin == "https://adfsdomain"));
app.UseHttpsRedirection();
app.UseStaticFiles();
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme);
}
else
{
await next();
}
});
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
I am using .Net Core 2.1 and ADFS 3.0. If I take ASP.NET MVC app and publish it with the same adfs setting my code works like charm, which tells me that the configuration on ADFS has been configured correctly.

Not sure if this can help you guys but I got my ADFS to working with the following:
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddWsFederation(options =>
{
options.Wtrealm = this._Configuration["wsfed:realm"];
options.MetadataAddress = string.Format("{0}/federationmetadata/2007-06/federationmetadata.xml", this._Configuration["wsfed:metadata"]);
})
.AddCookie();

You need app.UseAuthorization() before the app.Use(async ...). That's the middleware that takes the headers from the redirect and populates the context.User.

Related

Unable to create an object of type 'ApplicationDbContex' Blazor ASP.NET Core 6.0

I'm creating a web application in Blazor ASP.NET Core web hosted with ASP.NET Core 6 and when I'm migrating the database I'm getting this error:
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
I need to get a solution - this is my code:
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Prueba")));
builder.Services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["JwtIssuer"],
ValidAudience = builder.Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JwtSecurityKey"]))
};
});
builder.Services.AddAuthorization(config =>
{
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
builder.Services.AddSignalR();
builder.Services.AddMvc().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
builder.Services.AddControllersWithViews();
//builder.Services.AddRazorPages();
builder.Services.AddResponseCompression(option =>
{
option.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat
(new[] { "application/octet-stream"
});
});
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedData.Initialize(services);
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebAssemblyDebugging();
}
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.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<UsuariosHub>("/UsuariosHub");
});
app.Run();
ApplicationDbContext.cs
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using ProyectoBP.Shared.Models;
namespace ProyectoBP.Server.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
public DbSet<Movie> Movies { get; set; }
}
SeedData.cs
using Microsoft.EntityFrameworkCore;
using ProyectoBP.Server.Data;
namespace ProyectoBP.Shared.Models
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new ApplicationDbContext(
serviceProvider.GetRequiredService<
DbContextOptions<ApplicationDbContext>>()))
{
context.SaveChanges();
if (context == null || context.Movies == null)
{
throw new ArgumentNullException("Null ApplicationDbContext");
}
// Look for any movies.
if (context.Movies.Any())
{
return; // DB has been seeded
}
context.SaveChanges();
}
}
}
}
appsettings.json
{
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data Source=ISMAEL-PC;Initial Catalog=BP;Integrated Security=False;uid=JoseD;password=Laugama2021.",
"Prueba": "Data Source=ISMAEL-PC;Initial Catalog=BP;Integrated Security=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
I don't have a StartUp Class because apparently in ASP.NET Core 6 it's not needed
Add parameterless constructor on your DbContext (ApplicationDbContext.cs)
public ApplicationDbContext ()
{
}
Make sure you set the default startup project to the WebApplication
or add -s {Satrtup project name here} at end of the update command
Add Microsoft.EntityFrameworkCore.Design package to the WebApplication project
I solved my problem by checking the include prerelease option since I'm using ASP.NET Core 6 in a prerelease version but I was trying to migrate with the current stable version. So I hope this can help you.
You should replace
serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>()
to
serviceProvider.GetRequiredService<ApplicationDbContext>()
in SeedData class :
using Microsoft.EntityFrameworkCore;
using ProyectoBP.Server.Data;
namespace ProyectoBP.Shared.Models
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = serviceProvider.GetRequiredService<ApplicationDbContext>())
{
context.SaveChanges();
if (context == null || context.Movies == null)
{
throw new ArgumentNullException("Null ApplicationDbContext");
}
// Look for any movies.
if (context.Movies.Any())
{
return; // DB has been seeded
}
context.SaveChanges();
}
}
}
}
Temporary re-register all services from AddSingleton and AddScoped to AddTransient. It Worked for me.

Authorization in .Net Framework 4.8 returns Unauthorized using OpenIdentity4

I'm trying to use Authorization in .Net Framework 4.8, but making a get request returns Unauthorized using OpenIdentity4
I have to use Framework and not Core!
This is my Startup.cs:
public void Configuration(IAppBuilder app)
{
var authority = "https://localhost:5001";
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
authority + "/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var discoveryDocument = Task.Run(() => configurationManager.GetConfigurationAsync()).GetAwaiter().GetResult();
System.Console.WriteLine(discoveryDocument.AuthorizationEndpoint);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateAudience = false,
}
}) ;
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "RestAPI",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
app.UseWebApi(config);
}
}
}
OpenIdentity4 is running on https://localhost:5001
It's Startup:
public class Startup
{
public IWebHostEnvironment Environment { get; }
public Startup(IWebHostEnvironment environment)
{
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
services.AddControllers();
services.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy => {
policy.RequireAuthenticatedUser();
policy.RequireClaim("RestAPI", "APIRest");
});
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
//app.UseStaticFiles();
//app.UseRouting();
app.UseIdentityServer();
app.UseRouting();
// uncomment, if you want to add MVC
app.UseAuthorization();
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapDefaultControllerRoute();
//});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization("ApiScope");
});
}
}
}
It's probably a stupid mistake, but I can't figure it out.

ASP.NET MVC client connect ids4 SSo [Authorize] 302 redirect always

Client code
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
IdentityModelEventSource.ShowPII = true;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Authorization/AccessDenied";
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://jpprojectsso.inthink.top:5000"; //
options.RequireHttpsMetadata = false;
options.ClientId = "TestClient";
options.ClientSecret = "Client Secrets";
options.SaveTokens = true;
options.ResponseType = "code id_token";
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Clear();
options.Scope.Add(OidcConstants.StandardScopes.OpenId);
options.Scope.Add(OidcConstants.StandardScopes.Profile);
});
}
The controller looks like this:
[Authorize]
public IActionResult Privacy()
{
var user = User.Identity.Name;
return View();
}
ids4 connection client success. But the client always 302 redirect to sso and sso successfully redirects to client... in loop.
My sso use Jp project ids4. How can I resolve this problem?
Thanks,
I found the bug...
in void Configure function.
app.UseAuthorization() should be executed after app.UseAuthentication()
Then it works.

How to Query Database From Startup.CS

I am doing user authentication in my startup.cs. I need to query my database using the OpenIDConnect claims info. This is what I have done but don't know how to get the connection to work. I tried injecting the db query constructor at the top of the startup.cs like this and then calling the query as follows:
public class Startup
{
protected IAdoSqlService _adoSqlService;
public Startup(IConfiguration configuration, IAdoSqlService adoSqlService)
{
Configuration = configuration;
_adoSqlService = adoSqlService;
}
public void ConfigureServices(IServiceCollection services)
{
// do ConfigureServices stuff
options.Events = new OpenIdConnectEvents()
{
OnTokenValidated = async ctx =>
{
// This is the ClaimsIdentity created by OpenID Connect, you can add claims to it directly
ClaimsIdentity claimsIdentity = ctx.Principal.Identities.FirstOrDefault();
string userntid = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "preferred_username").Value;
//How do I call the database to run the following query
int isUser = _adoSqlService.isUser(userntid);
if (isUser > 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));
}
else
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "not authorized"));
}
}
}
//More stuff
}
}
When I run the above, it errors in program.cs before even running with the following error
System.InvalidOperationException: 'Unable to resolve service for type 'XXXX.Services.IAdoSqlService' while attempting to activate 'XXXX.Startup'.'
So how do I make the call _adoSqlService.isUser(userntid); to the database?
I am NOT using EF.
Solution
I figured this out by doing the following:
I moved most of my services to the top of the ConfigureServices section (based on something that #qudus said) before I performed my authentication.
I removed the database injection code from the top of the startup.cs.
Lastly I changed the OnTokenValidated to use the following:
ctx.HttpContext.RequestServices.GetRequiredService();
Here is the code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
internal static IConfiguration Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var connectionSection = Configuration.GetSection("ConnectionStrings");
services.Configure<ConnectionStrings>(connectionSection);
services.AddScoped<IAdoSqlService, AdoSqlService>();
services.AddControllersWithViews();
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSession();
// Load the Federation configuration section from app settings
var federationConfig = Configuration.GetSection("Federation");
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(2);//default is 14days
options.SlidingExpiration = true;// default
options.AccessDeniedPath = "/Error/AuthenticateError";// set a custom error access denied error page. this would need to be created/handled in your app.
})
.AddOpenIdConnect(options =>
{
//Set Options here......
//optional customizations to the auth and failure events
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
{
return Task.CompletedTask;
},
OnRemoteFailure = context =>
{
// handle an error response from Federation and redirect the user to a custom error page instead
context.Response.Redirect("/Error/401");
context.HandleResponse();
return Task.CompletedTask;
},
OnTokenValidated = async ctx =>
{
// This is the ClaimsIdentity created by OpenID Connect, you can add claims to it directly
ClaimsIdentity claimsIdentity = ctx.Principal.Identities.FirstOrDefault();
string userntid = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "preferred_username").Value;
string username = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "name").Value;
int isUser = 0;
int isAdmin = 0;
try
{
var db = ctx.HttpContext.RequestServices.GetRequiredService<IAdoSqlService>();
isUser = db.isUser(userntid);
isAdmin = db.isAdmin(userntid);
}
catch (Exception ex)
{
string error = ex.Message;
}
AppHttpContext.Current.Session.SetString("IsUser", "false");
if (isUser > 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));
AppHttpContext.Current.Session.SetString("IsUser", "true");
}
AppHttpContext.Current.Session.SetString("IsUserAdmin", "false");
if (isAdmin > 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
AppHttpContext.Current.Session.SetString("IsUserAdmin", "true");
}
if (isUser == 0 && isAdmin == 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "not authorized"));
}
}
};
});
Solution
I figured this out by doing the following:
I moved most of my services to the top of the ConfigureServices section (based on something that #qudus said) before I performed my authentication.
I removed the database injection code from the top of the startup.cs.
Lastly I changed the OnTokenValidated to use the following:
ctx.HttpContext.RequestServices.GetRequiredService();
Here is the code:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
internal static IConfiguration Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var connectionSection = Configuration.GetSection("ConnectionStrings");
services.Configure<ConnectionStrings>(connectionSection);
services.AddScoped<IAdoSqlService, AdoSqlService>();
services.AddControllersWithViews();
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSession();
// Load the Federation configuration section from app settings
var federationConfig = Configuration.GetSection("Federation");
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(2);//default is 14days
options.SlidingExpiration = true;// default
options.AccessDeniedPath = "/Error/AuthenticateError";// set a custom error access denied error page. this would need to be created/handled in your app.
})
.AddOpenIdConnect(options =>
{
//Set Options here......
//optional customizations to the auth and failure events
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
{
return Task.CompletedTask;
},
OnRemoteFailure = context =>
{
// handle an error response from Federation and redirect the user to a custom error page instead
context.Response.Redirect("/Error/401");
context.HandleResponse();
return Task.CompletedTask;
},
OnTokenValidated = async ctx =>
{
// This is the ClaimsIdentity created by OpenID Connect, you can add claims to it directly
ClaimsIdentity claimsIdentity = ctx.Principal.Identities.FirstOrDefault();
string userntid = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "preferred_username").Value;
string username = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "name").Value;
int isUser = 0;
int isAdmin = 0;
try
{
var db = ctx.HttpContext.RequestServices.GetRequiredService<IAdoSqlService>();
isUser = db.isUser(userntid);
isAdmin = db.isAdmin(userntid);
}
catch (Exception ex)
{
string error = ex.Message;
}
AppHttpContext.Current.Session.SetString("IsUser", "false");
if (isUser > 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "user"));
AppHttpContext.Current.Session.SetString("IsUser", "true");
}
AppHttpContext.Current.Session.SetString("IsUserAdmin", "false");
if (isAdmin > 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
AppHttpContext.Current.Session.SetString("IsUserAdmin", "true");
}
if (isUser == 0 && isAdmin == 0)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, "not authorized"));
}
}
};
});

Identity Server 4 JWT "access_token" is not valid for Identity Server Authorized endpoints

I have an Identity Server & API apps with ResourceOwnerPasswordAndClientCredentials flow.
After getting "access_token" from RequestResourceOwnerPasswordAsync method I can use my [Authorized] endpoints in my API project, but can't access [Authorized] endpoints in my Identity Server app.
May be I'm missing something in AllowedScopes?
Identity Server's Starup.cs:
public class Startup
{
public Startup(IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
var section = Configuration.GetSection("Logging");
loggerFactory.AddConsole(section);
loggerFactory.AddDebug();
loggerFactory.AddProvider(new FileLoggerProvider());
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<IdentityDataContext>(optionsAction =>
optionsAction.UseSqlServer(Configuration.GetConnectionString("DevelopersConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDataContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddIdentityServer()
.AddDeveloperSigningCredential(filename: "tempkey.rsa")
.AddInMemoryClients(IdentityConfig.GetClients("http://localhost:7017"))
.AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
.AddInMemoryApiResources(IdentityConfig.GetApiResources())
.AddAspNetIdentity<ApplicationUser>();
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
RolesData.SeedRoles(app).Wait();
app.UseCors(x =>
x.WithOrigins("http://localhost:7017")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
);
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvc();
}
}
Web Api Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(IDataRepository<>), typeof(DataRepository<>));
services.AddDbContext<DataContext>(optionsAction =>
optionsAction.UseSqlServer(Configuration.GetConnectionString("DevelopersConnection")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(
JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:7777"; // Auth Server
options.RequireHttpsMetadata = false; // only for development
options.ApiName = "api"; // API Resource Id
options.SupportedTokens = SupportedTokens.Jwt;
});
services.AddCors();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(x =>
x.WithOrigins("http://localhost:7017")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
);
app.UseAuthentication();
app.UseMvc();
}
}
IdentityConfig.cs:
public class IdentityConfig
{
public static IEnumerable<Client> GetClients(string hostname) => new List<Client>
{
new Client
{
ClientId = "client",
ClientName = "application",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AccessTokenType = AccessTokenType.Jwt,
AllowAccessTokensViaBrowser = true,
ClientSecrets = {new Secret("secret".Sha256())},
RequireConsent = false,
RedirectUris = { $"{hostname}/callback.html" },
PostLogoutRedirectUris = { $"{hostname}/index.html" },
AllowedCorsOrigins = { hostname },
AlwaysIncludeUserClaimsInIdToken = true,
AllowOfflineAccess = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
JwtClaimTypes.Role,
"api"
}
},
};
public static IEnumerable<IdentityResource> GetIdentityResources() => new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Email(),
new IdentityResources.Profile(),
new IdentityResource("role", new []{ JwtClaimTypes.Role })
};
public static IEnumerable<ApiResource> GetApiResources() => new List<ApiResource>
{
new ApiResource("api")
{
UserClaims =
{
JwtClaimTypes.Email,
JwtClaimTypes.Role,
JwtClaimTypes.IdentityProvider,
IdentityServerConstants.StandardScopes.OpenId
}
}
};
}
Getting Jwt access_token:
var disco = await DiscoveryClient.GetAsync("http://localhost:7777");
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse =
await tokenClient.RequestResourceOwnerPasswordAsync("username", "password", "openid api");
var access_token = tokenResponse.AccessToken;