Extend ClaimsAbpSession - aspnetboilerplate

I need to extend the ClaimsAbpSession and create new properties to be sent in the requests between the angular app and the server.
Actually, I´ve stored these new properties using claims. But when the user refreshes the page, the values in the claims are lost.

MyAppSession.cs
// Define your own session and add your custom field to it.
// Then, you can inject MyAppSession and use its new property in your project.
public class MyAppSession : ClaimsAbpSession, ITransientDependency
{
public MyAppSession(
IPrincipalAccessor principalAccessor,
IMultiTenancyConfig multiTenancy,
ITenantResolver tenantResolver,
IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) :
base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider)
{
}
public string UserEmail
{
get
{
var userEmailClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == "Application_UserEmail");
if (string.IsNullOrEmpty(userEmailClaim?.Value))
{
return null;
}
return userEmailClaim.Value;
}
}
}
UserClaimsPrincipalFactory.cs
// Override CreateAsync method to add your custom claim
public override async Task<ClaimsPrincipal> CreateAsync(User user)
{
var claim = await base.CreateAsync(user);
claim.Identities.First().AddClaim(new Claim("Application_UserEmail", user.EmailAddress));
return claim;
}

Related

ASP.Net Core:Claims Transformation and Authorization Policy

Please have a look at this url first https://weblogs.asp.net/imranbaloch/claims-transformation-and-authorization-policy-in-aspnet5-mvc6
public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity.IsAuthenticated)
{
// get this from cache or db
var country = "Pakistan";
(principal.Identity as ClaimsIdentity).AddClaim(new Claim("Nationality", country));
}
return Task.FromResult(principal);
}
when TransformAsync() will be called.......how to use it ?
[Authorize(Policy = "MustBePakistani")]
public IActionResult Message()
{
return Content("Hi Pakistani");
}
when Message action will be called then how asp.net mvc system will be able to understand what is user's nationality.....is it pakistani or indian ?
guide me how does it work. thanks
In addition to above two segments of the code in your question, you still need to add policy to authorization services in Startup.cs.
Example,
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.AddPolicy("MustBePakistani", policy =>
policy.RequireClaim("Nationality", "Pakistan"));
});
services.AddSingleton<IClaimsTransformation, ClaimsTransformer>();
...
}
private class ClaimsTransformer : IClaimsTransformation
{
// Can consume services from DI as needed, including scoped DbContexts
public ClaimsTransformer(IHttpContextAccessor httpAccessor)
{
}
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity.IsAuthenticated)
{
// get this from cache or db
var country = "Pakistan";
(principal.Identity as ClaimsIdentity)
.AddClaim(new Claim("Nationality", country));
}
return Task.FromResult(principal);
}
}
}

Custom authentication asp.net core web api

I want to use a secret key (api key) authorization asp.net core web api. The key will be passed in Authorization header like given below,
ex. Authorization keytype;h43484344343bbhfdjfdfhj34343
I want to write a middleware to read this key from request headers and call an internal api to validate the key.
In web api we can write a message handler to do this, but I am new to asp.net core. I'm seeing a lot of samples but they are using inbuilt JWT token authentication. But I wanted to use my own key and I decrypt this key and validate against a database entry.
Can anyone suggest some code samples on how to do this?
I have used this approach in a solution using asp core 1.1. First define a custom scheme:
public static class Authentication
{
public const string Scheme = "Custom";
}
You then have to inherit AuthenticationHandler<TOptions>. Here is where the logic for validating the header value will go:
public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authorizationHeader = Context.Request.Headers["Authorization"];
if (!authorizationHeader.Any())
return Task.FromResult(AuthenticateResult.Skip());
var value = authorizationHeader.ToString();
if (string.IsNullOrWhiteSpace(value))
return Task.FromResult(AuthenticateResult.Skip());
// place logic here to validate the header value (decrypt, call db etc)
var claims = new[]
{
new Claim(System.Security.Claims.ClaimTypes.Name, "Bob")
};
// create a new claims identity and return an AuthenticationTicket
// with the correct scheme
var claimsIdentity = new ClaimsIdentity(claims, Authentication.Scheme);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties(), Authentication.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
In order to inherit AuthenticationHandler you must create an options class where you set the AuthenticationScheme-property to the scheme you are using:
public class MyOptions : AuthenticationOptions
{
AuthenticationScheme = Authentication.Scheme;
}
After this you have to inherit AuthenticationMiddleware<TOptions>. This will create the handler you implemented in the previous step:
public class MyAuthenticationMiddleware : AuthenticationMiddleware<MyOptions>
{
public MyAuthenticationMiddleware(RequestDelegate next, IOptions<MyOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<MyOptions> CreateHandler()
{
return new MyAuthenticationHandler();
}
}
In order to easily plug in your middleware you can define these extension methods:
public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, IConfigurationSection config)
{
return app.UseMyAuthentication(options => {});
}
private static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, Action<MyOptions> configure)
{
var options = new MyOptions();
configure?.Invoke(options);
return app.UseMiddleware<MyAuthenticationMiddleware>(new OptionsWrapper<MyOptions>(options));
}
Then in your Startup class you can finally add your middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMyAuthentication(Configuration.GetSection("MyAuthenticationOptions"));
// other stuff
app.UseMvc();
}
Then add the AuthorizeAttribute on your actions specifying the scheme you just created:
[Authorize(ActiveAuthenticationSchemes = Authentication.Scheme)]
public IActionResult Get()
{
// stuff ...
}
There are a lot of steps but hopefully this will get you going!

MvxViewController.PresentationAttribute called before ViewModel is loaded

I have a problem with xamarin.ios and MvvmCross, I need to display an MvxViewController and it comes in two ways depending on who calls it, I get it with:
CustomViewController:
public partial class CustomViewController : MvxViewController<CustomViewModel>, IMvxOverridePresentationAttribute
{
public CustomViewController() : base("CustomViewController", null)
{
}
public MvxBasePresentationAttribute PresentationAttribute()
{
if (ViewModel.KindNavigation) //Here's the issue
{
return new MvxSidebarPresentationAttribute(MvxPanelEnum.Center, MvxPanelHintType.ResetRoot, true, MvxSplitViewBehaviour.Detail);
}
else
{
return new MvxModalPresentationAttribute
{
ModalPresentationStyle = UIModalPresentationStyle.OverFullScreen,
ModalTransitionStyle = UIModalTransitionStyle.CoverVertical
};
}
}
}
If I do ViewModel.anything, to get a parameter to define the type of presentation, the ViewModel, is null and I can't access. I have not even opened it, since the type of presentation for this view is not defined.
CustomViewModel:
public class CustomViewModel : MvxViewModel<string>, IDisposable
{
private readonly IMvxNavigationService _navigationService;
public CustomViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
private bool _KindNavigation;
public bool KindNavigation
{
get => _KindNavigation;
set => SetProperty(ref _KindNavigation, value);
}
public void Dispose()
{
throw new NotImplementedException();
}
public override Task Initialize(string parameter)
{
KindNavigation = Convert.ToBoolean(parameter);
System.Diagnostics.Debug.WriteLine("parameter: " + parameter);
return base.Initialize();
}
}
This is a restriction in MvvmCross, because the ViewModel is not loaded before the View is. This is also described in the documentation: https://www.mvvmcross.com/documentation/presenters/ios-view-presenter?scroll=446#override-a-presentation-attribute-at-runtime
To override a presentation attribute at runtime you can implement the IMvxOverridePresentationAttribute in your view controller and determine the presentation attribute in the PresentationAttribute method like this:
public MvxBasePresentationAttribute PresentationAttribute()
{
return new MvxModalPresentationAttribute
{
ModalPresentationStyle = UIModalPresentationStyle.OverFullScreen,
ModalTransitionStyle = UIModalTransitionStyle.CrossDissolve
};
}
If you return null from the PresentationAttribute the iOS View Presenter will fallback to the attribute used to decorate the view controller. If the view controller is not decorated with a presentation attribute it will use the default presentation attribute (a animated child presentation).
Note: Be aware that your ViewModel will be null during PresentationAttribute, so the logic you can perform there is limited here. Reason to this limitation is MvvmCross Presenters are stateless, you can’t connect an already instantiated ViewModel with a new View.

Custom route constraint causes intermittent 404 errors

I have an Asp.Net Core 1 RC1 application that uses a custom route constraint to control access to the application. The application (hosted on a server running IIS 7.5) is getting intermittent 404 errors which I suspect is caused by this routing constraint. Here you can see a screenshot that shows the intermittent 404 errors:
I suspect that this issue is related to the code that defines the route constraint not being thread-safe. The custom route constraint needs a DbContext because it needs to check in the database if the application is enabled for the brand specified in the route, and I suspect that this DbContext instance could be causing the issue. Here is how the routing is defined in the application:
// Add MVC to the request pipeline.
var appDbContext = app.ApplicationServices.GetRequiredService<AppDbContext>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "branding",
template: "branding/{brand}/{controller}/{action}/{id?}",
defaults: new { controller="Home", action="Index" },
constraints: new { brand = new BrandingRouteConstraint(appDbContext) });
});
And here is the custom route constraint:
// Custom route constraint
public class BrandingRouteConstraint : IRouteConstraint
{
AppDbContext _appDbContext;
public BrandingRouteConstraint(AppDbContext appDbContext) : base() {
_appDbContext = appDbContext;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
{
if (values.Keys.Contains(routeKey))
{
var whiteLabel = _appDbContext.WhiteLabels.Where(w => w.Url == values[routeKey].ToString()).FirstOrDefault();
if (whiteLabel != null && whiteLabel.EnableApplication != null && (bool)whiteLabel.EnableApplication)
{
return true;
}
}
return false;
}
}
Can anyone confirm that this issue is caused by the code not being thread-safe and recommend a way to change the implementation so that it is thread-safe?
I can't comment on RouteContraint's, haven't used them much, but have you tried Resource Based Authorization instead? Looks like it might be more suited to what you're trying to achieve?
From here and here:
Request authentication service inside your controller
public class DocumentController : Controller
{
IAuthorizationService authorizationService;
public DocumentController(IAuthorizationService authorizationService)
{
this.authorizationService = authorizationService;
}
}
Apply authorization checks in your Action:
public async Task<IActionResult> Edit(Guid documentId)
{
Document document = documentRepository.Find(documentId);
if (document == null)
{
return new HttpNotFoundResult();
}
if (await authorizationService.AuthorizeAsync(User, document, Operations.Edit))
{
return View(document);
}
else
{
return new HttpUnauthorizedResult();
}
}
I've used the OperationAuthorizationRequirement class in the sample, so define this class in your project:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = "Read" };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = "Update" };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = "Delete" };
}
Implement the authorization handler (using built in OperationAuthorizationRequirement requirement):
public class DocumentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
// Validate the requirement against the resource and identity.
// Sample just checks "Name"field, put your real logic here :)
if (resource.Name == "Doc1")
context.Succeed(requirement);
else
context.Fail();
}
}
And not forgetting ConfigureServices:
services.AddInstance<IAuthorizationHandler>(
new DocumentAuthorizationHandler());
It's a bit more work, but adds quite a lot of flexibility.

customizing odata output from asp.net web api

I'm using the new ASP.NET webapi odata (version 4.0.0 last published 27/2/2013 according to Nuget)
Basically I'm doing it as described here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api
I'm publishing my data transfer objects and the odata atom pub feed is created but I'd like to have some more control over it. Mainly I'd like to be able to do the following:
decide what goes on the title, author and updated elements for the feed
decide whether or not to have the edit links
change what is shown in <category term="X"and in m:type in sub properties that are classes in my application. Currently they expose the c# class names with the full namespace but I don't want to expose this.
Thanks.
The OData media type formatter is more extensible now. Samples follow.
1) decide what goes on the title, author and updated elements for the feed
public class AtomMetadataFeedSerializer : ODataFeedSerializer
{
public AtomMetadataFeedSerializer(IEdmCollectionTypeReference edmType, ODataSerializerProvider serializerProvider)
: base(edmType, serializerProvider)
{
}
public override ODataFeed CreateODataFeed(IEnumerable feedInstance, ODataSerializerContext writeContext)
{
ODataFeed feed = base.CreateODataFeed(feedInstance, writeContext);
feed.Atom().Title = new AtomTextConstruct { Kind = AtomTextConstructKind.Text, Text = "My Awesome Feed" };
return feed;
}
}
public class CustomSerializerProvider : DefaultODataSerializerProvider
{
public override ODataEntrySerializer CreateEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.IsCollection() && edmType.AsCollection().ElementType().IsEntity())
{
// feed serializer
return new AtomMetadataFeedSerializer(edmType.AsCollection(), this);
}
return base.CreateEdmTypeSerializer(edmType);
}
}
And register the custom serializer provider using,
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create(new CustomSerializerProvider(), new DefaultODataDeserializerProvider()));
2) customize edit links
public class CustomEntityTypeSerializer : ODataEntityTypeSerializer
{
public CustomEntityTypeSerializer(IEdmEntityTypeReference edmType, ODataSerializerProvider serializerProvider)
: base(edmType, serializerProvider)
{
}
public override ODataEntry CreateEntry(EntityInstanceContext entityInstanceContext, ODataSerializerContext writeContext)
{
ODataEntry entry = base.CreateEntry(entityInstanceContext, writeContext);
if (notProduceEditLinks)
{
entry.EditLink = null;
}
return entry;
}
}
public class CustomSerializerProvider : DefaultODataSerializerProvider
{
public override ODataEntrySerializer CreateEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.IsEntity())
{
// entity type serializer
return new CustomEntityTypeSerializer(edmType.AsEntity(), this);
}
return base.CreateEdmTypeSerializer(edmType);
}
}
and register the custom serializer provider as above.
We still don't support scenario 3 i.e aliasing type names and namespaces.

Resources