Showing posts with label authentication. Show all posts
Showing posts with label authentication. Show all posts

Wednesday, March 28, 2012

AJAX based Web service authentication issue

Hi, I have created a webservice for putting overlays on googlemap. I am using scriptmanager to generate java proxy class. Everything works as expected as long as I use localhost in the url e.g.http://localhost/test.aspx orhttp://localhost/map.asmx/js but if I use the actual domain name then it don't work anymore e.g.http://www.test.com/test.aspx orhttp://www.test.com/map.asmx/js. In case of IE7 i get script error 'test is undefined' and in case of Firefox, I get authentication failed. The result is same whether I run it on the server or on the client. I have tried every possible thing that I can think of. Environment is Win 2003 and IIS 6.0. Framework 2.0 is enabled in the web services entensions. I am using windows integrated authentication and anonymous access is also enable. I am new to ajax world and trying hard for last 2 days to solve this problem. Please help me. Thanks a lot

Hi,

Can you directly browse to http://www.test.com/map.asmx/js. and does that download the Script Proxy ?

Also , can you take a look at the IIS Logs to understand the behavior on the server when you browse to the page ?

Go to Start --> Run -->Type in "LogFiles"

In the folder that opens up , go inside W3SVC1.

Take a look at the File exmmddyy.log and paste the lines that havemap.asmx/js. here.


Thanks for quick response. When I directly browse to .../js in IE7, i get 500 internal error whereas in case of Firefox, it prompts to download a file and the contents of the file are: "{"Message":"Authentication failed.","StackTrace":null,"ExceptionType":"System.InvalidOperationException"}".

No such folder "W3SVC1" exists under LogFiles ... only following folders are present there "Cluster, HTTPERR, ShutDown" and in there I am not able to find anything related to my issue.

Please let me know what next I should do? Thanks


Hi,

HttpPost andHttpGet for webservices is disabled by default in ASP.net 2.0

See if you can find the following snippet in your web.config , if it doesnt exist, add it under the <System.Web> Tag

<webServices>
<protocols>
<add name="HttpSoap1.2"/>
<add name="HttpSoap"/>
<add name="HttpPost"/> // uncomment this
<add name="HttpGet"/> // uncomment this
<add name="HttpPostLocalhost"/>
<add name="Documentation"/>
</protocols>
<soapExtensionTypes>
</soapExtensionTypes>
<soapExtensionReflectorTypes>
</soapExtensionReflectorTypes>
<soapExtensionImporterTypes>
</soapExtensionImporterTypes>
<wsdlHelpGenerator href="http://links.10026.com/?link=DefaultWsdlHelpGenerator.aspx"/>
<serviceDescriptionFormatExtensionTypes>
</serviceDescriptionFormatExtensionTypes>
</webServices>

Hope this helps


Read this KB article about Protocols and web services

HOW TO: Limit the Web Services Protocols that a Server Permits

http://support.microsoft.com/?id=815150


Yes, it was not there and I copied it under system.web tag but the issue is still there. There is also an <httpHandles> tag under system.web. I am pasting the lines from my web.config related to ajax. May be it will give further clue. Please let me know if u see something wrong there or what should I do next. Thanks a lot.

--------

<configSections>
<sectionGroupname="system.web.extensions"type="System.Web.Configuration.SystemWebExtensionsSectionGroup,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<sectionGroupname="scripting" type="System.Web.Configuration.ScriptingSectionGroup,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<sectionname="scriptResourceHandler"type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="false"allowDefinition="MachineToApplication" />
<sectionGroup name="webServices"type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<sectionname="jsonSerialization"type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="false"allowDefinition="Everywhere" />
<sectionname="profileService"type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="false"allowDefinition="MachineToApplication" />

<section name="authenticationService"type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" requirePermission="false"allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>
<sectionGroup name="applicationSettings"type="System.Configuration.ApplicationSettingsGroup, System,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="wwwroot.Properties.Settings"type="System.Configuration.ClientSettingsSection, System,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"requirePermission="false" />
</sectionGroup>
</configSections>

<system.web>

<httpHandlers>
<remove verb="*" path="*.asmx" />
<add verb="*" path="*.asmx" validate="false"type="System.Web.Script.Services.ScriptHandlerFactory,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<add verb="*"path="*_AppService.axd" validate="false"type="System.Web.Script.Services.ScriptHandlerFactory,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<addverb="GET,HEAD" path="ScriptResource.axd"type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions,Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"validate="false" />

</system.web>

<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<add name="ScriptModule" preCondition="integratedMode"type="System.Web.Handlers.ScriptModule, System.Web.Extensions,Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</modules>
<handlers>
<remove name="WebServiceHandlerFactory-ISAPI-2.0" />
<add name="ScriptHandlerFactory" verb="*" path="*.asmx"preCondition="integratedMode"type="System.Web.Script.Services.ScriptHandlerFactory,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<addname="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd"preCondition="integratedMode"type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="ScriptResource" preCondition="integratedMode"verb="GET,HEAD" path="ScriptResource.axd"type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</handlers>
</system.webServer>

----------------


I have found the iis logfiles and the directory 'W3SVC1' ... this directory is present in another partition because of setting different path for logging from IIS console.

Below are the lines related to this webservice:

this is the line when called with the following url : http://localhost/WebServices/WSGoogleMaps.asmx

2007-02-22 17:06:54 127.0.0.1 GET /webservices/wsgooglemaps.asmx/js - 80 - 127.0.0.1 Mozilla/5.0+(Windows;+U;+Windows+NT+5.2;+en-US;+rv:1.8.1.1)+Gecko/20061204+Firefox/2.0.0.1 200 0 0

and this is the line when called with the following url: http://mytest.server.com/WebServices/WSGoogleMaps.asmx

2007-02-22 18:15:27 192.167.1.273 GET /WebServices/WSGoogleMaps.asmx/jsdebug - 80 - 132.136.112.84 Mozilla/4.0+(compatible;+MSIE+7.0;+Windows+NT+5.1;+InfoPath.2;+.NET+CLR+2.0.50727;+.NET+CLR+1.1.4322) 500 0 0

I have tried it on the server itself as well. If I use domainname then it is giving 500 internal error in IE7 or authentication failed in Firefox. This is the line when called from the server itself using domain name:

2007-02-22 16:41:04 192.167.1.273 GET /WebServices/WSGoogleMaps.asmx/jsdebug - 80 - 192.167.1.273 Mozilla/5.0+(Windows;+U;+Windows+NT+5.2;+en-US;+rv:1.8.1.1)+Gecko/20061204+Firefox/2.0.0.1 500 0 0

I simply don't understand that how same code on the same machine works with localhost in the url but gives error when called with domain name in the url. I am totally lost. Please help me solve it ... I need to solve it urgently. Thanks.


Hi there,

We are experiencing the same issue that seems to do with the domain name causing the authentication error in some environments. Did you ever get a resolution to this issue?

Monday, March 26, 2012

Ajax authentication, custom membership; cannot use Session object

I'm using ajax 1.0 authentication but based on a custom sql membership service which is working 100% except that in the membership code the Session object (HttpContext.Current.Session) cannot be accessed which though is crucial because I user Session["UsersID"] for login based functions. Any ideas how this can be fixed?

Hello,

Would you give me more details (when, where you can't access session object) ?

I don't have any problems with Session object even with Ajax async call back??

May be your situation is different from mine!


yes is in the authentication function in my custom membership provider code;

publicoverridebool ValidateUser(string username,string password)

{

SqlConnection conn =newSqlConnection(connectionString);SqlCommand cmd =newSqlCommand("ValidateUser", conn);

cmd.CommandType =

CommandType.StoredProcedure;

cmd.Parameters.Add(

"@.Username",SqlDbType.VarChar, 12).Value = username;

cmd.Parameters.Add(

"@.Password",SqlDbType.VarChar, 12).Value = password;

cmd.Parameters.Add(

"@.IPNumber",SqlDbType.VarChar, 16).Value =HttpContext.Current.Request.UserHostAddress;SqlDataReader reader =null;try

{

conn.Open();

reader = cmd.ExecuteReader(

CommandBehavior.SingleRow);if (reader.Read() ==false)

{

returnfalse;

}

int _usersID =Convert.ToInt32(reader["UsersID"]);

HttpContext.Current.Session["UsersID"] = _usersID;

}

...


Hi Dabbi2000,

That's indeed very tempting to do. However, this construct will never work. The deal is that your code is executed by an http module that runs before the AcquireRequestState event. In other words: there is no session yet.

The best way to achieve what you want is to store your user id in the items collection of the current context and than move it from context to session in the PostAcquireRequestState event. So replace the last line of your example with:

HttpContext.Current.Items["UsersID"] = _usersID;

and then after the session state has been loaded (for example in PostAcquireRequestState)

HttpContext.Current.Session["UsersID"] = HttpContext.Current.Items["" UsersID"];
HttpContext.Current.Items.Remove("UsersID");

HTH,

-- Henkk


Henk,

that is an excellent solution! Unfortunately the Items container doesn't contain the UsersID when accessing from PostAcquireRequestState event. Using VS Debugger and Fiddler it simply seems to be lost somewhere between the authentication code and the PostAcquireRequestState event (the requesting assembly is "~/Authentication_JSON_AppService.axd"). Still there are other system variables in the Item like Is there some obvious explanation for that??


hmm not entirely correct, it isn't lost but what happens is that json authentication causes PostAcquireRequestState event BEFORE the membership validateuser() function is called?!!


Hi Dabbi2000,

I wanted to see for myself and, yes, you're right. What's going on ? In a normal postback (including async ones) the pipeline follows the chain of events I described in a previous post.

The problem is: I thought you were using the normal forms based authentication stuff in combination with an Ajax enabled app. However, you appear to be using the Sys.Services.AuthenticationService. Unfortunately (my bad, sorry) this is not a normal postback. It's uses the same infrastructure (ScriptHandler) as script services (as far as I can see, it is a script service dressed up as a resource). This means that indeed by the time you reach the endpoint (the authentication service backend) the PostAcquireSessionState event is long gone.

Fortunately, there are other events to subscribe to. But, unfortunately, if the PostAcquireSessionState is done and there is no session state, they most have turned sessions off for this service (and indeed the Session property in the HttpContext returns null).

Meaning, there is no way for you to use the Session object.

However, if the only thing you want to do is get to the current user id, the FormsAuthenticationModule (like all other builtin authentication modules) makes sure the currently logged on user is reflected in the Page.User property (or HttpContext.User for which Page.User is just a wrapper).

If you need to store more, the profile should still work. BUT only after you made a roundtrip to the authentication service and you successfuly authenticated. The authentication service calls into the FormsAuthentication class to perform authentication and returns an authentication cookie like normal, but unfortunately it forgets to set HttpContext.User like the builtin authentication modules would do (feature request ...). This means the profile module has no info to fly on and will do an anonymous profile while in the call chain of the authentication service.

HTH,

-- Henkk


I wrote a blog post about the problem:http://www.jeffzon.net/Blog/post/Session-enabled-authentication-service.aspx

The only thing we should do is to use the following authentication service class instead of the default one:

using System;using System.Web;using System.Web.Services;using System.Web.Services.Protocols;using System.Reflection;using System.Web.Script.Services;using System.Web.UI;using System.Web.Security;[WebService(Namespace ="http://tempuri.org/")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)][ScriptService]public class SessionEnabledAuthService : System.Web.Services.WebService{private static MethodInfo checkEnabledMethodInfo;static SessionEnabledAuthService() { Type authServiceType =typeof(ScriptManager).Assembly.GetType("System.Web.Security.AuthenticationService"); checkEnabledMethodInfo = authServiceType.GetMethod("CheckAuthenticationServicesEnabled", BindingFlags.NonPublic | BindingFlags.Static); }private static void CheckAuthenticationServicesEnabled(bool enforceSSL) { checkEnabledMethodInfo.Invoke(null,new object[] { HttpContext.Current, enforceSSL }); } [WebMethod(EnableSession =true)] [ScriptMethod]public bool Login(string userName,string password,bool createPersistentCookie) { CheckAuthenticationServicesEnabled(true);if (Membership.Provider.ValidateUser(userName, password)) { FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);return true; }return false; } [WebMethod(EnableSession =true)] [ScriptMethod]public void Logout() { CheckAuthenticationServicesEnabled(false); FormsAuthentication.SignOut(); }}

Hi Jeff,

This of course would work, but a couple of remarks:

1) namespacehttp://tempuri.org/ is not very unique ;-) you'll want to change this into something that makes more sense

2) I wouldn't mark this service as basic profile compliant. In normal situations it doesn't even do SOAP (script services serialize JSON by default). And even when you call it using a normal proxy, it's main result is returned as a cookie (which is not basic profile compliant, although I don't think the builtin pipeline check catches that).

3) By using reflection to call into CheckAuthenticationServicesEnabled you now have to run full trust (you'll need SecurityPermissionFlags.UnmanagedCode). Which means you either have to run the webservice full trust, or you'll have to install an separate assembly in the GAC (or grant it full trust with a custom policy). You'd be better of implementing this yourself, because the only really interesting thing CheckAuthenticationServicesEnabled does for you is check if SSL is enabled, which you can easilty do yourself with HttpRequest.IsSecureConnection (the other part is checking if an enabled flag is set on AuthenticationService itself, but since you're implementing your own, you already know it's enabled ...)

4) just as a word of warning for those who want to use this construct in production: I rarely think it's a good idea to repeat builtin functionality (with a minor change). I guess the team had good reasons to disable sessions (they are chatty things anyway) and there's plenty of workarounds. I do think it is too bad they didn't setup HttpContext.User and Thread.CurrentPrincipal like the pipeline authentication modules do, but alas. Regardless, the problem of getting to the current user is solved in an entirely different way, so the original poster will not benefit from this service. He can simply look an Page.User and be done with it.

-- Henkk


hello,

your discussions are a little more advanced than my programming knowledge :) but I would like to comment on that solution of using Page.User. User.Identity stores the username which is very unpractical. Imagine all the tables where I have to refer rows to user e.g. forum posts. User names can be whole names, and in my web would contain icelandic letters and so I think they are very bad for primary keys. That is the reason for why I use Session["UsersID"] instead of the User object.


D'uh. Don't know what I was thinking, but you'll need reflectionPermissionFlags.MemberAccess. What I said about SecurityPermissionFlags.UnmanagedCode is complete and utter BS. The net result is the same though. You'll need full trust ...

-- Henkk


Great feedback, Henkk, I totoally agree with you.

Actually I just gave a prototoype of the solution - or more likely a work around. In this situation, if the poster really need to use session state in authentication, he can use the service class I wrote - remove the validation method will be enough.


Hi,

The mapping from identity.name to user id can be done in a variety of ways. There is a very elegant one that sticks out I think (this is my personal preference BTW), which has to do with the way membership providers work. The builtin membership providers can do a mapping of the current user onto a so called MembershipUser (just a class in the library). The membership user contains more info than your average principal (including password questions, email addresses, stuff like that). You can get to the membership user for the current user by calling Membership.GetUser().

Since you implement your own provider you get to fill this information yourself (in the GetUser(userName, userIsOnline) override on your custom membership provider). One of the bits of info in a membership user is a ProviderUserKey. This is an object that you can fill with whatever info you want. In your case, probably the user id. The standard SqlMembershipProvider stores a GUID in it, BTW.

Whenever you need to get to the current user id, just use Membership.GetUser().ProviderUserKey.

Be aware though that the mapping is not cached, so if it is expensive to get from current user name to current user id, you better cache the mapping (in the ASP.NET cache ...).

The beauty of the construct is that now all custom logic is in your membership provider ...

HTH,

-- Henkk


hope you guys are still watching this thread...

Henk's solution is tempting... it's a bit expensive for only retrieving an Int value but if it survives the AJAX authentication I'm ok with it! I'm no good at asp.net cache, how would I use that for this situation??


that is, how to make it specific to each user, I thought the cache object scope was application only?

Wednesday, March 21, 2012

AJAX and Forms Authentication

Hi

I'm just starting to look at building AJAX functionality into our existing ASP.NET 2.0 web application. The web app is a portal-type application and uses Forms Authentication and the ASP.NET Membership/Profile providers to manager users.

I've followed through the tutorial videos and successfully AJAX-enabled a grid view on one of the pages which is filtered by a drop down list. I've also added an UpdateProgress tag which seems to work fine. The problem arises when a user changes the value in the drop down list after the forms authentication ticket has expired (which is currently set to 30 mins). Without AJAX, any change in the drop down list first redirects back to the login back, then posts back the original page to show the newly filtered gridview. Howvever, with AJAX, the first change to the list appears to simply redraw the existing grid data (regardless of what is selected), and subsequent changes result in the following browser/client error. The status code varies between 12030 and 12031 on different runs of the application.

--------
Windows Internet Explorer
--------
Sys.WebForms.PageRequestManagerServerErrorException: An unknown error occurred while processing the request on the server. The status code returned from the server was: 12030
--------
OK
--------

I've searched around these forums, google, the online help etc. and it appears that you can use AJAX to control logins via forms authentication which implies that it must be possible to achieve what I'm after. However, so far it looks as though the AJAX callback is not respecting/using the forms authentication ticket.

Could anyone elighten me as to how AJAX callbacks work with forms authenticated sites and why this particular error message is occuring.

Best regards

Craig

Thanks for the link, I had a quick read through this yesterday, but there's been quite a few posts added in the last 12 hours or so.

I'm not sure it sounds exactly the same, but it could be related. As I said, I'm new to AJAX, so have only ever installed the RTM version and haven't tried anything earlier. My (limited) experience with update panels and logged-in users (via forms authetication) was that everything works fine until the authentication cookie expires - that's when the error occurs.

I'd be interested to know, from those who've used pre-RTM versions, what the expected behaviour should be. If an AJAX callback is made once the forms authentication cookie has expired, does the page redirect to the login page as with normal postbacks?

Thanks, Craig


Hi Craig,

This is an issue that has been introduced with the AJAX RTM. Several others of us are experiencing it but we haven't found a solution yet. We have a thread going about it at:

http://forums.asp.net/1553437/ShowThread.aspx#1553437


Yes, that would be the expected behavior.

The problem that I and others are seeing appears to be related to role caching though, not to cookie expiration.


Hi,

I had this exact same issue and found that it was related to the page validation and some hard coded text in a text box that contained "dangerous" characters. I had a textbox that had hard XML in the text area. Without the AJAX update panel, it would throw a warning error. With the AJAX update panel, it would throw the error you saw.

What I did for my testing purposes was switch the validaterequest to FALSE in the page declaration, and this fixed it. I know this is not ideal, but at least points to what the root cause is.

ValidateRequest="False"

Jason