I am trying to develop an ASP MVC application and tried to created a simple Authentication which always works when creating a new project on my computer.
Now, i have a solution that was created on another machine which throws an exception when being run on my computer.
Here is the code that's throwing the exception
public IdentitySignInManager(BusinessModuleList modules, IAuthenticationManager authenticationManager)
: base(modules.Get<IdentityUserManager<TUser, TDataAccessModule, TDataContext>>(), authenticationManager)
{
Prerequisites = new List<Prerequisite>();
Modules = modules;
}
The above method is inside the IdentitySignInManager.cs file which looks like the following
public class IdentitySignInManager<TUser, TDataAccessModule, TDataContext> : SignInManager<TUser, String>, IBusinessModule
where TUser: UserBase
where TDataAccessModule : IdentityDataAccessModule<TUser, TDataContext>
where TDataContext : IdentityDataContext<TUser>
{
#region Properties
/// <summary>
/// The module list.
/// </summary>
public BusinessModuleList Modules { get; private set; }
/// <summary>
/// The prerequisites of the normal functionality of the module.
/// </summary>
public List<Prerequisite> Prerequisites { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Initializes the new instance of IdentitySignInManager.
/// </summary>
/// <param name="modules">The module list.</param>
/// <param name="authenticationManager">The authentication manager.</param>
public IdentitySignInManager(BusinessModuleList modules, IAuthenticationManager authenticationManager)
: base(modules.Get<IdentityUserManager<TUser, TDataAccessModule, TDataContext>>(), authenticationManager)
{
Prerequisites = new List<Prerequisite>();
Modules = modules;
}
/// <summary>
/// Initializes the new instance of IdentitySignInManager. Supressed.
/// </summary>
private IdentitySignInManager(UserManager<TUser, String> userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
#endregion
#region Methods
/// <summary>
/// Creates the claims based identity from the user.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="authenticationType">The authentication type. Ther default is "ApplicationCookie".</param>
/// <returns>The claims based identity.</returns>
public async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user, String authenticationType = DefaultAuthenticationTypes.ApplicationCookie)
{
return await Modules.Get<IdentityUserManager<TUser, TDataAccessModule, TDataContext>>().CreateIdentityAsync(user, authenticationType);
}
/// <summary>
/// If disposing, calls dispose on the Context. Always nulls out the Context. Also disposes the Modules.
/// </summary>
/// <param name="disposing">True to dispose managed resources, false to not to dispose them.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
Modules.Dispose();
Modules = null;
}
base.Dispose(disposing);
}
#endregion
}
The exception it throws is the following
An exception of type:
'System.ArgumentNullException' occurred in
Microsoft.AspNet.Identity.Owin.dll but was not handled in user code
If i click F5 and continue with the code, it shows me this error on the browser
Value cannot be null.
Parameter name: userManager
Microsoft.AspNet.Identity.Owin.SignInManager2..ctor(UserManager2 userManager, IAuthenticationManager authenticationManager)
at Core.BusinessModules.Users.Modules.IdentitySignInManager3..ctor(BusinessModuleList modules, IAuthenticationManager authenticationManager) in d:\projects\payroll\Trunk\Sources\Core.BusinessModules.Users\Modules\IdentitySignInManager.cs:line 50
at PR.Web.Application.Common.Modules.AppSignInManager..ctor(BusinessModuleList modules, IAuthenticationManager authenticationManager) in d:\projects\payroll\Trunk\Sources\PR.Web.Application\Common\Modules\AppSignInManager.cs:line 30
at PR.Web.Application.Common.Web.OWinContextCallbacks.CreateBusinessModules(IdentityFactoryOptions1 options, IOwinContext context) in d:\projects\payroll\Trunk\Sources\PR.Web.Application\Common\Web\OWinContextCallbacks.cs:line 37
at Microsoft.AspNet.Identity.Owin.IdentityFactoryProvider1.Create(IdentityFactoryOptions1 options, IOwinContext context)
at Microsoft.AspNet.Identity.Owin.IdentityFactoryMiddleware`2.d__0.MoveNext()
On the startup.cs i have the following code, among others
private static void ConfigureAuthentication(IAppBuilder appBuilder)
{
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<AppUserManager, User>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie))
}
});
appBuilder.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
The exception is thrown immediately after it finishes executing the above code.
How would i fix this error?
Related
I am learning how to write GraphQL in asp.net core 5 project. I am using Hot Chocolate v5 and got an error when executing the following query:
query
{
platform
{
id
name,
commands
{
id
howTo
commandLine
}
}
}
Executing this query I got the following error:
{
"errors": [
{
"message": "There was no argument with the name platform found on the field commands.",
"locations": [
{
"line": 7,
"column": 5
}
],
"path": [
"platform",
4,
"commands"
],
"extensions": {
"fieldName": "commands",
"argumentName": "platform"
}
}, ...
If I execute the query where there the following queries then its working:
query
{
platform
{
id
name
}
}
query
{
command
{
id
howTo
commandLine
}
}
My code fragments that I think is relevant for this error are:
public class Command
{
/// <summary>
/// Represents the unique ID for the command.
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// Represents the how-to for the command.
/// </summary>
[Required]
public string HowTo { get; set; }
/// <summary>
/// Represents the command line.
/// </summary>
[Required]
public string CommandLine { get; set; }
/// <summary>
/// Represents the unique ID of the platform which the command belongs.
/// </summary>
[Required]
public int PlatformId { get; set; }
/// <summary>
/// This is the platform to which the command belongs.
/// </summary>
public Platform Platform { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<Platform> Platforms { get; set; }
public DbSet<Command> Commands { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Platform>()
.HasMany(x => x.Commands)
.WithOne(x => x.Platform!)
.HasForeignKey(x => x.PlatformId);
builder.Entity<Command>()
.HasOne(x => x.Platform)
.WithMany(x => x.Commands)
.HasForeignKey(x => x.PlatformId);
}
}
[GraphQLDescription("Represents the queries available.")]
public class Query
{
/// <summary>
/// Gets the queryable <see cref="Command"/>.
/// </summary>
/// <param name="context">The <see cref="AppDbContext"/>.</param>
/// <returns>The queryable <see cref="Command"/>.</returns>
[UseDbContext(typeof(AppDbContext))]
[UseFiltering]
[UseSorting]
[GraphQLDescription("Gets the queryable command.")]
public IQueryable<Command> GetCommand([ScopedService] AppDbContext context)
{
return context.Commands;
}
/// <summary>
/// Gets the queryable <see cref="Platform"/>.
/// </summary>
/// <param name="context">The <see cref="AppDbContext"/>.</param>
/// <returns>The queryable <see cref="Platform"/>.</returns>
[UseDbContext(typeof(AppDbContext))]
[UseFiltering]
[UseSorting]
[GraphQLDescription("Gets the queryable platform.")]
public IQueryable<Platform> GetPlatform([ScopedService] AppDbContext context)
{
return context.Platforms;
}
public class PlatformType : ObjectType<Platform>
{
protected override void Configure(IObjectTypeDescriptor<Platform> descriptor)
{
descriptor.Description("Represents any software or service that has a command line interface.");
descriptor.Field(p => p.Id)
.Description("Represents the unique ID for the platform.");
descriptor.Field(p => p.Name)
.Description("Represents the name for the platform.");
descriptor.Field(p => p.LicenceKey)
.Ignore();
descriptor.Field(p => p.Commands)
.ResolveWith<Resolvers>(p => p.GetCommands(default!, default!))
.UseDbContext<AppDbContext>()
.Description("This is the list of available commands for this platform.");
}
private class Resolvers
{
public IQueryable<Command> GetCommands(Platform platform, [ScopedService] AppDbContext context)
{
return context.Commands.Where(p => p.PlatformId == platform.Id);
}
}
public class CommandType : ObjectType<Command>
{
protected override void Configure(IObjectTypeDescriptor<Command> descriptor)
{
descriptor.Description("Represents any executable command.");
descriptor.Field(c => c.Id)
.Description("Represents the unique ID for the command.");
descriptor.Field(c => c.HowTo)
.Description("Represents the how-to for the command.");
descriptor.Field(c => c.CommandLine)
.Description("Represents the command line.");
descriptor.Field(c => c.PlatformId)
.Description("Represents the unique ID of the platform which the command belongs.");
descriptor.Field(c => c.Platform)
.ResolveWith<Resolvers>(c => c.GetPlatform(default!, default!))
.UseDbContext<AppDbContext>()
.Description("This is the platform to which the command belongs.");
}
private class Resolvers
{
public Platform GetPlatform(Command command, [ScopedService] AppDbContext context)
{
return context.Platforms.FirstOrDefault(p => p.Id == command.PlatformId);
}
}
public class Startup
{
private IConfiguration Configuration { get; }
public Startup(IConfiguration collection)
{
Configuration = collection;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureDbContext(Configuration);
services.ConfigureGraphQL();
}
// 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();
}
app.UseWebSockets();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
app.ConfigureGraphQL();
}
public static class ContexConfiguration
{
public static void ConfigureDbContext(this IServiceCollection services, IConfiguration configuration)
{
services.AddPooledDbContextFactory<AppDbContext>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
}
}
public static class GraphQLConfiguration
{
/// <summary>
/// Configuration of the GrapQL server
/// </summary>
/// <param name="services"></param>
public static void ConfigureGraphQL(this IServiceCollection services)
{
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddSubscriptionType<Subscription>()
.AddType<PlatformType>()
.AddType<AddPlatformInputType>()
.AddType<AddPlatformPayloadType>()
.AddType<CommandType>()
.AddType<AddCommandInputType>()
.AddType<AddCommandPayloadType>()
.AddFiltering()
.AddSorting()
.AddInMemorySubscriptions();
}
/// <summary>
/// GraphQL Voyager UI configuration
/// </summary>
/// <param name="app"></param>
public static void ConfigureGraphQL(this IApplicationBuilder app)
{
app.UseGraphQLVoyager(
options: new VoyagerOptions()
{
GraphQLEndPoint = "/graphql"
},
path: "/graphql-voyager"
);
}
}
found amswer
offical documentation:
I am working on .NET Core Web API localization and I have done all configurations but I am unable to get local specific values.
Created 3 resource files for de-DE, fr-FR and English languages.
Created a common Middleware to set the current culture based on the
query string value.
Trying to get the respective local key but it is always giving English value.
Can anyone suggest and tell did I miss anything in the configuration.
Middleware
namespace i18n
{
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
/// <summary>
/// The LocalizationMiddleware class.
/// </summary>
public class LocalizationMiddleware
{
private readonly RequestDelegate _next;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationMiddleware"/> class.
/// </summary>
/// <param name="next">The next.</param>
public LocalizationMiddleware(RequestDelegate next)
{
_next = next;
}
/// <summary>
/// Invokes the specified context.
/// </summary>
/// <param name="context">The context.</param>
/// <returns>Task.</returns>
public async Task Invoke(HttpContext context)
{
var localValue = "en-US";
if (context.Request.Query.ContainsKey("locale"))
{
localValue = context.Request.Query["locale"];
}
Thread.CurrentThread.CurrentCulture = new CultureInfo(localValue);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(localValue);
await _next.Invoke(context);
}
}
/// <summary>
/// The LocalizationMiddlewareExtensions class.
/// </summary>
public static class LocalizationMiddlewareExtensions
{
/// <summary>
/// Uses the localization middleware.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns>The ApplicationBuilder.</returns>
public static IApplicationBuilder UseLocalizationMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<LocalizationMiddleware>();
}
} }
Startup class
I have registered the Middleware.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseLocalizationMiddleware();
}
Web API Controller
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
var name = Employees.Name; // Always Name is coming english value
return new string[] { name };
}
This is my solution structure:
I'm working with an API and using the session feature in .Net Core 2.1 with the standard dependency injection to store and retrieve values in the session store. My issue is that I'm able to set and store string values into the Session.store but when I then try to retrieve those values in another method, the store is empty.
Here is my Startup.cs setting up the IHttpContextAccessor for DI and enabling session storage.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non- // essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// Configure app settings to inject in other classes.
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// Services to be injected.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthenticationService, AuthenticationService>();
services.AddSingleton<IConstituentsService, ConstituentsService>();
services.AddSingleton<ISessionService, SessionService>();
// Add MVC.
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Configure session.
services.AddMemoryCache();
services.AddDistributedMemoryCache();
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(10);
options.Cookie.Name = ".AuthCodeFlowTutorial.Session";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Here is the class I'm using where I'm storing in the session store and then the method right after to retrieve. SetTokens() and TryGetString() respectively.
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
namespace CHS.SkyApiAuthCodeFlow
{
/// <summary>
/// Sets, gets, and destroys session variables.
/// </summary>
public class SessionService : ISessionService
{
private const string ACCESS_TOKEN_NAME = "token";
private const string REFRESH_TOKEN_NAME = "refreshToken";
private readonly IHttpContextAccessor _httpContextAccessor;
public SessionService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Destroys access and refresh tokens from the session.
/// </summary>
public void ClearTokens()
{
try
{
_httpContextAccessor.HttpContext.Session.Remove(ACCESS_TOKEN_NAME);
_httpContextAccessor.HttpContext.Session.Remove(REFRESH_TOKEN_NAME);
}
catch (Exception error)
{
Console.WriteLine("LOGOUT ERROR: " + error.Message);
}
}
/// <summary>
/// Return access token, if saved, or an empty string.
/// </summary>
public string GetAccessToken()
{
return TryGetString(ACCESS_TOKEN_NAME);
}
/// <summary>
/// Return refresh token, if saved, or an empty string.
/// </summary>
public string GetRefreshToken()
{
return TryGetString(REFRESH_TOKEN_NAME);
}
/// <summary>
/// Sets the access and refresh tokens based on an HTTP response.
/// </summary>
public void SetTokens(HttpResponseMessage response)
{
if (response.IsSuccessStatusCode)
{
string jsonString = response.Content.ReadAsStringAsync().Result;
Dictionary<string, string> attrs = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);
_httpContextAccessor.HttpContext.Session.SetString(ACCESS_TOKEN_NAME, attrs["access_token"]);
_httpContextAccessor.HttpContext.Session.SetString(REFRESH_TOKEN_NAME, attrs["refresh_token"]);
}
}
/// <summary>
/// Return session value as a string (if it exists), or an empty string.
/// </summary>
private string TryGetString(string name)
{
byte[] valueBytes = new Byte[700];
bool valueOkay = _httpContextAccessor.HttpContext.Session.TryGetValue(name, out valueBytes);
if (valueOkay)
{
return System.Text.Encoding.UTF8.GetString(valueBytes);
}
return null;
}
}
}
Below is the AuthenticationService that calls on the SessionService methods.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.Extensions.Options;
namespace CHS.SkyApiAuthCodeFlow
{
/// <summary>
/// Contains business logic and helper methods that interact with the authentication provider.
/// </summary>
public class AuthenticationService : IAuthenticationService
{
private readonly IOptions<AppSettings> _appSettings;
private ISessionService _sessionService;
public AuthenticationService(IOptions<AppSettings> appSettings, ISessionService sessionService)
{
_appSettings = appSettings;
_sessionService = sessionService;
}
/// <summary>
/// Fetches access/refresh tokens from the provider.
/// <param name="requestBody">Key-value attributes to be sent with the request.</param>
/// <returns>The response from the provider.</returns>
/// </summary>
private HttpResponseMessage FetchTokens(Dictionary<string, string> requestBody)
{
using (HttpClient client = new HttpClient())
{
// Build token endpoint URL.
string url = new Uri(new Uri(_appSettings.Value.AuthBaseUri), "token").ToString();
// Set request headers.
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
client.DefaultRequestHeaders.TryAddWithoutValidation(
"Authorization", "Basic " + Base64Encode(_appSettings.Value.AuthClientId + ":" + _appSettings.Value.AuthClientSecret));
// Fetch tokens from auth server.
HttpResponseMessage response = client.PostAsync(url, new FormUrlEncodedContent(requestBody)).Result;
// Save the access/refresh tokens in the Session.
_sessionService.SetTokens(response);
return response;
}
}
/// <summary>
/// Fetches a new set of access/refresh tokens (from an authorization code).
/// <param name="code">The authorization code contained within the provider's authorization response.</param>
/// </summary>
public HttpResponseMessage ExchangeCodeForAccessToken(string code)
{
return FetchTokens(new Dictionary<string, string>(){
{ "code", code },
{ "grant_type", "authorization_code" },
{ "redirect_uri", _appSettings.Value.AuthRedirectUri }
});
}
/// <summary>
/// Refreshes the expired access token (from the refresh token stored in the session).
/// </summary>
public HttpResponseMessage RefreshAccessToken()
{
return FetchTokens(new Dictionary<string, string>(){
{ "grant_type", "refresh_token" },
{ "refresh_token", _sessionService.GetRefreshToken() }
});
}
/// <summary>
/// Builds and returns a string representative of the provider's authorization URI.
/// </summary>
public Uri GetAuthorizationUri()
{
return new Uri(
new Uri(_appSettings.Value.AuthBaseUri), "authorization" +
"?client_id=" + _appSettings.Value.AuthClientId +
"&response_type=code" +
"&redirect_uri=" + _appSettings.Value.AuthRedirectUri
);
}
/// <summary>
/// Determines if the session contains an access token.
/// </summary>
public bool IsAuthenticated()
{
return (_sessionService.GetAccessToken() != null);
}
/// <summary>
/// Destroys the access/refresh tokens stored in the session.
/// </summary>
public void LogOut()
{
_sessionService.ClearTokens();
}
/// <summary>
/// Encodes a string as Base64.
/// </summary>
private static string Base64Encode(string plainText)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(bytes);
}
}
}
I have a custom AuthorizationFilter Attribute on my Web Api project like this
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class GenericAuthenticationFilter : AuthorizationFilterAttribute
{
/// <summary>
/// Public default Constructor
/// </summary>
public GenericAuthenticationFilter()
{
}
private readonly bool _isActive = true;
/// <summary>
/// parameter isActive explicitly enables/disables this filter.
/// </summary>
/// <param name="isActive"></param>
public GenericAuthenticationFilter(bool isActive)
{
_isActive = isActive;
}
/// <summary>
/// Checks basic authentication request
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(HttpActionContext filterContext)
{
if (!_isActive) return;
var identity = FetchAuthHeader(filterContext);
if (identity == null)
{
ChallengeAuthRequest(filterContext);
return;
}
var genericPrincipal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = genericPrincipal;
if (!OnAuthorizeUser(identity.Name, identity.Password, filterContext))
{
ChallengeAuthRequest(filterContext);
return;
}
base.OnAuthorization(filterContext);
}
My StartUpClass is like this
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
WebApiConfig.Register(config);
IoC.Instance.RegisterApiControllers(Assembly.GetExecutingAssembly());
config.DependencyResolver =
new AutofacWebApiDependencyResolver(IoC.Instance.GetComponentsContainer());
// Register your Web Api controllers.
IoC.Instance.RegisterApiControllers(Assembly.GetExecutingAssembly());
IoC.Instance.RegisterWebApiModelBinders(Assembly.GetExecutingAssembly());
IoC.Instance.RegisterWebApiModelBinderProvider();
IoC.Instance.RegisterWebApiFilterProvider(config);
// Register the Autofac middleware FIRST, then the Autofac Web API middleware,
// and finally the standard Web API middleware.
app.UseAutofacMiddleware(IoC.Instance.GetComponentsContainer());
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
and my IoC class where all dependencies are resolved is like this
public class IoC : ContainerBuilder
{
/// <summary>
///
/// </summary>
private readonly static IoC _instance = new IoC();
/// <summary>
///
/// </summary>
private static object _lock;
/// <summary>
///
/// </summary>
private IContainer _componentsContainer;
/// <summary>
///
/// </summary>
public static IoC Instance
{
get
{
return _instance;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public IContainer GetComponentsContainer()
{
if (_componentsContainer == null)
{
lock (_lock)
{
if (_componentsContainer == null)
_componentsContainer = this.Build();
}
}
return _componentsContainer;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public T Resolve<T>() where T : class
{
return GetComponentsContainer().Resolve<T>();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public ILifetimeScope BeginLifetimeScope()
{
return GetComponentsContainer().BeginLifetimeScope();
}
/// <summary>
///
/// </summary>
private IoC()
{
_lock = new object();
ConfigureDependencies();
}
/// <summary>
///
/// </summary>
private void ConfigureDependencies()
{
//Configure all your depedendencies here!!
//Database connection
var connectionString = ConfigurationManager.ConnectionStrings["DBConnectionStringName"].ConnectionString;
this.Register(c => new SqlConnection(connectionString)).As<IDbConnection>().InstancePerRequest();// InstancePerLifetimeScope();
//Database Connection OrmLite
OrmLiteConfig.DialectProvider = SqlServerDialect.Provider;
//Register Repositories
this.RegisterType<Repository>().As<IRepository>().InstancePerRequest();// InstancePerLifetimeScope();
// Register Services
this.RegisterType<UserService>().As<IUserService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<TokenService>().As<ITokenService>().InstancePerRequest();
this.RegisterType<DKMenuService>().As<IDKMenuService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<DKGRIDTblService>().As<IDKGRIDTblService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<FKService>().As<IFKService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<LOVService>().As<ILOVService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<JobService>().As<IJobService>().InstancePerRequest();// InstancePerLifetimeScope();
this.RegisterType<MADEService>().As<IMADEService>().InstancePerRequest();// InstancePerLifetimeScope();
}
}
And I decorate my Controllers with this filter like this
[GenericAuthenticationFilter]
public AuthenticateController(ITokenService tokenService)
{
_tokenService = tokenService;
}
My Problem is that the OnAuthorazation method of the GenericAuthenticationFilter is never fired.
If on the IoC Class class I change InstancePerRequest to InstancePerLifetimeScope everything works ok, but I want my dependencies to work per Request
Any Ideas?
I'm not sure if this is part or all of your issue, but... you can only build a ContainerBuilder once. In Startup.Configuration() I see on lines 11-12:
config.DependencyResolver =
new AutofacWebApiDependencyResolver(IoC.Instance.GetComponentsContainer());
And IoC.Instance.GetComponentsContainer() calls Build() to create the container.
But just two lines later I see you're adding more components to the container, and after that I see a second call:
app.UseAutofacMiddleware(IoC.Instance.GetComponentsContainer());
Based on your code, that's going to be the same container that was built before you added the new registrations. The container won't include the API controllers, the model binders, or the filter provider.
I'm actually not sure why you're not having more problems than you're having now.
Try moving the setting of the containers (the calls to IoC.Instance.GetComponentsContainer()) until all the way at the end, after you've finished registering all of your dependencies.
The only configuration that worked was the following
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
WebApiConfig.Register(config);
// Register your Web Api controllers.
IoC.Instance.RegisterApiControllers(Assembly.GetExecutingAssembly());
IoC.Instance.RegisterWebApiModelBinders(Assembly.GetExecutingAssembly());
IoC.Instance.RegisterWebApiModelBinderProvider();
config.DependencyResolver =
new AutofacWebApiDependencyResolver(IoC.Instance.GetComponentsContainer());
// Register your Web Api controllers.
//IoC.Instance.RegisterApiControllers(Assembly.GetExecutingAssembly());
//IoC.Instance.RegisterWebApiModelBinders(Assembly.GetExecutingAssembly());
//IoC.Instance.RegisterWebApiModelBinderProvider();
// Register the Autofac middleware FIRST, then the Autofac Web API middleware,
// and finally the standard Web API middleware.
app.UseAutofacMiddleware(IoC.Instance.GetComponentsContainer());
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
I am still not sure whether it is right.
Examples of usage. I have a filter like the following
public class ApiAuthenticationFilter : GenericAuthenticationFilter
{
/// <summary>
/// Default Authentication Constructor
/// </summary>
public ApiAuthenticationFilter()
{
}
}
A method in this filter is using a service which is resolved like this
protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
{
var provider = actionContext.Request.GetDependencyScope().GetService(typeof(IUserService)) as IUserService;
}
My controllers on the other hand do not have parameterless constructors and the dependencies are resolved automatically
public class DKMenuController : ApiController
{
#region Private variable.
private readonly ITokenService _tokenService;
private readonly IDKMenuService _dkMenuService;
private readonly IUserService _userservice;
private const string Token = "Token";
#endregion
#region Public Constructor
/// <summary>
/// Public constructor to initialize DKMenu service instance
/// </summary>
public DKMenuController(ITokenService tokenService, IUserService userservice, IDKMenuService dkMenuService)
{
_tokenService = tokenService;
_dkMenuService = dkMenuService;
_userservice = userservice;
}
}
I don't know whether it is correct but it works
Is it possible with an Owin Middleware implementation to add claims prior to the execution of a Web API controller?
Created an OwinMiddleware implementation and added an identity:
var id = new ClaimsIdentity();
id.AddClaim(new Claim("Whatever", "is possible"));
context.Authentication.User.AddIdentity(id);
await Next.Invoke(context);
However, even this Invoke method call the identities are not updated (just the internal claims array). And the controller when executed of course never gets the new dummy claim.
Ideas?
There's already a class that can provide claims enrichment ClaimsAuthenticationManager, which you can extend so it handles your domain-specific claims, for example...
public class MyClaimsAuthenticationManager : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
return AddApplicationClaims(incomingPrincipal);
}
private ClaimsPrincipal AddApplicationClaims(ClaimsPrincipal principal)
{
// TODO: Add custom claims here based on current principal.
return principal;
}
}
Next task is to provide appropriate middleware to invoke this. For my projects I've written the following classes...
/// <summary>
/// Middleware component to apply claims transformation to current context
/// </summary>
public class ClaimsTransformationMiddleware
{
private readonly Func<IDictionary<string, object>, Task> next;
private readonly IServiceProvider serviceProvider;
public ClaimsTransformationMiddleware(Func<IDictionary<string, object>, Task> next, IServiceProvider serviceProvider)
{
this.next = next;
this.serviceProvider = serviceProvider;
}
public async Task Invoke(IDictionary<string, object> env)
{
// Use Katana's OWIN abstractions
var context = new OwinContext(env);
if (context.Authentication != null && context.Authentication.User != null)
{
var manager = serviceProvider.GetService<ClaimsAuthenticationManager>();
context.Authentication.User = manager.Authenticate(context.Request.Uri.AbsoluteUri, context.Authentication.User);
}
await next(env);
}
}
And then a wiring extension...
public static class AppBuilderExtensions
{
/// <summary>
/// Add claims transformation using <see cref="ClaimsTransformationMiddleware" /> any depdendency resolution is done via IoC
/// </summary>
/// <param name="app"></param>
/// <param name="serviceProvider"></param>
/// <returns></returns>
public static IAppBuilder UseClaimsTransformation(this IAppBuilder app, IServiceProvider serviceProvider)
{
app.Use<ClaimsTransformationMiddleware>(serviceProvider);
return app;
}
}
I know this is service locator anti-pattern but using IServiceProvider is container neutral and seems to be the accepted way of putting dependencies into Owin middleware.
Last you need to wire this up in your Startup, example below presumes Unity and registering/exposing a IServiceLocator property...
// Owin config
app.UseClaimsTransformation(UnityConfig.ServiceLocator);
You may find useful inheriting from Authorizate Attribute and extending it to meet your requirements:
public class DemoAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext){
if (Authorize(actionContext)){
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext){
var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized;
//Adding your code here
var id = new ClaimsIdentity();
id.AddClaim(new Claim("Whatever", "is possible"));
context.Authentication.User.AddIdentity(id);
challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
throw new HttpResponseException(challengeMessage);
}
private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext){
try{
var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();
// or check for the claims identity property.
return someCode == "myCode";
}
catch (Exception){
return false;
}
}
}
And in your controller:
[DemoAuthorize]
public class ValuesController : ApiController{
Here is a link on other custom implemenation for WebApi Authorizations:
http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/
This is how I ended up adding a new claim in owin middleware, based on the OP's comment about hooking into UseOAuthBearerAuthentication. It uses IdentityServer3.AccessTokenValidation, which calls UseOAuthBearerAuthentication internally and passes the OAuthBearerAuthenticationProvider through to it.
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer3.AccessTokenValidation;
using Owin;
using Microsoft.Owin.Security.OAuth;
//...
public void Configuration(IAppBuilder app)
{
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "http://127.0.0.1/identityserver",
TokenProvider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = AddClaim
}
});
}
private Task AddClaim(OAuthValidateIdentityContext context)
{
context.Ticket.Identity.AddClaim(new Claim("test", "123"));
return Task.CompletedTask;
}