private CookieAuthenticationOptions SetupAppCookie( // CookieAuthenticationOptions options, CookieAuthenticationEvents cookieEvents, cloudscribe.Core.Identity.SiteAuthCookieValidator siteValidator, string scheme, cloudscribe.Core.Models.SiteSettings tenant ) { var options = new CookieAuthenticationOptions(); options.AuthenticationScheme = $"{scheme}-{tenant.SiteFolderName}"; options.CookieName = $"{scheme}-{tenant.SiteFolderName}"; options.CookiePath = "/" + tenant.SiteFolderName; var tenantPathBase = string.IsNullOrEmpty(tenant.SiteFolderName) ? PathString.Empty : new PathString("/" + tenant.SiteFolderName); options.LoginPath = tenantPathBase + "/account/login"; options.LogoutPath = tenantPathBase + "/account/logoff"; cookieEvents.OnValidatePrincipal = siteValidator.ValidatePrincipal; options.Events = cookieEvents; options.AutomaticAuthenticate = true; options.AutomaticChallenge = true; return(options); }
/// <summary> /// Create an instance of the options initialized with the default values /// </summary> public CookieAuthenticationOptions() { ExpireTimeSpan = TimeSpan.FromDays(14); ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter; SlidingExpiration = true; Events = new CookieAuthenticationEvents(); }
public static CookieAuthenticationEvents GetModifiedEvents(CookieAuthenticationEvents standardEvents) { var options = new CookieAuthenticationEvents() { OnRedirectToLogin = (ctx) => { if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200) { ctx.Response.StatusCode = 401; return(Task.CompletedTask); } return(standardEvents.OnRedirectToLogin(ctx)); }, OnRedirectToAccessDenied = (ctx) => { if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200) { ctx.Response.StatusCode = 403; return(Task.CompletedTask); } return(standardEvents.OnRedirectToAccessDenied(ctx)); }, OnCheckSlidingExpiration = (ctx) => standardEvents.OnCheckSlidingExpiration(ctx), OnRedirectToLogout = (ctx) => standardEvents.OnRedirectToLogout(ctx), OnRedirectToReturnUrl = (ctx) => standardEvents.OnRedirectToLogout(ctx), OnSignedIn = (ctx) => standardEvents.OnSignedIn(ctx), OnSigningIn = (ctx) => standardEvents.OnSigningIn(ctx), OnSigningOut = (ctx) => standardEvents.OnSigningOut(ctx), OnValidatePrincipal = (ctx) => standardEvents.OnValidatePrincipal(ctx) }; return(options); }
/// <summary> /// Register cookie authentication related actors /// </summary> /// <remarks> /// Remember to configure 'UseCookieAuthentication()' and 'UseAuthorization()' /// after 'UseRouting()', but before 'UseEndpoints()' /// </remarks> /// <param name="services"></param> /// <param name="configRoot"></param> /// <param name="mode"></param> public static AuthenticationBuilder AddCookieAuthentication( this IServiceCollection services, IConfigurationRoot configRoot, SameSiteMode mode = SameSiteMode.Lax ) { var config = configRoot .GetSection(WebConfiguration.AUTHENTICATION) .GetSection(WebConfiguration.AUTHENTICATION_COOKIE) .Get <CookieAuthenticationConfiguration>(); if (config == null) { var configPath = $"{WebConfiguration.AUTHENTICATION}:{WebConfiguration.AUTHENTICATION_COOKIE}"; throw new Exception($"Unable to find configuration for '{configPath}' <CookieAuthenticationConfiguration>"); } var cookie = new CookieBuilder { Name = config.CookieName, SameSite = mode }; var cookieEvents = new CookieAuthenticationEvents { OnRedirectToAccessDenied = context => { context.Response.StatusCode = 403; // Don't redirect, set to forbidden return(Task.CompletedTask); }, OnRedirectToLogin = context => { context.Response.StatusCode = 401; // Don't redirect, set to unauthorized return(Task.CompletedTask); } }; services.AddSingleton((sp) => config); // Register actors return(services .AddAuthentication((options) => { options.DefaultAuthenticateScheme = options.DefaultChallengeScheme = options.DefaultScheme = options.DefaultSignInScheme = config.AuthenticationScheme; }) .AddCookie(config.AuthenticationScheme, options => { options.AccessDeniedPath = new PathString(config.AccessDeniedPath); options.Cookie = cookie; options.Events = cookieEvents; options.LoginPath = new PathString(config.LoginPath); })); }
public SiteIdentityOptionsResolver( IHttpContextAccessor httpContextAccessor, // CookieAuthenticationEvents cookieEvents, SiteAuthCookieValidator siteValidator ) { this.httpContextAccessor = httpContextAccessor; this.cookieEvents = new CookieAuthenticationEvents(); this.siteValidator = siteValidator; }
public SiteIdentityOptionsResolver( IHttpContextAccessor httpContextAccessor, IOptions <MultiTenantOptions> multiTenantOptionsAccessor, SiteAuthCookieValidator siteValidator ) { this.httpContextAccessor = httpContextAccessor; this.cookieEvents = new CookieAuthenticationEvents(); this.siteValidator = siteValidator; multiTenantOptions = multiTenantOptionsAccessor.Value; }
public SessionCookieOptions( CookieAuthenticationEvents cookieAuthenticationEvents, string authenticationType, string loginPath, string logoutPath) { CookieAuthenticationEvents = cookieAuthenticationEvents; AuthenticationType = authenticationType; LoginPath = loginPath; LogoutPath = logoutPath; }
/// <summary> /// Create an instance of the options initialized with the default values /// </summary> public CookieAuthenticationOptions() { AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme; AutomaticAuthenticate = true; ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter; ExpireTimeSpan = TimeSpan.FromDays(14); SlidingExpiration = true; CookieHttpOnly = true; CookieSecure = CookieSecurePolicy.SameAsRequest; SystemClock = new SystemClock(); Events = new CookieAuthenticationEvents(); }
private CookieAuthenticationOptions SetupAppCookie( CookieAuthenticationEvents cookieEvents, cloudscribe.Core.Identity.SiteAuthCookieValidator siteValidator, string scheme, bool useRelatedSitesMode, cloudscribe.Core.Models.SiteSettings tenant ) { var options = new CookieAuthenticationOptions(); if (useRelatedSitesMode) { options.AuthenticationScheme = scheme; options.CookieName = scheme; options.CookiePath = "/"; } else { options.AuthenticationScheme = $"{scheme}-{tenant.SiteFolderName}"; options.CookieName = $"{scheme}-{tenant.SiteFolderName}"; options.CookiePath = "/" + tenant.SiteFolderName; cookieEvents.OnValidatePrincipal = siteValidator.ValidatePrincipal; } var tenantPathBase = string.IsNullOrEmpty(tenant.SiteFolderName) ? PathString.Empty : new PathString("/" + tenant.SiteFolderName); options.LoginPath = tenantPathBase + "/account/login"; options.LogoutPath = tenantPathBase + "/account/logoff"; options.AccessDeniedPath = tenantPathBase + "/account/accessdenied"; options.Events = cookieEvents; options.AutomaticAuthenticate = true; options.AutomaticChallenge = false; options.CookieSecure = environment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always; return(options); }
public static CookieAuthenticationOptions SetupAppCookie( this IApplicationBuilder app, SiteAuthCookieValidator siteValidator, string scheme, bool useRelatedSitesMode, SiteContext tenant, CookieSecurePolicy cookieSecure = CookieSecurePolicy.SameAsRequest ) { var cookieEvents = new CookieAuthenticationEvents(); var options = new CookieAuthenticationOptions(); if (useRelatedSitesMode) { options.AuthenticationScheme = scheme; options.CookieName = scheme; options.CookiePath = "/"; } else { //options.AuthenticationScheme = $"{scheme}-{tenant.SiteFolderName}"; options.AuthenticationScheme = scheme; options.CookieName = $"{scheme}-{tenant.SiteFolderName}"; options.CookiePath = "/" + tenant.SiteFolderName; cookieEvents.OnValidatePrincipal = siteValidator.ValidatePrincipal; } var tenantPathBase = string.IsNullOrEmpty(tenant.SiteFolderName) ? PathString.Empty : new PathString("/" + tenant.SiteFolderName); options.LoginPath = tenantPathBase + "/account/login"; options.LogoutPath = tenantPathBase + "/account/logoff"; options.AccessDeniedPath = tenantPathBase + "/account/accessdenied"; options.Events = cookieEvents; options.AutomaticAuthenticate = true; options.AutomaticChallenge = false; options.CookieSecure = cookieSecure; return(options); }
public static void DisableRedirectForPath( this CookieAuthenticationEvents events, Expression <Func <CookieAuthenticationEvents, Func <RedirectContext <CookieAuthenticationOptions>, Task> > > expr, string path, int statuscode) { string propertyName = ((MemberExpression)expr.Body).Member.Name; var oldHandler = expr.Compile().Invoke(events); Func <RedirectContext <CookieAuthenticationOptions>, Task> newHandler = context => { if (context.Request.Path.StartsWithSegments(path)) { context.Response.StatusCode = statuscode; } else { oldHandler(context); } return(Task.CompletedTask); }; typeof(CookieAuthenticationEvents).GetProperty(propertyName).SetValue(events, newHandler); }
/// <summary> /// Register cookie authentication related actors /// </summary> /// <param name="services"></param> /// <param name="config"></param> public static IServiceCollection AddCookieAuthentication(this IServiceCollection services, IConfigurationRoot config) { var cookieConfig = config .GetSection(WebConfiguration.AUTHENTICATION) .GetSection(WebConfiguration.AUTHENTICATION_COOKIE) .Get <CookieAuthenticationConfiguration>(); // Configuration var cookie = new CookieBuilder { Name = cookieConfig.CookieName, SameSite = SameSiteMode.Lax }; var cookieEvents = new CookieAuthenticationEvents { OnRedirectToAccessDenied = context => { context.Response.StatusCode = 403; // Don't redirect, set to forbidden return(Task.CompletedTask); }, OnRedirectToLogin = context => { context.Response.StatusCode = 401; // Don't redirect, set to unauthorized return(Task.CompletedTask); } }; // Register actors services .AddSingleton((sp) => cookieConfig) .AddAuthentication(cookieConfig.AuthenticationScheme) .AddCookie(cookieConfig.AuthenticationScheme, options => { options.AccessDeniedPath = new PathString(cookieConfig.AccessDeniedPath); options.Cookie = cookie; options.Events = cookieEvents; options.LoginPath = new PathString(cookieConfig.LoginPath); }); return(services); }
private CookieAuthenticationEvents get_sams_authentication_events() { //https://stackoverflow.com/questions/52175302/handling-expired-refresh-tokens-in-asp-net-core var sams_endpoint_authorization = Configuration["sams:endpoint_authorization"]; var sams_endpoint_token = Configuration["sams:endpoint_token"]; var sams_endpoint_user_info = Configuration["sams:endpoint_user_info"]; var sams_endpoint_token_validation = Configuration["sams:token_validation"]; var sams_endpoint_user_info_sys = Configuration["sams:user_info_sys"]; var sams_client_id = Configuration["sams:client_id"]; var sams_client_secret = Configuration["sams:client_secret"]; var sams_callback_url = Configuration["sams:callback_url"]; var result = new CookieAuthenticationEvents { OnValidatePrincipal = context => { //check to see if user is authenticated first if (context.Principal.Identity.IsAuthenticated) { var expires_at = context.Request.Cookies["expires_at"]; var expires_at_time = DateTimeOffset.Parse(expires_at); /* * var accessToken = context.Request.HttpContext.Session.GetString("access_token"); * var refreshToken = context.Request.HttpContext.Session.GetString("refresh_token"); * var exp = context.Request.HttpContext.Session.GetInt32("expires_in"); */ /* * var tokens = context.Properties.GetTokens(); * var refreshToken = tokens.FirstOrDefault(t => t.Name == "refresh_token"); * var accessToken = tokens.FirstOrDefault(t => t.Name == "access_token"); * var exp = tokens.FirstOrDefault(t => t.Name == "expires_at"); * var expires = DateTime.Parse(exp.Value); */ //context.Request.Cookies.["sid"]. // var expires = DateTime.Parse(exp.ToString()); //check to see if the token has expired if (expires_at_time.DateTime < DateTime.Now) { try { var sid = context.Request.Cookies["sid"]; string request_string = Program.config_couchdb_url + $"/session/{sid}"; var curl = new cURL("GET", null, request_string, null, Program.config_timer_user_name, Program.config_timer_password); string session_json = curl.execute(); var session = Newtonsoft.Json.JsonConvert.DeserializeObject <mmria.common.model.couchdb.session> (session_json); var userName = context.Principal.Identities.First( u => u.IsAuthenticated && u.HasClaim(c => c.Type == ClaimTypes.Name)).FindFirst(ClaimTypes.Name).Value; if (!userName.Equals(session.user_id, StringComparison.OrdinalIgnoreCase)) { context.RejectPrincipal(); return(Task.CompletedTask); } var accessToken = session.data["access_token"]; var refreshToken = session.data["refresh_token"]; var exp = session.data["expires_at"]; expires_at_time = DateTimeOffset.Parse(exp); // server-side check for expiration if (expires_at_time.DateTime < DateTime.Now) { //token is expired, let's attempt to renew var tokenEndpoint = sams_endpoint_token; var tokenClient = new mmria.server.util.TokenClient(Configuration); //var name = HttpContext.Session.GetString(SessionKeyName); //var name = HttpContext.Session.GetString(SessionKeyName); var tokenResponse = tokenClient.get_refresh_token(accessToken.ToString(), refreshToken.ToString()).Result; //check for error while renewing - any error will trigger a new login. if (tokenResponse.is_error) { //reject Principal context.RejectPrincipal(); return(Task.CompletedTask); } //set new token values refreshToken = tokenResponse.refresh_token; accessToken = tokenResponse.access_token; var unix_time = DateTimeOffset.UtcNow.AddSeconds(tokenResponse.expires_in); session.data["access_token"] = accessToken; session.data["refresh_token"] = refreshToken; session.data["expires_at"] = unix_time.ToString(); context.Response.Cookies.Append("expires_at", unix_time.ToString()); session.date_last_updated = DateTime.UtcNow; var Session_Message = new mmria.server.model.actor.Session_Message ( session._id, //_id = session._rev, //_rev = session.date_created, //date_created = session.date_last_updated, //date_last_updated = session.date_expired, //date_expired = session.is_active, //is_active = session.user_id, //user_id = session.ip, //ip = session.session_event_id, // session_event_id = session.data ); Program.actorSystem.ActorOf(Props.Create <mmria.server.model.actor.Post_Session>()).Tell(Session_Message); //trigger context to renew cookie with new token values context.ShouldRenew = true; return(Task.CompletedTask); } } catch (Exception ex) { // do nothing for now document doesn't exsist. System.Console.WriteLine($"err caseController.Post\n{ex}"); } } } return(Task.CompletedTask); } }; return(result); }
// 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, IOptions <cloudscribe.Core.Models.MultiTenantOptions> multiTenantOptionsAccessor, IServiceProvider serviceProvider ) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseSession(); app.UseMultitenancy <cloudscribe.Core.Models.SiteSettings>(); //app.UseTenantContainers<SiteSettings>(); var multiTenantOptions = multiTenantOptionsAccessor.Value; app.UsePerTenant <cloudscribe.Core.Models.SiteSettings>((ctx, builder) => { var tenant = ctx.Tenant; var shouldUseFolder = !multiTenantOptions.UseRelatedSitesMode && multiTenantOptions.Mode == cloudscribe.Core.Models.MultiTenantMode.FolderName && tenant.SiteFolderName.Length > 0; var externalCookieOptions = SetupOtherCookies(cloudscribe.Core.Identity.AuthenticationScheme.External, tenant); builder.UseCookieAuthentication(externalCookieOptions); var twoFactorRememberMeCookieOptions = SetupOtherCookies(cloudscribe.Core.Identity.AuthenticationScheme.TwoFactorRememberMe, tenant); builder.UseCookieAuthentication(twoFactorRememberMeCookieOptions); var twoFactorUserIdCookie = SetupOtherCookies(cloudscribe.Core.Identity.AuthenticationScheme.TwoFactorUserId, tenant); builder.UseCookieAuthentication(twoFactorUserIdCookie); var cookieEvents = new CookieAuthenticationEvents(); var logger = loggerFactory.CreateLogger <cloudscribe.Core.Identity.SiteAuthCookieValidator>(); var cookieValidator = new cloudscribe.Core.Identity.SiteAuthCookieValidator(logger); var appCookieOptions = SetupAppCookie( cookieEvents, cookieValidator, cloudscribe.Core.Identity.AuthenticationScheme.Application, tenant ); builder.UseCookieAuthentication(appCookieOptions); // known issue here is if a site is updated to populate the // social auth keys, it currently requires a restart so that the middleware gets registered // in order for it to work or for the social auth buttons to appear builder.UseSocialAuth(ctx.Tenant, externalCookieOptions, shouldUseFolder); }); UseMvc(app, multiTenantOptions.Mode == cloudscribe.Core.Models.MultiTenantMode.FolderName); var storage = Configuration["DevOptions:DbPlatform"]; switch (storage) { case "NoDb": CoreNoDbStartup.InitializeDataAsync(app.ApplicationServices).Wait(); break; case "ef": default: // this creates ensures the database is created and initial data CoreEFStartup.InitializeDatabaseAsync(app.ApplicationServices).Wait(); // this one is only needed if using cloudscribe Logging with EF as the logging storage //cloudscribe.Logging.EF.LoggingDbInitializer.InitializeDatabaseAsync(app.ApplicationServices).Wait(); break; } }
/// <summary> /// 权限控制核心,即必须的配置 /// </summary> /// <param name="services"></param> /// <param name="action"></param> public static void AddCorePermission(this IServiceCollection services, Action <PermissionOptions> action) { services.AddSingleton <IToken, Token>(); #region 身份验证 var permissionOption = new PermissionOptions(); action(permissionOption); //addAuthentication不放到AddPermissionCore方法里,是为了外部可自己配置 // 当未通过authenticate时(如无token或是token出错时),会返回401,当通过了authenticate但没通过authorize时,会返回403。 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie( CookieAuthenticationDefaults.AuthenticationScheme, options => { //下面的委托方法只会在第一次cookie验证时调用,调用时会用到上面的permissionOption变量,但其实permissionOption变量是在以前已经初始化的,所以在此方法调用之前,permissionOption变量不会被释放 options.Cookie.Name = "auth"; options.AccessDeniedPath = permissionOption.AccessDeniedPath; // 当403时,返回到无授权界面 options.LoginPath = permissionOption.LoginPath; // 当401时,返回到登录界面,会自动在url后加上returnUrl=xxx options.ExpireTimeSpan = permissionOption.ExpireTimeSpan != default ? permissionOption.ExpireTimeSpan : new TimeSpan(12, 0, 0); options.ForwardDefaultSelector = context => { string authorization = context.Request.Headers["Authorization"]; //身份验证的顺序为jwt、cookie if (authorization != null && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { return(JwtBearerDefaults.AuthenticationScheme); } else { return(CookieAuthenticationDefaults.AuthenticationScheme); } }; var cookieAuthenticationEvents = new CookieAuthenticationEvents { OnSignedIn = context => { return(Task.CompletedTask); }, OnSigningOut = context => { return(Task.CompletedTask); } }; options.Events = cookieAuthenticationEvents; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { // jwt可用对称和非对称算法进行验签 SecurityKey key; if (permissionOption.IsAsymmetric) { key = new RsaSecurityKey(RSAHelper.GetRSAParametersFromFromPublicPem(permissionOption.RsaPublicKey)); } else { key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(permissionOption.SymmetricSecurityKey)); } options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = PermissionConstant.userIdClaim, RoleClaimType = PermissionConstant.roleIdsClaim, ValidIssuer = permissionOption.Issuer, ValidAudience = permissionOption.Audience, IssuerSigningKey = key, ValidateIssuer = false, ValidateAudience = false }; var jwtBearerEvents = new JwtBearerEvents { OnMessageReceived = context => { return(Task.CompletedTask); }, OnTokenValidated = context => { return(Task.CompletedTask); }, OnAuthenticationFailed = context => { return(Task.CompletedTask); } }; options.Events = jwtBearerEvents; }); #endregion #region 授权 //权限控制只要在配置IServiceCollection,不需要额外配置app管道 //权限控制参考:https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2 //handler和requirement有几种关系:1 handler对多requirement(此时handler实现IAuthorizationHandler);1对1(实现AuthorizationHandler<PermissionRequirement>),和多对1 //所有的handler都要注入到services,用services.AddSingleton<IAuthorizationHandler, xxxHandler>(),而哪个requirement用哪个handler,低层会自动匹配。最后将requirement对到policy里即可 services.AddAuthorization(options => { // 增加鉴权策略,并告知这个策略要判断用户是否获得了PermissionRequirement这个Requirement options.AddPolicy(PermissionConstant.PermissionAuthorizePolicy, policyBuilder => { policyBuilder.AddRequirements(new PermissionRequirement()); }); //options.AddPolicy(PermissionConstant.OnlyAuthenticationPolicy, policyBuilder => // { // policyBuilder.AddRequirements(new OnlyAuthenticationRequirement()); // }); }); services.AddScoped <IAuthorizationHandler, PermissionRequirementHandler>(); services.AddMemoryCache(); services.TryAddScoped <IApplicationContext, ApplicationContext>(); services.AddHttpContextAccessor(); services.Configure(action); #endregion }