One of my friends work for a company where he has to work in a remote PC and where he lives he does not have good internet connection. He neither has permission for opening up third party email website such GMail, nor he could download email with 100MBs of attachment using Outlook. He tried for even whole night trying download those email. However his poor internet connection did not let him download completely such single email from server. He had to redownload all the email from the beginning. So he asked me to help him out in such situation as he did not have more than 6 hours in hand to finish downloading those email and reply them. Unfortunately he was not a developer and I even myself did not have the time for it. Still for the sake of friendship, I agreed to help him out, and built an app in less than 10 minutes for him. This project has been hosted at MSDN Code Gallery.
I use many free components and tools everyday, I am grateful at, made my life lot easier. I wish I could write several blog posts on them, and thanking them for making it happening for me. One of them is OpenID and the .NET implementation DotNetOpenID that saved me a lot of coding and headache that day. My friend did not have any web server, so I built that app and hosted on my web server. But I already exceeded my database creation limit on that server and I really do not want to take a chance to screw up other existing databases. So I could not create tables to store membership information. His requirement was the site needs to be secured and password protected, and he can upload from remote PC and can download to his local PC through resume supported http downloaders such as Free Download Manager. Did I say I am a big fan of this free software too? Try it out for yourself.
From OpenID site:
OpenID eliminates the need for multiple usernames across different websites, simplifying your online experience.
You get to choose the OpenID Provider that best meets your needs and most importantly that you trust. At the same time, your OpenID can stay with you, no matter which Provider you move to. And best of all, the OpenID technology is not proprietary and is completely free.
For businesses, this means a lower cost of password and account management, while increasing site visitor registration conversion rates. OpenID lowers user frustration by letting users have control of their login.
For geeks, OpenID is an open, decentralized, free framework for user-centric digital identity. OpenID takes advantage of already existing internet technology (URI, HTTP, SSL, Diffie-Hellman) and realizes that people are already creating identities for themselves whether it be at their blog, photostream, profile page, etc. With OpenID you can easily transform one of these existing URIs into an account which can be used at sites which support OpenID logins.
And DotNetOpenID is a C# library adds OpenID 2.0 Provider and Relying Party, OAuth Consumer and Service Provider, and InfoCard Selector support to your web site both programmatically and through convenient drop-in ASP.NET controls.
How OpenID simplifies authentication
Here are the snapshots of the end product. There will be two other pages Upload and Browse which are pretty self-explanatory. You will be able to upload new file, browse files and delete unnecessary files. Most important part of this page is it has a login page which is not like regular login box where you enter username and password. It has a OpenID Login instead. You will have to type your OpenID URL here. If you do not have one, you can click register and have the same. The reason behind no username rather than OpenID URL is, there could be many different OpenID provider websites that will provide you with OpenID account creation opportunity. Many popular providers are ClaimID, MyOpenID, MyID and so on. No matter what you chose to open OpenID account with, you will still be able to access all the sites that support OpenID. That is why OpenID identifies yourself as an URL instead of Username/ID.
When you type your OpenID URL there and hit Login, you will be redirected to the provider’s page like the following where you will have to type your password in and allow access the site to use your account information such as your name, email address and so on. See the highlighted key information below:
If you click on Stay signed in, you will never have to write password again. Whenever the authenticated application in our case our DropZone application will redirect to the provider’s login page it will be automatically authenticated and redirected back to our app. So, you will not see the login page again. Consider OpenID as a global ID or passport that you can use in many giant and popular websites. Popularity of OpenID is being increased everyday exponentially.
When you are done with authenticating yourself to the application you will be redirected to the Upload page where you can select a file and upload it to server. The beauty of OpenID here is you do not have to worry about creating tables in your database for keeping track of users, change password, forgot password, register, login mechanism, manage sessions, cookies and what not functionalities you can imagine. You will also find another page named Browse which has a simple GridView that displays file list from the directory where files will be stored. We will see shortly how we would do that. You will also be able to delete files from the grid which results into deleting the actual uploaded file from the server
Configuring for custom users and paths
Before we get into how we could setup OpenID for our application, let us take a look at some configuration that we will be using through out its development lifecycle. You can write comma separated values for allowed OpenID users who can authenticate and use this application. You may want to keep this list short and filled up with OpenID URLs of friends only. You will also need to configure the store path where files will be stored and URL prefix for the files to download. Here are the configuration you will have to place inside web.config file:
<appSettings>
<add key="allowed_users" value="user1@myopenid.com,user2@myopenid.com"/>
<add key="store_path" value="d:\\dropzone\\"/>
<add key="download_url_prefix" value="http://localhost/uploads/"/>
</appSettings>
Getting Started with DotNetOpenID
DotNetOpenID is an implementation of OpenID API and as the name suggests of course it is meant for .NET applications. After you have referenced the DLL you will be able to use the OpenID Login control in your WebForm. You can use the design-time view and set properties for the control like the following:
From HTML view it may look like the following snippet:
<cc1:OpenIdLogin ID="OpenIdLogin1" runat="server" CssClass="openid_login" RequestCountry="Request"
RequestEmail="Request" RequestGender="Require" RequestPostalCode="Require" RequestTimeZone="Require"
RememberMeVisible="False" PolicyUrl="~/PrivacyPolicy.aspx" TabIndex="1"
OnLoggedIn="OpenIdLogin1_LoggedIn" OnCanceled="OpenIdLogin1_Canceled" OnFailed="OpenIdLogin1_Failed" />
You will notice the RequestEmail, RequestCountry and so on properties, there are the items that will be asked to and bring back from the OpenID provider. You will also see event handlers are being registered to customize our needs to perform specific actions upon those events. Now that we have included the OpenID login control, its time for implementing the event handlers.
I used the same State class that was being shipped with the Samples for DotNetOpenID, which looks like the following:
public class State
{
public static void Clear()
{
ProfileFields = null;
FriendlyLoginName = null;
PapePolicies = null;
}
public static ClaimsResponse ProfileFields
{
get { return HttpContext.Current.Session["ProfileFields"] as ClaimsResponse; }
set { HttpContext.Current.Session["ProfileFields"] = value; }
}
public static string FriendlyLoginName
{
get { return HttpContext.Current.Session["FriendlyUsername"] as string; }
set { HttpContext.Current.Session["FriendlyUsername"] = value; }
}
public static PolicyResponse PapePolicies
{
get { return HttpContext.Current.Session["PapePolicies"] as PolicyResponse; }
set { HttpContext.Current.Session["PapePolicies"] = value; }
}
}
This class holds information for the logged on user in static properties that include Profile detail as well as FriendlyLoginName which is the OpenID URL for the logged on user. You will also notice that the class has a Clear method which nullifies every property to state that there is currently no logged on user at the moment. LoggedIn is the event that gets fired when an user successfully login using his OpenID. So we need to write code to handle that:
1: protected void OpenIdLogin1_LoggedIn(object sender, OpenIdEventArgs e)
2: {
3: if (e.Response.Status == AuthenticationStatus.Authenticated)
4: {
5: var isAllowed = false;
6: var allowedUsers = ConfigurationManager.AppSettings["allowed_users"].Split(",".ToCharArray());
7: foreach (var allowedUser in allowedUsers)
8: {
9: if (e.Response.FriendlyIdentifierForDisplay.Equals(allowedUser, StringComparison.InvariantCultureIgnoreCase))
10: {
11: isAllowed = true;
12: break;
13: }
14: }
15:
16: if (isAllowed)
17: {
18: State.FriendlyLoginName = e.Response.FriendlyIdentifierForDisplay;
19: State.ProfileFields = e.Response.GetExtension<ClaimsResponse>();
20: State.PapePolicies = e.Response.GetExtension<PolicyResponse>();
21: Response.Redirect("Upload.aspx");
22: }
23: else
24: {
25: State.Clear();
26: }
27: }
28: }
This code takes care of the information that are passed to this method after successful login from OpenID, and iterates through the allowed OpenIDs from web.config to validate whether the user should get access to the resources. If the user is found, State is being populated with the information received from OpenID. Next thing is to protect pages from being accessed without authentication by hotlink. We could do this by validating whether the State has FriendlyLoginName data like string.IsNullOrEmpty(State.FriendlyLoginName).
Production Setup
1. Set write properties for your Uploads folder such as this from IIS:
2. For additional security to the Uploads folder you could also set permission for Administrator/other users you like:
3. Also make sure the file is being uploaded can be handled by the server. For instance, if you upload 7z files and your server does not have a clue how to handle this request, you could set the mime type for that particular type of file like the following:
This will ensure the response header would be set to application/x-zip-compressed which will cause the browser or file downloader software to initiate the download. However, regular extensions should work just fine without adding entries to mime types.
4. ASP.NET by default allows you to upload maximum 4MB size of file. Exceeding that limit would be easy to configure in web.config by the attribute in httpRuntime. You will have to determine the maximum limit of megabytes you are going to allow by multiplying with 1024. Like 200MB * 1024 = 204800 Kilobytes.
<httpRuntime maxRequestLength="204800" />
5. ASP.NET default maximum timeout for each request is 30 seconds, after that you request will be ended prematurely. If you need to upload larger files that needs sufficient timeout value, you can also set that up from the same XML element in web.config like the one mentioned above. This value is in seconds. We are setting here 12 hours timeout.
<httpRuntime executionTimeout="43200" />
Conclusion
Keep in mind that this application is open source which has not gone through thorough testing efforts and you should use this totally at your own risk. However, I used this application by myself to transfer hundreds of megabytes of large files without any problem. You will also find I did not put any effort for good architecture for this tiny tool, nor have I incorporated any exception handling mechanisms.
Do you know from where I got the theme for this application? FreeCssTemplates is another free website that I am grateful at, for letting me use numerous of their templates in my applications for no charge.