I am using Owin and Unity and on every request the startup config is being called and working as expected but if their is some database connection issue or some other exception I am handling that exception in Global.asax and redirecting to another controller but for now the problem is as the exception is in startup.cs while I a redirecting to my Error controller the startup is again getting called and I am getting the same exception again making a redirect loop. Is their any way to disable owin startup for one controller or something lie that.
Some Code
var uow = container.Resolve<IUnitOfWorkAsync>();
var cc = (ClaimCache)container.Resolve<IClaimCache>();
var cts = new HaiClaimsTransformationService(uow,cc);
var ctos = new HaiClaimsTransformationOptions(cts);
app.UseClaimsTransformation(ctos);
Related
I've got a working ASP.NET Core 2.2 implementation that utilizes both MVC and API controllers, and I'm putting together an integration test project to cover everything that has already been tested manually - the basic crud, mostly. Everything works except the tests that use PostAsync to POST data. These tests always get a 500 Internal Server Error as a response from the client, and I cannot for the life of me figure out why. Oi!
The TestServer setup is a pretty standard approach that I've seen on many different blogs and articles. A TestStartup class extends the standard Startup, and overrides the configuration to use in-memory database with seed data. My test fixture base then uses the TestStartup to create a server, and a client, and has a method, which I know works fine, to extract the antiforgery token and add it to forms and headers. All other tests verifying all other aspects of CRUD are working, proving that the seeded data can be retrieved, via both MVC and API calls.
The POST calls that eventually fail with a 500 Internal Server Error do make it into the controller, and the subsequent repository, just fine. With all these aspects in place, I've yet to be able to see the source of the 500.
[Fact]
public async void CreatePost_ShouldReturnViewWithNewlyCreatedLabelData()
{
// Arrange
var formData = await EnsureAntiForgeryTokenOnForm(new Dictionary<string, string>()
{
{ "Name", TestDataGraph.Labels.LabelNew.Name },
{ "WebsiteUrl", TestDataGraph.Labels.LabelNew.WebsiteUrl }
});
// Act
var response = await Client.PostAsync("/labels/create", new FormUrlEncodedContent(formData));
// Assert
Assert.Equal(HttpStatusCode.Found, response.StatusCode);
Assert.Equal("/Labels", response.Headers.Location.ToString());
}
This is a simple example test in Xunit that attempts to validate the creation of a new simple object, type Label, via MVC route, which follows the standard path format, having been scaffolded. This test will make it into the controller, and its repository, but the response will be a 500 Internal Server Error.
Could I have missed something important in Startup? Any ideas for finding further details about this failure? Thanks in advance! I can post more code or details if they will be helpful.
Try adding trace logging... Trace logging will display activity in the .Net Core framework.
...
public static ILogger<ConsoleLoggerProvider> AppLogger = null;
public static ILoggerFactory loggerFactory = null;
//
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(builder => builder
.AddConsole()
.AddFilter(level => level >= LogLevel.Trace)
);
loggerFactory = services.BuildServiceProvider().GetService<ILoggerFactory>();
AppLogger = loggerFactory.CreateLogger<ConsoleLoggerProvider>();
...
An example trace log:
trce: Microsoft.AspNetCore.Mvc.Razor.Internal.RazorViewCompiler[7]
Could not find a file for view at path '/Views/Home/_Layout.cshtml'.
After you have resolved the issue, change the LogLevel to a more appropriate value.
I am using the github example active-directory-b2c-dotnet-webapp-and-webapi.
I configured my own b2c tenant and got the MVC sample to work successfully.
However, I need this to work with a WebForms app. I created a new Webforms app, checked that the references were pointing to the same versions of the dlls as the working sample and adjusted the code to work in WebForms.
Everything works up until the time that I issue the Authentication.Challenge and process the AuthorizationCodeReceivedNotification in the OnAuthorizationCodeReceived function.
The Session object in notification.OwinContext.Environment["System.Web.HttpContextBase"] is null whereas at this point in the MVC sample the session object in notification.OwinContext.Environment["System.Web.HttpContextBase"].Session is a System.Web.HttpSessionStateWrapper with a valid SessionId.
In WebForms, HttpContext.GetOwinContext throws an error as not being available, so I then tried HttpContext.Current.GetOwinContext which returned an HttpContext object instead of a HttpContextBase object. So I finally used
if (!Request.IsAuthenticated)
{
HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
context.GetOwinContext().Authentication.Challenge();
return;
}
instead of the following which is used in the MVC sample
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge();
return;
}
At this point in the code, in both MVC and WebForms the Authentication object has a valid Session object in its OwinContext.Environment prior to the Challenge being issued. The problem is that whilst in the MVC version the resulting notification includes the session object, the WebForms version of the notification has a null object.
The problem finally surfaces when getting the TokenCache by calling the MSALSessionCache which uses the httpContext.Session object (MVC works; Webforms throws Null reference exception)
I am aware that MVC and Webforms treat the session differently but cannot work out how to solve this problem.
EDIT
Whilst this worked it did not address the problem regarding the session id. I added
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
MessageReceived = OnMessageReceived,
SecurityTokenReceived=OnSecurityTokenReceived,
SecurityTokenValidated= OnSecurityTokenValidated
},
to your code with the appropriate stubs for these functions. MessageReceived was invoked as was SecurityTokenReceived and Validated. However, AuthorizationCodeReceived was never invoked. When the first three were invoked, I examined the notification.OwinContext.Environment["System.Web.HttpContextBase"] and in all cases the Session object is null - which is what my problem was to start with.
EDIT (includes answer)
Thanks to #Ramakrishna the solution is to add A RequireAspNetSession helper function at the beginning of ConfigureAuth. The revised code snippet for his sample is as follows:
public void ConfigureAuth(IAppBuilder app)
{
RequireAspNetSession(app);
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
// Configure OpenID Connect middleware for each policy
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfilePolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}
public static void RequireAspNetSession(IAppBuilder app)
{
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
// To make sure the above `Use` is in the correct position:
app.UseStageMarker(PipelineStage.MapHandler);
}
you will need to add the following references:
using System.Web.SessionState;
using Microsoft.Owin.Extensions;
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
}
Try this in webforms.
EDIT
Sample Application hosted at https://github.com/Zen3InfoSolutions/B2CSamples/tree/master/B2C-WebForms
Zero configuration required to run and verify the policies.
I am trying to get autofac to work, but having issues with my unitofwork / user manager classes.
Initially I set my unit of work up as a per request instance like this:
builder.RegisterType<UnitOfWork<DatabaseContext>>().As<IUnitOfWork>().InstancePerRequest();
But in my StartupConfig.cs I was trying to set up oAuth like this:
private static OAuthAuthorizationServerOptions ConfigureOAuthTokenGeneration(IAppBuilder app, ILifetimeScope scope)
{
var t = scope.Resolve<IPasswordHasher>();
// Get our providers
var authProvider = scope.Resolve<OAuthProvider>();
var refreshTokenProvider = scope.Resolve<IAuthenticationTokenProvider>();
// Create our OAuth options
return new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true, // TODO: Remove this line
TokenEndpointPath = new PathString("/oauth/access_token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"),
Provider = authProvider,
RefreshTokenProvider = refreshTokenProvider
};
}
The scope is obtained by this:
var scope = config.DependencyResolver.GetRootLifetimeScope();
Because of this, I could not use InstancePerRequest for the UnitOfWork, instead I changed it to this:
builder.RegisterType<UnitOfWork<DatabaseContext>>().As<IUnitOfWork>().InstancePerLifetimeScope();
Now the application actually runs, but I get a new error with my UserProvider, it is instantiated like this:
builder.RegisterType<UserProvider>().As<IUserProvider>().InstancePerRequest();
But if I run that, I get this error:
No scope with a tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested.
If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself.
This is actually being invoked by the line:
var authProvider = scope.Resolve<OAuthProvider>();
which is in my StartupConfig.cs. The OAuthProvider does need the UserProvider, the signature looks like this:
public OAuthProvider(IAdvancedEncryptionStandardProvider helper, IUserProvider userProvider)
{
this._helper = helper;
this._userProvider = userProvider;
}
So because this is not in the "request", I changed the UserProvider to be resolved like this:
builder.RegisterType<UserProvider>().As<IUserProvider>().InstancePerLifetimeScope();
which matches the UnitOfWork now, the project will load. But if I have an interface that tries to do 2 things (get the current user and list all users) it creates 2 requests, both creating a new instance of the UserController:
public UsersController(IUserProvider provider)
{
this._provider = provider;
}
which in turn tries to create 2 instances of the UserProvider. This throws an error:
The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.
So, I guess I need to know how I can resolve this.
It's like I need 2 scopes, one for the start of the application and then another for everything else.
Can anyone help me with this?
The issue
Since, at the time of the OWIN middleware registration, you need to provide an instance of OAuthAuthorizationServerOptions, there's no way to resolve the Provider and RefreshTokenProvider properties per HTTP request.
What we need is a way to create the OAuthAuthorizationServerOptions per HTTP request. By extension, the same concept would apply to the OAuthAuthorizationServerMiddleware.
A possible solution
That's exactly what AutofacMiddleware<T> does; It wraps an OWIN middleware by resolving it during the HTTP request from the lifetime scope stored in the OWIN context, then executes it. This means that we can now instantiate a new OAuthAuthorizationServerMiddleware for each HTTP request.
As explained in the documentation, when you use app.UseAutofacMiddleware(container) in your Startup class, Autofac does 2 things:
it hooks itself in the OWIN pipeline to create a nested lifetime scope for each HTTP request
it wraps all the OwinMiddleware services registered in the container with AutofacMiddleware and adds them to the OWIN pipeline
The solution is then to register OAuthAuthorizationServerMiddleware and all its dependencies in the Autofac container, and it will be automatically resolved for each request and executed.
OAuthAuthorizationServerMiddleware has 3 dependencies:
The next OWIN middleware in the pipeline, which AutofacMiddleware takes care of as it provides it to the Resolve method - TypedParameter.From(this.Next)
An instance of IAppBuilder
The OAuthAuthorizationServerOptions instance
We have to register the last two dependencies plus the middleware itself in the container. Let's have a look at what this could look like:
Disclaimer: I didn't try the code below
// Here go all the registrations associated with the `Provider`
// and `RefreshTokenProvider` properties with the appropriate lifetimes
builder
.RegisterInstance(app)
.As<IAppBuilder>();
builder
.Register(x => new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true, // TODO: Remove this line
TokenEndpointPath = new PathString("/oauth/access_token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
AccessTokenFormat = new Business.Authentication.JwtFormat("http://localhost:62668"),
Provider = x.Resolve<OAuthProvider>(),
RefreshTokenProvider = x.Resolve<IAuthenticationTokenProvider>()
})
.AsSelf()
// InstancePerDependency is same as InstancePerLifetimeScope
// in this case since the middleware will get resolved exactly one
// time per HTTP request anyway
.InstancePerDependency();
builder.RegisterType<OAuthAuthorizationServerMiddleware>();
Controlling the middleware order
While this could work, it's possible that it doesn't suit your needs since the OAuth middleware will be registered in the OWIN pipeline where you call app.UseAutofacMiddleware(container).
If you want more control over middleware order, you can separate the Autofac lifetime scope creation from the middleware registration in the OWIN pipeline:
Disclaimer: I didn't try the code below
// creates the per HTTP request lifetime scope
app.UseAutofacLifetimeScopeInjector(container);
// "usual" OWIN middlewares registrations
app.UseFoo();
// now use one from the container
app.UseMiddlewareFromContainer<OAuthAuthorizationServerMiddleware>();
// other "usual" OWIN middlewares registrations
app.UseBar();
I'm using the Simple Injector IoC container in my web api service. Currently, I'm experiencing an error that I'm finding very difficult to investigate.
The container is configured as follows:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.Register<IQueryHandler<LoginQuery, AccountDTO>, LoginQueryHandler>(
Lifestyle.Scoped);
When requests come into the web service, rather than being routed to controllers, they are instead handled by a custom message handler. These handlers are created by inheriting from the DelegatingHandler class (see here for more info).
The custom message handler will then look at the request and invoke a specific handler to handle the request. This is done as follows:
// For the sake of the example (and my testing), I have hard coded the types
// below, but usually these are pulled from the request.
Type handlerType = typeof(IQueryHandler<,>)
.MakeGenericType(typeof(AccountQuery), typeof(AccountDTO));
try
{
// Something does wrong here
dynamic handler = this.handlerFactory.Invoke(handlerType);
// Code execution never reaches this point, exception is not caught
... other code
}
// I have also just tried catching the general exception
catch (TargetInvocationException ex)
{
// do something
}
My current problem is that something goes wrong when trying to invoke the handler from the handler factory (simple injector). When I'm debugging, as soon as I step through that line, I imagine the web service hits an exception, and returns (my client just gets an "Internal Server Error" message). Despite the try/catch, no exceptions are caught, so I have absolutely no idea what is happening. I would think I probably have something configured wrong for simple injector, but I just don't know what (simple injector verifies successfully).
Does anyone have suggestions on what I can do here to figure out what is going on?
After a question as per why the ApplicationDbContext from Asp.Net Identity is created and disposed twice per Request I did some research why this would happen. I found that the ApplicationDbContext is actualy created once per HttpRequest but when using the Owin pipeline the Owin Middleware will be created a second time after the HttpRequest has ended.
Because of this the ApplicationDbContext is indeed created for a second time when a user clicks one link giving the impression that the object is created twice per WebRequest.
After a lot of research I decided to start a plain MVC 5 project without using any authentication. After adding the Owin Middleware from NuGet I created to following Owin Middleware component. Which basically checks if some fake object exist in the HttpContext dictionary and creates one when it doesn't. The output is written to the debug window to keep things simple.
[assembly: OwinStartupAttribute(typeof(MvcPlain.Startup))]
namespace MvcPlain
{
public class Startup {
public static int Counter;
public static string FakeKeyName;
public void Configuration(IAppBuilder app) {
app.Use(async (context, next) =>
{
Debug.WriteLine("Owin middleware entered => begin request");
FakeKeyName = "owinKey" + Counter.ToString();
var fakeKeyPresent = HttpContext.Current.Items.Contains(FakeKeyName);
Debug.WriteLine(string.Format("{0} key present in HttpContext?: {1}",
FakeKeyName, fakeKeyPresent));
if (!HttpContext.Current.Items.Contains(FakeKeyName))
{
Counter += 1;
HttpContext.Current.Items.Add(FakeKeyName, "someValue");
}
await next.Invoke();
Debug.WriteLine("Owin middleware exited => end request");
var keyStillPresent = HttpContext.Current.Items.Contains(FakeKeyName);
Debug.WriteLine(string.Format("{0} still present in HttpContext?: {1}",
FakeKeyName, keyStillPresent));
});
}
}
}
Then added this to the Index ActionMethod of the HomeController to check if the created object still exists.
public ActionResult Index()
{
Debug.WriteLine("Index actionmethod called");
var fakeKeyPresent = HttpContext.Items.Contains(Startup.FakeKeyName);
Debug.WriteLine(string.Format("{0} key present in HttpContext?: {1}",
Startup.FakeKeyName, fakeKeyPresent));
return View();
}
When ran the output window shows the following output (added comment for clarity):
--- home link clicked ---
Owin middleware entered => begin request
owinKey2 key present in HttpContext?: False
Index actionmethod called
owinKey2 key present in HttpContext?: True
Owin middleware exited => end request
owinKey2 key still present in HttpContext?: True
--- end of 'normal' request ---
Owin middleware entered => begin request
owinKey3 key present in HttpContext?: False
Owin middleware exited => end request
owinKey3 key still present in HttpContext?: True
So why, after the comment end of 'normal' request, is the middleware created and entered again? Anyone has any idea or explanation?
Steps to reproduce:
Start a new MVC 5 project in VS 2013 without authentication
Add Owin from NuGet using Install-Package Microsoft.Owin.Host.SystemWeb in the package manager
Add a startup class to the project as shown above
Add the code to the Index ActionMethod of the HomeController
Hit F5 in debug mode
Click on the 'Home' link on the start page
Watch the output in the output (or immediate depending on your VS setup) window
What's most likely happening here is that there are actually two separate requests happening. The first is for your Home/Index view, and the second is probably the browser making a request for something like favicon.ico. (Browsers tend to do that automatically.)
At the beginning of your middleware, insert a debugging helper that reveals the value of context.Request.Path to see what URL is being requested each time.