Access IIS Express from another machine

The Problem

You have a website running in your local machine using IIS Express and you want another computer on your local network to be able to reach said website.

This becomes useful never when said website is some REST API. The coder next to you needs your local developments to test some of his stuff but what you are doing is so epic, that cannot be committed to source control just yet.

Enter, sharing access.

The Solution

You should not have the problem above. You should commit your code. The developments of each programmer should be contained and not affect other people.

Yeah, after you have listened to your inner scrum master telling you all the above and maybe more, let’s get back to business.

There’s two ways to go around this:

  1. The easy way: npm install your way out this
  2. The right way: tell IIS to allow external access

The Easy Way

You can install this little helper IIS Proxy with:

    $ npm install -g iisexpress-proxy
    $ iisexpress-proxy host:localPort to proxyPort
    # now open <your-ip>:<proxyPort> on "any" machine and marvel...

Sample output:

    $ iisexpress-proxy 192.168.1.93:60534 to 5001
    IIS Express Proxy 1.1.8
    Proxying http://192.168.1.93:60534 to network interfaces:
            VPN Connection: 10.50.100.173:5001
            Local Area Connection: 192.168.1.93:5001
            VirtualBox Host-Only Network: 192.168.56.1:5001
    Listening... [press Control-C to exit]

The Right Way

You can tell IIS Express to allow external requests. By default it blocks them (allows only localhost).

Now the IF. If you are using Visual Studio 2015 you’ve go to:

$ cd <src-root>
$ vim .vs/config/applicationhost.config

Find your website entry in there (there may be several) and update the binding:

<site name="Epic Website" id="1">
    <application path="/">
        <virtualDirectory path="/" physicalPath="%SystemDrive%/road/to/nowhere/app" />
    </application>
    <bindings>
        <!-- Replace your binding with this line -->
        <!-- The last * tells IIS that it should reply to anything -->
        <binding protocol="http" bindingInformation="*:80:*" />
    </bindings>
</site>

If you are using and older VS (before 2015) you need to to apply the same configuration update but the file’s location will be:

$ vim %USERPROFILE%\Documents\iisexpress\config\applicationhost.config

After updating the configuration, the website must be restarted. If all goes well, you should be able to access the local website from the local network.

I was able to get this working without any ACL/HTTP.sys magic as mentioned here but YMMV.

Sources

Glossary

Term Description
IIS Internet Information Services – Microsoft Web Server
IIS Express Lightweight, self-contained version of IIS optimized for development work
YMMV your mileage may vary
Authentication on .NET Core REST API with Identity Server

Now that’s a long title!

You probably know .Net core and you probably know Identity Server. I hope you know REST too. That is not what this post is about.

What this post is about, is how to setup Identity Server to generate JWT tokens for our REST API calls to be secured. Furthermore, how to do this in .Net Core.

Yeah, I will need that long title…

Setup Identity Server

I won’t go into details on how to setup IS4. There is already a lot on information on the web about it including the official documentation.

What we need here is to access IS4 Admin panel, and create:

  • a new client (let’s say with ID clientid) – use the defaults
  • setup a secret on the client’s configuration (let’s say secret nooneknows)
  • setup a scope on the client’s configuration (let’s say scope insidelayer)
  • make sure the AccessTokenType is JWT and set a sensible AccessTokenLifetime (defaults to 3600 but I like 86400 for development)

Setup REST API

Again, I will not explain how to create a .NET Core REST API. The web is full of information about it, including example source code from microsoft itself. The purpose here is to help someone integrate authentication into an existing code base, so I think skipping this part is fair game.

How Authentication will work

So, we have our Rest API and we can use Postman or equivalent, to call some dummy controller on it. Cool.

What we will need is to tell the API server to expect a JWT token on all HTTP requests, more preciselly on the authorization header. Then, it needs to validate the token against the issuer of that token (Identity Server in this example). If the token validates, we allow the request to hit the controller code, otherwise its blocked, returning HTTP 401 Unauthorized Status code.

Nice.

Using it / Testing

I think it gives the reader a better view of what we aim to achive setting this section before the actual code. However, you’re free to read this page in any order you like.

First we need to get a token from IS4. We do that with the following HTTP call to IS4 /connect/token resource:

# from this it should be trivial to build the postman call
# dont use spaces, they are just there for readability
curl -X POST
     -H "Content-Type: application/x-www-form-urlencoded"
     -H "Cache-Control: no-cache"
     -d 'client_id=clientid       &
         scope=insidelayer        &
         client_secret=nooneknows &
         grant_type=client_credentials'
     "http://identity-server-url:8001/connect/token"

Note that for this to work the grant_type must be client_credentials. You can read the full story here: Grant Types. It took me some time to find that out.

The response will be something simple where you can extract the token and use it to call our API:

# from this it should be trivial to build the postman call
# dont use spaces, they are just there for readability
curl -X Get
     -H "Authorization: Bearer eyJh...the token is really long..."
     -H "Cache-Control: no-cache"
     "http://our-api-url/api/values"

Here, I am calling the ValuesController with a GET request, providing a JWT token. The server will validate it, accept it, execute the controller code and provide me with a HTTP 200 Status OK response with the data I (don’t) need. Any other case such as no token provided, the token does not validate, the token format is wrong, the token is expired and so on, the server will reply with HTTP 401 Unauthorized and no data. The controller code never gets hit.

Seems simple? It should be. To use, not to setup I mean…

From theory to code

So, we do all this by decorating all our contollers (all those we want to protect, at least) with the [Authorise] attribute. Like so:

    [Route("api/[controller]")]
    [Authorize]
    public class ValuesController : Controller {
        // (...) controller code
    }

The [Authorise] attribute can be used at the class level (applies to all public methods) or at the method level for finner control. There is also the [AllowAnonymus] attribute that does what the name says. I find that usefull to allow login method calls or if your API will allow some type of public/anonymus read only operations. Anyway, the internet explains all that much better, I just want to leave a remark about it.

Then, we need to tell the code how and where the “token issuer” is. We do that as follows:

using System.IdentityModel.Tokens.Jwt;

    public class Startup
    {
        // (...)

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            SetupAuthentication(app);
            // (...) rest of the code
        }

        private void SetupAuthentication(IApplicationBuilder app)
        {
            var auth = new AuthenticationConfiguration();
            Configuration.GetSection("authentication").Bind(auth);

            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
            {
                RequireHttpsMetadata = auth.RequireHttps,
                Authority = auth.IdentityServerBaseUrl,
                AllowedScopes = auth.AllowedScopes,
                AutomaticAuthenticate = true,
                AutomaticChallenge = true
            });
        }

    }

We can see that we load configurations from the appsettings.config file, which you can read more about it here.

{
  "authentication": {
    "IdentityServerBaseUrl": "http://identity-server-url:8001",
    "AllowedScopes": [ "insidelayer" ],
    "RequireHttps":  false 
  }
}

All the magic happens in the framework method UseIdentityServerAuthentication. We just need to add some depencies for the code to compile:

{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.1.0",
      "type": "platform"
    },

    "IdentityServer4.AccessTokenValidation": "1.0.2",
    "Microsoft.AspNetCore.Authorization": "1.1.0",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0"
  }
}

Not just one dependency but 3 packages to our project.json file.

That is it. You can now build and run your API and call it with a valid/invalid token to see that you can/can’t get access to it.

Make it work on development: Policies

All of this is fine.

However, when we are developing the API code we do not want to worry about generating tokens and tokens expiring and so on. I needed a simple way to disable authentication for the development enviroment.

I could comment out code but that brings more trouble to the table than it solves because now I have to remember not to commit this commented code and which code to comment out if I stop to work on this for a few days (read hours). Sharing this “commented code” knowledge among several team members is also, hum, stupid.

If this was not .NET core we could inherite from the Authorize attribute class and role our own with an IF inside. However, .NET Core developers do not like that messing around and do not allow it anymore. What they allow is for you to create your own Policy and use that instead. Once I knew this, it was peanuts to get the code working. Finding this out was not so easy. Google floods me with pre-dot NET core knowledge, and it took some digging.

So, we create our policy according to the environment, like so:

public class Startup
{
    private readonly IHostingEnvironment _env;

    public Startup(IHostingEnvironment env)
    {
        // ...
        _env = env;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        BypassAuthenticationForDevelopment(services);
        // ...
    }

    private void BypassAuthenticationForDevelopment(IServiceCollection services)
    {
        if (_env.IsDevelopment())
        {
            services.AddAuthorization(options => options.AddPolicy("AllowDevCalls", policy => policy.RequireAssertion(c => true)));
        }
        else
        {
            services.AddAuthorization(options => options.AddPolicy("AllowDevCalls", policy => policy.RequireAuthenticatedUser()));
        }
    }
}

And we update all of our Authorize attributes to use the following policy:


    [Authorize(Policy="AllowDevCalls")]

The above works with some dependency injection magic that the .Net framework provides us. I encapsulated some code in a private method for readability purposes only.

Notes on security

You may notice that I am using HTTP to reach Identity Server and setting RequireHttps to false which are both obvious mistakes. I use HTTP to have a simpler development environment and as an example, but all this must use HTTPS to be taken seriously.

Additionally, setting the AccessTokenLifettime to big values (as I did) is not a good practice. Setting these times is a somewhat tricky business as it is always a compromise between security and usability. Invalidating tokens too often will required IS4 to be queried more often. If this leads to a user login, then the user experience is basically crap.

There are some options like refresh tokens and such, but again, this is a topic on itself and one that is heavily dependent on the problem at hand.

Sources

I.E.: Not all of this comes from my own ideas:

Glossary

Term Description
IS4 Identity Server v4
DI Dependency Injection
JWT JSON Web Token
HTTPS All The Things

One does not simply use HTTPS. At least not yet.

Github Pages is great. That is why I use it.

One of the features it lacks though, is the ability to use HTTPS with a custom domain. You may read that again. It will allow it eventually, but not today.

However, fear not! This is 2017 and we use computers, therefore we can do whatever we want. It’s just a matter of how much effort are you willing to put in.

Some clever folks come up with some workarounds like this and this.

The trick is to use Cloudflare’s free service that provides a SSL certificate to have HTTPS going from the user to the cloudflare servers. Then they redirect the traffic to github pages on HTTP. As I said, a workaround.

Although not perfect, this solves most of the issues (e.g. the hacker is on the same unsecured Wi-Fi network) and it allows your site to behave as if it has SSL (e.g. for web crawlers, APIs). Besides, all this is free.

If you know better, please do let me know on the comments below!

This post is too short, I want to read more:

Initial Commit

Hello Internet! There is a new blog in town.

I build sites for a living, but oddly enough I do not have a website of my own. Had, … humm I mean, did not had…

I will share my ideas and opinions publicly and for free, as in “free beer”. Be advised. Therefor, I took the time to add disqus to all posts so that you can tell me just how wrong I am on the internet!

If you have loads of free time and are bored to death multiple times a day, I also have a:

RSS FEED

(Do RSS feeds still exist in 2017? humm… Must investigate this later…)

And don’t forget to check “The Cool Internet” where I keep a list of (only) better sites than this one:

The Cool Internet