Watch videos with subtitles in your language, upload your videos, create your own subtitles! Click here to learn more on "how to Dotsub"

Simple Web Service Authentication

0 (0 Likes / 0 Dislikes)
  • Embed Video

  • Embed normal player Copy to Clipboard
  • Embed a smaller player Copy to Clipboard
  • Advanced Embedding Options
  • Embed Video With Transcription

  • Embed with transcription beside video Copy to Clipboard
  • Embed with transcription below video Copy to Clipboard
  • Embed transcript

  • Embed transcript in:
    Copy to Clipboard
  • Invite a user to Dotsub
[Microsoft ASP.net] [www.asp.net] >> Hi folks, Joe Stagner from Microsoft here, and in this video, I'm going to demonstrate an easy mechanism that you can add to your applications to restrict unauthorized access to your ASMX web services. Now, as with all web applications security choices, we need to weigh the actual level of security that's required for our application with the labor investment that will be required to implement that level of security. In this video, I'll show you a couple of simple authentication mechanisms that you can use with web services, and I'll discuss the strengths and weaknesses of each. So to get started, let's go ahead and say Create New Project, we'll select the default ASP.net web application template, and I'm going to choose a name—chose web application. So we click Okay. Now, the default web application template for ASP.net applications includes some built-in facilities, like if we look at the accounts folder, we have the mechanism to log in, register, or change our password. So since we're going to be working with authenticated web services, we're going to need some user credentials with which we can authenticate. So to create them, I'm going to go into the projects ASP.net configuration utility, I'm going to select Security, and what we're going to see right out of the blocks is that we get an error. Now, the reason we get this error is simply that there isn't enough information for this tool to get started, and that's because we haven't built the project. We can do 2 things. We can either just run the project, which will cause it to build by default, or we can explicitly build. Once we've got a successful build, then we can go into our project and now we can select Security, and instead of the error, we'll see the security choices, 1 of which is create user. Here, I can say let's create a user name, Joe User, let's enter a demo password, and an email address, and now I can click on Create User, and that user's successfully created. Now, the thing to note is that if we choose Show All Files, you see that the ASP.NETDB.MDF file has been created here in my app_data folder. Now, this happens on my machine because the default configuration, which is in place on my machine, is that the machine.config file indicates that the membership repository should be found in the app_data folder, and it's a sql express database. This just happens to be the default, and it's on my development machine. If on your development machine you've set up some other ASP.net application services configuration, it will resolve the ASP.net membership tables wherever you've specified. So in my case, I'm going to modify this ASP.NETDB.MDF sql express database, and in order to be able to do that from inside a Visual Studio, I'm going to right click and I'm going to say Include this Database in my Project. Now it will appear in my server explorer so that I can make changes to it as we go through the rest of this video. So the next thing that we're going to need is a web service. Let's go ahead and right click, and we'll say Add a New Item. We'll go down and select Web Service, and we'll change the name from Swan to Joe's Web Service. Now, a note about how web services work. When we create a web service and then we want to call that web service, or when someone else has created a web service and we want to call it, we don't actually talk to that web service directly. What we do is we use the Visual Studio tool to generate a proxy local to our solution in the language that we're working in, so in our case it will be in C#, and that proxy is a class that we can create an instance of, and we can use that class to call the web service. 1 of the caveats is that when we create a reference to that web service, and we'll do this momentarily by right clicking on the references folder and saying Add a Web Reference. But when we generate that proxy, that proxy has 2 sets of information that are of interest to us in this demo. 1 is the proxy has all of the method signatures for the web methods in that web service. The other thing that's important to us is that that proxy is generated to point to the end point, the URL, where we created that web reference from. There's 2 things to note. First, because we're using Visual Studio, and by default in Visual Studio I'm using the built-in web server, for security purposes, every time Visual Studio starts the built-in web server, it dynamically allocates a port number to start the web server on. If we're generating a proxy based on a URL end point, and we're doing that inside Visual Studio, then next time we start Visual Studio and try and work with that web service proxy, the Visual Studio will start an instance of that web server on a different port number than the 1 that we used to generate the proxy. We'll have to constantly go in and change that. There are 2 solutions to this. One is to simply install the full version of ISO on your development server and program against that through local hosts and it will always work against port 80. That's great. That's my preference, but for lots of shops, that's either not allowed or folks don't want the overhead on their machine running a full-blown web server all the time. There's an alternative mechanism that we can employ. In Solution Explorer, I select My Solution, and I go down and I click on the Property Pages button down in the property section, I get the property page for my solution. Then if I click on the web collection and I scroll down and find the servers properties. Notice this property here—Auto Assign Port. What I'm going to do, instead of say, I'm going to assign a specific port and I can put in any valid port number than I want, and then I'm going to save this. Now, whenever I'm working on this specific solution, every time Visual Studio starts an instance of the built-in web server, it will start it on that static port number. It won't be running all the time, but our proxy code will always work, because Visual Studio will always start the web server on the same port number. Now, what I can do is address the second issue, and that is that when that proxy is generated, it creates a class based on all of the publicly exposed web methods and their method signatures. Every time we add a method that we want to talk to or we change the method signature so we add parameters, we have to regenerate the proxy. So the real world, that's not necessarily a problem, but for the purpose of this demo, I'm just going to predefine the method signatures so we only have to generate the proxy 1 time. This Hello World method we'll call with no authentication at all. The second one we'll call, with user name and password, we'll call this Hello World by User, and that's going to take 2 input parameters, a string that we'll call USR, and a string that we'll call PWD for password. The second mechanism will be calling with an authentication token, so we'll call this method Hello World by Token, and we're going to take a single parameter that is an authorization token. Now, let's demonstrate generating the web proxy. If I right click in my Solution Other References folder and select Add Web Reference, notice that the Add Web Reference tool gives us a choice. We can, in the URL box, enter a specific URL, so I could enter www.microsoft.com/ whatever the web service end point is, or I could say browse to web services in this solution on this local machine, or in a UDDI directory, which is basically like a phone book for public web services. In my case, I'm going to choose browse to web services in the solution, and we're going to see the Joe's Web Service, because there's a ASMX file there. However, when I click on it, I'm going to an error. The reason I'm going to get an error is that we haven't built the solution yet, so the tool doesn't have enough information to generate the proxy. If I close out the tool, build my solution, then say right click on References, Add Web Reference, web services in the solution, Joe's Web Service, now what you'll see is the WSDL has been fetched from the server, and this is a collection of the methods that are available. Now, when I say the WSDL has been downloaded from the server, we happen to be running in the same solution and therefore the same server. But there's no innate binding between ASMX web services or any web service and the web pages that call them, so it's just coincidental in our case that, building this demo, we put them both in the same solution. But there is no innate coupling, so we need to specify the specific end point for the file, and you'll see here local host on port 1,111, Joe's Web Service ASMX as the end point. The web reference name that we want to specify, let's call this Joe's Securable Services. We can add the web reference. Now, the other thing that you might be thinking if you've done some ASP.net programming is you've worked with membership, and you're aware of the Is Authenticated property. You might be wondering, "Why don't I just check the Is Authenticated property in the code for my ASMX file?" Well, the reason you can't do that is what I just mentioned, and that is that there's no innate binding between ASMX and the ASP.net pages that call those ASMX files, even though they happen to live in the same solution. The nature of web services is uncoupled, so there's no binding and no assumption that the user of the web service is going to be in the same solution as the web service itself. That's why we need to add our own authentication mechanism. What we've done so far is we've created a project from the template, we've created the ASP.net membership repository and added a user, we've created a web service with the 3 method signatures that we're going to be programming, we've generated a web service reference— note we have the discovery file. Let's look at these real quickly. Here's the discovery file. Here's all the things that are available at that service end point. Here's the WSDL for the service that we selected, so you can see here. For example, here's 1 of the methods that we defined, Hello World by User, here are the parameters defined, 1 named user, it's a string, one named password, PWD, it's a string, so that's the WSDL. The WSDL is used to generate first this reference map. This reference map basically points to the services, and note in this case localhost:portnumber1111 and the end points for discovery and ASMX. Now obviously, these will need to be updated when we deploy the web service out on a public end point using a domain name, but it'll work fine for now. Next, if we look below the reference map file, if we double click on reference.cs, we'll see that this is the class that represents the proxy to our web service. If we scroll down a little bit, we'll find things like—let's see if we can find a method definition. For example, here's our Hello World method. If we keep looking, we can find— here's our Hello World by User method, with the user name and password being called in. This is the class that makes it easy for us to call our web services, and if we scroll farther down, we can see there is logic that does all of the bundling in an XML format of the parameters in the return results. We don't have to worry about that, because ASP.net and Visual Studio takes care of all the plumbing for us. Now, we can start by calling our first web service with an unauthenticated user interface. Here is our default ASMX page. Let's get rid of the introductory content in our default template. We're going to need 2 controls here. Let's start by adding a label control. I'm going to switch it to source view to make things easier, as that's where I prefer to work. Let's also add a button control. Let's change the names here to be something a little bit more meaningful. Let's call this label Service Result, and let's get rid of the text, because we don't want to display anything in that label until we've successfully called our service, and we'll call this label Call Service. Our button Call Service, I should say. Click to Call Service. Now, if we switch back into design mode and double click on that button, we'll get a click event handler for that button, and here's where we can program against the service itself, so what we want to say here is recall our web service reference is Joe's Securable Service. What I'm going to say is I want joessecurableservice.joeswebservice ws = new. Now I have an instance of the proxy to that web service, and now I can say labelserviceresult.text = ws, and now you can see in Intelli-sense, if we look at Hello World, we get Hello World, Hello World by User, Hello World by Token. We're just going to call the default Hello World here, and let's run. If we click on the button, Call to Service, we'll get Hello World, but here's the problem. If we recall, the end point for our web service is joeswebservice.ASMX. Let's go to our server here and say we want to directly call joeswebservice.ASMX. Well, lo and behold, this is the test harness for that web service. We can turn this off in productions, so we don't have to show this, but notice here are 3 methods. We're interested in Hello World, and the part that's concerning to us is this Invoke button here. If we Invoke, what we see is all that is is a link, and it's a link to our web service, /methodname, Hello World, and there's no authentication that's taking place here. What this means is that anybody can call this web service method at this end point. That's not what we want. We only want authenticated users to be able to call this web service. What we need to do is add an authentication mechanism, and the first 1 we're going to add is an explicitly provided user name and password. Back in our default ASP.net page, let's switch into source view, and here, we're going to have to add a little bit of UI to allow the user to enter a user name and a password. Let's go down here, and let's say user name, and we'll add an ASP.net text box. We'll call this text box user name. A break, and then we'll use the magic of cut and paste to clone that input entry. This will be for the user's password. We'll call it Text Box Password. Let's add 1 more break. If we go back into design view, we can see we have the user name and password fields. Here, we can say instead of Hello World, we're now going to call Hello World by User. We'll call it Hello World by User, and as Intelli-sense tells us, this requires 2 input parameters, the user name and the password, so we'll use the text property of text box user name, and we'll use the text property of text box password. Now, back in our web service code, we're going to have to do something to authenticate against the membership repository, so here we can say— first, in order to do this of course, we'll need to add the appropriate using statements. Let's say usingsystem.web.security. Once we've done that, now we can go down into our Hello World by User method and say ifmembership.validateuser, and we're going to take the 2 parameters, so we'll say user and PWD. Note that Valid User doesn't log the user in. All it does is say is this a valid password and user name combination in the membership repository? If this is true, then we can return something positive, so we can do whatever the web service is supposed to do and return the result set. Now, in our case, we're just going to return a string value. We're not going to add any logic, but in your case, you would actually, once the user was successfully authenticated, validate user returns a true value, then you would do whatever the web service is supposed to do. In our case, we're going to say Hello Authenticated User, and we could specify the user name if we wanted to. Now, in the negative, meaning that validate user returned a false value, in our case, we're just going to return a string. Now, in your case, you might want to do special things. You might want to return an exception message that could be interpreted by your client code that says, "Hey, this user's not validated." You might want to do some application logging so that you can do reporting on unauthenticated requested for your web service. Really, it's up to the specifics of your application. In our case, we're just going to say denied. Only authenticated users may call this service. Okay. Now, let's go ahead and build our project and run, after we fix that little syntax error. Now we can run. What we'll see is our default ASMX page. If we don't provide credentials of course, we'll get the denied, only authenticated users may call this service. If I pass in valid credentials— notice I'm just using a default password— Now we'll get Hello Authenticated User. If we pass in bogus passwords, we'll get denied, only authenticated users may call this service again. One little change we ought to make here though is we never want to expose that password, so here in our text box, let's find a text mode property and change it from single line to password. Now, when we run— now we can enter the user name and password, and the password is masked, as we would expect it to be. Of course, we need to enter the actual password. Now, so this clearly works, but there are a couple of deficits. You'll note that we are not using SSL, so the protocol is http and not https. What this means is that in the http request that gets sent to the server, there's no encryption. It's in what we call clear text, and that means theoretically speaking, a man in the middle who's intercepting those http packets could gather enough information that included your credentials, your user name and your password to be able to play them back. This creates 2 problems. It means that they can use that user name and password to call your web service. It also means you can use that user name and password to authenticate to your web application. We're passing those in clear text every single time we call the web service. That's not a good idea, so if you really need security, if you really need those user names and passwords to be protected, you want to do this authentication over SSL. If you're a banking application or an E-commerce application, you don't want them to be passed in clear text. For lots of applications though, passing them once in clear text is not such a big deal, but passing them all the time, frequently for each user in every application instance— that's probably not something that you ever want to do, even if your application is just My Family Photo Album. What we'll do next is we'll have the user authenticate before they try and call the web service, and we'll create a tokenized scheme so that only the token gets past, just a GUID gets past when calling the web service, and that GUID has a limited life span. So if the user ever intercepted it, they couldn't use that GUID to log in to the web application with the user's account, and it would have a very limited life span as a valid web service authentication token. In fact, it will only be valid as long as the user is logged in and has an active session in our web application. Let's do that next. You'll recall that I mentioned that using the default ASP.net template, we have some membership facilities already built in. For example, see this log in button. I click on the log in button, I get redirected to the account login.ASMX page. Now, this is where the user is going to provide their user name and password, so when they submit this, when the user's successfully logged in, this is where we're going to want to create that token. Now, what we're going to do with this token is we're going to place it in 2 spots in our application. The first spot is going to be in a session object, so that when the user is in an ASMX page and wants to call a web service, we can grab the token right out of the session object for this user. The other place we're going to put it is in a special table in the database in our application so that the service method, the ASMX, can take that token and find out if it's in a table in the database. If it exists in the table in the database, that means that that is a valid token for honoring the web service request. The first thing that we need to do is create a new table in our ASP.NETDB database. Now, you don't have to create it in this database. There are a couple ways you could do it. You could create a separate but complementary database, to the ASP.NETDB database. You could even include a field in 1 of the tables in the ASP.net database. I prefer not to do that though, because I want the changes that I make to be independent of the default scheme for the database. What I'm going to do is just add a table. So to do that, I'm going to right click, I'm going to say Add a New Table, and my table's going to contain 2 fields. First, we're going to have an ID field. It's going to be an integer. This is going to be my primary key, and I want it to be an identity column that auto increments, so I'll go down into the column properties and find Is Identity, double click. The next thing that I'm going to need is a field to house the token itself, and this is going to be a unique identifier. We're going to store a GUID in this column. That's it. Now, all I need to do is Save, and I'm going to call this Table Service Tokens. That's all we need for the table. Now that we have the table, the next thing that we're going to need to do is we're going to need a way to program against that table. Let's create 1. There are lots of different ways to access sql databases, but I prefer to use the Entity framework. What I'll do is right click on Solution Explorer, say Add, New Item, choose the data collection from Install Templates, choose an ADO.net entity data model, and just call it ASP.net application services.edmx and select Add. The Builder Wizard will pop up and say, "Do I want to generate this from a database?" and I say yes. We'll just leave the default connection string ASP.NETDB entities, and I'll also use that for the model names base. I could just pick the specific table that I'm interested in, but I like having access to all of the repository so that I can extend my code logic to do whatever I need, so I'm just going to grab everything that's in the ASP.net database and include it in the model. Notice these messages here. These are just a few messages that some of the tables don't have primary keys, and that's not going to be an impedance for us for the purposes of this demonstration. Now I want to make sure that I save the model and rebuild my project. Now I'm ready to program against my ASP.NETDB database through that NA data model. So go ahead and close the EDMX file, and the first thing that we're going to need to do is create and store that token. We're going to do that in the login.aspx page and it's code behind. We're here in the log in user.aspx page, and the first thing that we need to do is we need to find the log in control. Let's scroll up. Here's our log in control instance. Now, I want to program against 1 of the events here. We're going to do this by clicking on this little lightening bolt icon. If you don't see it, switch into split-screen view and it should appear. I'm going to switch to the events viewer in the properties window, and I want to generate a log in event handler. What I want to happen is that after this control takes the user name and password and successfully authenticates against the membership repository, I want to run some code, so that event is logged in, and I'm going to double click next to that logged in event, to generate the logged in event handler for that control. Now, here in the logged in event handler, you recall the first thing that we need to do is create a GUID—this is going to become the authentication token. I'm simply going to say GUID newtokenID, since this GUID is going to be an authentication token, we'll now start referring to it as a token, and we're just going to say new GUID. We have our new token. Now, 1 of the things that we're going to need to do is we're going to need to place is in a session ID. We're going to create a session, and that session name is going to be userservicetoken. Now, you'll recall that session objects are not strongly typed, so you want to make really sure that you get the spelling right. Sometimes just even working in C#, the wrong pacing in this string specification in session object can cause an error that might be a little bit hard to track down. We've got this thing, and now we're just going to set it in the session object. What we've done so far is we've created a GUID object instance and the default constructor automatically assigns a value, which is the new GUID value, and then we're going to create a session object and place that GUID in the session object. Now, the next thing we need to do is place it in the database so that our service, which doesn't have access to the session object, can access the table that we just created, the service tokens table, and see if there's a record in that service tokens table that contains this GUID that we just created on authentication of the user. In order to do that, we're going to say using asp.net db entities = new ASP.net db entities, and here, we're going to first create an instance of the model representation for a service token record or a record in the service token table. We'll simply say service token, and this is going to be a new record, so we'll say newtokenrecord = newservicetoken. We can populate the token column, so the token property of this model, by saying newtokenrecord.token is equal to the GUID that we just created. Now all we need to do is add that record to the service tokens table. We'll say DB.add to service tokens, and we'll pass in as a parameter the new record entity that we just created, and then all we need to do is say DB.save changes. Okay, so what we've done is, as soon as the user logs in, we've created a new GUID and we've stored that GUID in the session object and in the service tokens table. Now what we need to do is, back in the service, we need to catch that service token and do a look up in the service tokens table to see if there's a record that contains the GUID that we just created and stored there. So back in our web service code behind file, here where we're programming against the Hello World by Token service method, first thing we want to do is find out if we got a token. If the user just enters the URL, as I showed you how to do, to this web service method, we want to find out is there a token attached. If there's no token, the request can't be authenticated. Let's say if the off token is equal to an empty string or it's just not present at all, then we're not going to honor the request. Again, in your case, you may implement some specific defensive logic. In my case, I'm just going to return an access denied message. Of course, the alternative is we did get something by waiving off an authentication string, and if that's case, then we need to take whatever we got and see if it's a valid token that exists in the service tokens table of our database. In order to do that, we're going to say using asp.net db entities = new asp.net db entities, and then we're going to first convert the string that was received called off token into an actual GUID, right? Pass it as a string, because it's going to go over http, so basically we serialize it into a string. Now we're going to turn it back into a GUID. Say GUID, the user's token is what we'll call it, = new GUID, and you'll note here that GUID has 6 overloaded constructors. 1 of them will take a string value and convert that string into a GUID. Now, 1 of the questions you might be asking is, "Hey, what if the user passed a string but it's not even a GUID? It's just a bunch of characters?" In our case, we could check to see if it could be converted to a valid GUID, and that would save us the query against the database, and that would be a little bit more efficient, but only if we were really getting a lot of requests for invalid web service method calls. In our case, we'll just say convert whatever string we got, and if it's not a valid GUID, then we won't get a match in the database. Next, we're going to build a query expression. Again, we're just going to do framework, so we'll be building a link query. We'll say var, my token record, = from tn, my database, .service tokens, where the token, so that's the token column in our table, is equal to our user token GUID, then we want to go ahead and select from our table. Now, when we do the select, there should never be more than 1 occurrence of any GUID. GUIDs are universal and unique, so we're going to say first or default. Now, if my token record is null, then the user failed to authenticate, right? If my token record is not equal to null, that means that our query resulted in a record being retrieved based on the where statement. If that's the case, we've got an authenticated user. If that's not the case, we don't have an authenticated user. If we don't have an authenticated user, we'll say denied. If we do have an authenticated user, we'll say hey there. And build. Now we need to modify our default asp.net page. Instead of to pass aspx to password to retrieve this session value, the token that's in the session passes to the web service. We don't need this user name and password set of fields anymore, so let's go to the code behind, and we're going to do a couple of things here. The first thing I want to do is add a little bit of logic. 1 of the things that our UI currently allows is if the user's not logged in, we still let them click on that button and call the web service. Now, the logic of our application dictates that an unauthenticated user who's not logged in is not going to get a web service call successfully. Since we know that the token gets created when the user's logged in, and that token is required for the web service to successfully execute and return it's values, we know if the user's not logged in, there's no sense in calling the web service. Let's prevent that by saying if http context.current. user.identity.is authenticated. If the user is not authenticated, let's give them a chance to authenticate. We'll say response.redirect, and we're just going to redirect them to the log in page, which we know is in account.login.aspx. It doesn't make any sense to go ahead and call the service if we know that they're not authenticated. The next thing that we want to do is if the user is authenticated, we want to grab the session token. Let's go back to the log in page's code behind and look at the bit of code that was used to store that session. What we're looking for is session, user service token. I'm going to cut and paste this again—because of the string dependency, there's no typing. Let's go here, and I can say let's find out if that service token even exists in the session object. If session user service token is not equal to null, well, then I know that we probably want to try and call that service. Otherwise, if it is equal to null, we know that the web service is not going to honor our request, so let's go ahead and redirect the user to the log in page. Now, you might want to be a little bit more detailed since, at this point, we're already logged in. If we get here, what's happened is the user has logged in, but for some reason, that session token didn't get created. You might want to be a little bit more detailed in terms of what you implement in this L statement. In our case, we'll just redirect them to the log in page and give them a second chance to create that session object. Now here, this is where we're going to need to add the logic that will call that web service by token. So let's get rid of the call to Hello World by User, and let's instead call Hello World by Token, and of course, token's going to take 1 string, so the string is going to be whatever we find in the session object for user service token. Now, note, this is still giving us an error message that's saying the best overload method match for this service method has some invalid arguments. Well, that's because we stored a GUID in the session object and not a string. Let's just go ahead and convert it to a string so that it can be serialized over http, and now we should be able to build. All right, so let's go ahead and test our app. So, if we hit run, click Call The Service, we get automatically redirected to the log in page. Let's select Joe User, and let's give a valid password. Now if we log in—authenticated user. So you'll see we only have to log in once. Now whenever we call that web service, we pass the token and the token gets found in the table so we don't have to pass our user credentials in clear text. This is a pretty good mechanism, because now, after the original log in, the only thing that gets passed over the Internet in clear text in terms of authentication criteria is that token. There is 1 problem though. We never added any logic to remove the token from the database. We don't want those tokens to be valid forever. If they are valid forever, meaning if we never remove them, then anytime a hacker stumbled across a valid GUID, they could write a brute force attack appliction that just basically took random GUIDs that were computer generated and fired them against that web service, and sooner or later, if we never delete them from the service tokens table, they might stumble across a token that's valid. We need to remove them. We need to remove them in 2 different ways. Certainly, the easiest way to remove them is when the user logs out, find the token that's in the session and get rid of it. The other time we need to remove them though is what if the user never logs out and the session just expires? We might want to add a session end bit of logic that would remove them as well. Let's go ahead and do that. Now, there's 1 more thing to note. It's theoretically possible that if an application, an ASP.net application, gets terminated, or crashes, for example, and needs to be restarted, and we're using in-process sessions, then those service token records in the service tokens table might get orphaned. We might want to have a clean up utility or you might want to put a time-out value on them. We could do that if we needed a more robust security mechanism. The other thing that I want to note here is that this mechanism is working simply using in-process session state. If we were going to use, for example, a sql server-based mechanism, we could modify this to perhaps be even more efficient, because we're using database session objects. In any event, let's go and add some logic to remove the session objects when the user logs out or when the user's session expires. The first place that we need to add some logic is in this Login View control. This Login View control basically has multiple states, and in the case of our user, it says, "Hey, if the user's not logged in, show this set of content. If the user is logged in, show this other set of content, and this other set of content includes a link to let the user log out." This lives in the master page, so that's where we're going to have to address the issue. Let's find our master page. You'll note here that we have our Login View control, and within the Login View control, we have a logged-in template, and within that, we have this log in status control. This log in status control is the 1 that will let the user log out if they're already logged in, so you see log out text. What we need is we need to add a on logged out event handle, so when the user logs out— once they're successfully logged out—we want to get rid of that session object. That service token, so we're going to say— let's create an on logged out event handler, and I'll name it remove service token. Now, here in code behind for our master page, here we can implement the remove service token method. I'm just going to clone the page load event. Let's call this token, and here, all we need to do is we can say— we don't need to go yet. First we need to say using ASP.net db entities, so we'll say again, create an instance, db = new, ASP.net db entities, and now, we can do a query, and in order to do a query, we're going to have to select a record that contains the service token that we stored there that the user has in their session object that we need to remove, so let's go ahead and say GUID = user GUID new GUID, and we're going to want to create 1 from the session object, so let's go back to the code behind for log in and make sure that we get the exact name, so I'll cut and paste. Here, we're going to say we're going to pass in the GUID, and we just do this sort of the easy way. Next, we're going to use that GUID as a variable, so let's say var my token = from tn db. service tokens where t.token = the user GUID that we just created and populated, select t, and as we mentioned previously, there should only be 1 instance, so we're going to say first or default. Now I'll just clean up my syntax here. From tn db service tokens where t.token = user GUID select t first or default. Now we can test and say if my token is equal to null, that's a problem. We're going to assume that it's not going to be, so let's say if my token is not equal to null, let's go ahead and remove the record that we just retrieved from the database. We'll say db.delete object, and the object that we're going to delete is the record set that we just retrieved, which we know logically will only create 1 record. We simply say save changes. Now, when the user logs out, they'll no longer be able to authenticate for web service calls. The next thing that we need to do though is what if the user never logs out, they just close the browser? They don't specifically log out? If they don't specifically log out, then this will never get called at the current state of our application's development. The next thing that we want to do is we can add the same logic in our session close event. Let's pick up this exact set of logic. Control C. Now let's go into our global.asax code behind, and in the session end event, let's go down here and implement this. We're also going to have to check to make sure that the session object is not null, so let's say if this session object is not equal to null, then remove the session object from the database. Now, 1 caveat. You'll note here, this advisement in the session end object of the global.asax code behind file. Code that runs when a session ends. Note the session end event is raised only when the session state mode is set to InProc in the web.config file. This, of course, is the default. If the session mode is set to state server or sql server, the event is not raised. If you're not using InProc session state management, this won't work. You'll need to implement something else, and you can add a time stamp to say that hey, this session object is only valid for a certain amount of time and then you can update it on a sliding scale. The other thing you could do if you're using a sql server is you could add a trigger event to clean them up every 20 minutes or so. Again, if you're using a different mechanism for session management, you'll want to implement different session end clean up. But for InProc session management, this'll work just fine. Now, let's just quickly, before we wrap up, let's talk about what we've just implemented. We've created this session GUID, we've placed it in the session object, and in the service tokens database. We pass it from the session object across the wire to the service call, and then the web service method checks to see if it's a record in the service tokens table that contains that GUID. If it finds 1, then the request is authenticated and should be honored. If it doesn't find 1, the request is unauthenticated, and should not be honored. When the user logs out, we remove that session from the table, so it's no longer there. We could also remove the session object. We could remove the token from the session object here, but it's not really necessary as long as it's not in the table. We could also say session user token =, after we do the database thing, we could set it equal to null or a empty string. We could likewise do that in the code behind file here. But in any event, the user is no longer authenticated from a web service method call perspective. We've done clean up, both for when the user logs out as well as when the user's session expires, and we've made note of the fact that our session expiration logic will only execute if we're using InProc session management. I've mentioned a couple of other things you can do if you were using out of Proc session management. We could have some sort of a clean up management utility, a window service that runs, perhaps. Not a web service, but a window service that runs, perhaps, on the state manager, or if we're using a sql repository for session management We could have a trigger event there as well. Recall this is a pretty simple way to restrict unauthorized access to your web service methods. It does have the liability of passing the original authentication mechanism, the user name and password, in clear text over the Internet, but by using this token-based method, we're only passing the credentials once, and for lots of applications, that's an acceptable risk. For other applications, you want to do the authentication over SSL, and then you can still use this token-based mechanism and it will be reasonably secure. [Microsoft ASP.net] [www.ASP.net]

Video Details

Duration: 54 minutes and 54 seconds
Country: United States
Language: English
License: All rights reserved
Genre: None
Views: 15
Posted by: neudesicasp on Sep 18, 2013

In this video, Joe Stagner demonstrates different mechanisms that can be added to an ASP.NET application to restrict unauthorized access to ASMX web services.

Caption and Translate

    Sign In/Register for Dotsub to translate this video.