Category: IdentityServer3

Multi-Tenanted Authentication with Azure AD and Office 365 (and IdentityServer3)

With solutions such as Azure AD and Office 365 becoming more common as a source of an organisations identity on the Internet it can be useful to have an application offer authentication against them. As a typical scenario let’s imagine you’ve developed a time tracking SaaS solution and you have a number of customers who wants to use it but logon with their Office 365 identities.

It’s a bit of a lengthy process but not difficult to do when you know how and hopefully by way of this worked example I can show you how to add this functionality yourself.

As it’s fairly commonplace to want to offer other external identities alongside Office 365 I’m going to integrate this into the excellent IdentityServer3 OpenID Connect Provider however nothing here really depends on IdentityServer3 and if you’ve got a basic familiarity with OWIN it should be fairly obvious how to decouple what I’ve done here.

The source code for all the below can be found in GitHub.

Setup the Visual Studio Project

Firstly open up Visual Studio (I’m using 2015) and create a new ASP.Net Web Application. In the New ASP.Net Project dialog select to build an MVC application and ensure that the authentication method is set to No Authentication:

1

Now you need to add a set of NuGet packages to the project:

Install-Package Thinktecture.IdentityServer3
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Security.OpenIdConnect

Now in the properties pane for the project enable SSL and note the port number:

2

And in the project properties (I love how this is spread liberally about the place!) set the start page to be the https site:

3

Now add a class called Startup to the root of the project as below. As we’ve added the Microsoft.Owin.Host.SystemWeb package it’s going to look for this on startup and will throw an exception if it can’t find it – we’ll fill it in later.

4

 

Now add the following to your web.config file:

15

 

At this point you should be able to run your solution and the boilerplate MVC website should appear secured with a self signed SSL certificate generated by Visual Studio.

Setup an Azure AD

We’re going to use two domains for the following – one for our application to authenticate against and federate against other directories and a second domain that we’re going to use to login with.

If you’ve got an Azure subscription you should already have a default domain so we’ll use that for logging in.

To enable the multi-tenanted scenario create a second Azure AD in the (old) management portal. This AD is used to host an Azure AD Application and handles the federation between the other domains.

Go to the Applications tab for your directory and add an application, select “Add an application my organisation is developing”. Give the application and appropriate customer facing name (they will be able to see this in their own domain configuration and during sign up) and set this to be a web application:

5

In the next page set the sign on URL to be based on the the root of your web project (as you noted earlier) extended with the path /identity/signin-azuread giving you a URL such as:

https://localhost:44300/identity/signin-azuread

In the second box you need to set a unique URI for your application. The best way to do this is based on your own domain in the form http://mydomain.onmicrosoft.com/myapplication:

6

Azure will now whirr away for a second or two before showing you the application dashboard. Click the configure tab. Move down to the middle of the page and set Application is Multi-Tenant to on and create a new key. Then hit save. The key will display after you’ve hit save. Note down both the key and the client ID as you will need them later.

7

Configure IdentityServer3 and Azure AD / Office 365 as an Identity Provider

Back in Visual Studio edit the Startup class we created earlier. Firstly add a folder to the root of the project called Configuration, download the IdentityServer3 test certificate, and add it to this folder setting it to Copy if Newer in the properties panel:

8

IdentityServer3 will use this certificate (when configured below) to sign the tokens it issues however it’s important to be clear that in a production environment this certificate needs to be generated and kept securely. The certificate used here is a certificate used in the IdentityServer3 tests and stored / loaded in a manner designed for brevity in the source code rather than security. In a production environment you’d keep this in your certificate store.

Now configure IdentityServer3 as an endpoint within this solution by adding the following to the Startup class:

9

We’ve set IdentityServer3 up to use it’s in memory stores and created a client that we’ll later authenticate against (see the IdentityServer3 docs for more details on this).

We now need to configure Azure AD / Office 365 as an identity provider by filling in the ConfigureIdentityProviders method:

10

It’s vital that the client ID and redirect URI supplied to the Open ID Connect options exactly match those in the Azure AD application you created earlier and the Authority string must look exactly as written here including the trailing /.

As this point things will build but there’s nothing in our solution requiring authorization however you can navigate to the published configuration endpoint and should see some json describing our service. My endpoint is at:

https://localhost:44300/identity/.well-known/openid-configuration

Authorizing and Viewing Claims

We’re going to use an Authorize attribute on an MVC action to test our work so far. To begin with we need to get our MVC project to use the token end point we’ve created above. We need to add the following to the bottom of our Configuration method:

11

You need to ensure you use the port your website is running on on your development machine. Mine is on 44300.

Modify the About action in HomeController.cs to look like the following:

12

And the corresponding View to:

13

Now run the project and if you’ve got all the magic numbers and URLs right you should be navigated to the Azure AD logon page:

14

Login using a different AD to the one we set up earlier and if you now login you should end up back at the about page with a set of claims showing:

16

If you now head back into the Azure Management Portal and inspect the Applications tab for the Azure AD you used to login with you should find that the application we created earlier has been added here:

17

If all that worked then great – that’s the happy, easy, path dealt with! If not I suggest checking out my number one tip for dealing with IdentityServer3 issues.

The Administrator Consent Workflow

Depending on your domain configuration (the one you logged in with) then you might have hit the error page shown below:

18

Many domains, particularly of larger organisations, will have enabled Administrator Consent. This prevents users from being able to use their domain account to access resources they are not authorized to – unfortunately the error above isn’t very useful to end users.

To allow users in a domain configured this way to be able to access your application you need to implement a workflow that allows the Administrator to grant consent for users. Before continuing, if you’ve followed the steps above, then in Azure delete the application from your login domain – select it in the management portal and click Manage Access, then click Remove Access.

Firstly let’s turn on Administrator Consent in our login domain – select the domain in the management portal and click the Configure tab. When Azure has finished whirring away scroll down to the Integrated Applications section and set “USERS MAY GIVE APPLICATIONS PERMISSION TO ACCESS THEIR DATA” and “USERS MAY ADD INTEGRATED APPLICATIONS” to no. Then press save.

19

Now in the users section of your domain add a new user and give them only the User role:

20

Next head back to the Azure AD application we created at the start of this post, click the Configure tab and scroll down to the single sign on section. Enter a URL for the root of your website and click save:

24

Ensure the browser is closed and if you now run the Visual Studio solution again and attempt to login with this new user then you should see the error message shown earlier. If not check the configuration in Azure once more.

At this point we’ve got a domain that requires administrator consent for users to attach to our application. Begin by adding a new Nuget package:

Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

Then add a class called AzureTenant as shown below:

21

We now need to be able to manage our tenants. Normally you’d use some form of persistent store for this but I’m going to use a very basic in memory service by way of an example. The full service code is in GitHub here however there are two important sections. Firstly our controller (which we’ll work on later) will create a temporary tenant before handing off to Azure and later on after association is complete we convert this to a permanent tenant. This two stage process can be useful as in a more realistic example you may want to track some additional state across this request however all you need is someway of attaching data to a persistent unique ID – reusing the tenant like this keeps the example simple. However this code can be seen below:

25

When our controller hands off to Azure to handle the domain association we need to do so by forming up a URL and doing this can be seen below:

26

And finally following the association we need to retrieve the tenant ID for the newly associated tenant (and this is where your client key created at the beginning of this piece is required):

Now we need to add an MVC controller that is going to present this functionality to the user. There is some boilerplate at the bottom but I want to focus here on the AdminConsent and Associate methods as shown below:

22

The default admin consent view presents a button to the user and a paragraph confirming what they are about to do – when the post back hits the controller it does two things. Firstly it creates a temporary tenant in our data store and then redirects to the Azure authorization endpoint using the temporary ID of the tenant as the state.

When the user confirms the association in the Azure AD management page presented during the redirect then it will call back to the Associate action passing in the temporary tenant ID as the state. In more complex examples tracking the state in this way allows us to track back the Azure request to any information we’ve gathered before confirmation. The Associate action then uses our in memory service to confirm the association and handles the routing off to the appropriate page based on success or failure.

Finally having done all of the above we’re going to put a link to our admin consent page on the home page. I modified the Jumbotron to point at the AdminConsent view.

At this point you should be able to run the solution (and this is the complete solution in GitHub) and if things are wired up correctly then you should be able to click your admin consent link. Try this with your test user and it will fail but if you then try it with your admin user you should see a screen like this:

23

However if you’ve got everything right you should end up back at the success screen on your website and can now navigate into the About section.

Hopefully that helps anyone trying to figure out how to do this themselves and feel free to ask me questions in the comments or on Twitter.

Finally, worth repeating – the code is all available on GitHub.

My Number One Tip for Working With IdentityServer3

It’s a real simple one but honestly a real time saver:

Turn on logging

IdentityServer3 is a great piece of software with excellent documentation but the identity space is inherently complex and gets quite fiddly quite quickly. Fortunately if you turn on the logging the output is rich and incredibly useful.

I was working on standing up another STS this morning based on it and was just cracking through it thinking “yeah I know this” and hadn’t bothered to turn on logging. Pride, of course, always comes before a fall and so of course I hit a weird and inscrutable problem: the STS let me log in with Google but then never redirected back to my application. Spent a little while thrashing around then remembered my own number one tip, turned on logging, and there in the trace window was the source of my problem (in my case I hadn’t run the new client properly through my custom user service and wasn’t marking users as active in a particular work flow).

Turning it on is really simple. All you need to do is add a NuGet package for one of the supported loggers and configure it. In my case I use Serilog and the code for configuring it is as on the IdentityServer3 website:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Trace()
    .CreateLogger();

AngularJS, IdentityServer3 and OAuth2 Plugin Sample

By popular(ish) demand I’ve published to GitHub a sample that demonstrates using my OAuth 2 Angular plugin (also hosted on GitHub) requesting tokens from IdentityServer3 and using them to access protected Web API resources.

The sample can be found on GitHub and should be pretty easy to get running as long as you have the pre-reqs (npm, git, grunt and bower primarily):

  1. Open the SLN file in Visual Studio and start both projects.
  2. Open a NodeJS command prompt and navigate to the UI folder.
  3. Type “bower install” to install the plugins.
  4. Type “grunt serve” to run the Angular app.

The identity server is prewired with a single user account details as follows:

Username: auser
Password: password

It should be noted that this is illustrative code, particularly in the C# projects, and it’s been written as such. I didn’t want to cloud the example with dependency injectors and other such components.

Thanks to everyone for their comments, questions, and feedback on the plugin both here and on GitHub.

Authenticating AngularJS against OAuth 2.0 / OpenID Connect

I’ve recently found myself doing quite a bit of work putting in place an STS (Security Token Service) based around the excellent Thinktecture IdentityServer 3. I have a variety of different client types that need to authenticate including JavaScript Single Page Applications using the AngularJS framework.

IdentityServer 3 implements the Open ID Connect protocol for clients to authenticate against, Open ID Connect being an extension to OAuth 2.0.

There’s an existing open source plugin for authenticating with OAuth 2.0 called oauth-ng that utilises the implicit authentication flow that I wanted to use however I wanted some different behaviour and was interested in implementing my own plugin as a learning exercise with the protocol itself and with AngularJS. Massive credit to the author of that plug-in for inspiration and readable code, this is the first non-trivial AngularJS directive I’ve developed and so it was incredibly useful to be able to look at oauth-ng and riff off it’s design. As another reference the Adal-Angular project was also really useful.

The main features of the plugin I’ve developed are:

  • Sign in / sign out button
  • Specify which routes require a token for access to protected resources and automatically handle sign in if required when they are accessed
  • Storage of the token in the browsers session storage via the ngStorage module
  • Automatic insertion of a bearer token into HTTP requests once a user has authenticated

The plug-in can be found on GitHub here. It works and I’ve tested it against both Thinktecture IdentityServer3 and Google’s OAuth2 endpoint but is still quite early code in terms of testing and so if you encounter any issues please do log them on the GitHub issues page or submit a pull request with a fix.

All the code samples given below are from the sample app which is basically the Yeoman generated scaffold and that you can find on GitHub here and which I’ve configured to authenticate directly against Google. You will need to obtain your own client ID and configure Google as per the instructions here.

Getting Started

You can either grab the scripts from GitHub or, more easily, install the plugin as a bower package which you can install as follows:

bower install angularjs-oauth2 --save

First you’ll need to add the module to your applications list of dependencies in app.js and you need to make sure that ngStorage is also included:

angular
  .module('angularJsApp', [
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngStorage',
    'ngSanitize',
    'ngTouch',
    'afOAuth2'
  ])

The default template that is supplied for the sign in / out button expects to be placed inside a bootstrap navbar.

Typically in the above example you would select a scope appropriate to the resource you wished to access (if that terminology is confusing then I have a series of blog posts on the way as an intro to Open ID Connect and OAuth 2.0) – I’ve just picked one that we’ll have access to without additional configuration.

Now let’s modify the app.js file so that clicking the About link will require the user to be signed in:

.config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .when('/about', {
        templateUrl: 'views/about.html',
        controller: 'AboutCtrl',
        requireToken: true
      })
      .otherwise({
        redirectTo: '/'
      });
  });

Note the addition of requireToken:true to the route for about.

Now run the app (if you’re using the yeoman builder like myself then type grunt serve). You should see something much like the following appear in your browser:

indexScreen

The only difference from the standard Yeoman template (at least at the time I wrote this) is the Sign In button at the top right. If you click that, or the about link, then you should be redirected to the Google sign in page that looks like this:

signonSuccess

If you’ve not got things wired up “just so” in the Google console then you’ll see an error. Generally they are reasonably informative and normally, in my experience, the redirect URI in the console doesn’t quite match the redirect URI in the app (they’re sensitive over things like the / on the end or not).

After signing in the token will be added as an Authorization header to all http calls using the ‘Bearer my token’ format and so any calls you make to a remote resource that require authorisation will be supplied the token they need to verify the user.

Options

The oauth2 tag has a number of attributes that can be specified as follows.

authorisation-url: The URL to request the token from.
client-id: The client ID to supply to the token provider.
redirect-url: The URL the token provider should redirect to on a successful sign in.
response-type: Optional. The required token type. Defaults to token.
scope: The resources that access is requested to.
state: A magic number to send to the token provider to protect against CSRF attacks.
template: Optional. URL of a Angular template to use for the sign in / out button in place of the built in template.
buttonClass: Optional. Defaults to “btn btn-primary”. The class to apply to the button in the standard template.
signInText: Optional. Defaults to Sign In.
signOutText: Optional. Defaults to Sign Out.
signOutUrl: Optional. The URL to call to ask the token provider to perform a sign out. See notes on sign out below.
signOutAppendToken: Optional. Defaults to “false”. If set to “true” then the access token will be appended to the signOutUrl.
signOutRedirectUrl: Optional. The URL that the token provider, if it supports redirects, should redirect to following a sign out.

Signing Out

Signing out in the OAuth world can be… complicated. When the user presses the sign out button presented by this plug in the token that is stored in session storage is cleared and as we’re using session storage as soon as the session ends (window or tab closed) then they’ll be logged out.

However they may still be logged in with the token provider depending on how the token provider behaves and the options the user has selected their.

This plugin does allow a URL to be supplied to initiate a logout at the token provider where the token provider allows for that but if the token provider is using a persistent cookie and the user shuts the window without clicking sign out then they could remain logged in.

It’s worth thinking about if / when you choose to use OAuth.

Contact

  • If you're looking for help with C#, .NET, Azure, Architecture, or would simply value an independent opinion then please get in touch here or over on Twitter.

Recent Posts

Recent Tweets

Invalid or expired token.

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez