passing custom options to AuthenticationStateProvider - c#

Advice?
I have a blazor server application using a custom authentiationstateprovider. It uses ProtectedSessionStorage to save the autentication state. Since I may want to reuse this code I want to pass the session key to the constructor but I'm not sure how to do that.
I'm thinking something like this:
Program.cs
.
.
builder.Services.AddScoped<ProtectedSessionStorage>();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>(options => { sessionKey = "abcdef"} );
.
.
CustomAuthenticationStateProvider.cs
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly ProtectedSessionStorage _sessionStorage;
private readonly string _sessionKey;
public CustomAuthenticationStateProvider(ProtectedSessionStorage sessionStorage, sessionKey = "12345")
{
_sessionStorage = sessionStorage;
_sessionKey = sessionKey;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
try
{
var userSessionStorageResult = await _sessionStorage.GetAsync<UserSession>(_sessionKey);
.
.
.
}
.
.
.
}
This of course did not work. Can anyone tell me if I'm even on the right track?

Add the session key in AppSettings and then access and inject it via the Options pattern.
See this Ms article to show you how it's done - https://learn.microsoft.com/en-us/dotnet/core/extensions/options.

Related

Inject custom created object based on claims

I'm using claims based authentication in my Blazor Server app. When the user logs in to my app, I define a claim which contains a specific ID to identify the user within the database.
Now I want to get an object based on its value which I can use within my app.
For example: let's say the value from my claim is 1. Now I need a way to get the data for user 1 from the database and inject the object into my razor components/pages to make all properties accessible at any time within my app. I think this can be achieved with some sort of middleware but I'm not sure about this.
My current approach is to access the HttpContext within the _Host.cshtml file which loads the appropriate data to the page on a page reload but not when changing pages using a NavLink or the NavigationManager.
How can I get the relevant data to load each time the active page is changed?
I tried to adjust #Hans code but by using AuthenticationStateProvider
using System.Security.Claims
using Microsoft.AspNetCore.Components.Authorization
public class ClaimsPrincipalDataService
{
private readonly AuthenticationStateProvider AuthenticationStateProvider;
private readonly DbContext DbContext;
public ClaimsPrincipalDataService(AuthenticationStateProvider AuthenticationStateProvider , DbContext DbContext)
{
this.AuthenticationStateProvider = AuthenticationStateProvider;
this.DbContext = DbContext;
}
private async Task<User> GetUserAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
var userId = user.FindFirst(ClaimTypes.NameIdentifier).Value;
return await DbContext.Users.FindAsync(userId);
}
else
{
//do something
}
}
}
Add scope
services.AddScoped<ClaimsPrincipalDataService>();
Inject the service in your component
#inject ClaimsPrincipalDataService ClaimService
#code {
private User _user;
protected override async Task OnInitializedAsync()
{
_user = await ClaimService.GetUserAsync();
}
}

Pass a value globally

Im new to xamarin/c#, im trying to make an application with login , and Im trying to pass the logged in userid inside the application, the question is , how do I pass or make the user id keeps floating inside after the login page? Should I keep passing it in every page using queryproperty or there's better way to keep it , like a specific file to to put it so that every page can call it?
You can use the Application.Properties collection to store things that need to be accessible to the entire application.
To store the user ID you would use
Application.Current.Properties("UserID") = UserID;
and to retrieve it you would use
UserID = Application.Current.Properties("UserID");
In C# it's not possible to define true global variables (meaning that they don't belong to any class). using a static class is a valid alternative option so you can create something like this:
public static class Globals
{
public Dictionary<int, UserObject> Users = new Dictionary<int, UserObject>();
}
Now, you'll be able to access The Users's dictionary property and add/remove/modify login users
Following Hans Kesting comment, Please note that An Xamarin app servers a single user at at time, so you can refactor the above from a dictionary to UserObject
Static classes - bad practic for contains info. You can use IoC-container. I don't know xamarin, if you have startup-class (how WPF), you can make ioc-container:
Install Autofac Install-Package Autofac -Version 5.0.0
Rigster user content:
class StartupClass
{
public static IContainer Container { get; set; }
public void OnStartup()
{
var containerBuilder = new ContainerBuilder();
var userContent = smthMethodForGiveUserContent();
containerBuilder.RegisterInstance<User>(userContent); //register UserType!
Container = containerBuilder.Build();
}
}
Resolve user content:
class SmthClass
{
public void Method()
{
using(var scope = StartupClass.Container.BeginLifetimeScope())
{
var userContent = scope.Resolve<User>(); //IT'S YOUR OBJECT, USE!!
//smth code..
}
}
}
The quickest way is to define a variable in App, it can be accessible to the entire project .
Because App itself has been defined inside Application class ,and it is a static property .
public partial class App : Xamarin.Forms.Application
{
public string UserID;
}
// set or get
(App.Current as App).UserID

Can we Pass ClaimIndentity in constructor

I want to implement a scenario where, Presently in each method of my project, i am accessing the userId which i got from claims.
So instead of passing claim.UserId in every method, i want to implement a solution in such way that claims can initialised in service DI and it does not need to pass in every method so whenever the service initialise that claims could also get initialise same time.
for e.g
Service
{
// something at here
method1{
}
method2{
}
}
please suggest me any doc or article or best way to do it.
You could use IHttpContextAccessor for it.
public class ServiceWithUserId
{
private readonly Guid userId;
private const string nameIdentifierType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
public ServiceWithUserId(IHttpContextAccessor httpContextAccessor)
{
userId = Guid.Parse(httpContextAccessor.HttpContext.User.Identities.Single().Claims.Single(c => c.Type == nameIdentifierType).Value);
}
public void Method1()
{
if (userId == Guid.Empty)
{
// ...
}
}
}
You also have to add IHttpContextAccessor into ServiceCollection.
services.AddHttpContextAccessor();

How to conditionally use Authorization based on appsettings in ASP.NET Core

I have a ASP.NET Core 3.1 WebApi in which I using OAuth based Authentication and Authorization. On the Controllers, I add the Authorize attributes providing the policy name to apply. However, for my local development, I want to skip the authentication/authorization part. Lets say, I add some appsettings to indicate whether to use AA or not. Based on that, in the Configure method in startup class, I can conditionally use below code snippet to activate the required middleware.
if(configuration.GetSection("OAuth:Enabled") == true)
{
app.UseAuthentication();
app.UseAuthorization();
}
However, my controller is still decorated with Authorize attribute. Currently, unless I comment it out on each controller, i cannot disable Authorization. Is there any suggestion on how this can be achieved or any alternate option to temporarily bypass Authorization based on single configuration?
Thanks!
You could just create a custom configuration and wrap Authorize attributes with compiler conditionals e.g.
#if !BYPASS_AUTH
[Authorize()]
#endif
public void Blah(...)
... but this is a bit messy, and if you're like me and don't like seeing compiler directives littered throughout your code, you'd probably like to hide this away in a custom authorize attribute or something where you can do imperative auth as described here.
There's probably multiple ways to do this, but here's one way using an IAsyncActionFilter & TypeFilterAttribute implementations. This approach allows me to dependency inject the IAuthorizationService for imperative auth.
Note: following code is untested, so might be typos and might need refinement...
public class CustomAuthorizeAttribute : TypeFilterAttribute
{
public CustomAuthorizeAttribute(string policyName)
: base(typeof(CustomAuthorizeAsyncActionFilterAttribute))
{
Arguments = new object[] {policyName};
}
}
public class CustomAuthorizeAsyncActionFilterAttribute : Attribute, IAsyncActionFilter
{
private readonly IAuthorizationService _authorizationService;
private readonly AuthSettings _authSettings;
private readonly string _policyName;
public CustomAuthorizeAsyncActionFilterAttribute(
IAuthorizationService authorizationService,
IOptions<AuthSettings> authSettings
string policyName)
{
_authorizationService = authorizationService;
_authSettings = authSettings.Value;
_policyName = policyName;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (_authSettings.AuthEnabled)
{
var authorizationResult = await _authorizationService.AuthorizeAsync(context.HttpContext.User, context.ActionArguments, _policyName);
if (authorizationResult.Succeeded)
{
await next.Invoke();
}
else
{
context.Result = new ForbidResult();
}
}
else
{
await next.Invoke();
}
}
}
Oh and you'll need to change all your [Authorize] attributes to [CustomAuthorize].

Accessing Session state outside controller

I am trying to access data saved in Session state, in an ASP.Net Core Web Application, outside the controller, but the httpcontext is always null, how do I send the state over to a class?
I have added the correct statements in Startup.cs, to use sessions.
Furthermore, using this inside the controller works perfectly fine:
HttpContext.Session.SetString("Threshold",threshold);
HttpContext.Session.GetString("Treshold");
both work completely fine when accessing within the controller, yet I want to access this data in another class. Currently I am just using a static variable, but this is of course not the way to go, I want to access the session in here:
public class ImageAnalysisExtensionValues
{
public static double ConfidenceThreshold { get; set; }
}
(Data has been converted to double).
What do I do?
You can make use of Asp.Net Cores dependency injection and use the IHttpContextAccessor interface.
You have to register it first in your Startup.cs class (it is not always registered as default - therefore the use of TryAddSingleton<>()):
public void ConfigureServices(IServiceCollection services)
{
// ...
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// ...
}
Then use it like this:
public YourClassOutsideOfController
{
private IHttpContextAccessor _contextAccessor;
public YourClassOutsideOfController(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
private void YourMethod()
{
var context = _contextAccessor.HttpContext;
context.Session.SetString("Threshold",threshold);
context.Session.GetString("Threshold");
}
}

Categories