2
votes

I'm building ASP .NET Core WebAPI application and trying to provide Token authentication to my app:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services
                .AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;

                })
                .AddJwtBearer("Bearer", jwtBearerOptions =>
                {
                    jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
                    {
                        //ValidIssuer = "Issuer"
                        ValidateIssuer = false,

                                    ////ValidAudience = "WishlistAppClient",
                                    //ValidateAudience = false,

                                    ////ClockSkew = TimeSpan.FromSeconds(5),
                                    //ValidateLifetime = false,
                                    //RequireExpirationTime = false,
                                    //RequireSignedTokens = false,                          
                                };
                });

            services.AddMvc().AddJsonOptions(options =>
                {
                    options.SerializerSettings.ContractResolver = new DefaultContractResolver()
                    {
                        NamingStrategy = new SnakeCaseNamingStrategy()
                    };
                    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                });

            services.AddDbContext<SchemaContext>(options =>
                    options.UseSqlServer(
                        Configuration.GetConnectionString("DefaultConnection"), optionBuilder => optionBuilder.MigrationsAssembly("EventManager.DAL")
                        ));



            new DALRegistration().ConfigureServices(services);

            var mappingConfig = new MapperConfiguration(configuration =>
            {
                configuration.AddProfile(new MappingProfile());
            });
            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);

            services
                .AddIdentity<SystemUser, SystemRole>()
                .AddEntityFrameworkStores<SchemaContext>()
                .AddDefaultTokenProviders();

            services.AddScoped<IUserManager, UserManager>();
            services.AddScoped<ILoginProvider, LoginProvider>();



            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/dist";
            });

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
                c.DescribeAllEnumsAsStrings();
            });
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/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.UseAuthentication();
            app.UseMvc(routes =>
            {
                routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
            });
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSpaStaticFiles();



            var todayDate = DateTime.Now.ToShortDateString().Replace('/', '.');



            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                c.DocExpansion(DocExpansion.None);
            });

            loggerFactory.AddFile(Path.Combine(Directory.GetCurrentDirectory(), "LogInformation", $"{DateTime.Now.ToShortDateString().Replace('/','.')}.txt"));
            var logger = loggerFactory.CreateLogger("New Logger");


            app.Use(async (context, next) =>
            {
                logger.LogTrace("Processing request {0}", context.Request.Path);
                await next.Invoke();
            });

            app.UseSpa(spa =>
            {
                spa.Options.SourcePath = "ClientApp";
                spa.Options.StartupTimeout = new TimeSpan(0, 2, 0);
                if (env.IsDevelopment())
                {
                    spa.UseAngularCliServer(npmScript: "start");
                }
            });

        }
    }

API Code is protected by [Authorize(AuthenticationSchemes = "Bearer")] When I send request with any token, I always receive 401. The trouble is, that i turned off all token validation, but it does not help.

There is a picture of request in Postman

enter image description here

Response body is empty. Response headers(if you can't load image):

  • HTTP/1.1 401 Unauthorized
  • Server: Kestrel
  • WWW-Authenticate: Bearer error="invalid_token", error_description="The signature key was not found"
  • X-SourceFiles: =?UTF-8?B?RDpcUmVsZWFzZVxldmVudG1hbmFnZXJcRXZlbnRNYW5hZ2VyXEV2ZW50TWFuYWdlclxhcGlccGFydGljaXBhbnRz?=
  • X-Powered-By: ASP.NET
  • Date: Thu, 20 Feb 2020 11:47:54 GMT
  • Connection: close
  • Content-Length: 0

Request:

  • GET
  • https://localhost:44372/api/participants?pageSize=30&page=1 HTTP/1.1
  • Host: localhost:44372
  • Accept: application/json
  • Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJc3N1ZXIiOiJJc3N1ZXIiLCJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.eNvdqZ4NbLXesaJOV-a1CzbJh_QbfTdtqwZmrFI2MLY
  • User-Agent: PostmanRuntime/7.22.0
  • Cache-Control: no-cache
  • Postman-Token: dcf57c4f-b08a-43e0-8d15-85a49e9de795
  • Accept-Encoding: gzip, deflate, br
  • Connection: close
1
"There is a picture of request in Postman" - pictures of text are uselessvasily.sib
It was the best way to show, that I'm doing a valid requestAndyPearce
Well, specifically I and some other users can't see any images on stackoverflow due to firewall rulesvasily.sib
It's a trouble, I'm not allowed yet to add pictures to posts, I can attach link onlyAndyPearce
don't post images of text, just copy text and paste it herevasily.sib

1 Answers

1
votes

Here is an example to of how I've implemented

    services.AddAuthentication()
        .AddCookie()
        .AddJwtBearer(cfg =>
        {
            cfg.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidIssuer = Configuration["Tokens:Issuer"],
                ValidAudience = Configuration["Tokens:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
            };
        });

and on the controller, similar to yours

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

In the authentication controller, which is called by Login page with credentials. You have to implement the below code after checking if the username and password is correct

    var claims = new[]
                    {
                      new Claim(JwtRegisteredClaimNames.Sub, user.Email),
                    };

                    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
                    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                    var token = new JwtSecurityToken(
                      _config["Tokens:Issuer"],
                      _config["Tokens:Audience"],
                      claims,
                      expires: DateTime.Now.AddMinutes(30),
                      signingCredentials: creds);

                    var results = new
                    {
                        token = new JwtSecurityTokenHandler().WriteToken(token),
                        expiration = token.ValidTo
                    };