I'm stuck and I can't figure it out I have a simple class with an interface. I inject EFContext and Logger into this service. For some reason, no matter how I register the service, he is always singleton. I put the Guid property in a class to see if it changed for each request, but it remains the same.
Here is the AccountService class and its interface:
public interface IAccountService
{
Account GetAccountByEmailAndPassword(string emailAddress, string password);
}
public class AccountService : IAccountService
{
private readonly IEFContext _context;
private readonly ILogger<AccountService> _logger;
private string _guid;
public AccountService()
{
_context = context;
_logger = logger;
_guid = Guid.NewGuid().ToString();
}
public Account GetAccountByEmailAndPassword(string emailAddress, string password)
{
try
{
var account = _context.Account.FirstOrDefault(x => x.EmailAddress == emailAddress);
if (account == null)
return null;
var accountHash = GeneratePasswordSaltHash(account.Password, account.PasswordSalt);
var passedInHash = GeneratePasswordSaltHash(
Convert.ToBase64String(HashPassword(password)),
account.PasswordSalt);
return accountHash == passedInHash ? account : null;
} catch (Exception ex)
{
_logger.LogError("Exception in AccountService: " + ex.ToString());
throw;
}
}
}
This is how I register services:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
var connectionString = Configuration["AppSettings:Data:ConnectionString"];
services.AddDbContext<EFContext>(options => options.UseSqlServer(connectionString));
services.AddMvc();
services.AddScoped<IEFContext, EFContext>();
services.AddScoped<IAccountService, AccountService>();
}
Here is the EFContext class and its interface:
public interface IEFContext
{
DbSet<Account> Account { get; set; }
int SaveChanges();
EntityEntry Update(object entity);
}
public class EFContext : DbContext, IEFContext
{
public EFContext(DbContextOptions options) : base(options) {}
public DbSet<Account> Account { get; set; }
}
, . , , , , . , , , , .AddScoped<>, . , _guid AccountService, , , . .AddTransient<>. . .
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddSerilog();
var secretKey = "mysupersecret_secretkey!123";
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
ValidateIssuer = true,
ValidIssuer = "ExampleIssuer",
ValidateAudience = true,
ValidAudience = "ExampleAudience",
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = tokenValidationParameters
});
var options = new TokenProviderOptions
{
Audience = "ExampleAudience",
Issuer = "ExampleIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
app.UseMvc();
}
Token AccountService, :
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
private readonly IAccountService _accountService;
public TokenProviderMiddleware(RequestDelegate next, IOptions<TokenProviderOptions> options, IAccountService accountService)
{
_next = next;
_options = options.Value;
_accountService = accountService;
}
public Task Invoke(HttpContext context)
{
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
return _next(context);
}
if (!context.Request.Method.Equals("POST")
|| !context.Request.ContentType.Contains("application/json"))
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context);
}
private async Task GenerateToken(HttpContext context)
{
var rawAccount = await new StreamReader(context.Request.Body).ReadToEndAsync();
var authAccount = JsonConvert.DeserializeObject<AuthAccount>(rawAccount);
var account = _accountService.GetAccountByEmailAndPassword(authAccount.EmailAddress, authAccount.Password);
if (account == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid email address or password.");
return;
}
var now = DateTime.UtcNow;
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, account.EmailAddress),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, ((DateTimeOffset)now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
new Claim(ClaimTypes.Role, account.RoleId.ToString()),
new Claim(ClaimTypes.Name, account.EmailAddress)
};
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new ApiResponse<AuthAccount>
{
StatusCode = (int)HttpStatusCode.OK,
Message = "Access granted",
Data = new AuthAccount
{
Access_Token = encodedJwt,
Expires_In = (int)_options.Expiration.TotalSeconds
}
};
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
}