/// <summary> /// Add JWT authentication scheme and config /// </summary> /// <param name="services">IServiceCollection</param> /// <param name="appSettings">AppSettings</param> /// <returns>IServiceCollection</returns> public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, AppSettings appSettings) { IdentityModelEventSource.ShowPII = true; //Add this line services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // "Bearer" options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // "Bearer" }).AddJwtBearer(options => { //options.Authority = "https://localhost:6001"; // Base-address of your identityserver //options.RequireHttpsMetadata = true; string authServerBaseUrl = appSettings.Host?.AuthServer ?? "https://localhost:6001"; bool isRequireHttpsMetadata = (!string.IsNullOrEmpty(authServerBaseUrl) && authServerBaseUrl.StartsWith("https")) ? true : false; options.Authority = string.IsNullOrEmpty(authServerBaseUrl) ? "https://localhost:6001" : authServerBaseUrl; options.RequireHttpsMetadata = isRequireHttpsMetadata; options.MetadataAddress = $"{authServerBaseUrl}/.well-known/openid-configuration"; // Optional options.Audience = appSettings?.AuthOptions?.Audience ?? ApiResources.MyBackendApi2; // API Resource name options.TokenValidationParameters.ClockSkew = TimeSpan.Zero; // The JWT security token handler allows for 5 min clock skew in default options.BackchannelHttpHandler = AuthMetadataUtils.GetHttpHandler(); //options.MetadataAddress = $"{authServerBaseUrl}/.well-known/openid-configuration"; options.Events = new JwtBearerEvents() { OnAuthenticationFailed = (e) => { // Some callback here ... return(Task.CompletedTask); } }; }); return(services); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddTransient <IHttpContextAccessor, HttpContextAccessor>(); services.AddControllers() .AddNewtonsoftJson() .SetCompatibilityVersion(CompatibilityVersion.Version_3_0); #region Enable Authentication IdentityModelEventSource.ShowPII = true; //Add this line services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { //options.Authority = "https://localhost:6001"; // Base-address of your identityserver //options.RequireHttpsMetadata = true; string authServerBaseUrl = this.Configuration["Host:AuthServer"]; bool isRequireHttpsMetadata = (!string.IsNullOrEmpty(authServerBaseUrl) && authServerBaseUrl.StartsWith("https")) ? true : false; options.Authority = string.IsNullOrEmpty(authServerBaseUrl) ? "https://localhost:6001" : authServerBaseUrl; options.RequireHttpsMetadata = isRequireHttpsMetadata; options.Audience = "MyBackendApi2"; // API Resource name options.TokenValidationParameters.ClockSkew = TimeSpan.Zero; // The JWT security token handler allows for 5 min clock skew in default options.BackchannelHttpHandler = AuthMetadataUtils.GetHttpHandler(); options.Events = new JwtBearerEvents() { OnAuthenticationFailed = (e) => { // Some callback here ... return(Task.CompletedTask); } }; }); #endregion #region Enable policy-based authorization // Required: Role "admin" services.AddAuthorization(options => options.AddPolicy("AdminPolicy", policy => policy.RequireRole("admin"))); // Required: Role "user" services.AddAuthorization(options => options.AddPolicy("UserPolicy", policy => policy.RequireRole("user"))); // Required: Role "sit" services.AddAuthorization(options => options.AddPolicy("SitPolicy", policy => policy.RequireRole("sit"))); // Required: Role "admin" OR "user" services.AddAuthorization(options => options.AddPolicy("AdminOrUserPolicy", policy => policy.RequireRole("admin", "user"))); // Required: Department "Sales" services.AddAuthorization(options => options.AddPolicy("SalesDepartmentPolicy", policy => policy.RequireClaim(CustomClaimTypes.Department, "Sales"))); // Required: Department "CRM" services.AddAuthorization(options => options.AddPolicy("CrmDepartmentPolicy", policy => policy.RequireClaim(CustomClaimTypes.Department, "CRM"))); // Required: Department "Sales" AND Role "admin" services.AddAuthorization(options => options.AddPolicy("SalesDepartmentAndAdminPolicy", policy => policy.RequireClaim(CustomClaimTypes.Department, "Sales").RequireRole("admin"))); // Required: Department "Sales" AND Role "admin" or "user" services.AddAuthorization(options => options.AddPolicy("SalesDepartmentAndAdminOrUserPolicy", policy => policy.RequireClaim(CustomClaimTypes.Department, "Sales").RequireRole("admin", "user"))); // Required: Department "Sales" OR Role "admin" services.AddAuthorization(options => options.AddPolicy("SalesDepartmentOrAdminPolicy", policy => policy.RequireAssertion( context => context.User.Claims.Any( x => (x.Type.Equals(CustomClaimTypes.Department) && x.Value.Equals("Sales")) || (x.Type.Equals(ClaimTypes.Role) && x.Value.Equals("admin")))))); #endregion #region Enable custom Authorization Handlers (The registration order matters!) services.AddSingleton <IAuthorizationHandler, EmailDomainAuthHandler>(); services.AddSingleton <IAuthorizationHandler, UserNameAuthHandler>(); services.AddAuthorization(options => { var emailDomainRequirement = new EmailDomainRequirement("fake.com"); var userNameRequirement = new UserNameRequirement("jblin"); // options.InvokeHandlersAfterFailure = false; // Default: true options.AddPolicy("DoaminAndUsernamePolicy", policy => policy.AddRequirements(emailDomainRequirement, userNameRequirement)); }); #endregion #region Inject AppSetting configuration services.Configure <AppSettings>(this.Configuration); #endregion #region HttpClient Factory services.AddHttpClient("AuthHttpClient", config => { config.Timeout = TimeSpan.FromMinutes(5); // config.BaseAddress = new Uri("https://localhost:6001/"); config.DefaultRequestHeaders.Add("Accept", "application/json"); }) .ConfigurePrimaryHttpMessageHandler(h => { var handler = new HttpClientHandler(); // Enable sending request to server with untrusted SSL cert handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; return(handler); }) .SetHandlerLifetime(TimeSpan.FromMinutes(5)); // HttpMessageHandler lifetime = 2 min // services.AddHttpClient<IIdentityClient, IdentityClient>().SetHandlerLifetime(TimeSpan.FromMinutes(2)) // HttpMessageHandler default lifetime = 2 min // .ConfigurePrimaryHttpMessageHandler(h => // { // var handler = new HttpClientHandler(); // if (this.env.IsDevelopment()) // { // //Allow untrusted Https connection // handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; // } // return handler; // }); #endregion #region Identity Client services.AddSingleton <IIdentityClient, IdentityClient>(); #endregion #region Inject Cache service services.AddCacheServices(); #endregion }
/// <summary> /// Add custom authentication /// </summary> /// <param name="services">IServiceCollection</param> /// <param name="appSettings">AppSettings</param> /// <returns>Self</returns> public static IServiceCollection AddOpenIdAuthentication(this IServiceCollection services, AppSettings appSettings) { IdentityModelEventSource.ShowPII = true; JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; // "Cookies" options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; // "Cookies" options.DefaultChallengeScheme = "oidc"; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme) .AddOpenIdConnect("oidc", options => { const string CODE_VERIFIER_KEY = "code_verifier"; const string CODE_CHALLENGE_KEY = "code_challenge"; const string CODE_CHALLENGE_METHOD_KEY = "code_challenge_method"; // Get config values from AppSetting file string oidcServerBaseUrl = appSettings?.Host.AuthServer; bool isRequireHttpsMetadata = !string.IsNullOrEmpty(oidcServerBaseUrl) && oidcServerBaseUrl.StartsWith("https"); options.Authority = string.IsNullOrEmpty(oidcServerBaseUrl) ? "https://localhost:6001" : oidcServerBaseUrl; options.RequireHttpsMetadata = isRequireHttpsMetadata; options.MetadataAddress = $"{oidcServerBaseUrl}/.well-known/openid-configuration"; options.BackchannelHttpHandler = AuthMetadataUtils.GetHttpHandler(); options.ClientId = "PkceCodeBackend"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.ResponseMode = "form_post"; options.CallbackPath = "/signin-oidc"; options.SignedOutCallbackPath = "/signout-callback-oidc"; options.SaveTokens = true; options.Scope.Add(appSettings?.AuthOptions?.Audience); options.Scope.Add("offline_access"); // Get refresh token options.Events.OnRedirectToIdentityProvider = context => { // only modify requests to the authorization endpoint if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication) { // generate code_verifier var codeVerifier = CryptoRandom.CreateUniqueId(32); // store codeVerifier for later use context.Properties.Items.Remove(CODE_VERIFIER_KEY); context.Properties.Items.Add(CODE_VERIFIER_KEY, codeVerifier); // create code_challenge string codeChallenge; using (var sha256 = SHA256.Create()) { var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); codeChallenge = Base64Url.Encode(challengeBytes); } // add code_challenge and code_challenge_method to request context.ProtocolMessage.Parameters.Remove(CODE_CHALLENGE_KEY); context.ProtocolMessage.Parameters.Remove(CODE_CHALLENGE_METHOD_KEY); context.ProtocolMessage.Parameters.Add(CODE_CHALLENGE_KEY, codeChallenge); context.ProtocolMessage.Parameters.Add(CODE_CHALLENGE_METHOD_KEY, "S256"); } return(Task.CompletedTask); }; options.Events.OnAuthorizationCodeReceived = context => { // only when authorization code is being swapped for tokens if (context.TokenEndpointRequest?.GrantType == OpenIdConnectGrantTypes.AuthorizationCode) { // get stored code_verifier if (context.Properties.Items.TryGetValue(CODE_VERIFIER_KEY, out var codeVerifier)) { // add code_verifier to token request context.TokenEndpointRequest.Parameters.Add(CODE_VERIFIER_KEY, codeVerifier); } } return(Task.CompletedTask); }; }); return(services); }