Skip to contentSkip to author details

ASP.NET

A 8-post collection

ASP.NET Core, JWT Tokens, and the User.Identity.Name property - A Discovery

Written by Michael Earls
 ASP.NET  programming  .NET Core

I've been working on creating a token-based auth system and I wanted to write about a discovery that I made. I've been following the excellent ASP.NET Core Token Authentication Guide.

I was able to get everything up and running as suggested in the guide, but when I accessed the User.Identity.Name property, it was null. I was hoping to find the Name of the user there, but it wasn't. After some exploration, I was able to determine the solution. You simply add the following code in Startup.cs. I added this to the TokenValidationParameters area as outlined in the Guide.

var tokenValidationParameters = new TokenValidationParameters
{
    // Ensure that User.Identity.Name is set correctly after login
    NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",

    ... Existing code here ...

};

If you want to use a different claim as your User.Identity.Name, then use that claim name instead of the XmlSoap schema above. We're actually not using username, we're using an Id number that identifies the user.

Adding the [Authorize] attribute to an ASP.NET Web Forms Page control using custom routing

Written by Michael Earls
 ASP.NET  programming  VB  VB.NET  WebForms

I am developing a system that uses custom routing to load pages. I needed a quick way to secure the pages so I didn’t have to put the same

The "old" way

If Not Page.User.Identity.IsAuthenticated Then  
    Response.Redirect("~/Account/Login?ReturnUrl=" & Server.UrlEncode(Request.Url.ToString())
End If

You get the idea.

Anyway, I remember how beautifully WebAPI and MVC handle this by using the “AuthorizeAttribute” attribute. So, I decided to implement this in my page router.

The "new" way

First, you’ll want to enable custom routing in your application as outlined here. Notice that the article I linked to has a solution already outlined for security. I decided not to go that route as I like this solution better.

I have a custom class for handling the IRouteHandler interface implementation required by the custom routing solution.

Within that handler, I do various things like check the virtual paths to make sure that they are valid. Most custom routes get converted to a custom base page that has a public property on it for use with my system. I instantiate that class, set the custom property, and then return that page as the IHttpHandler required by the GetHttpHandler method of the IRouteHandler interface.

Here is the definition of the route in my RouteConfig.vb file:

'Content editor route '  
routes.Add("Edit Content", New Route _  
    ( _ 
        "EditContent/{contentControl}", New CMSPageRouteHandler("~/Pages/CMSContentEditor.aspx") _
     ))

Here is the class definition of a page with the Authorize attribute on it:

<Authorize(Roles:="Editor")> _  
Public Class CMSContentEditor Inherits System.Web.UI.Page  
    ...
End Class

Only users in the Editor role will be able to access this page. Let’s see why.

To start off with, we need to see if our page is decorated with the Authorize attribute. I created a function that does this. I pass in the requestContext that was handed off to me in the GetHttpHandler() method (make sure you Import the System.Web.Http namespace):

Protected Function IsPageAuthorized(ByVal page As Page, ByVal requestContext As System.Web.Routing.RequestContext)  
    Dim authorized As Boolean = requestContext.HttpContext.User.Identity.IsAuthenticated 
    ' Check to see if this page is decorated with the "Authorize" attribute '    
    Dim authAttribute As AuthorizeAttribute = Nothing 
    Dim ptype As Type = page.GetType() 
    For Each attr In ptype.GetCustomAttributes(True) 
        If TypeOf attr Is AuthorizeAttribute Then 
            authAttribute = attr 
            Exit For 
        End If 
    Next 
    If authAttribute Is Nothing Then 
        ' there was no Authorize attribute on this page, so go ahead and let them in '
        authorized = True 
    Else 
        ' see if this page is authorized '
        If Not String.IsNullOrEmpty(authAttribute.Users) Then 
            ' reset the authorized variable since we have Users listed '
            authorized = False 
            If authAttribute.Users.ToLower().Split(New Char() {","}, StringSplitOptions.RemoveEmptyEntries).Contains(requestContext.HttpContext.User.Identity.Name.ToLower()) Then 
                authorized = True 
                GoTo EndOfAuthCheck 
            End If 
        End If 
        If Not String.IsNullOrEmpty(authAttribute.Roles) Then 
            ' reset the authorized variable since we have roles '
            authorized = False 
            ' split the roles into an array '
            Dim roles() As String = authAttribute.Roles.Split(New Char() {","}, StringSplitOptions.RemoveEmptyEntries) 
            If roles.Count > 0 Then 
                For Each role In roles 
                    If requestContext.HttpContext.User.IsInRole(role) Then
                        authorized = True 
                        GoTo EndOfAuthCheck 
                    End If 
                Next 
            End If 
        End If 
    End If 

EndOfAuthCheck:  
    Return authorized 

End Function

First, we set the authorized variable to equal whether or not the user is authenticated. This will save us time later when none of the other checks fail.

After we loop through the attributes on the page, the authAttribute will either be null or it will be equal to the instance of the AuthorizeAttribute that is on the page. If we check it and it’s null, then we know the page is authorized, so we go ahead and set the authorized variable to true and continue on. However, if the authAttribute has a value, then we need to check the properties on the attribute to see if the page is authorized.

User list check

First, we look to see if the User’s name is included in the array of Users (we get it by splitting the Users property of the attribute by commas). I went ahead and converted the Users property to lower case for comparison sake, just in case.

If we find the User’s name in the Users property, then we simply set the authorized variable to true and continue on. I used a GoTo to short circuit and save the work of the next step:

Role checks

Next, we split the Roles into an array, then loop through each one, seeing if the user is in that role. If so, we set the authorized variable to true and continue on.

Authorized or not?

Once all of this has taken place, I return the authorized variable to the caller.

Handling the authorization response

In the calling method, here’s what I do:

If IsPageAuthorized(redirectPage, requestContext) Then  
    Return redirectPage 
Else  
    RedirectToLoginPage(requestContext) 
End If

Here is the RedirectToLogin() method. I add a referrer to the ReturnUrl so I know where the originating request came from. Otherwise, it gets lost when the Login page redirects to the desired page as the Login is now the referrer.

Protected Sub RedirectToLoginPage(ByVal requestContext As System.Web.Routing.RequestContext)  
    ' redirect to the login page, but send the current referrer as a QueryString on the return page so it can be accessed '
    Dim referrer As String = String.Empty 
    If Not requestContext.HttpContext.Request.UrlReferrer Is Nothing Then 
        referrer = "/?referrer=" & requestContext.HttpContext.Request.UrlReferrer.AbsolutePath 
    End If 
    requestContext.HttpContext.Response.Redirect("~/Account/Login?ReturnUrl=" & requestContext.HttpContext.Server.UrlEncode(requestContext.HttpContext.Request.Url.AbsolutePath & referrer)) 
End Sub

Then, in my custom page, I simply check the value of the referrer like so:

If Not Request.UrlReferrer.AbsolutePath.ToLower().Contains("login") Then  
    Session(SESSION_KEY_REFERRER) = Request.UrlReferrer.ToString() 
Else  
    Session(SESSION_KEY_REFERRER) = Request.QueryString("referrer") 
End If

I can’t use ViewState because of issues I’ve run into with custom routing and ViewState with my pages, so I’ve had to resort to using the Session.

As you can see, adding the Authorize attribute to a custom page is easy when you’re doing custom routing. Of course, if custom routing isn’t for you, then this solution won’t work, but I thought I’d share it for those of you who do use custom routes with dynamic pages.

Calling DataBind() on a GridView within an UpdatePanel to handle a row delete command

Written by Michael Earls
 .NET  ASP.NET  C#  VB.NET

I was working on maintaining a site that used a GridView within an UpdatePanel and had difficulties with the DataBind() call. I added a delete button (using a CommandField) to the grid and wired up the command logic to delete from a generic list that I was keeping up with. The grid was bound to the List. It just wasn’t updating the contents of the Grid when I removed records from the source.

It turns out that when I added the Delete command, I was also supposed to handle the OnRowDeleting event. I didn’t receive an error message to let me know this until I removed the UpdatePanel and tried it again. Once I added the handler for the event, it worked just fine. So I added the UpdatePanel back and everything now works as it should.

Basic Authentication with AngularJS and WebAPI Part 2 - The Angular Client

Written by Michael Earls
 AngularJS  ASP.NET  featured  security  WebAPI

Update: I’m an Angular newbie. I’ve learned a bit about Angular JS since this was written. As such, I don’t recommend using $scope as I did, I recommend using Controller as. I also believe that there may be a replacement for $broadcast, but I’m not sure.

Here is a great blog entry about some common Angular JS mistakes from Jeremy Likness.

In part 1, I linked to a few blog entries about getting Basic authentication to work with AngularJS. I outlined what was required to make the suggested idea work in ASP.NET WebAPI, but I didn’t detail what was required in the AngularJS client. The blog entries that I linked to were also very light on details, so I thought I’d put together this blog post to show an end-to-end solution for this problem.

This project is available on GitHub at https://github.com/cerkit/BasicAuthWebApiSample.git

I’m missing a few features that were intended to be included due to my newbie status with AngularJS. In particular, I do not currently have a way to save the failed requests for later to retry them after the user successfully logs in. That will probably have to wait until I get better with AngularJS. Also, there may be a better way to inject the credentials into the web request other than putting the code into the controller. I’ll try to improve on this model at a later date.

To start, let’s take a look at the .config for the app.

/*  
* $http interceptor. 
*
* On 401 response - it stores the request and broadcasts 'event:auth-loginRequired'. 
*/ 
angular.module('app').config(function ($httpProvider){  
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
var interceptor = ['$rootScope', '$q', function (scope, $q) {  
    function success(response) { return response; } 
    function error(response) { 
        var status = response.status; 
        if (status === 401) { 
            var deferred = $q.defer(); 
            var req = { 
                config: response.config, deferred: deferred 
        }; 
        //scope.requests401.push(req); 
        scope.$broadcast('event:auth-loginRequired'); 
        return deferred.promise; 
    } 
    // otherwise return $q.reject(response); 
    } 
    return function (promise) { 
            return promise.then(success, error); 
        } 
    }]; $httpProvider.responseInterceptors.push(interceptor); 
});

Notice that I add a header on line 7 that sends the XMLHttpRequest to the server. If you’ll remember from the last post, this will cause the server to send an “xBasic” authentication method, thereby bypassing the browser’s built-in security dialog. This will allow us to answer the 401 Unauthorized response with our own login dialog.

Here is the template for the login dialog:

<div class="modal h1">  
    <div class="h2 container alert-info modal-body" data-backdrop="static">                 
        Login <div class="alert-danger">{{error}}</div> 
        <ng-form class="form-horizontal"> 
        Username: <input class="form-control" ng-model="username" />                    
        Password: <input class="form-control" type="password" ng-model="password" /> 
        <button class="btn-default" ng-click="login(username, password)">Login</button> 
        </ng-form> 
     </div> 
</div>

This contains the modal class that the dialog requires to be a modal dialog. In order for the dialog to be opened, a request has to be made to an $http resource. This is done by the main controller, but the actual work is done by an $httpProvider interceptor. You can see the interceptor definition on lines 8-27 of the above code. Notice that when the status of a request is 401, it fires the auth-loginRequired event. This indicates that the dialog needs to be shown. This event is answered by a directive that then launches the dialog. Here is the directive:

angular.module('app').directive('loginDialog', function () {  
        return { 
            templateUrl: 'app/templates/loginDialog.html', 
            restrict: 'E', 
            replace: true, 
            controller: 'CredentialsController', 
            link: function (scope, element, attributes, controller){
                scope.$on('event:auth-loginRequired', 
                    function () { 
                        console.log('got login event');     
                        element.modal('show'); 
                    });  
                    scope.$on('event:auth-loginConfirmed',
                        function () { 
                            element.modal('hide');                            
                            scope.credentials.password = ''; 
                        }); 
            } 
        } 
    });

The CredentialsController performs the login:

function CredentialsController($scope, $http, $cookieStore, Base64) {  
        $scope.login = function (userName, password) { 
            var encodedUserNameAndPassword = Base64.encode(userName + ':' + password); 
            $http.defaults.headers.common['Authorization'] = 'Basic ' + encodedUserNameAndPassword; 
            $cookieStore.put('basicCredentials', encodedUserNameAndPassword);  
            $http.get(baseUrl + '/Values') 
                .success(function() { 
                    $scope.$broadcast('event:auth-loginConfirmed');  
                    $scope.password = ''; 
                }) 
                .error(function() { 
                    $scope.error = 'Invalid Login'; 
                }); 
        }; 
    };

Notice that the login method calls the event for auth-loginConfirmed. This will cause the modal dialog to be hidden. It is also answered by the SampleController so that it can refresh its data.

We’ll then see the login-dialog directive being used at the bottom our index.html page. This is there just so that we can respond to the event and show the dialog.

<html ng-app="app">  
    <title>Sample Angular Client for Basic Authentication</title> 
    <meta name="description" content=""> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
    <link rel="stylesheet" href="css/normalize.css"> 
    <link rel="stylesheet" href="css/main.css"> 
    <!-- Latest compiled and minified CSS for Bootstrap --> 
    <link href="css/bootstrap.min.css" rel="stylesheet" /> 
    <!-- Optional theme --> 
    <!--<link href="css/bootstrap-theme.min.css" rel="stylesheet" />--> 
    <link href="css/bootstrap.flatly.min.css" rel="stylesheet" /> 
    <script src="js/vendor/modernizr-2.6.2.min.js"></script> 
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.js"></script> 
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.js"></script>
    <script>window.jQuery || document.write('<script src="js/vendor/jquery-1.10.2.min.js"></script>')</script>
    <script src="js/plugins.js"></script> 
    <script src="js/bootstrap.min.js"></script> 
    <script src="js/vendor/angular-resource.min.js"></script>
    <script src="js/vendor/angular-cookies.min.js"></script> 
    <script src="js/vendor/angular-timer.min.js"></script> 
    <script src="app/app.js"></script> 
    <script src="app/factories/base64-factory.js"></script> 
    <script src="app/controllers/controllers.js"></script>  
    <body ng-controller="SampleController"> 
        <div class="h2 label-info">{{currentUser | uppercase}}</div> 
        <ul> 
            <li ng-repeat="value in Model"> {{value}} </li> 
        </ul> 
        <button class="btn-default h2" ng-click="logout()">Logout</button>     
        <login-dialog />  

Since we’re using the SampleController, let’s take a look at what it does to get the data:

var baseUrl = 'http://localhost:49587/api';  
    angular.module('app').controller('SampleController', function ($scope, $http, $cookieStore, Base64) { 
        $scope.refreshData = function () { 
            //Used to display the data 
            if ($cookieStore.get('basicCredentials')) {
                $http.defaults.headers.common['Authorization'] = 'Basic ' + $cookieStore.get('basicCredentials'); 
            } 
            $http.get(baseUrl + '/Values')
                .success(function (data) { 
                    $scope.Model = data; 
                    $scope.loading = false; 
                    $scope.currentUser = Base64.decode($cookieStore.get('basicCredentials')).split(':')[0]; 
                }) 
                .error(function () { 
                   $scope.error = "An Error has occurred while loading data";
                   $scope.loading = false; 
                }); 
        } 
        $scope.loading = true; 
        $scope.refreshData(); 
        $scope.logout = function () {
            $cookieStore.remove('basicCredentials'); 
            $scope.currentUser = null; 
            $scope.Model = null; 
            $http.defaults.headers.common.Authorization = '';
            $scope.refreshData(); 
        } 
        $scope.updateValue = function (model) { 
            $http.put(baseUrl + '/Values', model); 
            window.setTimeout(function () { 
                $scope.refreshData() 
            }, 1000);  
        } 
        $scope.$on('event:auth-loginConfirmed', function () {
            $scope.refreshData(); 
        }); 
    });

The refreshData() function checks to see if there’s a credentials cookie. If so, it sends the credentials along in the Authorization header. This is the part that should be changed. I think it needs to go in the interceptor, but I haven’t quite figured out how to do it. After setting credentials, the controller has various functions for dealing with the data. It also sets the currentUser property on the scope so that it can be displayed on the home page.

This should illustrate the end-to-end process of enabling basic authentication on an AngularJS application when used with ASP.NET WebAPI.

I’ve been very happy with the results. Just make sure you secure all of this behind SSL.

Http Error 503 - Service Unavailable with .NET 4 - IIS

Written by Michael Earls
 .NET  ASP.NET  IIS

When trying to debug my web service today after upgrading the project to .NET 4, I was told by Visual Studio that it could not debug and to try running the project outside the browser. When I did, I received a 503 Error, Service Unavailable.

I check the Event Log and found this:

The worker process for application pool 'PartsWebService' encountered an error 'Cannot read configuration file due to insufficient permissions ' trying to read configuration data from file '\?C:WindowsMicrosoft.NETFrameworkv4.0.30319CONFIGmachine.config', line number '0'. The data field contains the error code.

Simple enough, I just have to give the correct permissions to that file. It seems that my service’s application pool identity doesn’t have rights. So I’ll navigate to the path that contains the machine.config and set permissions there.

You can’t just set permissions on the parent folder, you have to give permissions to the machine.config file itself. Add IIS_IUSRS from your local machine to the read and execute roles.

Also, load IIS Manager and check your application pool as this error usually stops the application pool if it’s running.

This should clear up any issues you were having.

Enabling Basic Authentication for Angular and jQuery clients for your ASP.NET WebApi

Written by Michael Earls
 ASP.NET  featured  security  WebAPI

I have been working on a WebAPI for a client over the past few months and I’ve successfully implemented Basic authentication on my WebAPI and connected to it using a Xamarin Android client.

I’ve come to a point where I need to start supporting AngularJS clients, but I don’t like how the browsers each have their own default login dialogs that are ugly. I found a great blog entry solving this exact issue, but the author wrote their server side in Sinatra.

Here is the blog entry with the situation and solution:

http://olefriis.blogspot.com/2014/01/http-basic-authentication-in-angularjs.html

You’ll need to implement Basic authentication on your WebAPI to make this work. To implement Basic authentication on your ASP.NET WebAPI, follow the instructions here:

http://www.asp.net/web-api/overview/security/basic-authentication

One thing to make sure you do (in Web.config) is change the

type="WebHostBasicAuth.Modules.BasicAuthHttpModule, BasicAuth"

to reflect the project name and binary name of your project (your’s shouldn’t be named WebHostBasicAuth.Modules.BasicAuthHttpModule. It will be the name of your project instead.

type="{ProjectName}.Modules.BasicAuthHttpModule, {BinaryName}

It turns out that the changes required to make the solution outlined in Ole Friis Østergaard’s post is very easy to implement in ASP.NET.

Here is the code you need to change in the Basic Authentication Implementation from above:

// If the request was unauthorized, add the WWW-Authenticate header  
// to the response. 
private static void OnApplicationEndRequest(object sender, EventArgs e)  
{
    var response = HttpContext.Current.Response; 
    // see if the request sent an X-Requested-With header (Non-Browser request - 
    // used by jQuery and Angular implementations to prevent the browser from 
    // presenting the default Login dialog) 
    var request = HttpContext.Current.Request; 
    string authType = "Basic"; 
    if (response.StatusCode == 401) 
    { 
        if (request.Headers.AllKeys.Contains("X-Requested-With")) 
        { 
            if (request.Headers["X-Requested-With"] == "XMLHttpRequest") 
            { 
                authType = "xBasic"; 
            } 
        } 
        response.Headers.Add("WWW-Authenticate", string.Format("{0} realm="{1}"", authType, Realm));
    } 
}

That’s it! Now, when you pass in the X-Requested-With header value of "XMLHttpRequest", you’ll get back a WWW-Authenticate header with a type of “xBasic” instead of “Basic”. That will cause the browser to ignore the request and suppress the login dialog, letting you display your own pretty dialog.

I have started a GitHub project that has my Sample WebAPI in it. I’ll be updating that to include the AngularJS code as I create it to demonstrate exactly how to pull this off since Ole’s post is light on details.

Here is the GitHub project:

https://github.com/cerkit/BasicAuthWebApiSample.git

IIS Error Message - An attempt was made to load a program with an incorrect format.

Written by Michael Earls
 32-Bit  64-Bit  ASP.NET  IIS

If you receive the following error message when running a website (usually after deploying onto a 64-bit machine):

Could not load file or assembly ‘PROJECTNAME’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

It could indicate that your web app (or one of its dependencies) was built for x86 and 32-bit applications are turned off in your application pool.

  1. Open IIS Administration Tool
  2. Click on sites
  3. Click on the site you’re trying to run
  4. On the right side, click on “Advanced Settings”
  5. A dialog box pops up with information about the site in it
  6. Look for the name of the Application Pool
  7. Close this window
  8. Click on “Application Pools” in the tree on the left
  9. Click on the Application Pool from the screen you just looked at
  10. Click on “Advanced Settings” on the right
  11. Look at the value for “Enable 32-Bit Applications”
  12. Make sure this is set to True (it is False by default)

You could also recompile your project for x64, but any dependencies you have might not work.

Remote Debugging Issue with ASP.NET - Unable to attach to the process

Written by Michael Earls
 .NET  ASP.NET  Debugging  Remote Debugging  Visual Studio 2013

I have been trying to get remote debugging working on one of our servers so we can share the development work on a WebAPI and I kept getting the following error message on my Visual Studio 2013 Attach to process dialog:

“Unable to attach to the process. The RPC Server is unavailable”

It turns out that all I had to do was add C:Program Files (x86)Microsoft Visual Studio 12.0Common7IDEdevenv.exe to the Firewall allow list.

I knew it had to be something with the firewall, but I was trying to discover which ports the remote debugging RPC was using so I could open them. All I had to do was add the whole process and everything worked.

Now I can happily debug the code on my server.