OpenID Connect and JS applications with `oidc-client-js`

OpenID Connect

I’ve been using OpenID Connect for some time now. OIDC is a specification built on top of OAuth 2 to which it adds authentication capabilities, where OAuth only provides autorisation. It’s been around for a couple of years now and big names in IT like Google and Microsoft have adopted it. If you want to know more about it, I would suggest reading the official spec. If the specness of the document is a bit scary, the docs from Google and Microsoft will feel more user-friendly.

Hosting your own Identity Provider

Being a .NET developer, I came across IdentityServer, a .NET implementation of the OpenID Connect specification. This means you can run your OpenID Connect compliant server in less than 10 minutes. The creators and main contributors of the project, Dominick Baier and Brock Allen, have been doing a wonderful job over the years as IdentityServer is extensible and very easy to use.

Out of the box, it comes with several adapters, so you can hook it with ASP.NET Identity or MembershipReboot, Brock Allen’s own vision of what an identity management and authentication system should look like. You can also store IdentityServer’s configuration with Entity Framework.

Last but not least, the team is already working on an ASP.NET Core version.

What about client applications?

Identity Server also comes with a library that allows to protect your ASP.NET Web API with Identity Server in minutes. Wiring it is so easy you might forget how much complexity it takes care of. There can be many roundtrips between your client application and the Identity Provider, and the client has to validate the different tokens it receives. The validation can involve X.509 certificates, encryption and hashing algorithms and other non-trivial operations.

What does it take to create a JS client application?

You might be wondering how much work would be involved if you were to create a JS-only application that would delegate authentication to an OpenID Connect provider. Getting tokens, validating them, renewing them when they are about to expire…

The IdentityServer team had created oidc-token-manager which took care of most aspects of dealing with an OpenID Connect identity provider.

A specific aspect of JS applications built with OpenID Connect is the session management. In a nutshell, it allows the JS application to be notified if the user’s session state at the IdP has changed - let’s say because they logged out. Implementing that feature in a JS application is not trivial since, to minimise network traffic between the application and the IdP, it is based on the client application loading a hidden iframe from the IdP and polling it with the postMessage API.

While being an amazing library, oidc-token-manager didn’t help much when it came to implementing that specific feature.

Luckily for us, the Identity Server has been hard at work and created oidc-client-js, the successor of oidc-token-manager. From their creators:

[oidc-client-js is a] library to provide OpenID Connect (OIDC) and OAuth2 protocol support for client-side, browser-based JavaScript client applications. Also included is support for user session and access token management.

Wait, user session management?!

That’s right, user session management! And it’s enabled by default - look for monitorSession.

This means that as soon as a user is logged in, the library takes care of creating the hidden iframe from the OIDC IdP, and polls it at a regular and configurable interval to be aware of a potential change in the user session. Madness! If it detects it’s changed, it will raise an event that’s very easy to handle

For those who have already used oidc-token-manager, the API is similar, so it’s pretty easy to make the shift.

Show me the code!

Here’s a small commented example of what the library can do.

var settings = {
    // URL of your OpenID Connect server.
    // The library uses it to access the metadata document
    authority: 'https://localhost:44300',

    client_id: 'js',

    popup_redirect_uri: 'http://localhost:56668/popup.html',
    silent_redirect_uri: 'http://localhost:56668/silent-renew.html',
    post_logout_redirect_uri: 'http://localhost:56668/index.html',

    // What you expect back from The IdP.
    // In that case, like for all JS-based applications, an identity token
    // and an access token
    response_type: 'id_token token',

    // Scopes requested during the authorisation request
    scope: 'openid profile email api',

    // Number of seconds before the token expires to trigger
    // the `tokenExpiring` event
    accessTokenExpiringNotificationTime: 4,

    // Do we want to renew the access token automatically when it's
    // about to expire?
    automaticSilentRenew: true,

    // Do we want to filter OIDC protocal-specific claims from the response?
    filterProtocolClaims: true
};

// `UserManager` is the main class exposed by the library
var manager = new Oidc.UserManager(settings);
var user;

// You can hook a logger to the library.
// Conveniently, the methods exposed by the logger match
// the `console` object
Oidc.Log.logger = console;

// When a user logs in successfully or a token is renewed, the `userLoaded`
// event is fired. the `addUserLoaded` method allows to register a callback to
// that event
manager.events.addUserLoaded(function (loadedUser) {
    user = loadedUser;
    display('.js-user', user);
});

// Same mechanism for when the automatic renewal of a token fails
manager.events.addSilentRenewError(function (error) {
    console.error('error while renewing the access token', error);
});

// When the automatic session management feature detects a change in
// the user session state, the `userSignedOut` event is fired.
manager.events.addUserSignedOut(function () {
    alert('The user has signed out');
});

// In that case, we want the library to open a popup for the user
// to log in. Another possibility is to load the login form in the main window.
$('.js-login').on('click', function () {
    manager
        .signinPopup()
        .catch(function (error) {
            console.error('error while logging in through the popup', error);
        });
});

// Here we want to redirect the user to the IdP logout page in the main window.
// We can also choose to do it in a hidden `iframe`
$('.js-logout').on('click', function () {
    manager
        .signoutRedirect()
        .catch(function (error) {
            console.error('error while signing out user', error);
        });
});

As you can see, using the library is very easy. It’s also worth to mention that if you’re using TypeScript, you’re covered as the library also comes with a definition file. If you want to try using oidc-client-js, the Identity Server JS walkthrough has been updated to use it.

Playing with TFS webhooks and REST API

WebHooks

I’m working on a project that contains 100+ git repositories. They are linked in the sense that the main repository has dependencies on the other ones.

An issue we’re facing comes from the fact that the CI build is only triggered when a PR is merged on that main repo, which is far from being the most active one. As a result we have to manually queue a build every time a PR on any other repo is merged.

To overcome this, I’ve decided to play with TFS webhooks and the REST API. The idea is to be notified by a webhook when a pull request on any repo is merged so we can queue a build for the main repository through the REST API.

What are webhooks?

Webhooks are a way for applications to provide notifications to external systems when specific events occur. Most of the time, the source application makes an HTTP request to the URL configured for the webhook, passing relevant data depending on the event. Several services provide webhooks support, like GitHub or Slack.

TFS 2015 and VSTS also support webhooks for a variety of events. In our case the one we’re interested in it is when a pull request is updated

I hear you saying that the pull request merged event suits perfectly our needs. You’re right, but these docs are for VSTS, and TFS - 2015 Update 2, which we’re using - does not support this event.

Great! So how do we put everything together?

While my first idea was to create a hand-rolled API to get these notifications, a quick online search pointed me to ASP.NET WebHooks, a library to produce and consume webhooks.

Out of the box, it provides several connectors to consume webhooks from different services, one of which is VSTS.

Despite still being a pre-release, I decided to give it a go. It turned out to be very easy to setup. The official documentation was quite slim, but the extensive samples made up for it. This library is distributed via NuGet with the Microsoft.AspNet.WebHooks.Receivers and Microsoft.AspNet.WebHooks.Receivers.VSTS packages.

Enabling webhooks support is a matter of calling an extensoin method on the HttpConfiguration class.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var httpConfiguration = new HttpConfiguration();
        httpConfiguration.MapHttpAttributeRoutes();
        httpConfiguration.InitializeReceiveVstsWebHooks();

        app.UseWebApi(httpConfiguration);
    }
}

The next step is to create a handler class that gets registered automatically through ASP.NET Web API assembly resolution system.

public class VstsWebHookHandler : VstsWebHookHandlerBase
{
    public override async Task ExecuteAsync(WebHookHandlerContext context, GitPullRequestUpdatedPayload payload)
    {
        return Task.FromResult(true);
    }
}

The base class defines multiple virtual methods, one for each supported TFS event. The GitPullRequestUpdatedPayload class is a strong;y-typed representation of the JSON payload TFS sends as part of the request. It contains lots of information such as the repository on which the pull request was created, the target branch or the status of the pull request.

Queuing a new build

If the pull request matches our criteria, we can queue a new build by calling the TFS REST API.

Again, I was thinking of using the HttpClient class to do so, but there’s an official .NET client library. Several NuGet packages must be installed in this case: Microsoft.TeamFoundationServer.Client, Microsoft.VisualStudio.Services.Client and Microsoft.VisualStudio.Services.InteractiveClient.

The .NET API is not very discoverable, and the documentation is here very sparse, but queuing a build is easy

var connection = new VssConnection(
    new Uri("https://tfs/DefaultCollection"),
    new VssCredentials(true)); // Use default credentials

var buildClient = await connection.GetClientAsync<BuildHttpClient>();
var buildDefinitions = await buildClient.GetBuildDefinitionsAsync("TeamProjectName", "BuildDefinitionName");

if (buildDefinitions.Count > 0)
{
    var buildDefinition = buildDefinitions[0];
    var newBuild = new Build
    {
        Definition = new DefinitionReference
        {
            Id = buildDefinition.Id
        },
        Project = buildDefinition.Project
    };

    await buildClient.QueueBuildAsync(newBuild);
}

Going one step further

In our case, working on a feature often involves working on different repositories, hence multiple PRs. Since we don’t want to queue n PRs related to a single feature, some smarts were introduced so a new build is not queued if there’s already one in the queue - not currently running.

You can have a look at the solution we ended up with on this GitHub repository. While the NTLM authentication works with an on-premise installation of TFS, it would require some modifications to work with VSTS.

Properties attributes in Polymer

I feel like there’s a misunderstanding about some of the attributes that we can apply to properties in Polymer. Let’s go through them.

reflectToAttribute

This is to me the most misused attribute. I haven’t been doing Polymer for very long, and I got this one completely wrong at first. I thought that in order to have a property bindable, we had to throw this attribute on it. This is wrong, as every property we declare is by default bindable, except of course for the ones having the readOnly or computed attribute.

What the reflectToAttribute does is instruct Polymer that it has to serialise the property’s value to an HTML attribute on the actual HTML element representing our component.

Let’s say we have the following element declaration:

Polymer({
  is: 'my-element',
  properties: {
    tasks: {
      type: Array,
      reflectToAttribute: true
    }
  }
});

and we bind an array to that property. This is what the DOM could look like:

<my-element tasks='[{"id":1,"name":"foo"},{"id":2,"name":"bar"}]'>
</my-element>

This is bad because we may not want to expose all that data directly in the DOM. Plus, there must be some performance overhead associated with the serialisation of the value every time it changes.

When to use it, then? Great question! So far, I’ve only seen one case where it’s useful to use it, and that is CSS styling. Given the property and its value will be represented as an HTML attribute, we can then have specific CSS rules depending on:

  • the existence of the attribute
  • the value of the attribute

A good example is the paper-button and its raised property. We can see on the declaration of the raised property that it has the reflectToAttribute set to true. In the associated styles, here and there, we have specific style rules are applied if the element has the raised attribute.

Boolean properties are special because the way Polymer treats them is the following: the attribute will exist only if the value is true, and it will have no value - like the common checked and disabled attributes on input - otherwise it won’t be present.

So most of the time, we won’t need that attribute, so I think a good practice - and this applies to the following attribute, too - is not to throw it automatically on every property.

notify

This attribute is often applied to properties without an analysis of whether it’s really needed or not. It has to do with child-to-host binding.

It is useful only if we want a parent element to be notified that the property of your component changed, and by the same time update the property of the parent element to which it is bound. I hope that makes sense.

Let’s see an example:

<dom-module id="my-child-element">
  <template>
  </template>
  <script>
    Polymer({
      is: 'my-child-element',
      properties: {
        notifier: {
          type: String,
          notify: true
        }
      }
    });
  </script>
</dom-module>

<dom-module id="my-parent-element">
  <template>
    <my-child-element notifier="">
    </my-child-element>
  </template>
  <script>
    Polymer({
      is: 'my-parent-element',
      properties: {
        notified: String
      }
    });
  </script>
</dom-module>

In this case it makes sense to have notify: true on the notify property because we want the notified property of the parent element to be automatically updated when notifier changes. What we have here is a child-to-host data flow. Also notice the use of curly braces in the binding, which are necessary to have the parent element’s property updated automatically.

Let’s now imagine we only want to propagate the value from the host to the child, that is to the parent to the child element. We can modify our code:

<dom-module id="my-child-element">
  <template>
  </template>
  <script>
    Polymer({
      is: 'my-child-element',
      properties: {
        destination: String
      }
    });
  </script>
</dom-module>

<dom-module id="my-parent-element">
  <template>
    <my-child-element destination="[[source]]">
    </my-child-element>
  </template>
  <script>
    Polymer({
      is: 'my-parent-element',
      properties: {
        source: String
      }
    });
  </script>
</dom-module>

The names of the properties were changed so that they still make sense. We now face a host-to-child data flow. Because the destination property doesn’t have the notify attribute, it doesn’t make sense to use curly braces anymore, so we swapped them for square ones.

It’s not always easy to figure out if a property will have to let know a parent element that its value has changed, especially when we deal with global components that are used all over the code base. But for some higher level components, let’s say at the page level, it’s easier to figure out the scope of the properties and apply the notify attribute correctly.

readOnly

I personally like this attribute and I think I don’t use it as often as I could. Applying it to a property prevents that property from being changed via direct assignment or data-binding. Polymer creates under the hood a private function that allows the change its value. I say private (you should see air quotes, here) because the function is not really private, in the sense that another component could call it.

See this example:

Polymer({
  is: 'my-element',
  properties: {
    internalState: {
      type: String,
      readOnly: true
    }
  }
});

As stated earlier, the internalState property could not be modified with binding or direct assignment, but Polymer created for us a _setInternalState function that allows us to change the value of the property. Still, another component could invoke this function, but we know we’re badass when we invoke a function starting with an underscore, right?

This attribute allows us to implicitly state that the value of this property is the sole responsibility of the component it’s defined in. But this property could be used by a parent component!

A great example comes from Polymer itself in the iron-media-query element. It has 2 properties:

  • The query property in which we pass the media query we target
  • The queryMatches property, which value is a Boolean taht indicates if the media query is matched or not

Now, it’s the responsibility of the iron-media-query element to determine if the media query is matched or not, and it doesn’t want us to trick it, so the queryMatches property is defined with the readOnly attribute.

<dom-module id="my-element">
  <template>
     <iron-media-query query="(max-width: 768px)" query-matches=""></iron-media-query>
  </template>
  <script>
    Polymer({
      is: 'my-element',
      properties: {
        isSmartphone: Boolean
      }
    });
  </script>
</dom-module>

We give the element an input from which it computes some value and returns it back to us, all of that with automatic binding. Easier would be hard to achieve.


A small words about braces

This is not actually about properties attributes, but I feel like it fits well with the general message of the post.

My rule of thumb is the following: always use square braces if I don’t expect a child-to-host data flow.

This applies to more places than you’d think:

<template is="dom-if" if="[[applyLogic(myProperty)]]">
</template>

<template is="dom-repeat" items="[[myItems]]">
</template>

<span>[[myLabel]]</span>

There is no way we’ll get data back in these cases, so why not make it even more obvious by using square braces? I don’t know if there’s actually a performance penalty when using curly braces, but let’s play it safe, and the visual cue of the square braces is a good enough reason for me.