I want to use ASP.NET SimpleMembership to authenticate users that consume my WebAPI. Thinktecture has a wonderful authentication library called Thinktecture.IdentityModel (http://thinktecture.github.com/Thinktecture.IdentityModel.45/) with an example that ties Forms Auth with Basic Auth (source). However, the example uses Membership.ValidateUser() which doesn't work without a ASP.NET Membership provider, which isn't supported by SimpleMembership (source) (edit: this isn't entirely true, see Mark's answer below).
Edit:
Here's what I did:
1) Create a new MVC Internet Application
2) Install Thinktecture.IdentityModel via NuGet
3) Create a model and an api controller via scaffolding:
public class Goober
{
public int GooberId { get; set; }
public string GooberWords { get; set; }
}
4) Ran the project, created a new user and created a new Goober using Fiddler
5) Added [Authorize] to GetGoober(int id)
6) In WebApiConfig.cs added:
var authConfig = new AuthenticationConfiguration();
authConfig.AddBasicAuthentication((userName, password) =>
Membership.ValidateUser(userName, password));
config.MessageHandlers.Add(new AuthenticationHandler(authConfig));
When I run the project and hit api/goober/1 with Fiddler I get a 401 www-Authenticate: unspecified. But if I log in first using the AccountController then use Fiddler I get a 200 and everything is peachy.
Edit
Okay, I think the problem isn't related to my initial question. I suspect it's related to the initialization of SimpleMembership in the template. When I open the project and run debug then hit the api with Fiddler I can't get past Auth. But when I simply click the "register" link on the web frontend I get past Auth. I'm guessing it's because the InitializeSimpleMembershipAttribute is called at the AccountController so doesn't initialize until the controller is called?
I've tried using WebSecurity.Login() in the place of Membership.ValidateUser() but that doesn't work.
I'm at a loss on how to actually implement this. Does anyone have any advice? Or maybe I'm attempting to tackle this problem from the wrong angle?
You are correct that ASP.NET Membership provider is not compatible with the SimpleMembershipProvider however SimpleMembershipProvider does support ValidateUser, see here. Assuming SimpleMembership is correctly configured and initalised you should still be able to call Membership.ValidateUser().
If you have already tried Membership.ValidateUser() and got an error please let me know and we can try resolve it.
Update
So having followed your reproduction steps I have managed to pin-point an error. Having brought the Thinktecture AuthenticationHandler handler inline and run in debug. 30 seconds after a request to the api controller a database connection error is being raised. This is failing asynchronously and silently.
After some fiddling around I believe it is the DefaultConnection connection string which is at fault.
Like me your default connection probably contains a file name like this:
AttachDBFilename=|DataDirectory|\aspnet-MvcApplication3-20121215104323.mdf"
When ValidateUser is called inside the delegate registered at app start up (for validating the credentials) it appears to be failing to resolve |DataDirectory| I found that by updating this path to the full name my connection problems went away.
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-MvcApplication3-20121215104323;Integrated Security=SSPI;AttachDBFilename=C:\mydatabase\file\path\example\aspnet-MvcApplication-20121215104323.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>
I then found this post here, it indicates that the AppDomain has not had it's data directory set correctly at this point.
So once the config set up and the connection string altered with a proper file path and a user name "test" and a password "testing" this request through fiddler got a 200:
GET http://localhost/api/goober/1
User-Agent: Fiddler
Host: localhost
Authorization: Basic dGVzdDp0ZXN0aW5n
As an aside
I found that to get the forms authorisation token to also allow access to the api controllers I had to add this. Otherwise the Thinkteckture code sets the principle back to anonymous:
Add this
authConfig.InheritHostClientIdentity = true;
To counteract this (line 52):
if (_authN.Configuration.InheritHostClientIdentity == false)
{
//Tracing.Information(Area.HttpAuthentication, "Setting anonymous principal");
SetPrincipal(Principal.Anonymous);
}
Related
I have a DNN 9.2 website set up on a local server. I use a HttpPOST to send JSON to the web.api. This was working last week and has now stopped working when I am attempting to upgrade from Angular 4.1 to 4.3.
When I try to access the api endpoint from within my app or a postman call I get a 500 error and no message.
Here is my Controller Code:
[HttpPost]
[ActionName("postPost")]
[AllowAnonymous]
public ApiResult UpsertPost([FromBody]Post post)
{
try
{
DO STUFF HERE
}
catch (System.Exception ex)
{
CATCH ERROR
}
}
What is funny here is that this was working and All I did was change to use the new httpClient from Angular 4.3. I know the api endpoint is taking requests.
What I tried:
I tried to take angular out of the loop by using postman calls. However, I also can not use the postman calls I had set up as tests and I get the same error.
I tried changing the security attribute from [AllowAnonymous] to ** a DNN built in security attribute: [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Anonymous)]** but I get the following error instead of a 500 error:
Authorization has been denied for this request.
The same error is given as above if I change the security attribute to [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.View)]
I made sure the POST action was supported in my web.config file:
add name="ExtensionlessUrl-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"
I know the api endpoints are getting hit because if I use GET instead of POST I can hit all my breakpoints in the web.pai code. As well as the given "Authorization has been denied for this request" is given from the api service.
QUESTION
Can someone help me figure out why my DNN api's with post are not accepting requests?
I know that you got an answer by yourself, but can be usefull these links:
You can find a "scafolding angular project" here: DNN-Angular-6-7-CLI
And DNN module where to deploy your project: SPA DNN9: DNN-9.x-SPA-and-Angular-6-7
ASCX for all DNN: DNN-7.x-8.x-9.x-and-Angular-6-7
all works with dnn webapi and security.
I used these projects with my customers.
Matteo
Okay, so no surprise that it was my fault. I had moved the authorization process for the http requests to the new httpClient interceptors. I had an error in the interceptor function that was creating a poorly formed request. Once I fixed the interceptor the API requests work properly now.
Hope this might help someone
I am developing a web app with mix authentication (Owin Token Based and Windows authentication). I have implemented Owin token based authentication and want to implement windows authentication for the users which are marked as active directory users.
In Owin middleware, I want to get requesting user's windows username. I am getting object in OwinContext.Request.User.Identity. However, OwinContext.Request.User.Identity.Name is always blank string.
I found that I should add below lines in startup.cs:
var listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
However, I am getting key not found exception. "System.Net.HttpListener" is not present in Properties array. I have installed Microsoft.Owin.SelfHost, Microsoft.Owin.Host.HttpListener. However, I am still getting the error.
Any help is greatly appreciated.
Thanks,
GB
For me issue was that project was started as shared lib, not a web app.
Solution was to add a line into .cspro file after <ProjectGuid> line.
<ProjectTypeGuids>{349C5851-65DF-11DA-9384-00065B846F21};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
Then you dont need to add HttpListener explicitly, just reload project and follow this instructions starting from Properties edition part.
Enabling Windows Authentication in Katana
You can access principal the same ways:
Get the user in an OWIN middleware:
public async Task Invoke(IDictionary<string, object> env)
{
OwinContext context = new OwinContext(env);
WindowsPrincipal user = context.Request.User as WindowsPrincipal;
//...
}
Get the user in a Web API Controller:
// In a web api controller function
WindowsPrincipal user = RequestContext.Principal as WindowsPrincipal;
UPD: List of Visual Studio Projects GUIDs
I have a problem in SignalR connection with Win Auth. When I enable anonymous in IIS Authorize settings, it works but sometimes it gives HTTP 403 Forbidden error. After I researched, I found that I need to disable Anonymous Connections. But when disable and enable the windowsAuth in IIS then there is always HTTP 401.2 UnAuthorized error. How I can connect with WinAuth? For my project I need to use WinAuth.
Not1 : I am using Silverlight 5.
Not2 : I have already tried possible solutions on StackOverflow but none of them worked.
So why cant I use WinAuth? It is enabled everywhere in config files, in IIS settings as well as in my web.config.
I spent 2 days but still I could not find a solution. If you need more information just write a comment. I am not sure what else information I can share. I dont want to put here lots of unnecessary texts.
EDIT:
When I use this code, i.e if I enter the username and password explicitly then it works. Internet Explorer first uses Anonymous Authentication and then it fails then it uses NetworkCredentials directly. This is the code
hubConnection = new HubConnection("URL");
hub = hubConnection.CreateHubProxy("HUBNAME");
hubConnection.Credentials = new System.Net.NetworkCredential("ID", "PASSWORD");
(The ones with capital letters are specific to my app.)
So how can I get Windows Credentials for my Silverlight App? DefaultCredentials does not work for silverlight.
Have you added authorization to your hub?
[Authorize(Roles = "Admin")]
public class AdminAuthHub : Hub
{
}
I am building an ASP.NET MVC 5 website and am using Unity for dependency injection. I get an Invalid Token exception when some time has passed (more than an hour, less than a day) between the token generation and the token validation.
I have the following code in my Startup.cs:
internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
public void Configuration(IAppBuilder app)
{
DataProtectionProvider = app.GetDataProtectionProvider();
ConfigureAuth(app);
}
I have the following code in the conctructor of my ApplicationUserManager class (timespan is set to 7 days now just to make sure that is not the issue):
var dataProtectionProvider = Startup.DataProtectionProvider;
if (dataProtectionProvider != null)
{
this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser> (dataProtectionProvider.Create("ASP.NET Identity")) {
TokenLifespan = TimeSpan.FromDays(7)
};
}
In Startup.Auth.cs, I have the following line of code in the ConfigureAuth method:
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
In UnityConfig.cs, I have set up dependency injection:
container.RegisterType<ApplicationDbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
container.RegisterType<ApplicationRoleManager>();
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication)
);
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(ApplicationDbContext))
);
container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(
new InjectionConstructor(typeof(ApplicationDbContext))
);
I have to add that one of my scenarios allows for the creation of a Contact with an associated user account. The Contact and associated user account are created in the ContactController, while the ConfirmEmail method is in the AccountController. Both take ApplicationUserManager as a constructor parameter, which means the ApplicationUserManager is injected into the controllers.
That does not appear to be the issue, since everything works fine if I confirm right after receiving the confirmation email. However, if I wait an hour or so, and then try to confirm, I get the Invalid Token exception.
I have already verified that I am not accidentally mixing different token types, both generation and verification are for email confirmation. I have also verified that I am url encoding (and decoding) the token.
I am currently testing on an Azure virtual machine with a static IP that is running its own IIS, the production environment will most likely be on a non-Azure VPS, als running its own IIS. I am not an Azure expert, but to my knowledge, I didn't select any options that are related to load balancing.
I would really appreciate your help, as I have already tried any possible solutions I was able to find here and on other websites.
The Invalid token issue has been solved. Since it only happened after some time had passed, I figured it might have something to do with my application recycling.
I managed to get rid of the problem by setting a fixed MachineKey in my web.config. You can use IIS Manager to generate it, as described here: https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-the-iis-manager/
Please note: for some reason, IIS adds the IsolateApps when it generates a machine key. However, this results in ASP.NET throwing an exception. After you manually remove the IsolateApps and save, it should work as expected.
In my MVC application I want to render a table in a cshtml file, if the current log in user is some x person. I am using windows authentication and I have made the following changes in web.config file.
<authentication mode="Windows">
</authentication>
And in my controller when I am trying to access the current user name I am not getting any user name. I am trying the following:
ViewBag.LogInUserName = Request.RequestContext.HttpContext.User.Identity.Name;
This above line was working before. But I don't know whats wrong now. Also I have hosted my application on IIS now.
Take a look at the web project's properties, in particular:
Anonymous Authentication - Set to "Disabled"
Windows Authentication - Set to "Enabled"
By default these are set to the opposite of what you're probably looking for.
(Image sourced from MSDN)
You need to put the [Authorize] attribute on your controller.
You can use User.Identity.Name in your controllers.
[Authorize]
public class YourController : Controller
{
public ActionResult SomeAction()
{
var userName = User.Identity.Name;
}
}
A little bit late, but this may serve others in the future.
I had the same problem once after deploying my site to a new IIS server, and the anonymous authentication was enabled, so make sure that anonymous authentication is disabled and it should work.