Beispiel #1
0
        public static IServiceCollection AddRealDoxServices(this IServiceCollection services, IConfiguration config)
        {
            //
            // Configuration.
            //
            services.AddOptions();
            services.Configure <JwtOptions>(config.GetSection("JwtSecurityToken"));

            //
            // Security.
            //
            // We use token (JWT) based authentication here.
            // For a better understanding regarding token authentication, see below page.
            // https://stormpath.com/blog/token-authentication-asp-net-core
            //

            // Turn off Microsoft's JWT handler that maps claim types to .NET's long claim type names
            // See more details at https://github.com/IdentityServer/IdentityServer3.Samples/issues/173
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            var jwtOptions                = config.GetSection("JwtSecurityToken").Get <JwtOptions>();
            var invalidTokenDictionary    = new InvalidTokenDictionary();
            var jwtValidator              = new JwtValidator(jwtOptions.SigningAlgorithm, invalidTokenDictionary);
            var tokenValidationParameters = new TokenValidationParameters
            {
                // Check if the token is issued by us.
                ValidateIssuer           = true,
                ValidIssuer              = jwtOptions.Issuer,
                ValidateAudience         = true,
                ValidAudience            = jwtOptions.Audience,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey         = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey)),
                // Check if the token is expired.
                ValidateLifetime = true, //validate the expiration and not before values in the token
                // This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
                // when validating the lifetime. As we're creating the tokens locally and validating them on the same
                // machines which should have synchronized time, this can be set to zero. and default value will be 5minutes
                ClockSkew = TimeSpan.Zero,
            };

            // For demo purpose, we will show two ways to store the token: cookie or HTTP Authorization Header.
            // We will use these two authentication schemes on different Controllers.

            // Note: AddIdentity() will enable Cookies as default authentication scheme.
            services.AddIdentity <ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores <SecurityDbContext>()    // Switches between EF and MongoDB with below line.
                                                               //.AddMongoDbStores(options => options.ConnectionString = config.GetConnectionString("Mongo"))
            .AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(options => {
                // Prevents cookies from client script access. So we are safe for XSS attack.
                options.Cookie.HttpOnly = true;
                options.Cookie.Name     = jwtOptions.CookieName;
                options.Cookie.Domain   = jwtOptions.DomainName;
                options.Cookie.Path     = jwtOptions.CookiePath;
                //options.Cookie.Expiration = new DateTimeOffset(DateTime.Now.AddDays(jwtOptions.RememberMeExpireInDays)).To(), //TimeSpan.FromMinutes(jwtOptions.ExpireInMinutes);
                options.Cookie.SameSite = SameSiteMode.Strict;
                // Tells system how to verify.
                //options.TicketDataFormat = new JwtSecureDataFormat(tokenValidationParameters, jwtValidator);
                // Stops redirections for api.
                options.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, options.Events.OnRedirectToAccessDenied),
                    OnRedirectToLogin        = ReplaceRedirector(HttpStatusCode.Unauthorized, options.Events.OnRedirectToLogin),
                };
                options.SlidingExpiration = true;
            });

            /*Reference this package in project.json: Microsoft.AspNetCore.Identity.MongoDB
             * Then, in ConfigureServices--or wherever you are registering services--include the following to register both the Identity services and MongoDB stores:*/
            //services.AddIdentityWithMongoStores("mongodb://localhost/myDB");

            /*services.AddIdentityWithMongoStoresUsingCustomTypes<ApplicationUser, ApplicationRole>(config.GetConnectionString("Mongo"))
             *  .AddDefaultTokenProviders();*/

            // Since AddIdentity() has already added cookies, here we only change some settings.
            // Check below page for details on how to add cookie authentication w/ or w/o Identity framework.
            // https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x
            services.AddAuthentication()

            /*.AddCookie(options => {
             * // Prevents cookies from client script access. So we are safe for XSS attack.
             * options.Cookie.HttpOnly = true;
             * options.Cookie.Name = jwtOptions.CookieName;
             * options.Cookie.Domain = jwtOptions.DomainName;
             * options.Cookie.Path = jwtOptions.CookiePath;
             * //options.Cookie.Expiration = new DateTimeOffset(DateTime.Now.AddDays(jwtOptions.RememberMeExpireInDays)).To(), //TimeSpan.FromMinutes(jwtOptions.ExpireInMinutes);
             * options.Cookie.SameSite = SameSiteMode.Strict;
             * // Tells system how to verify.
             * //options.TicketDataFormat = new JwtSecureDataFormat(tokenValidationParameters, jwtValidator);
             * // Stops redirections for api.
             * options.Events = new CookieAuthenticationEvents
             * {
             *  OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, options.Events.OnRedirectToAccessDenied),
             *  OnRedirectToLogin = ReplaceRedirector(HttpStatusCode.Unauthorized, options.Events.OnRedirectToLogin),
             * };
             * options.SlidingExpiration = true;
             * })*/

            // Adds authentication via Jwt to validate token.
            .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
            {
                options.RequireHttpsMetadata      = false; // Todo: remove this line in production.
                options.SaveToken                 = true;
                options.TokenValidationParameters = tokenValidationParameters;
                // Since we have custom validation logics so remove the default validator.
                options.SecurityTokenValidators.Clear();
                options.SecurityTokenValidators.Add(jwtValidator);
            });

            Func <RedirectContext <CookieAuthenticationOptions>, Task> ReplaceRedirector(
                HttpStatusCode statusCode,
                Func <RedirectContext <CookieAuthenticationOptions>, Task> existingRedirector) =>
            context =>
            {
                // Does not touch non-api calls.
                if (!context.Request.Path.StartsWithSegments("/api"))
                {
                    return(existingRedirector(context));
                }

                // No direction and directly return code.
                context.Response.StatusCode = (int)statusCode;
                return(Task.CompletedTask);
            };

            // Adds token black list, used to revoke or logout.
            // Todo: need a background worker to clean this list to remove tokens that already expired.
            services.AddSingleton(invalidTokenDictionary);
            services.AddSingleton(jwtValidator);

            // Custom Policy-Based Authorization.
            services.AddAuthorization(options =>
            {
                options.AddPolicy(AuthorizationPolicies.Admin,
                                  builder => builder.RequireClaim(CustomClaimTypes.Operator).RequireClaim(CustomClaimTypes.User));

                options.AddPolicy("DisneyUser",
                                  policy => policy.RequireClaim("DisneyCharacter", "IAmMickey"));
            });



            //Repository and InitofWork, and other application services
            //Adding repositories and UnitofWork
            services.AddScoped <IToDoRepository, ToDoRepository>();
            services.AddScoped <IAuthorRepository, AuthorRepository>();
            //Add application services
            services.AddTransient <RandomNumberProviderFilter>();
            services.AddTransient <RandomNumberService>();

            services.AddTransient <IEmailSender, EmailSender>();

            //services.AddScoped<ISeedDataService, SeedDataService>();


            return(services);
        }
Beispiel #2
0
 /// <inheritdoc />
 /// <summary>
 /// Initializes a new instance of <see cref="JwtValidator"/>.
 /// </summary>
 /// <param name="algorithm">Signature signing algorithm name.</param>
 /// <param name="invalidTokenDictionary">Invalid token dictionary.</param>
 public JwtValidator(string algorithm, InvalidTokenDictionary invalidTokenDictionary)
 {
     _algorithm = algorithm;
     _invalidTokenDictionary = invalidTokenDictionary;
 }
Beispiel #3
0
        public static IServiceCollection AddTodoServices(this IServiceCollection services, IConfiguration config)
        {
            //
            // Configuration.
            //
            services.AddOptions();
            services.Configure <JwtOptions>(config.GetSection("JwtSecurityToken"));

            //
            // Object mapper.
            //
            var adapterFactory = new AutoMapperTypeAdapterFactory();

            TypeAdapterFactory.SetCurrent(adapterFactory);
            services.AddSingleton <ITypeAdapterFactory>(adapterFactory);

            //
            // Logging.
            //
            services.AddSingleton(LogFactory.GetCurrent());

            //
            // Security.
            //
            // We use token (JWT) based authentication here.
            // For a better understanding regarding token authentication, see below page.
            // https://stormpath.com/blog/token-authentication-asp-net-core
            //

            // Turn off Microsoft's JWT handler that maps claim types to .NET's long claim type names
            // See more details at https://github.com/IdentityServer/IdentityServer3.Samples/issues/173
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

            var jwtOptions                = config.GetSection("JwtSecurityToken").Get <JwtOptions>();
            var invalidTokenDictionary    = new InvalidTokenDictionary();
            var jwtValidator              = new JwtValidator(jwtOptions.SigningAlgorithm, invalidTokenDictionary);
            var tokenValidationParameters = new TokenValidationParameters
            {
                // Check if the token is issued by us.
                ValidIssuer              = jwtOptions.Issuer,
                ValidAudience            = jwtOptions.Audience,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey         = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.SigningKey)),
                // Check if the token is expired.
                ValidateLifetime = true,
                // This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time
                // when validating the lifetime. As we're creating the tokens locally and validating them on the same
                // machines which should have synchronized time, this can be set to zero. and default value will be 5minutes
                ClockSkew = TimeSpan.Zero,
            };

            //services.AddDbContext<SecurityDbContext>(options =>
            //    options.UseSqlServer(config.GetConnectionString("Default"), sqlOptions =>
            //        sqlOptions.MigrationsAssembly("Todo.EntityFrameworkCore")
            //    )
            //);

            // For demo purpose, we will show two ways to store the token: cookie or HTTP Authorization Header.
            // We will use these two authentication schemes on different Controllers.

            // Note: AddIdentity() will enable cookie as default authentication scheme.
            services.AddIdentity <ApplicationUser, ApplicationRole>()
            //.AddEntityFrameworkStores<SecurityDbContext>() // Switches between EF and MongoDB with below line.
            .AddMongoDbStores(options => options.ConnectionString = config.GetConnectionString("Mongo"))
            .AddDefaultTokenProviders();

            // Since AddIdentity() has already added cookies, here we only change some settings.
            // Check below page for details on how to add cookie authentication w/ or w/o Identity framework.
            // https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x
            services.ConfigureApplicationCookie(options =>
            {
                // Prevents cookies from client script access. So we are safe for XSS attack.
                options.Cookie.HttpOnly = true;
                options.Cookie.Name     = jwtOptions.CookieName;
                // Tells system how to verify.
                options.TicketDataFormat = new JwtSecureDataFormat(tokenValidationParameters, jwtValidator);
                // Stops redirections for api.
                options.Events = new CookieAuthenticationEvents
                {
                    OnRedirectToAccessDenied = ReplaceRedirector(HttpStatusCode.Forbidden, options.Events.OnRedirectToAccessDenied),
                    OnRedirectToLogin        = ReplaceRedirector(HttpStatusCode.Unauthorized, options.Events.OnRedirectToLogin),
                };
            });

            Func <RedirectContext <CookieAuthenticationOptions>, Task> ReplaceRedirector(
                HttpStatusCode statusCode,
                Func <RedirectContext <CookieAuthenticationOptions>, Task> existingRedirector) =>
            context =>
            {
                // Does not touch non-api calls.
                if (!context.Request.Path.StartsWithSegments("/api"))
                {
                    return(existingRedirector(context));
                }

                // No direction and directly return code.
                context.Response.StatusCode = (int)statusCode;
                return(Task.CompletedTask);
            };

            // Adds authentication to validate token.
            services.AddAuthentication(options =>
            {
                // This will override Identity's scheme, so you can use Authorize attributes to protect data.
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                //options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata      = false; // Todo: remove this line in production.
                options.SaveToken                 = true;
                options.TokenValidationParameters = tokenValidationParameters;
                // Since we have custom validation logics so remove the default validator.
                options.SecurityTokenValidators.Clear();
                options.SecurityTokenValidators.Add(jwtValidator);
            });

            // Adds token black list, used to revoke or logout.
            // Todo: need a background worker to clean this list to remove tokens that already expired.
            services.AddSingleton(invalidTokenDictionary);
            services.AddSingleton(jwtValidator);

            // Custom Policy-Based Authorization.
            services.AddAuthorization(options =>
            {
                options.AddPolicy(AuthorizationPolicies.Admin,
                                  builder => builder.RequireClaim(PermissionClaims.Operator).RequireClaim(PermissionClaims.User));
            });

            //
            // Unit of Work.
            //
            services.AddScoped <IUserIdProvider, AspNetUserIdProvider>();

            //services.AddDbContext<TodoDbContext>(options =>
            //    options.UseSqlServer(config.GetConnectionString("Default"), sqlOptions =>
            //        sqlOptions.MigrationsAssembly("Todo.EntityFrameworkCore")
            //    )
            //);
            //services.AddScoped<IUnitOfWork, TodoDbContext>();
            services.AddScoped <IUnitOfWork, MongoDbUow>(sp => new MongoDbUow(config.GetConnectionString("Mongo"), sp.GetService <IUserIdProvider>()));
            services.AddScoped(sp => new MongoDbUow(config.GetConnectionString("Mongo"), sp.GetService <IUserIdProvider>()));

            //
            // Repositories.
            //
            //services.AddScoped<IRepository<TodoItem, IUnitOfWork>, EfRepository<TodoItem, TodoDbContext>>();
            services.AddScoped <IRepository <TodoItem, IUnitOfWork>, TodoItemRepository>();

            //
            // Application services
            //
            services.AddTransient <ITodoAppService, TodoAppService>();


            //
            // API management.
            //

            // Register the Swagger generator, defining one or more Swagger documents
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info {
                    Title = "Todo API", Version = "v1"
                });
            });

            return(services);
        }