Click here to Skip to main content
Email Password   helpLost your password?
LogonDemo

Introduction

A question was posed in the C# forum today about logging in to an application using the user's Windows account. In essence, if the user is logged into his account, and he tries to run an application, it's kind of pointless (and annoying) to re-request his login info. However, making a user login to an application does allow the programmer to dictate the terms, specifically, what roles the user has on the computer in question. This article demonstrates not only this aspect of application access, but also allows the program to have its own xml-based database of users.

The Windows Authentication Problem

Since we don't have to worry about the user's name and/or password, this process is reduced to a much easier task - determining if the user is in an acceptable group/role. With the .Net framework, this is easy as pie, involving just three lines of code:

using System.Security.Principal;

public bool UserInSystemRole(WindowsBuiltInRole role)
{
    WindowsIdentity  identity = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(identity);
    return principal.IsInRole(role);
}

The function above is from the supplied sample application, and is called by passing the desired WindowsBuiltInRole ordinal (like WindowsBuiltInRole.Administrator). You can also check for custom roles such as "MySuperRole". Below is the function from the sample application:

using System.Security.Principal;

public bool UserInCustomRole(string role)
{
    WindowsIdentity  identity = WindowsIdentity.GetCurrent();
    WindowsPrincipal principal = new WindowsPrincipal(identity);
    return principal.IsInRole(role);
}

Sometimes, .Net really does make things too easy on us. :)

The Application Authentication Problem

When you want something application-specific, this is probably the best way to go. The data file can be stored on any network share (for easy administration), and you can go as far as you want or need regarding security. For this sample application, I chose not to implement any kind of encryption or hashing of passwords because that's not what this article is about (and I pretty much didn't feel like doing it). Here's the function used to authenticate via the application's XML-based user database:

public bool ValidateApplicationUser(string userName, string password)
{
    bool validUser = false;

    // if you want to do encryption, I recommend that you encrypt the password 
    // here so that you don't have to mess with the LINQ query below, but you 
    // can still do a direct comparison.

    try
    {
        // setup the filename
        string fileName = System.IO.Path.Combine(Application.StartupPath, "users.xml");

        // load the file
        XDocument users = XDocument.Load(fileName);

        // query the file with LINQ - this query only returns one record from 
        // the file, and only if the user name and password match.
        XElement userElement = (from subitem in 
                    (from item in users.Descendants("user") select item) 
                     where subitem.Element("name").Value.ToLower() == userName.ToLower() 
                     && subitem.Element("password").Value == password 
                     select subitem).SingleOrDefault();

        // if you get here without an exception, and if the returned XElement isn't null
        // then the user is valid
        validUser = (userElement != null);
    }

    catch (Exception ex)
    {
        if (ex != null) {}
    }

    return validUser;
}

Notice that we used our new friend, LINQ, again. LINQ is just too handy to ignore. While I wouldn't use it all the time, it's great for dealing with XML files like our user database.

Notes 

You can easily combine the application-specific authentication with the role validation to further control access to your applications. 

The provided sample application has NOT been thoroughly tested (I simply don't have the time right now), so run your login code through the debugger a couple of times to make sure it's going to do what you want it to do.

History

10/03/2008: Changed the LINQ statement that retrieves the userElement in the ValidateApplicationUser() method to return null instead of waiting for an exception in the event that the user isn't found. I did not change the code in the download file, so remember to make the same change in your own code.

10/02/2008: Original article posted.

  

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGood aarticle
Donsw
12:20 22 Jan '09  
Good article, Nice use of linq. this is as you pointed out a great use of linq.
GeneralVery nice article John
Sacha Barber
8:52 13 Oct '08  
I like this one a lot. 5 *

Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: Very nice article John
John Simmons / outlaw programmer
9:00 13 Oct '08  
I think you should vote 5's on all of my articles. Smile


"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001


GeneralRe: Very nice article John
Sacha Barber
9:02 13 Oct '08  
Of course you do! I think that most of the time I more than likely would, I just don't know C++, so have to pass those ones up.

Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralChange To Article
John Simmons / outlaw programmer
0:26 3 Oct '08  
I change the text in the article to reflect a better method for retrieving a single record without necessarily throwing an exception. I DID NOT CHANGE THE CODE IN THE DOWNLOAD FILE. The change wasn't necessary, and doesn't make the code any more reliable, but it avoids forcing the sample program to throw an exception if the user isn't found (and avoiding exceptions is always a noble goal).

Many thanks to Navaneeth for telling me about it.


"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001


modified on Friday, October 3, 2008 6:16 AM

GeneralSome suggestions
N a v a n e e t h
18:32 2 Oct '08  
John Simmons / outlaw programmer wrote:
A question was posed in the C# forum today


typo?

Your article is good. But using try/catch for setting validUser flag seems to be a bad idea. You can change the LINQ a bit so that it returns NULL when item is not found. I think code would be much better then.
XElement userElement = (from subitem in 
(from item in users.Descendants("user") select item)
where subitem.Element("name").Value.ToLower() == userName.ToLower()
&& subitem.Element("password").Value == password select subitem).First();
Change to
XElement userElement = (from subitem in 
(from item in users.Descendants("user") select item)
where subitem.Element("name").Value.ToLower() == userName.ToLower()
&& subitem.Element("password").Value ==
password select subitem).SingleOrDefault();
return userElement != null;
SingleOrDefault will return NULL instead of throwing exception if item not found.


GeneralRe: Some suggestions [modified]
John Simmons / outlaw programmer
0:10 3 Oct '08  
Well, the tray/catch block has to be there anyway (it's just a matter of good coding practice), but I have to agree that you don't want code to throw exceptions needlessly, so your approach is probably a better one. Besides all that, I'm just getting started with LINQ, so I don't know all of the nooks and crannies of the library. Thanks for pointing it out.

(Posed is spelled correctly). Smile


"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001


modified on Friday, October 3, 2008 5:26 AM

GeneralRe: Some suggestions
N a v a n e e t h
18:23 5 Oct '08  
Glad to hear that it helped. Smile


GeneralYour work
nelsonpaixao
14:37 2 Oct '08  
Hi,

i am going to check it out your work, i manage login/logout in winforms with database.Rose

nelsonpaixao@yahoo.com.br

trying to help & get help

GeneralUseful
DaveyM69
13:36 2 Oct '08  
I knew this stuff was in there but never got around to investigating. I'm gonna need some of this soon in one of my current projects so bookmarked (to save me the effort of 'googling' and the pain of MSDN) and 5'd! Big Grin

Dave
BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
Visual Basic is not used by normal people so we're not covering it here. (Uncyclopedia)

GeneralNice, but some suggestions...
marco_br
11:50 2 Oct '08  
Good article: I just have two small suggestions...

First: I'd write the catch block like this:

catch (Exception ex)
{
if (ex != null) {}
}


Second: I know with LINQ this is just as easy to manage as XML, but you could at least mention the case where the user base of the application is stored in a Database.
GeneralNice, but some suggestions... (correction)
marco_br
11:53 2 Oct '08  
Sorry, I forgot to change the code in my previous message... please disregard it and consider this one. Sigh

Good article: I just have two small suggestions...

First I'd write the catch block like this:

catch
{
validUser = false;
}


Second:; I know with LINQ this is just as easy to manage as XML, but you could at least mention the case where the user base of the application is stored in a Database.

GeneralRe: Nice, but some suggestions... (correction)
John Simmons / outlaw programmer
12:04 2 Oct '08  
First: I don't need to set validUser to false in the catch block because that's the default value (initialized at the top of the method), and the only way it will change to true is if there is no exception encountered while trying to find the user.

Second: I *did* mention it in the section labeled "The Application Authentication Problem".


"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001


AnswerRe: Nice, but some suggestions... (correction)
marco_br
2:43 3 Oct '08  
For the first point, I agree it is not necessary... but I just think it is better to reset a field's value (even if it is rather redundant) than to simply "swallow" the exception. It makes the code more readable IMO and prevents some unecessary processing of capturing the exception in the variable "ex".

As for the second point, I read the article again and it seems to me you mention only XML-based DB, not relational DB.

But, as I said, the article is good, and it got my 5.
GeneralNice use of Linq in there.
Pete O'Hanlon
11:23 2 Oct '08  
Don't tell me you've moved over to the dark side of .NET 3.5 John. Anyway - gets my 5 to counteract the univoting asswipe.

Deja View - the feeling that you've seen this post before.

My blog | My articles | MoXAML PowerToys



GeneralRe: Nice use of Linq in there.
John Simmons / outlaw programmer
11:46 2 Oct '08  
Pete O'Hanlon wrote:
gets my 5 to counteract the univoting asswipe.


Unfortunately, there's more than one asswipe here. Smile

This is a perfect example of why simply voting an article useful and not allowing a numeric rating system would be better for everyone involved. It would make life easier on those posting articles, and it would free up a lot of spare time for people that would otherwise spend the day voting 1s on articles that obviously don't deserve low scores.


"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001


GeneralRe: Nice use of Linq in there.
Jean-Paul Mikkers
11:59 2 Oct '08  
I've been thinking about those unavoters. Perhaps those are people that live in countries where a 1 is the best test score (I think Germany uses a system like that). Sniff
GeneralRe: Nice use of Linq in there.
jpsstavares
1:05 3 Oct '08  
Jean-Paul Mikkers wrote:
Perhaps those are people that live in countries where a 1 is the best test score


Well, I don't think it's the case though. Before 1 it's clearly stated Poor and after 5 Excellent, so I don't think this is a problem.
GeneralRe: Nice use of Linq in there.
Pete O'Hanlon
12:02 2 Oct '08  
John Simmons / outlaw programmer wrote:
Unfortunately, there's more than one asswipe here


Yeah - but I like to consider myself a vicious, sarcastic asswipe. Wink

Deja View - the feeling that you've seen this post before.

My blog | My articles | MoXAML PowerToys




Last Updated 2 Oct 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010