I am trying to setup IdentityServer4 for the first time, and am following the steps in the docs for adding a JS client. I must have something configured incorrectly, but I can't figure out what it is. The flow is as follows:
User hits "login" on Client A
Client A sends login request to IS4 (upon debugging, the "returnUrl" parameter seems correct)
using the IS4 extension method I am signing the user in (HttpContext.SignInAsync)
user is redirected to "connect/authorize/callback" which redirects them to the login method again and the circular reference continues until the browser stops it and throws error.
Relevant code:
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UsersContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("UsersRuntime")));
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer(o =>
{
})
.AddTestUsers(Identity.Users.Get())
.AddConfigurationStore(o =>
{
o.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseSqlServer(Configuration.GetConnectionString("UsersRuntime"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
InitializeDatabase(app);
app.UseRouting();
app.UseDefaultFiles();
app.UseStaticFiles();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseHttpStatusCodeExceptionMiddleware();
}
else
{
app.UseHttpStatusCodeExceptionMiddleware();
app.UseHsts();
}
app.UseCors(MyAllowSpecificOrigins);
if (env.IsProduction())
{
app.UseHttpsRedirection();
}
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Users API");
c.RoutePrefix = string.Empty;
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<UsersContext>().Database.Migrate();
var persistedGrantDbContext =
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>();
persistedGrantDbContext.Database.Migrate();
var configDb = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
configDb.Database.Migrate();
var testClient = new Client
{
ClientId = "TestClient",
RequireClientSecret = false,
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedIdentityTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
UpdateAccessTokenClaimsOnRefresh = true,
RefreshTokenExpiration = (int)TokenExpiration.Sliding,
AllowedGrantTypes = new List<string>
{
IdentityServerConstants.PersistedGrantTypes.AuthorizationCode
},
AllowedScopes = new List<string>
{
"Read",
"Write"
},
AllowedCorsOrigins = new List<string>
{
"https://localhost:5003"
},
RedirectUris = new List<string>{"https://localhost:5003/callback.html"}
};
configDb.Clients.Add(testClient.ToEntity());
configDb.SaveChanges();
var resource = new ApiResource
{
Name = "TestApi",
ShowInDiscoveryDocument = true,
AllowedAccessTokenSigningAlgorithms = new List<string>{SecurityAlgorithms.RsaSha256},
Scopes = new List<string>
{
"Read",
"Write"
}
};
configDb.ApiResources.Add(resource.ToEntity());
var readScope = new ApiScope("Read");
var writeScope = new ApiScope("Write");
configDb.ApiScopes.AddRange(new []{readScope.ToEntity(), writeScope.ToEntity()});
configDb.SaveChanges();
}
}
login controller
[Route("account/login")]
[Produces("application/json")]
[ApiController]
public class LoginControllerOidc: ControllerBase
{
[HttpGet]
public async Task<IActionResult> Get(string returnUrl)
{
await HttpContext.SignInAsync(new IdentityServerUser("Test")
{
DisplayName = "Test Display Name",
AdditionalClaims = new List<Claim>
{
new Claim("additionalClaim", "claimValue")
}
});
return Redirect(returnUrl);
}
}
config for oidc-client.js
var config = {
authority: "https://localhost:5001",
client_id: "TestClient",
redirect_uri: "https://localhost:5003/callback.html",
response_type: "code",
scope:"Read Write",
post_logout_redirect_uri : "https://localhost:5003/index.html"
};
Redirect Issue Screenshot
I'm at a loss for what it left to do. Following the docs I think I have everything setup correctly. Guides I am following can be found Here (adding javascript client) and Here (sign in)
The cookie is being set correctly (I think) as seen here
The problem that I was having was due to the subjectId in the controller not matching a subjectId in the TestUsers.
Related
I'm trying to implement authorization in asp.ne core webapi web application using jwt tokens.
but when I send a request with bearer authorization and the jwt token generated, the response is always 401 unauthorized
I'm using .Net 5.0 version
what I'm doing wrong ?
here is my startup.cs file
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<JwtSettings>(Configuration.GetSection("Jwt"));
var jwtSettings = Configuration.GetSection("Jwt").Get<JwtSettings>();
services.AddControllers();
var dataAssemblyName = typeof(CRMContext).Assembly.GetName().Name;
services.AddDbContext<CRMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"), x => x.MigrationsAssembly(dataAssemblyName)));
services.AddIdentity<User, Role>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1d);
options.Lockout.MaxFailedAccessAttempts = 5;
})
.AddEntityFrameworkStores<CRMContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtAuthentication:JwtIssuer"],
ValidAudience = Configuration["JwtAuthentication:JwtIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtAuthentication:JwtKey"])),
};
});
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IApplicationUserService, ApplicationUserService>();
services.AddMvc().AddControllersAsServices();
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Cloud 9", Version = "v1" });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT containing userid claim",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
});
var security =
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
},
UnresolvedReference = true
},
new List<string>()
}
};
options.AddSecurityRequirement(security);
});
services.AddAutoMapper(typeof(Startup));
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddAuth(jwtSettings);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
MyServiceProvider.ServiceProvider = app.ApplicationServices;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
// app.UseSwagger();
// app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ids.Crm.Api v1"));
app.UseAuthorization();
app.UseAuth();
app.UseCors("MyPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "test v1");
c.ConfigObject.AdditionalItems.Add("syntaxHighlight", false);
c.ConfigObject.AdditionalItems.Add("theme", "agate");
});
}
Your request pipeline is missing the authentication middleware. So you couldn't possibly authenticate or possibly authorize. Simply add the middleware before the authorization middleware in the Configure method
app.UseAuthentication();
app.UseAuthorization();
Update: It seems app.UseAuth(); is your authentication middleware. If it is, then place it above the Authorization middleware.
app.UseAuth();
app.UseAuthorization();
I found the solution.... I was declaring a key in the generation of the token which is different from the one I've declared in the appsetting.json
so when the TokenValidationParameters takes the wrong key it was preventing authorization
I have a solution named Messenger which contains 3 projects:
Messenger.IdentityServer
Messenger.Api
Messenger.BlazorWasmClient
This is my IdentityServer Startup.cs:
public class Startup
{
public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
Environment = environment;
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// configures IIS out-of-proc settings (see https://github.com/aspnet/AspNetCore/issues/14882)
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
// configures IIS in-proc settings
services.Configure<IISServerOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
services.AddDbContext<IdentityServerDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityServerUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityServerDbContext>()
.AddDefaultTokenProviders();
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients)
.AddAspNetIdentity<IdentityServerUser>();
// 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();
}
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
Config.cs:
public static class Config
{
public static IEnumerable<IdentityResource> Ids =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
// let's include the role claim in the profile
new ProfileWithRoleIdentityResource(),
new IdentityResources.Email()
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
// the api requires the role claim
new ApiResource("messenger.api", "Messenger API", new[] { JwtClaimTypes.Role })
};
// machine to machine client (from quickstart 1)
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "messenger.blazorwasmclient",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
AllowedCorsOrigins = { "https://localhost:5001" },
AllowedScopes = { "openid", "profile", "email", "messenger.api" },
RedirectUris = { "https://localhost:5001/authentication/login-callback" },
PostLogoutRedirectUris = { "https://localhost:5001/" },
Enabled = true
}
};
}
In the BlazorWasmClient, I added an AddOidcAuthentication method to the Program.cs like this:
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services
.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.UserOptions.RoleClaim = "role";
})
.AddAccountClaimsPrincipalFactory<ArrayClaimsPrincipalFactory<RemoteUserAccount>>();
await builder.Build().RunAsync();
}
}
appSettings.json:
{
"oidc": {
"Authority": "https://localhost:5000/",
"ClientId": "messenger.blazorwasmclient",
"DefaultScopes": [
"openid",
"profile",
"email",
"messenger.api"
],
"PostLogoutRedirectUri": "/",
"ResponseType": "code"
}
}
But I don't understand why I have this error message every time I start my solution:
You need to configure the redirect URI and the PostLogoutUri must be an abosolut URI, not relative. Uris must exactly match what you setup in the IndentityServer4 client :
appsettings.json
{
"oidc": {
"Authority": "https://localhost:5000/",
"ClientId": "messenger.blazorwasmclient",
"DefaultScopes": [
"openid",
"profile",
"email",
"messenger.api"
],
"PostLogoutRedirectUri": "https://localhost:5001/",
"RedirectUri": "https://localhost:5001/authentication/login-callback",
"ResponseType": "code"
}
}
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
};
}
);
I follow the tutorial link below.
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login
I am trying to understand how it works and I want to use role-based authentication using this token. so I made another policy in the Startup.cs file as below.
And I tried to use it like [Authorize(Policy = "admin")] the controller but every time I try I get unauthenticated using postman.
What am I missing? how to make roles-based authentication based on the tutorial?
Configure
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200", "http://localhost:44318")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.AddAuthorization(options =>
options.AddPolicy("admin", policy => policy.RequireRole("admin"))
);
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<CDSPORTALContext>().AddDefaultTokenProviders().AddRoles<IdentityRole>();
//.AddRoles<IdentityRole>()
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IRegionRepository, RegionRepository>();
services.AddScoped<IRegionService, RegionService>();
services.AddScoped<IEmailHelper, EmailHelper>();
}
// 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("/Home/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.UseCors("CorsPolicy");
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
app.UseRouting();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Client";
spa.UseAngularCliServer(npmScript: "start");
});
}
}
Auth Controller
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var id = identity.Claims.Single(c => c.Type == "id").Value;
var user = await _userManager.FindByIdAsync(id);
IList<string> role = await _userManager.GetRolesAsync(user);
var jwt = await Tokens.GenerateJwt(identity, role[0], _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
I tried with all method and none of them working
[Authorize(Policy = "ApiUser")]
[HttpGet("getPolicy")]
public string GetPolicy()
{
return "policyWorking";
}
[Authorize(Roles = "admin")]
[HttpGet("getAdmin")]
public string GetAdmin()
{
return "adminWorking";
}
[Authorize ]
[HttpGet("getAuthorize")]
public string GetAuthorize()
{
return "normal authorize Working";
}
Decoded Token
"jti": "840d507d-b2d0-454b-bd1f-007890d3e669",
"iat": 1587699300,
"rol": "api_access",
"id": "1ac370e2-f5e9-4404-b017-8a3c087e2196",
"nbf": 1587699299,
"exp": 1587706499
I forgot to add this to appsetting.
"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:4200/"
}
I want to create a centralize asp.net core API project with Entity-FrameWork core for membership management such as login, register and etc. and Separately I want to create another asp.net core project and use centralized project for membership like google.com.
After a lot of search, i understood that should use IdentityServer4. I read this document and got sample from Github, but it was not clear and I was confused.
Who can explain clearly and step by step?
thank you
IdentityServer4 has simple MeadleWare for use in Asp.Net Core
public void ConfigureServices(IServiceCollection services){
...
var cert = new X509Certificate2("/Cert/cert.pfx", "123456");
services.AddIdentityServer()
.AddInMemoryApiResources(Config.GetApisResources())
.AddSigningCredential(cert)
.AddInMemoryClients(Config.GetClients())
.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
...
}
public void Configure(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env){
...
app.UseIdentityServer();
...
}
and create a config file:
public class Config
{
public static IEnumerable<ApiResource> GetApisResources()
{
return new[]
{
// simple API with a single scope (in this case the scope name is the same as the api name)
new ApiResource("api1"),
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "spa",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
//IdentityTokenLifetime=10,
AllowOfflineAccess=true,
RefreshTokenExpiration = TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime = 999999,
RefreshTokenUsage=TokenUsage.ReUse,
AccessTokenType=AccessTokenType.Jwt,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes =
{
"api1",
IdentityServerConstants.StandardScopes.OfflineAccess
},
AccessTokenLifetime=36000
}
};
}
}
then in resourceServer use Bellow MW:
public void ConfigureServices(IServiceCollection services){
...
services.AddAuthentication(o =>
{
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.Authority = "http://localhost:5000";
o.Audience = "self";
o.RequireHttpsMetadata = false;
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
RequireExpirationTime = true,
ClockSkew = TimeSpan.Zero
};
o.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = c =>
{
c.NoResult();
c.Response.StatusCode = 401;
c.Response.ContentType = "text/plain";
return c.Response.WriteAsync(c.Exception.ToString());
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnChallenge = context =>
{
return Task.CompletedTask;
}
};
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseAuthentication();
...
}