asp.net core controller not authorize with jwt - authentication

I use Microsoft.AspNetCore.Authentication.JwtBearer Library for jwt authentication in ASP.Net Core Web Api.When i request a token, it work fine and i get a token.
But when i send request to secure Action i get 401 status code.
in my Startup Class:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,IOptionsSnapshot<SiteSettings> siteSettings)
{
var jwtOption = siteSettings.Value.JwtOption;
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtOption.Issuer,
ValidateAudience = true,
ValidAudience = jwtOption.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = false,
ClockSkew = TimeSpan.Zero
};
app.UseMvc();
app.UseApiCustomIdentityServices();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Audience = jwtOption.Audience,
Authority = jwtOption.Audience,
RequireHttpsMetadata = false,
TokenValidationParameters = tokenValidationParameters
});
}
And my TokenController:
public class TokenController : Controller
{
private readonly IUserManager _userManager;
private readonly IJwtService _jwtService;
private readonly IOptionsSnapshot<SiteSettings> _siteSettings;
private readonly JsonSerializerSettings _serializerSettings;
public TokenController(
IUserManager userManager,
IJwtService jwtService,
IOptionsSnapshot<SiteSettings> siteSettings
)
{
_userManager = userManager;
_jwtService = jwtService;
_siteSettings = siteSettings;
_serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented
};
}
[HttpPost]
public async Task<IActionResult> Post([FromBody]LoginViewModel model)
{
if (!ModelState.IsValid)
return BadRequest();
var identity = await GetClaimsIdentity(model.Username, model.Password);
if (identity == null)
{
return BadRequest();
}
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
auth_token = await _jwtService.GenerateEncodedToken(model.Username, identity),
expires_in = (int)_siteSettings.Value.JwtOption.ValidFor.TotalSeconds
};
var json = JsonConvert.SerializeObject(response, _serializerSettings);
return new OkObjectResult(json);
}
private async Task<ClaimsIdentity> GetClaimsIdentity(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
var userToVerify = await _userManager.FindByNameAsync(userName);
if (userToVerify != null)
{
if (await _userManager.CheckPasswordAsync(userToVerify, password))
{
return await Task.FromResult(_jwtService.GenerateClaimsIdentity(userName, userToVerify.Id.ToString()));
}
}
}
return await Task.FromResult<ClaimsIdentity>(null);
}
what's wrong?

Related

JWT Authentication And Authorization In .NET 6.0 401Unauthorized Error

i am creating a token and it does not accept the token I created, the error code 401 returns
appsettings.json
{
"TokenOptions": {
"Audience": "https://localhost:7098",
"Issuer": "https://localhost:7098",
"AccessTokenExpiration": 500,
"SecurityKey": "mysecretkeymysecretkey"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
TokenOptions.cs
namespace Shared.Utilities.Security.Jwt;
public class TokenOptions
{
public string Audience { get; set; }
public string Issuer { get; set; }
public int AccessTokenExpiration { get; set; }
public string SecurityKey { get; set; }
}
JwtHelper.cs
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Shared.Entities.Concrete;
using Shared.Extensions;
using Shared.Utilities.Security.Encyption;
namespace Shared.Utilities.Security.Jwt;
public class JwtHelper : ITokenHelper
{
public IConfiguration Configuration { get; }
private TokenOptions _tokenOptions;
private DateTime _accessTokenExpiration;
public JwtHelper(IConfiguration configuration)
{
Configuration = configuration;
_tokenOptions = Configuration.GetSection(key: "TokenOptions").Get<TokenOptions>();
}
public AccessToken CreateToken(User user, List<OperationClaim> operationClaims)
{
_accessTokenExpiration = DateTime.Now.AddMinutes(_tokenOptions.AccessTokenExpiration);
var securityKey = SecurityKeyHelper.CreateSecurityKey(_tokenOptions.SecurityKey);
var signingCredentials = SigningCredentialsHelper.CreateSigningCredentials(securityKey);
var jwt = CreateJwtSecurityToken(_tokenOptions, user, signingCredentials, operationClaims);
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
var token = jwtSecurityTokenHandler.WriteToken(jwt);
return new AccessToken
{
Token = token,
Expiration = _accessTokenExpiration
};
}
public JwtSecurityToken CreateJwtSecurityToken(TokenOptions tokenOptions, User user, SigningCredentials signingCredentials, List<OperationClaim> operationClaims)
{
var Jwt = new JwtSecurityToken(
issuer: tokenOptions.Issuer,
audience: tokenOptions.Audience,
expires: _accessTokenExpiration,
notBefore: DateTime.Now,
claims: SetClains(user, operationClaims),
signingCredentials: signingCredentials
);
return Jwt;
}
private IEnumerable<Claim> SetClains(User user, List<OperationClaim> operationClaims)
{
var claims = new List<Claim>();
claims.AddNameIdentifier(user.Id.ToString());
claims.AddEmail(user.Email);
claims.AddName($"{user.FirstName} {user.LastName}");
claims.AddRole(operationClaims.Select(c => c.Name).ToArray());
return claims;
}
}
ITokenHelper.cs
using Shared.Entities.Concrete;
namespace Shared.Utilities.Security.Jwt;
public interface ITokenHelper
{
AccessToken CreateToken(User user, List<OperationClaim> operationClaims);
}
AccessToken.cs
namespace Shared.Utilities.Security.Jwt
{
public class AccessToken
{
public string Token { get; set; }
public DateTime Expiration { get; set; }
}
}
HashingHelper.cs
using System.Text;
namespace Shared.Utilities.Security.Hashing;
public class HashingHelper
{
public static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
}
}
public static bool VerifyPasswordHash(string password, byte[] passwordHash, byte[] passwordSalt)
{
using (var hmac = new System.Security.Cryptography.HMACSHA512(passwordSalt))
{
var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != passwordHash[i])
{
return false;
}
}
}
return true;
}
}
SigningCredentialsHelper.cs
using Microsoft.IdentityModel.Tokens;
namespace Shared.Utilities.Security.Encyption;
public class SigningCredentialsHelper
{
public static SigningCredentials CreateSigningCredentials(SecurityKey securityKey)
{
return new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
}
}
SecurityKeyHelper.cs
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Shared.Utilities.Security.Encyption;
public class SecurityKeyHelper
{
public static SecurityKey CreateSecurityKey(string securityKey)
{
return new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
}
}
ClaimExtensions.cs
using System.Security.Claims;
using Microsoft.IdentityModel.JsonWebTokens;
namespace Shared.Extensions;
public static class ClaimExtensions
{
public static void AddEmail(this ICollection<Claim> claims, string email)
{
claims.Add(new Claim(type: JwtRegisteredClaimNames.Email, value: email));
}
public static void AddName(this ICollection<Claim> claims, string name)
{
claims.Add(new Claim(type: ClaimTypes.Name, value: name));
}
public static void AddNameIdentifier(this ICollection<Claim> claims, string nameIdentifier)
{
claims.Add(new Claim(type: ClaimTypes.NameIdentifier, value: nameIdentifier));
}
public static void AddRole(this ICollection<Claim> claims, string[] roles)
{
roles.ToList().ForEach(role => claims.Add(new Claim(type: ClaimTypes.Role, value: role)));
}
}
AutofacBusinessModule.cs
using Autofac;
using Data.Abstract;
using Data.Concrete.EntityFramework;
using Services.Abstract;
using Services.Concrete;
using Shared.Utilities.Security.Jwt;
namespace Services.DependencyResolvers.Autofac
{
public class AutofacBusinessModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ProductManager>().As<IProductService>();
builder.RegisterType<EfProductRepository>().As<IProductRepository>();
builder.RegisterType<CategoryManager>().As<ICategoryService>();
builder.RegisterType<EfCategoryRepository>().As<ICategoryRepository>();
builder.RegisterType<UserManager>().As<IUserService>();
builder.RegisterType<EfUserRepository>().As<IUserRepository>();
builder.RegisterType<AuthManager>().As<IAuthService>();
builder.RegisterType<JwtHelper>().As<ITokenHelper>();
}
}
}
AuthController.cs
using Entities.Dtos;
using Microsoft.AspNetCore.Mvc;
using Services.Abstract;
namespace WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private IAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[HttpPost("login")]
public async Task<IActionResult> Login(UserForLoginDto userForLoginDto)
{
var userToLogin = await _authService.Login(userForLoginDto);
if (!userToLogin.IsSuccess)
return BadRequest(userToLogin.Message);
var result = await _authService.CreateAccessToken(userToLogin.Data);
if (result.IsSuccess)
return Ok(result.Data);
return BadRequest(result.Message);
}
[HttpPost("register")]
public async Task<IActionResult> Register(UserForRegisterDto userForRegisterDto)
{
var userExists = await _authService.UserExists(userForRegisterDto.Email);
if (!userExists.IsSuccess)
return BadRequest(userExists.Message);
var registerResult = await _authService.Register(userForRegisterDto);
var result = await _authService.CreateAccessToken(registerResult.Data);
if (result.IsSuccess)
return Ok(result.Data);
return BadRequest(result.Message);
}
}
}
Program.cs
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Services.DependencyResolvers.Autofac;
using Shared.Utilities.Security.Encyption;
using Shared.Utilities.Security.Jwt;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()).ConfigureContainer<ContainerBuilder>(builder =>
{
builder.RegisterModule(new AutofacBusinessModule());
});
var tokenOptions = builder.Configuration.GetSection(key: "TokenOptions").Get<TokenOptions>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey),
ValidateIssuerSigningKey = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
CreateTokenPostman.cs
enter image description here
401ErrorPostman.cs
enter image description here
Thanks. I found the error related to the latest version of JWT. The error was fixed when I downloaded the old version of JWT.
The version I used is 6.0.7:
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.7" />
I have compared your code with this repo. And I find something, it maybe useful to you.
You should change your code like below.
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey),
ValidateIssuerSigningKey = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
//ClockSkew = TimeSpan.Zero
};
Or
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = tokenOptions.Issuer,
ValidAudience = tokenOptions.Audience,
IssuerSigningKey = SecurityKeyHelper.CreateSecurityKey(tokenOptions.SecurityKey),
ValidateIssuerSigningKey = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero,
// add this line
RequireExpirationTime = true
};

How to get the id of the user out of jwt token in .net core or how to return a value from a custom authorization attribute?

I create my token in the following way
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString()),
new Claim(ClaimTypes.Role, "tada")
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var encryptedtoken = tokenHandler.WriteToken(token);
And now i would like to simply get the users id from my authorize attribute and put it in the context somehow?
I know i can decode the token like so
[Authorize(Roles = "tada")]
public IActionResult Get()
{
var token = HttpContext.Request.Headers[HeaderNames.Authorization][0];
var tokenArray = token.Split(" ");
var handler = new JwtSecurityTokenHandler();
var tokenS = handler.ReadToken(tokenArray[1]) as JwtSecurityToken;
return Ok(tokenS.Payload.SingleOrDefault(t => t.Key == "unique_name").Value);
}
But how do i reuse this code in a more clever way can i create my own authorization attribute that will store it in the context if there is no way, how do i create a singleton/scoped/transient service?
here's how i configure jwt
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.AddDbContext<CatstagramDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddIdentity<User, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequiredLength = 6;
})
.AddEntityFrameworkStores<CatstagramDbContext>();
var applicationSettingConfiguration = Configuration.GetSection("ApplicationSettings");
services.Configure<AppSettings>(applicationSettingConfiguration);
var appSettings = applicationSettingConfiguration.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.ApplyMigrations();
}
}
I am using this function to get any token claim value
public static string GetClaimValue(HttpContext httpContext, string valueType)
{
if (string.IsNullOrEmpty(valueType)) return null;
var identity = httpContext.User.Identity as ClaimsIdentity;
var valueObj = identity == null ? null : identity.Claims.FirstOrDefault(x => x.Type == valueType);
return valueObj==null? null:valueObj.Value;
}
you can use it like this
var name = GetClaimValue(HttpContext, "unique_name");
When the authentication middleware authenticates the request, it populates HttpContext.User property with a ClaimsPrincipal that holds the claims for the current user.
ClaimsPrincipal class has an extension method in System.Security.Claims in namespace called FindFirstValue.
User.FindFirstValue("unique_name");
gives you the value for the first unique_name claim.
Source: https://github.com/dotnet/aspnetcore/blob/2be49d930a5fb53e781abd175c3b2a8f8b7827d4/src/Identity/Extensions.Core/src/PrincipalExtensions.cs

Role based Authentication: Bearer Token to Cookie using Microsoft .NetCore 3.1

I'm trying to transform.NetCore 3.1 Code from Bearer Token implementation to Cookie-based implementation Also trying to make Role-based authorization work with existing code. Can you please help me to change this code? The below code shows currently how Bearer Token is retrieved and the next part shows how role-based authorization is implemented in code.
Here is the current Bearer Token implementation.
var key = Encoding.ASCII.GetBytes(Configuration["AppSettings:Secret"]);
var signingKey = new SymmetricSecurityKey(key);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
IssuerSigningKey = signingKey,
ValidateIssuer = false,
ValidateAudience = false
};
});
Following annotation currently used for Role-based Authorization -
[Authorize(Roles = "1")]
[Route("api/[controller]")]
[ApiController]
public class JobLogsController : ControllerBase
{
private readonly EtpRepoContext _context;
private IJobLogsRepository _jobLogsRepository;
private IConfiguration _configuration;
public JobLogsController(EtpRepoContext context, IJobLogsRepository jobLogsRepository, IConfiguration configuration)
{
_context = context;
_jobLogsRepository = jobLogsRepository;
_configuration = configuration;
}
// GET: api/JobLogs
[HttpGet]
public async Task<ActionResult<IEnumerable<JobLog>>> GetJobLog()
{
return await _context.JobLog.ToListAsync();
}
// GET: api/JobLogs/5
[HttpGet("{id}")]
[ProducesResponseType(typeof(JobDetail), 200)]
[ProducesResponseType(typeof(string), 400)]
public IActionResult FindById([FromRoute] String id)
{
string contentStr = "";
try
{
if(id.Length >= 10)
{
contentStr = _jobLogsRepository.GetLogById(id);
}
else
{
contentStr = _jobLogsRepository.GetFileById(id);
}
var content = Newtonsoft.Json.JsonConvert.SerializeObject(new { content = contentStr });
return Ok(content);
}
catch (Exception ex)
{
return StatusCode(500, "Internal server error");
}
}
This is how the Microsoft identity model is used to claim the token.
public class ClaimsTransformer : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
ClaimsIdentity claimsIdentity = (ClaimsIdentity)principal.Identity;
// flatten realm_access because Microsoft identity model doesn't support nested claims
// by map it to Microsoft identity model, because automatic JWT bearer token mapping already processed here
if (claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim((claim) => claim.Type == "identity"))
{
var realmAccessClaim = claimsIdentity.FindFirst((claim) => claim.Type == "identity");
dynamic realmAccessAsDict = JsonConvert.DeserializeObject<Object>(realmAccessClaim.Value);
string role = realmAccessAsDict.role.ToString();
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
//var role = realmAccessClaim.
//var realmAccessAsDict = JsonConvert.DeserializeObject<Object>(realmAccessClaim.Value);
/*if (realmAccessAsDict["role"] != null)
{
foreach (var role in realmAccessAsDict["role"])
{
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", role));
}
}*/
}
return Task.FromResult(principal);
}
}
}

Add To Context Items in SignalR Middleware

So I want to setup a User Middleware which works for SignalR Hubs and Controllers.
It works fine with normal requests but with signalr it gets called but doesnt add to context.
Is it even possible? If so how can i do it?
namespace PortalCore.Middleware
{
public class JwtMiddleware
{
private readonly RequestDelegate _next;
public JwtMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, AuthService authService)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
{
AttachUserToContext(context, authService, token);
}
await _next(context);
}
private async void AttachUserToContext(HttpContext context, AuthService authService, string token)
{
User user = null;
var tokenHandler = new JwtSecurityTokenHandler();
try
{
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey =
new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(authService.SecretKey)),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
user = await authService.GetUserByUid(jwtToken.Claims.FirstOrDefault()?.Value);
}
catch (Exception e)
{
}
context.Items["User"] = user;
}
}
}
if you want to check auth of signalR hub then you can do it with query string.you can send token with signalR client url.After take token from query string and set to context.
Hub Code:
[Authorize]
public class ChatHub : Hub
you can add token in Context :
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrWhiteSpace(accessToken) &&
(path.StartsWithSegments("/api/hubs/chatHub")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
},

Unable to resolve service for type at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider

I am attempting to receive data from the server controller, stocks.
I get this error:
"System.InvalidOperationException: Unable to resolve service for type myBackEnd.Models.StockContext' while attempting to activate 'myBackEnd.Controllers.StockController'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService
(IServiceProvider sp, Type type, Type requiredBy, Boolean
isDefaultParameterRequired"
Here is my stocks controller code:
namespace myBackEnd.Controllers
{
[Route("api/stock")]
[Produces("application/json")]
public class StockController : ControllerBase
{
private readonly int fastEmaPeriod = 10;
private readonly IHttpClientFactory _httpClientFactory;
private readonly Models.StockContext _context;
public StockController(Models.StockContext context, IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
_context = context;
}
// POST api/values
[HttpPost]
public async Task<IActionResult> Post([FromBody]Models.Stock stock)
{
_context.Stocks.Add(stock);
await _context.SaveChangesAsync();
return Ok(stock);
}
This is the startup.cs code:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("MyPolicy", corsBuilder =>
{
corsBuilder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}));
services.AddDbContext<DataContext>(x => x.UseInMemoryDatabase("TestDb"));
services.AddHttpClient();
services.AddAutoMapper();
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
var userId = int.Parse(context.Principal.Identity.Name);
var user = userService.GetById(userId);
if (user == null)
{
// return unauthorized if user no longer exists
context.Fail("Unauthorized");
}
return Task.CompletedTask;
}
};
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
// configure DI for application services
services.AddScoped<IUserService, UserService>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
This worked before I added the registration, login and
// configure DI for application services
services.AddScoped();
The problem was the DB context was not registered for dependency injection.
Adding:
services.AddDbContext<Models.StockContext>(opt => opt.UseInMemoryDatabase("item"));
fixed the problem.