Blazor : How to read appsetting.json from a class in .NET 6? - blazor-server-side

The following is working for me, but not sure this is the right way to do use DI in .NET6 blazor.
I have the following class
public class Authentication
{
private IConfiguration _configuration;
private AppState _appState;
public Authentication(IConfiguration Configuration, AppState appState)
{
_configuration = Configuration;
_appState = appState; ;
}
public async Task<AccessToken?> getAccessToken()
{
var tokenServer = _configuration.GetValue<string>("tokenUrl");
var clientID = _configuration.GetValue<string>("ABC:ClientID");
var clientSecret = _configuration.GetValue<string>("ABC:ClientSecret");
var grantType = _configuration.GetValue<string>("ABC:GrantType");
AccessToken? accessToken = null;
.............
............
return accessToken;
}
}
in my code behind of razor page
namespace XXXXXXXXXXX.Pages
{
public partial class Index
{
[Inject]
public ILogger<Index> _Logger { get; set; }
[Inject]
public IConfiguration Configuration { get; set; }
[Inject]
public AppState _appState { get; set; }
**Authentication auth;**
protected override void OnInitialized()
{
**auth = new Authentication(Configuration, _appState);**
base.OnInitialized();
}
private async Task HandleValidSubmit()
{
_Logger.LogInformation("HandleValidSubmit called");
auth.getAccessToken();
// Process the valid form
}
}
}
My Question is I was Expecting the DI to do its magic and Insert the Dependency in my class.
but to get this working i had to write
auth = new Authentication(Configuration, _appState);
I was expecting to instantiate
using auth = new Authentication() , but this one throws compiler error.

Related

Asp.Net Core 3.1 LinqToDB.Identity UserManager CreateAsync Error

I have implemented LinqToDB.Identity into my project. I have been trying to create a user by using .Net Identity UserManager, but I am getting an error. I have also implemented LinqToDB.Identity optimizations, such as AddLinqToDBStores and IdentityConnectionFactory.
As I have mentioned about it, I am getting an error like this when I try to create an user.
{"Method not found: 'Int32 LinqToDB.DataExtensions.Insert(LinqToDB.IDataContext, System.__Canon, System.String, System.String, System.String)'."}
Here is my AddLinqToDBStores options and configurations.
public static void AddDevPlatformAuthentication(this IServiceCollection services, IConfiguration configuration)
{
services.AddIdentity<AppUser, LinqToDB.Identity.IdentityRole<int>>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 4;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.User.RequireUniqueEmail = true;
//TODO
//options.User.RequireUniqueEmail = true;
//options.SignIn.RequireConfirmedEmail = true;
//options.Lockout.MaxFailedAccessAttempts = 5;
//options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(3);
}).AddLinqToDBStores<int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>(new
IdentityConnectionFactory(new SqlServerDataProvider(ProviderName.SqlServer, SqlServerVersion.v2017), "SqlServerIdentity", DataSettingsManager.LoadSettings().ConnectionString))
.AddDefaultTokenProviders();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
// Uncomment the following lines to enable logging in with third party login providers
JwtTokenDefinitions.LoadFromConfiguration(configuration);
services.ConfigureJwtAuthentication();
services.ConfigureJwtAuthorization();
}
Here is my IdentityConnectionFactory class that has inherited from IConnectionFactory interface.
public class IdentityConnectionFactory : IConnectionFactory
{
private static readonly Dictionary<string, HashSet<string>> _tables = new Dictionary<string, HashSet<string>>();
private readonly string _configuration;
private readonly string _connectionString;
private readonly string _key;
private readonly IDataProvider _provider;
public IdentityConnectionFactory(IDataProvider provider, string configuration, string connectionString)
{
_provider = provider;
Configuration.Linq.AllowMultipleQuery = true;
//DataConnection.AddConfiguration(configuration, connectionString, provider);
_configuration = configuration;
_connectionString = connectionString;
_key = _configuration + "$$" + _connectionString;
}
public IDataContext GetContext()
{
return new DataContext(_provider, _connectionString);
}
public DataConnection GetConnection()
{
var db = new DataConnection(_provider, _connectionString);
db.AddMappingSchema(AdditionalSchema);
return db;
}
protected MappingSchema AdditionalSchema
{
get
{
if (!(Singleton<MappingSchema>.Instance is null))
return Singleton<MappingSchema>.Instance;
Singleton<MappingSchema>.Instance =
new MappingSchema(_provider.Name) { MetadataReader = new FluentMigratorMetadataReader() };
return Singleton<MappingSchema>.Instance;
}
}
There are so many code blocks that I can not paste here. I would be very happy if someone could help.
If you would like to see the project, you can check here;
https://github.com/dogaanismail/DevPlatform
This problem has been solved by adding LinqToDB.Identity class library into the solution. I have created an issue on Github. You can check from this link.
https://github.com/linq2db/linq2db/issues/2400
I have uninstalled LinqToDB.Identity that is a nuget package. Instead of using LinqToDB.Identity nuget package, it is better to use LinqToDB.Identity class library. In addition, I can debug this class library. It is really useful!
You can obtain LinqToDB.Identity class library with this link
https://github.com/linq2db/LinqToDB.Identity/tree/master/src/LinqToDB.Identity
or if you would, you can check my project that is called DevPlatform.
https://github.com/dogaanismail/DevPlatform
In addition to all of these, I have added an IdentityAttribute for my AppUser primary key. I did not create an user without this attribute.
public class AppUser : IdentityUser<int>, IEntity
{
[Required, Identity]
[Key]
public override int Id { get => base.Id; set => base.Id = value; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public int? CreatedBy { get; set; }
public int? ModifiedBy { get; set; }
public int? StatusId { get; set; }
}
As a result of creating a class library and adding LinqToDB.Identity classes, such as IdentityUser, DefaultConnectionFactory, IConcurrency etc. I created an user successfully by using UserManager.

How can I pass the authenticated User Id to the class library project in asp.net core via DI?

I have a NLayers application:
asp.net core mvc
asp.net web api
and some of my class libraries:
DataLayer
DomainClasses
Models
Services
here is my BaseService in ServicesLayer:
public abstract partial class BaseService
{
protected BaseService(AppDbContext dbContext
, UserManager<MyApplicationUser> userManager
, int authenticatedUserId)
{
DbContext = dbContext;
AuthenticatedUserId = authenticatedUserId;
MyUserManager = userManager;
Init();
}
public AppDbContext DbContext { get; }
protected UserManager<MyApplicationUser> MyUserManager;
public string AuthenticatedUserId { get; }
protected virtual void Init()
{
//
}
...
}
and one of my child service classes:
public class BookService :BaseService
{
public BookService(AppDbContext dbContext
, UserManager<MyApplicationUser> userManager
, int authenticatedUserId)
:base(dbContext,userManager, authenticatedUserId)
{
}
}
I want to access the authenticated user id (from Asp net core) in my services (class library). How can I pass it via DI or something else?
Updated based on #Frank's suggestion:
public class CommonServicesContainer
{
public AppDbContext DbContext { get; set; }
public AppUserManager UserManager { get; set; }
public int AuthenticatedUserId{ get; set; }
public CommonServicesContainer(AppDbContext appDbContext, AppUserManager userManager, string authenticatedUserId)
{
DbContext = dbContext;
UserManager = userManager;
AuthenticatedUserId = autheticatedUserId;
}
}
my startup:
services.AddScoped<AppDbContext>();
services.AddScoped<AppUserManager>();
services.AddScoped(x =>
{
var authenticatedUserId = x.GetRequiredService<IHttpContextAccessor>().HttpContext.User.Identity.Name;
return new CommonServicesContainer(x.GetRequiredService<AppDbContext>()
, x.GetRequiredService<AppUserManager>()
, authenticatedUserId);
});
AccountController :
private readonly CommonServicesContainer _commonServicesContainer;
public AccountController(CommonServicesContainer commonServicesContainer)
{
_commonServicesContainer = commonServicesContainer;
// ...
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
// ...
if(ModelState.IsValid)
{
var isValid = await _usersService.AreCredentialsValidAsync(model.Username, model.Password);
if(isValid)
{
var foundUser = await _usersService.GetByUserNameAsync(model.Username);
await HttpContext.SignInAsync(
foundUser.SubjectId,
foundUser.UserName);
//_commonServicesContainer.AuthenticatedUserId = foundUser.Id;
// ...
}
// ...
}
You can do that by register a AuthenticatedUser type as a AddScoped.
class AuthenticatedUser {
public int? UserId {get;set;}
public bool IsAuthenticated => int.HasValue;
}
in Startup.cs of your AspNetCore project:
public IServiceProvider ConfigureServices(IServiceCollection services) {
...
services.AddScoped<AuthenticatedUser>();
...
}
Somewhere you do the authentication, you get the AuthenticatedUser and set the UserId.
Since AuthenticatedUser is added as scoped it acts as global (same instance) for the particular httprequest scope. So all .GetService<AuthenticatedUser> / .GetRequiredService<AuthenticatedUser> will have the same instance - within the same scope.
Each http-request has it is own scope, and thereby also their own AuthenticatedUser.
When the user is Authenticated, using AspNetCore Identity, you can find the AspNetUsers Id by:
if( httpContext.User.Identity.IsAuthenticated ) {
var userIdClaim = httpContext.User.Claims.SingleOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
var aspNetUsersId = userIdClaim?.Value ?? 0;
}
This can be done as Middleware, then setting AuthenticatedUser.UserId.

In ASP.NET Core 2.2, How To Get Base URL in startup service

I've got an asp.net core 2.2 project with a startup.cs service that does an async REST GET call back to my current site and then returns the result (View Component using DI) back to the razor view.
In the service, I want to call "/api/sessions" and not "http://localhost:3433/api/sessions". I know I could use the ~ ta helper if I were inside my razor page to get the base path to the web server, but how can I get that from a service?
Here is my service and relevant code.
From: SessionsService.cs (this is where I don't want http://localhost but just ~/
public class Session
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
public class SessionsService : ISessionsService
{
public async Task<List<Session>> GetSessions(int speakerId)
{
var uri = new Uri("http://localhost:50463/api/sessions");
var httpClient = new HttpClient();
var result = await httpClient.GetStringAsync(uri);
var sessions = JsonConvert.DeserializeObject<List<Session>>(result);
return sessions;
}
}
From: startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISessionsService, SessionsService>();
From: index.cshtml
<vc:speaker-card speaker="#speaker" ></vc:speaker-card>
From: SpeakerCardViewComponent.cs
{
private ISessionsService _sessionsService;
public SpeakerCardViewComponent(ISessionsService sessionsService)
{
_sessionsService = sessionsService;
}
public async Task<IViewComponentResult> InvokeAsync(
Speaker speaker)
{
var sessions = await _sessionsService.GetSessions(101);
speaker.Sessions = sessions;
return View(speaker);
}
}
ANSWER AS SUGGESTED BY KIRK LARKIN FOLLOWS:
public async Task<List<Session>> GetSessions(int speakerId,string baseUrl)
{
var uri = new Uri($"{baseUrl}api/sessions");
var httpClient = new HttpClient();
var result = await httpClient.GetStringAsync(uri);
var sessions = JsonConvert.DeserializeObject<List<Session>>(result);
return sessions;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISessionsService, SessionsService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
public class SpeakerCardViewComponent : ViewComponent
{
private ISessionsService _sessionsService;
private IHttpContextAccessor _httpContextAccessor;
public SpeakerCardViewComponent(ISessionsService sessionsService, IHttpContextAccessor httpContextAccessor)
{
_sessionsService = sessionsService;
_httpContextAccessor = httpContextAccessor;
}
public async Task<IViewComponentResult> InvokeAsync(
Speaker speaker)
{
var isHttps = _httpContextAccessor.HttpContext.Request.IsHttps;
var baseUrl = isHttps ? "https://" : "http://"
+ _httpContextAccessor.HttpContext.Request.Host.Value
+ "/";
var sessions = await _sessionsService.GetSessions(speaker.SpeakerId, baseUrl);
speaker.Sessions = sessions;
return View(speaker);
}
}

.Net Core How to Access Configuration Anywhere in application

I have read through the documentation on the different ways to setup and access configuration in .Net Core 2.1 and also the options pattern that seems to be recommended (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1). However, I can't seem to get what I want working:
I have done the following:
AppSettings:
{
"ConnectionStrings": {
"DefaultConnStr": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true",
"AW2012ConnStr": "Server=localhost;Database=AW2012;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security=true"
}
}
MyConfig:
public class MyConfig
{
public string AWConnStr { get; }
public string DefaultConnStr { get; }
}
Startup:
public class Startup
{
public IConfiguration _config { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
_config = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
//add config to services for dependency injection
//services.AddTransient<IMyConfig, MyConfig>();
//services.AddScoped<IMyConfig, MyConfig>();
var section = _config.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
private static void HandleGetData(IApplicationBuilder app)
{
//DataHelper dataHelper = new DataHelper(_dataHelper);
var _dataHelper = app.ApplicationServices.GetService<DataHelper>();
app.Run(async context =>
{
//await context.Response.WriteAsync("<b>Get Data</b>");
//await context.Response.WriteAsync(dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
await context.Response.WriteAsync(_dataHelper.GetCompetitions(context.Request.QueryString.ToString()));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Map("/Route1", HandleRoute1);
app.Map("/Route2", HandleRoute2);
app.Map("/GetData", HandleGetData);
app.Run(async (context) =>
{
await context.Response.WriteAsync("Non Mapped Default");
});
}
}
I would like to then access the configuration in any class anywhere in my code. So for example I have the following class where I would like to just read the configuration information:
public interface IDataHelper
{
string GetCompetitions(string val);
}
public class DataHelper : IDataHelper
{
private readonly MyConfig _settings;
public DataHelper(IOptions<MyConfig> options)
{
_settings = options.Value;
}
public string GetCompetitions( string queryStringVals)
{
return _settings.AWConnStr;
}
}
As shown above in my Startup class I then want to access/call something in the HandleGetData function in my startup, so that when I browse to the following route: http://localhost:xxxxx/getdata I get back the response from the Something.GetData function.
Is this correct? The problem I'm having is that when I create an instance of class Something, it is requiring me to pass in the configuration object, but doesn't that defeat the purpose of injecting it. How should I be setting this up to work similar to how DBContext gets the context injected with the configuration options. And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
I would say that in .Net Core application you shouldn't pass instance of IConfiguration to your controllers or other classes. You should use strongly typed settings injected through IOtions<T> instead. Applying it to your case, modify MyConfig class (also property names should match names in config, so you have to rename either config (DefaultConnection->DefaultConnStr, AW2012ConnStr->AWConnStr or properies vice versa):
public class MyConfig
{
public string AWConnStr { get; set; }
public string DefaultConnStr { get; set; }
}
Register it:
public void ConfigureServices(IServiceCollection services)
{
// in case config properties specified at root level of config file
// services.Configure<MyConfig>(Configuration);
// in case there are in some section (seems to be your case)
var section = Configuration.GetSection("ConnectionStrings");
services.Configure<MyConfig>(section);
}
Inject it to required service:
public class MyService
{
private readonly MyConfig _settings;
public MyService(IOptions<MyConfig> options)
{
_settings = options.Value;
}
}
And what's the difference between services.AddTransient and
services.AddScoped? I've seen both as a way to register the service.
Transient lifetime services are created each time they're requested.
Scoped lifetime services are created once per request.
You have to do the same thing for the Something as you did for MyConfig like:
public interface ISomething
{
string GetSomeData();
}
Then:
public class Something : ISomething
{
public IConfiguration _config { get; set; }
public Something(IConfiguration configuration)
{
_config = configuration;
}
public string GetSomeData()
{
return _config["DefaultConnStr"];
}
}
Then in the ConfigureService method of the Startup class as follows:
services.AddScoped<ISomething,Something>();
Then call the GetSomeData() as follows:
public class CallerClass
{
public ISomething _something { get; set; }
public CallerClass(ISomething something)
{
_something = something;
}
public string CallerMethod()
{
return _something.GetSomeData();
}
}
Then:
And what's the difference between services.AddTransient and services.AddScoped? I've seen both as a way to register the service.
Here is the details about this from microsoft:
Service Lifetime details in ASP.NET Core

GraphQL authentication with Asp.net core using JWT

I am using for GraphQL for .NET package for graphql. But I couldn't understand how can I authentication with JWT in graphql query or mutation.
I read the guide about authorization but I couldn't accomplish.
I need help with GraphQL for .NET authentication.
Any help will be appreciated.
Thanks
The guide is around authorization. The step you're looking for is the authentication and since graphql can be implemented using a ASP.Net API controller, you can implement JWT authentication as you would with any controller.
Here is a sample grapql controller using an Authorize attribute. You could, however, implement this using filter or if you want full control, custom middleware.
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class GraphQLController : ControllerBase
{
private readonly IDocumentExecuter executer;
private readonly ISchema schema;
public GraphQLController(IDocumentExecuter executer, ISchema schema)
{
this.executer = executer;
this.schema = schema;
}
[HttpPost]
public async Task<ActionResult<object>> PostAsync([FromBody]GraphQLQuery query)
{
var inputs = query.Variables.ToInputs();
var queryToExecute = query.Query;
var result = await executer.ExecuteAsync(o => {
o.Schema = schema;
o.Query = queryToExecute;
o.OperationName = query.OperationName;
o.Inputs = inputs;
o.ComplexityConfiguration = new GraphQL.Validation.Complexity.ComplexityConfiguration { MaxDepth = 15};
o.FieldMiddleware.Use<InstrumentFieldsMiddleware>();
}).ConfigureAwait(false);
return this.Ok(result);
}
}
public class GraphQLQuery
{
public string OperationName { get; set; }
public string Query { get; set; }
public Newtonsoft.Json.Linq.JObject Variables { get; set; }
}
In the Startup.cs I have configured JWT bearer token authentication.
Hope this helps.
I myself struggled for two days as well. I'm using https://github.com/graphql-dotnet/authorization now with the setup from this comment (from me): https://github.com/graphql-dotnet/authorization/issues/63#issuecomment-553877731
In a nutshell, you have to set the UserContext for the AuthorizationValidationRule correctly, like so:
public class Startup
{
public virtual void ConfigureServices(IServiceCollection services)
{
...
services.AddGraphQLAuth(_ =>
{
_.AddPolicy("AdminPolicy", p => p.RequireClaim("Role", "Admin"));
});
services.AddScoped<IDependencyResolver>(x => new FuncDependencyResolver(x.GetRequiredService));
services.AddScoped<MySchema>();
services
.AddGraphQL(options => { options.ExposeExceptions = true; })
.AddGraphTypes(ServiceLifetime.Scoped);
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
...
app.UseMiddleware<MapRolesForGraphQLMiddleware>(); // optional, only when you don't have a "Role" claim in your token
app.UseGraphQL<MySchema>();
...
}
}
public static class GraphQLAuthExtensions
{
public static void AddGraphQLAuth(this IServiceCollection services, Action<AuthorizationSettings> configure)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthorizationEvaluator, AuthorizationEvaluator>();
services.AddTransient<IValidationRule, AuthorizationValidationRule>();
services.AddTransient<IUserContextBuilder>(s => new UserContextBuilder<GraphQLUserContext>(context =>
{
var userContext = new GraphQLUserContext
{
User = context.User
};
return Task.FromResult(userContext);
}));
services.AddSingleton(s =>
{
var authSettings = new AuthorizationSettings();
configure(authSettings);
return authSettings;
});
}
}
public class GraphQLUserContext : IProvideClaimsPrincipal
{
public ClaimsPrincipal User { get; set; }
}
public class MapRolesForGraphQLMiddleware
{
private readonly RequestDelegate _next;
public MapRolesForGraphQLMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// custom mapping code to end up with a "Role" claim
var metadata = context.User.Claims.SingleOrDefault(x => x.Type.Equals("metadata"));
if (metadata != null)
{
var roleContainer = JsonConvert.DeserializeObject<RoleContainer>(metadata.Value);
(context.User.Identity as ClaimsIdentity).AddClaim(new Claim("Role", string.Join(", ", roleContainer.Roles)));
}
await _next(context);
}
}
public class RoleContainer
{
public String[] Roles { get; set; }
}