I have an Azure SQL DB that initially had the following columns:
user name password hash password salt
This DB serves a .NET Core C# API that checks username and password to return a JWT token.
The API had a User object that comprised all three columns with the correct types, a DbContext with a DbSet, and an IServiceCollection that used said DbContext.
The API worked fine, returning a JWT token as needed.
I have since needed to add an extra parameter to check and pass to the JWT creation - the relevant column has been created in the DB, the User object in the API has been updated to include the extra parameter and that extra parameter is observed in the Intellisense throughout the API code.
The issue is that when the API is deployed to Azure, the extra parameter isn't being recognised and populated; how do I make the API correctly update to use the new DbContext and retrieve the User with the extra parameter?
(I've omitted the interfaces for brevity, as they're essentially the corresponding classes)
User, UserRequest and MyApiDbContext Classes:
using Microsoft.EntityFrameworkCore;
namespace MyApi.Models
{
// Basic user model used for authentication
public class User
{
public string UserId { get; set; }
public byte[] PasswordHash { get; set; }
public byte[] PasswordSalt { get; set; }
public string ExtraParam { get; set; } // newly added parameter
}
public class UserRequest
{
public string UserId { get; set; }
public string password { get; set; }
}
public class MyApiDbContext : DbContext
{
public MyApiDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet Users { get; set; }
}
}
The AuthRepository that retrieves the user:
using Microsoft.EntityFrameworkCore;
using MyApi.Interfaces;
using MyApi.Models;
using System.Threading.Tasks;
namespace MyApi.Services
{
public class AuthRepository : IAuthRepository
{
private readonly MyApiDbContext _context;
public AuthRepository(MyApiDbContext context)
{
_context = context;
}
public async Task Login(string username, string password)
{
// my test user gets returned
User returnedUser = await _context.Users.FirstOrDefaultAsync(x => x.UserId == username);
if (returnedUser == null)
{
return null;
}
// the password get verified
if (!VerifyPasswordHash(password, returnedUser.PasswordHash, returnedUser.PasswordSalt))
{
return null;
}
// this does not get changed, but the value set in the DB is definitely a string
if (returnedUser.ExtraParam == null || returnedUser.ExtraParam == "")
{
returnedUser.ExtraParam = "placeholder"
}
return returnedUser;
}
}
}
The AuthService that calls the AuthRepository for the user then "creates the JWT token" (just returning a string for this example), currently set up to return the user details:
using Microsoft.Extensions.Options;
using MyApi.Interfaces;
using MyApi.Models;
using System;
using System.Threading.Tasks;
namespace MyApi.Services
{
public class AuthService : IAuthService
{
private readonly IOptions _settings;
private readonly IAuthRepository _repository;
public AuthService(IOptions settings, IAuthRepository repository)
{
_repository = repository;
_settings = settings;
}
public async Task Login(string username, string password)
{
User returnedUser = await _repository.Login(username, password);
if (returnedUser != null)
{
// currently returns "UserIdInDB,ProvidedPasswordFromLogin,"
return $"{returnedUser.UserId},{password},{returnedUser.ExtraParam}";
}
return null;
}
}
}
The controller that calls the AuthService:
using Microsoft.AspNetCore.Mvc;
using MyApi.Interfaces;
using MyApi.Models;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MyApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly MyApiDbContext _context;
private readonly IAuthService _authService;
public AuthController(MyApiDbContext context, IAuthService authService)
{
_context = context;
_authService = authService;
}
[HttpPost("login")]
public async Task Login(UserRequest loginUser)
{
string token = await _authService.Login(loginUser.UserId, loginUser.Password);
if (token != null)
{
return Ok(token);
}
return Unauthorized("Access Denied!!");
}
}
}
The startup class that registers everything:
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using MyApi.Interfaces;
using MyApi.Models;
using MyApi.Services;
using Microsoft.Extensions.Azure;
using Azure.Storage.Queues;
using Azure.Storage.Blobs;
using Azure.Core.Extensions;
using System;
namespace MyApi
{
public class Startup
{
public IConfiguration Configuration { get; }
private readonly ILogger _logger;
private readonly IConfiguration _config;
public Startup(ILogger logger, IConfiguration config)
{
_logger = logger;
_config = config;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add dBContext for DB
services.AddDbContextPool(options => options.UseSqlServer(_config.GetConnectionString("MyAzureDb")));
// Add DI Reference for Repository
services.AddScoped();
// Add DI Reference for Azure Blob Storage Processes
services.AddScoped();
// DI Reference for AuthService
services.AddScoped();
// Add configuration section for Constructor Injection
services.Configure(_config.GetSection("MyApiBlobStorage"));
services.AddMvc(mvcOptions => mvcOptions.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Latest);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII
.GetBytes(_config.GetSection("MyApiBlobStorage:Secret").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
options.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = context =>
{
_logger.LogWarning("Token authentication failed whilst attempting to upload file");
return Task.CompletedTask;
}
};
});
services.AddAzureClients(builder =>
{
builder.AddBlobServiceClient(Configuration["ConnectionStrings:MyApiBlobStorage/AzureBlobStorageConnectionString:blob"], preferMsi: true);
});
}
// 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();
}
else
{
// 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.UseHttpsRedirection();
app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
app.UseAuthentication();
app.UseMvc();
}
}
internal static class StartupExtensions
{
public static IAzureClientBuilder AddBlobServiceClient(this AzureClientFactoryBuilder builder, string serviceUriOrConnectionString, bool preferMsi)
{
if (preferMsi && Uri.TryCreate(serviceUriOrConnectionString, UriKind.Absolute, out Uri serviceUri))
{
return builder.AddBlobServiceClient(serviceUri);
}
else
{
return builder.AddBlobServiceClient(serviceUriOrConnectionString);
}
}
public static IAzureClientBuilder AddQueueServiceClient(this AzureClientFactoryBuilder builder, string serviceUriOrConnectionString, bool preferMsi)
{
if (preferMsi && Uri.TryCreate(serviceUriOrConnectionString, UriKind.Absolute, out Uri serviceUri))
{
return builder.AddQueueServiceClient(serviceUri);
}
else
{
return builder.AddQueueServiceClient(serviceUriOrConnectionString);
}
}
}
}
Let me know if there is anything else required for understanding: the only difference between before and now is the addition of ExtraParam and the corresponding references throughout for the API, and the DB getting the identically named column.
I tried adding the parameter and deploying it to Azure and making the POST request as normal, starting and stopping the app service, deploying the API while the app service was stopped and starting it again, and restarting the app service. I don't know how much I could try changing up what I'm doing, I'm trying to do exactly the same as before, but with an extra parameter getting requested from the DB.
I can also confirm that the DB contains the ExtraParam column, and that it contains values against the existing data rows, as viewed using the Azure Portal's DB Query Editor.
JavaScript questions and answers, JavaScript questions pdf, JavaScript question bank, JavaScript questions and answers pdf, mcq on JavaScript pdf, JavaScript questions and solutions, JavaScript mcq Test , Interview JavaScript questions, JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)