/// <summary> /// Add cookie backed JWT authentication to the application using the passed in validation parameters /// </summary> /// <param name="services"></param> /// <param name="tokenValidationParams"></param> /// <param name="applicationDiscriminator"> /// An optional unique string that identifies the application to the data protection api for encryption key isolation. /// </param> /// <param name="authUrlOptions"></param> /// <returns></returns> public static IServiceCollection AddJwtAuthenticationWithProtectedCookie(this IServiceCollection services, TokenValidationParameters tokenValidationParams, string applicationDiscriminator = null, AuthUrlOptions authUrlOptions = null) { if (tokenValidationParams == null) { throw new ArgumentNullException( $"{nameof(tokenValidationParams)} is a required parameter. " + $"Please make sure you've provided a valid instance with the appropriate values configured."); } var hostingEnvironment = services.BuildServiceProvider().GetService <IHostingEnvironment>(); // The JwtAuthTicketFormat representing the cookie needs an IDataProtector and // IDataSerialiser to correctly encrypt/decrypt and serialise/deserialise the payload // respectively. This requirement is enforced by ISecureDataFormat interface in ASP.NET // Core. Read more about ASP.NET Core Data Protection API here: // https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/ // NB: This is only required if you're using JWT with Cookie based authentication, for // cookieless auth (such as with a Web API) the data protection and serialisation // dependencies won't be needed. You simply need to set the validation params and add // the token generator dependencies and use the right authentication extension below. var t = services.AddDataProtection(options => options.ApplicationDiscriminator = $"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}") .SetApplicationName($"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}"); services.AddScoped <IDataSerializer <AuthenticationTicket>, TicketSerializer>(); services.AddScoped <IJwtTokenGenerator, JwtTokenGenerator>(serviceProvider => new JwtTokenGenerator(tokenValidationParams.ToTokenOptions(10))); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { // cookie expiration should be set the same as the token expiry (the default is 5 // mins). The token generator doesn't provide auto-refresh of an expired token so the // user will be logged out the next time they try to access a secured endpoint. They // will simply have to re-login and acquire a new token and by extension a new cookie. // Perhaps in the future I can add some kind of hooks in the token generator that can // let the referencing application know that the token has expired and the developer // can then request a new token without the user having to re-login. options.Cookie.Expiration = TimeSpan.FromMinutes(10); // Specify the TicketDataFormat to use to validate/create the ASP.NET authentication // ticket. Its important that the same validation parameters are passed to this class // so that the token validation works correctly. The framework will call the // appropriate methods in JwtAuthTicketFormat based on whether the cookie is being // sent out or coming in from a previously authenticated user. Please bear in mind // that if the incoming token is invalid (may be it was tampered or spoofed) the // Unprotect() method in JwtAuthTicketFormat will simply return null and the // authentication will fail. options.TicketDataFormat = new JwtAuthTicketFormat(tokenValidationParams, services.BuildServiceProvider().GetService <IDataSerializer <AuthenticationTicket> >(), services.BuildServiceProvider().GetDataProtector(new[] { $"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}-Auth1" })); options.LoginPath = authUrlOptions != null ? new PathString(authUrlOptions.LoginPath) : new PathString("/Account/Login"); options.LogoutPath = authUrlOptions != null ? new PathString(authUrlOptions.LogoutPath) : new PathString("/Account/Logout"); options.AccessDeniedPath = options.LoginPath; options.ReturnUrlParameter = authUrlOptions?.ReturnUrlParameter ?? "returnUrl"; }); return(services); }
public static IServiceCollection AddJwtAuthenticationWithProtectedCookie( this IServiceCollection services, TokenOptions tokenOptions, string applicationDiscriminator = null, AuthUrlOptions authUrlOptions = null) { if (tokenOptions == null) { throw new ArgumentNullException( $"{nameof(tokenOptions)} is a required parameter. " + $"Please make sure you've provided a valid instance with the appropriate values configured."); } var hostingEnvironment = services.BuildServiceProvider() .GetService <IHostingEnvironment>(); services.AddDataProtection(options => options.ApplicationDiscriminator = $"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}") .SetApplicationName( $"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}"); services.AddScoped <IDataSerializer <AuthenticationTicket>, TicketSerializer>(); services.AddScoped <IJwtTokenGenerator, JwtTokenGenerator>( serviceProvider => new JwtTokenGenerator( tokenOptions)); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(options => { options.Cookie.Expiration = TimeSpan.FromDays(1); options.TicketDataFormat = new JwtAuthTicketFormat( tokenOptions.ToTokenValidationParams(), services.BuildServiceProvider() .GetService <IDataSerializer <AuthenticationTicket> >(), services.BuildServiceProvider() .GetDataProtector(new[] { $"{applicationDiscriminator ?? hostingEnvironment.ApplicationName}-Auth1" })); options.LoginPath = authUrlOptions != null ? new PathString(authUrlOptions.LoginPath) : new PathString("/Account/Login"); options.LogoutPath = authUrlOptions != null ? new PathString(authUrlOptions.LogoutPath) : new PathString("/Account/Logout"); options.AccessDeniedPath = options.LoginPath; options.ReturnUrlParameter = authUrlOptions?.ReturnUrlParameter ?? "returnUrl"; }); return(services); }