Yes, that is correct! I'm going back to my old blog at msmvps.com. I'm doing this for a couple of reasons:

  • I want to have a personal blog where I can post whatever I want. Since I've received some poor feedback on 2 non-technical posts I've written and which got published on the main feed, I'm going back to my old blog. If tagging the posts with something like Trivia isn't enough for filtering the posts, then something is wrong. I really don't have the time to write my posts on the web interface so that I can change a setting which only appears on the advanced posting options.
  • I'm not taking any crap from guys like Mo who insist in commenting my private posts with "I don't care if you are back. This doesn't help me improve my ASP.NET. Stop posting stuff like this to the main feed.". Well Mo, guess what: I'm slow, but not that slow, so there really isn't any point on repeating the same comment on two different posts.
  • Finally, I don't like seeing all those advertisements after my posts.

So, if you're not Mo and you want to keep reading the posts I write, you can subscribe to this feed (which at the end of the day will aready be pointing back to the old msmvps.com feed). If you prefer to read my blog online, please update your bookmark.

P.S.: Yes Mo, this is being published on the main RSS feed and by reading it you won't be improving your ASP.NET. Better luck next time...

I've just finished uploading some photos from my trip to London. Check them out in facebook.

In my previous posts I've been talking about the basic features introduced by the new Applications Services which were added during the latest release of the .NET platform. Today we'll see how easy it is to configure our windows form app so that it is able to authenticate a user in an offline scenario.

As you might expect, you'll only be able to validate a user (on an offline scenario) if that user has already been validated previously in an online scenario. So, the 1st thing you need to do is to change your app.config file so that the hash of the password is stored in the machine. To achieve this, you need to set the savePasswordLocally attribute of the provider entry:

<add name="ClientAuthenticationMembershipProvider"
            savePasswordHashLocally="true"
             type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
             serviceUri=http://localhost/login/Authentication_JSON_AppService.axd
             credentialsProvider="login.Login, login" />

Even though this step is required in order to save a local copy of the hash of the password, it is not enough for making the platform authenticate the credentials against the local saved copy of a previously successful authentication. The offline authentication scheme will only be used when the ConnectivityStatus.IsOffline is set to true. So, you need to change your startup code to something like this for authenticating a user in an offline scenario:

ConnectivityStatus.IsOffline = true;
if(! Membership.ValidateUser("", "") )
{
     return;
}
Application.Run(new Form1());

By now you must be wondering where the platform is saving the hash of the password. If you don't do anything, the hash will be saved in a file named User_username.clientdata in a folder named after your windows app (placed inside the user's app data folder). Just replace username with the username of the user that is trying to log on and you should find the file by performing a search on your disk. If you open that file, you should find something like this on it:

<?xml version="1.0" encoding="utf-8"?>
<ClientData>
   <LastLoggedInUserName></LastLoggedInUserName>
   <LastLoggedInDateUtc>1C73F36A9D78ED2</LastLoggedInDateUtc>
  <PasswordHash>oSmbK+DdLg9KhsOKZHg5qKisALg=</PasswordHash>
   <PasswordSalt>EUQ3dz7qihlivjokvdkVBg==</PasswordSalt>
   <Roles>
      <item>Role2</item>
   </Roles>
   <RolesCachedDateUtc>1C85E0822CC597B</RolesCachedDateUtc>
   <SettingsNames></SettingsNames>
   <SettingsStoredAs></SettingsStoredAs>
   <SettingsValues></SettingsValues>
   <SettingsNeedReset>0</SettingsNeedReset>
   <SettingsCacheIsMoreFresh>0</SettingsCacheIsMoreFresh>
   <CookieNames>
      <item>9e7450b9c96a487eb57880ecfa4b96c9</item>
   </CookieNames>
   <CookieValues>
    <item>.ASPXAUTH=</item>
   </CookieValues>
</ClientData>

As you can see, there's a lot more info besides the password hash+salt used for authenticating the user in an offline scenario. For instance, roles can also be cached...

Currently, you can also save the user info in an SQL Compact database. To do that, you'll need to specify a SQL Compact connection string or you can simply use the special connection string "Data Source=|SQL/CE|". Here's what you need in order to achieve this:

<add name="ClientAuthenticationMembershipProvider"
            savePasswordHashLocally="true"
           connectionStringName="Data Source=|SQL/CE|"
            type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
            serviceUri="http://saturn/login/Authentication_JSON_AppService.axd"
            credentialsProvider="login.Login, login"

Do notice that this will only work in x86 builds (ie, SQL Compact will only run in WoW mode in x64 bits systems). So, if you also have a x64 version of an OS installed, don't forget to explicitly set the build to x86 on the configuration of your project. One more thing you might notice after looking at the previous config entry is that I'm passing the special connection string to an attribute which is called connectionStringName. Well, if you want, you can pass it the name of an existing entry on the connectionString section instead (internally, the provider will try to get an existing entry named with the value you pass to the connectionStringName attribute. If it can't find any, it will simply use the value you've passed to that atribute).

If you use reflector to check the code, you'll see that the helper classes do have code that would let you use Isolated Storage for saving these data. However, there's really no way to set it up (at least, I didn't find any - maybe I'm missing something!) so you'll have to choose between using the file system or a SQL compact database.

Before ending this post, there's still one more thing you should keep in mind: you should always set the ConnectivityStatus.IsOffline property. If you explicitely set the property, then that value will be respected. If you don't, then the value returned from the property will depend on the checking of a special file called AppIsOffline. This file is used as a marker and is created when you set the  the ConnectivityStatus.IsOffline property to true (notice that it will be deleted when you set the property to false). What this means is that if you've previously set that property to false and then don't set it back to true, authentication will always be performed offline since that property will return true.

And that's all for today...more to come tomorrow :)

After a five day trip to London where I was able to catch up with my good friend JPC. London is really a cool city with lots of stuff to see. Since I was only there for a few days, I didn't really saw half of what I wanted to check out. Well, at least I was able to see several cool cars, like this Audi A5 S5:

IMG_1525

Today I was reading Brad Abrams's blog and I've noticed that he was talking about a book called Microsoft Ajax Library Essentials. At the end Brad says that "(...) I think the book misses out on talking about how to use the Ajax Control Toolkit completely from client side javascript, but maybe that is for the 2nd edition ;-)".

I think that I can answer that. The last time I looked (and I know it was really a long time ago), that wasn't really possible. Why? simply because at the time the client error messages were maintained as embedded resources. Unlike what happens with the AJAX library, they didn't have any download for the js files which means that you'd have to 1.) use the ScriptManager control to get those errors injected on the page or 2.)  write some code that would go through the resources and export it to a js file which could be embedded as a resource.

Since several months have passed since then, I'm hoping that I'm wrong on this...though I wouldn't bet on that.

Hello.

My name is Luis Abreu and I'm a Portuguese ASP.NET MVP. Those of you that read my old blog already know what to expect from this blog: it's all about .NET programming (with special emphasis on the web area). Thank's for reading.

More Posts