Using AD for authentication between Web app and Web API

In my earlier post about the problem with ACS I promised to blog about how to configure Microsoft Azure Active Directory (MaaD) to support a client app calling a Web API using a client secret (as opposed to presenting an authentication dialogue to an interactive user).

To get started (and save me a lot of typing) I suggest you get started by reading Johan Danforth’s great blog post on how to do this for an interactive login as this is where I started)

If you follow the instructions carefully you will have a Web API published and registered with AD and you will have updated the application’s manifest file in AD to allow you to configure permissions to it in other applications in the directory)

You will also have a client app, also configured in AD (as a ‘native application’) in which you have configured permission to access the Web API and the relevant call to issue the request to AD to get the token and to the Web API bearing that token.

Crucially – you will have written very little code to do this. AD, the ADAL Nuget Package and the OWIN library (on the server side) will have done all the heavy lifting for you.

What I wanted to do is to remove the interactive user from the equation – in my scenario a user uses a web app, which in turn uses my Web API, but this is not a delegation scenario, the web app can call the web api on its own right.

To achieve this, I needed to make two changes from Johan’s example –

Firstly I needed to register my web app as a client app which isn’t a native client as a native client app in AD does not have configuration for client secret. so I’ve added a new app to my directory, made sure to select the ‘web application and/or web api application’ for the type and configured a name, APP ID and (in my case a mock) sign-on url.

I had also set the permission from my web client application to the web-api, in the same way that I did earlier for my native client.

And finally – I added a new key by selecting a duration and copied the secret (important: the key cannot be accessed after its creation, make sure to copy it)

With the AD configuration done I make the relevant changes to the client code to use the client secret rather than the interactive sign on

The original code includes the following line - 

var ar1 = ac.AcquireToken(“https://. . . WEB Api APP ID URI. . .”,
              "e9b5d821-. . .Native Client’s Client Id",
              new Uri("http:// . .  Native Client’s APP ID URI"));

so support authenticating using client secret, this needed to change to

ClientCredential cc = new ClientCredential("e9b5d821-Web Client Client ID",
"Qxc49i. . .client secret generated in AD portal"); var ar1 = ac.AcquireToken(https://. . . WEB Api APP ID URI. . .”, cc);

and with this done (the rest of the code remains unchanged), when I now run the client, rather than being prompt to authenticate the call to the web api is made and authentication is done using the client secret.

In closing, for completeness, the interaction really translates to two web requests – the first is to AD to retrieve the access token, and in my case this looks like this –

POST https://login.windows.net/[1db093b3-…AD tenant ID]/oauth2/token HTTP/1.1

Host: login.windows.net

Content-Type: application/x-www-form-urlencoded

Content-Length: 212

grant_type=client_credentials&client_id=[e9b5d821-…client app client id]&client_secret=[Qxc49iXn…client secret]&resource=[https%3A%2F%2F…..protected resource app ID]

This request, if successful, returns a JSON response which includes a JWT token from AD that can be used in subsequent requests to the application. That subsequent requests looks like this (token trimmed for readability) –

GET http://localhost/WebApplication1/api/Values HTTP/1.1

Host: localhost

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJh……..

Finally – whilst I was experimenting with this I encountered a few errors that are worth noting here –

The one I was most confused by was

"AADSTS90014: The request body must contain the following parameter: ‘client_secret or client_assertion’."

In my case this happened when I tried to request a token from AD through my client app when it was still coded for interactive user sign-on. when the AD application used was native, using the interactive logon code worked fine, as soon as I tried to use a web api AD application without changing the code to use client_secret I got the error above.-

Final thought – ACS is slowly being embedded in AD but for the time being I find the experience a little bit fiddly and short of documentation (in particular the Manifest file manipulation) and lack of flexibility

The integration above currently only works with JWT tokens and client secret authentication (SWT is not supported I believe as is certificate based authentication) but this will be corrected over time, I’m sure.

 

[cross posted on the Solidsoft blog]

The problem with ACS….

…is that it currently has no published SLAs and there’s no out-of-the-box solution for Disaster Recovery in an active scenario (as in – web services, REST services)

Let me expand on these two points –

I could not find any SLA information about ACS, so I asked Microsoft who responded that, as things stand ACS is a free service and they do not offer SLAs for free. This might change as and when ACS is properly rolled into Microsoft Azure Active Directory with its paid, Premium option, but that’s not the case at the moment.

Pragmatically – I have not seen any ACS issues in the past and there are several Azure services that do carry SLA that take a dependency on ACS, namely the Service Bus and BizTalk Services. Make of that what you will – you have to either accept or reject this risk.

To make matters worse – there’s no easy solution for DR for ACS in an active scenario.

Federated identity follows two approaches – when dealing with users and browsers the passive approach is used and that relies on browser redirects. This means that the web app is responsible for redirecting users to the Identity Provider (IdP) and holds the configuration of where the IdP is. That means, for example, a web role deployed across data centres and fronted by Traffic Manager, can have separate configurations to redirect users to different ACS namespaces, depending on the environment.

When dealing with services (SOAP with WS-Federation or REST with oAuth) the service client holds the configuration about where the IdP is (in the SOAP case this is part of the service contract in oAuth it is simply exchanged out of band) and it is the service client’s responsibility to go and get a token from the IdP before calling the service.  This means that if the ACS namespace changes (for example due to outage and switch to DR), the client will need to know to go to the alternative the service won’t tell it automatically.

With this in mind, what are the options?

I can think of 3 or 4. would love to hear additional views –

1. Accept the risk and the need to reconfigure clients when switching to DR.

2. If you have control over the client – implement an operation to automatically (on failure) and/or regularly request the metadata from your web role, which would allow it to auto-recover

3. If either of the above are not acceptable – roll your own oAuth solution on your web role which would mean that client access the same url (managed by traffic manager) to access the service and the IdP.

A better alternative is to use the bit of ACS that is rolled into Active Directory through the configuration of Applications for both the Web API and the client. I’ll post an entry on this shortly. the upside is that if you then make sure your directory is a Premium one you are covered by SLAs, although I’m still not sure what the DR story for AD is.

The main downside of this approach at this point in time is that, currently at least, there’s not much configuration that can be done – it only supports a client secret and JWT tokens with no control over token TTL etc. I also the the process is quite fiddly, requiring tempering with the app manifest manually etc, but I’m sure all of this will improve over time.

[cross posted on the Solidsoft Blog]

Role-based authorisation with Windows Azure Active Directory

Using Window Azure Active Directory (WaaD) to provide authentication for web applications hosted anywhere is dead simple. Indeed in Visual Studio 2013 it is a couple of steps in the project creation wizard.

This provides means of authentication but not authorisation, so what does it take to support authorisation is the [Authorise(Role=xxx)] approach?

Conceptually, what’s needed is means to convert information from claims to role information about the logged-on user.

WaaD supports creating Groups and assigning users to them, which is what’s needed to drive role-based authorisation, the problem is that, somewhat surprising perhaps, group membership is not reflected in the claim-set delivered to the application as part of the ws-federation authentication.

Fortunately, getting around this is very straight forward. Microsoft actually published a good example here but I found it a little bit confusing and wanted to simplify the steps somewhat to explain the process more clearly, hopefully I’ve succeeded –

The process involves extracting the claims principal from the request and, from the provided claims,  find the WaaD tenant.

With that and with prior knowledge of the clientId and key for the tenant (exchanged out of band and kept securely, of course) the WaaD GraphAPI can be used to query the group membership of the user

Finally – the groups can be used to add role claims to the claim-set, which WIF would automatically populate as roles allowing the program to use IsInRole and the [Authorise] attribute as it would normally.

So – how is all of this done? –

The key is to add a ClaimsAuthenticationManager, which will get invoked when an authentication response is detected, and in it perform the steps described.

A slightly simplified (as opposed to better!) version of the sample code is as follows 

 public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
        {
            //only act if a principal exists and is authenticated
            if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
            {
                //get the Windows Azure Active Directory tenantId
                string tenantId = incomingPrincipal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;

                // Use the DirectoryDataServiceAuthorizationHelper graph helper API
                // to get a token to access the Windows Azure AD Graph
                string clientId = ConfigurationManager.AppSettings["ida:ClientID"];
                string password = ConfigurationManager.AppSettings["ida:Password"];

                //get a JWT authorisation token for the application from the directory 
                AADJWTToken token = DirectoryDataServiceAuthorizationHelper.GetAuthorizationToken(tenantId, clientId, password);

                // initialize a graphService instance. Use the JWT token acquired in the previous step.
                DirectoryDataService graphService = new DirectoryDataService(tenantId, token);

                // get the user's ObjectId
                String currentUserObjectId = incomingPrincipal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

                // Get the User object by querying Windows Azure AD Graph
                User currentUser = graphService.directoryObjects.OfType<User>().Where(it => (it.objectId == currentUserObjectId)).SingleOrDefault();


                // load the memberOf property of the current user
                graphService.LoadProperty(currentUser, "memberOf");
                //read the values of the memberOf property
                List<Group> currentRoles = currentUser.memberOf.OfType<Group>().ToList();

                //take each group the user is a member of and add it as a role
                foreach (Group role in currentRoles)
                {
                    ((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, role.displayName, ClaimValueTypes.String, "SampleApplication"));
                }
            }
            return base.Authenticate(resourceName, incomingPrincipal);
        }

You can follow the comments to pick up the actions in the code; in broad terms the identity and the tenant id are extracted from the token, the clientid and key are read from the web.config (VS 2013 puts them there automatically, which is very handy!), an authorisation token is retrieved to support calls to the graph API and the graph service is then used to query the user and its group membership from WaaD before converting, in this case, all groups to role claims.

To use the graph API I used the Graph API helper source code as pointed out here. in Visual Studio 2013 I updated the references to Microsoft.Data.Services.Client and Microsoft.Data.OData to 5.6.0.0.

Finally, to plug in my ClaimsAuthenticationManager to the WIF pipeline I added this bit of configuration –

  <system.identityModel>
    <identityConfiguration>
          <claimsAuthenticationManager
type="WebApplication5.GraphClaimsAuthenticationManager,WebApplication5" />

With this done the ClaimsAuthenticationManager kicks in after the authentication and injects the role claims, WIF’s default behaviour then does its magic and in my controller I can use, for example –

        [Authorize(Roles="Readers")]
        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

Cross posted on the Solidsoft blog

End-to-end authentication and authorisation scenario for MVC+ACS

Background

Windows Azure’s Access Control Service (ACS) enables developers of web application and services to provide a seamless single-sign-on experience for their users, easily and quickly, building on standard protocols such as OAuth, WS-Federation and SAML.

ACS’ built in support for Live, Google, Yahoo and Facebook Identities as well as the easy integration with ADFS and AD means that authentication with the most used identities is literally done with a few clicks and a little bit of configuration.

For an overview of the ACS service and a useful how-to tutorial see MSDN on – http://msdn.microsoft.com/en-us/library/gg429781.aspx

Using ACS with well-known identity providers, other than custom authentication solutions, as part of the application provides several benefits –

From the users’ perspective it prevents the need to remember a different set of credentials for the application, instead using existing identities to sign-in; this also increases security as users tend to use the same credentials for many applications, not all are good at protecting this information.

From the application’s perspective it removes some of the effort required in building scenarios such as managing credentials – storing them securely, implementing authentication functionality as well as capabilities such as reminding/resetting passwords, etc.

However – whilst integrating an application with an identity provider (or several) provides two (generally trust-worthy) facts – the knowledge that the user has been authenticated by the approved identity provider(s) and a unique identifier for that user – it does not, on its own, provide a complete end-to-end solution for authentication and authorisation; several pieces are needed on top of the ACS and IP integration beyond uniquely identifying a user, such as managing the user’s profile and implementing role based authorisation.

In this post I will be looking at the steps that are required to provide an end-to-end story for an ASP.net / MVC application using ACS with multiple identity providers to drive authentication and authorisation scenarios, I’ll start by discussing managing users’ profiles –

Managing Users’ Profiles

The Identity Providers, through ACS, will provide the application a unique identifier for the user –

The initial request from the user’s browser to the application will come as unauthenticated; at this point, given the right configuration,  Windows Identity Foundation will redirect the request to ACS which will, in turn, interact with the identity provider (as needed) before redirecting back to the application, this time with a bunch of claims regarding the details of the identity provider and the user’s identity provided through the IClaimsPrincipal object.

It does not, at this point, give you much information about the user – some identity providers, such as Google, might provide the user’s first and last name and perhaps an email address, others, such as Live ID, will only provide a unique identifier – nor does it tell you whether the user is allowed to access your application. All you know is that this is user x as declared by identity provider y.

This might be good enough for web sites that do not restrict access, and only need to know a unique id for a user, for example for personalisation purposes or to store data for a particular user, http://www.stackoverflow.com is an example for such site.

Most web sites, however, would like to, at the very least, know some basic information about the user, such as full name, perhaps date of birth or email address; some – of course – require a much more elaborate profile.

Some web sites will let anybody in, but will require updating the profile, others are membership only and so – knowing the identity of the user is one half of the story – matching it against a membership database being the other.

To support either of these scenarios, the application will need to have its own store of users’ information, linked to the identity provided through ACS.

As requests arrive from the ACS the application will need to be able to refer to this store to identify whether the user is known or new. Known users will be let in (subject to authorisation, discussed later in this article); unknown users will be, for example, directed to a registration page.

image

This really isn’t much different from how this would be implemented without the ACS- if identity was provided by ASP.net membership, for example – the main difference is that when implementing single-sign-on the identity piece and the profile management/authorisation piece are separated.

On Windows Azure, table storage is a great option for storing user’s profile – records could be stored, for example, against the identity provider (as the partition key) and user’s identity (as the row key), and given that this will generally be the only access mechanism required (I’ll be discussing a variation of that – for supporting multiple identities for the same user), it keeps the solution nice and simple.

From a technical point of view – the application needs to first pick up the user’s identity, as provided by ACS, and check that against the user’s store, and it needs to do that before running the application’s code so that the user can be considered when evaluation authorisation.

One way of achieving this is leveraging the Windows Identity Framework pipeline by implementing a custom authorisation manager by inheriting from ClaimsAuthenticationManager 

By overriding the Authenticate method you can get access to the identity provided by ACS, interrogate the claims provided with it and even make changes to the claim-set as needed.

The first step in the authenticate method would be to extract the principal as an IClaimsPrincipal –

IClaimsIdentity identity = (IClaimsIdentity)incomingPrincipal.Identity;

The next step would be to ensure that the user has actually been authenticated as this method will get called twice – once for the initial unauthenticated request, before the redirection to ACS, and once when the user is redirected back to the application from ACS with the authentication token; We’re only interested in the second call and so if the user is not authenticated we do nothing. The module’s default behaviour will take care of redirecting unauthenticated users if this was the WIF configuration.

if(identity.IsAuthenticated)
{       //code goes here
}

For authenticated users, we need to extract the claims we’re expecting from the token and ensure they exist – these are the nameidentifier and identityprovider claims

Claim id = identity.Claims.FirstOrDefault(claim => claim.ClaimType ==
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"); Claim provider = identity.Claims.FirstOrDefault(claim => claim.ClaimType ==
"http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider"); if (id == null || provider == null) throw new ApplicationException(
"Reuqest did not contain the necessary authenticaiton information");

It might be obvious, but to avoid any doubt it is important to note that the user’s identity has to be composed of these two – the identity is unique in the context of the identity provider, theoretically two provides might use the same identity.

So – now that we have the user’s unique identity we can check whether it exist in our users store, this is straight forward coding against Table storage, so there’s no point repeating all the details, I have it encapsulated in two method calls

UserLineDataServiceContext context = UserLineDataServiceContext.GetContext();
UserLine user = context.FindUser(provider.Value, id.Value);

At this point user either hold the details of the user found or is null if the user has never been registered; if the user has been registered before I populate a bunch of claims specific to my application –

if (user != null) { identity.Claims.Add(new Claim(ClaimTypes.Role, "RegisteredUser")); Claim nameClaim = identity.Claims.FirstOrDefault(
                                 c => c.ClaimType == ClaimTypes.Name); if (nameClaim != null) identity.Claims.Remove(nameClaim);

      identity.Claims.Add(new Claim(ClaimTypes.Name,
                                user.FirstName + " " + user.LastName)); identity.Claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName)); identity.Claims.Add(new Claim(ClaimTypes.Surname, user.LastName)); }

The first claim I populate is a role claim of a ‘RegisteredUser’, I will be using this in my application to ensure that only users with this role can access pages other than the register page as part of my authorisation implementation.

I then populate the name-related claims; this allows me to present the user’s name as given to my application in the sign-out control and other areas of my application.

Note: some IPs (such as Google) will provide you with the user’s name, others might not, in either case I allow my user to override the name with the one she wishes to use in my application, and so for registered users I need to override any claims provided by the IP.

At the end of the Authenticate method I call the base method to ensure any standard behaviour of WIF is executed –

return base.Authenticate(resourceName, incomingPrincipal);

And so – by using a few lines of code in a custom ClaimsAuthenticationManager and utilising Windows Azure Table, we’ve enabled the application to manage it’s users, distinguishing between registered and unregistered users.

The next  step would be to implement authorisation and allow unregistered users to become registered –

Role Based Authorisation and the registration page

You would have noticed the custom Claims Authorisation Manager added, for known users, the RegisterUser claim – a claim of type ‘ClaimType.Role’ – by default WIF translates claims of this type to ASP.net roles allowing familiar role based authorisation techniques to be used, in my example I’ve used this to control access to the rest of the application and to direct unknown users to the registration page.

In my case I’ve decided that the application can be accessed by any user, but it requires that users register with it application directly.

To this behaviour, preventing unregistered users access to the majority of the application, I’ve added the authorise attribute, requiring the ‘RegisteredUser’ role for access, on all my controllers other than the default controller –

[Authorize(Roles="RegisteredUser")]

By doing so, and given that this role will only be available for users that were found in the applications user’s repository, I ensure that unknown users, even if authenticated by the identity provides supported, will not be able to access any part of the application other than the home controller (which does not have this attribute)

Note: To bulletproof this approach the ClaimsAuthenticationManager should check that incoming requests do not contain the Role Claim with the text ‘RegisteredUser’ 

On the home controller I have two actions, Index and About, both are available for any user, the Index action has the following code –

public ActionResult Index()
        {
            if (!User.IsInRole("RegisteredUser"))
                return RedirectToAction("Register", "Account");
            else
            {
                return View();
            }
        }

As you can see – as users land in the default action for the application, if they are not registered they are redirected to the Register action of the Account Controller – an action that is available to any users, this action will display the registration form asking the user for details such as name and date of birth, the post action for this form looks as follows –

UserLine user = new UserLine(encodeKey(collection["IdentityProvider"]), encodeKey(collection["Identity"]));
            user.FirstName = collection["FirstName"];
            user.LastName = collection["LastName"];
            user.EmailAddress = collection["EmailAddress"];

            UserLineDataServiceContext context = UserLineDataServiceContext.GetContext();
            context.AddUser(user);
            ClaimsIdentity identity = User.Identity as ClaimsIdentity;
            
            identity.Claims.Add(new Claim(ClaimTypes.Role,"RegisteredUser"));
            //add name claims according to registration information
            identity.Claims.Add(new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName));
            identity.Claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName));
            identity.Claims.Add(new Claim(ClaimTypes.Surname, user.LastName));

            return RedirectToAction("Index", "Home");

Admittedly not the most robust code in the world, but good enough as a sample it creates a new UserLine, populating it with the information from the form and adds it to the Table before adding all the necessary claims for this user.

These claims, including the RegistredUser role claim would normally be added by the ClaimsAuthenticationManager but in this case they are now added in this form to allow the user to be treated as a recognised user by the application.

With these set the user can be redirected back to the default action, this time with the correct role which would allow the default view to be returned.

Supporting Multiple Identities for same user

Everything that discussed so far assumes the use is only identified using one identity provider and whilst this is a fair assumption for some web sites, most of those who wish to support identity federation want to support more than one provider and to make user’s life as convenient as possible it is important to be able to recognise users using more than one identity provider.

Given that there’s no way for any one IP. or the ACS, to link identities, this is up for the application, or – more accurately- up for the application to allow the user to do so.

I haven’t fully implemented this for my sample, but the approach would be to allow the user, in the registration page, to indicate that she is already known using a different identity and then to be able to provide a token for this identity (through ACS, of course).

The key to this is to expand the user’s repository – every user should be given an ‘internal identity’ – managed by the application and separate table will link any IP-provided identity to the relevant internal identity, any records in the application should always be stored against the internal identity.

Summary

I hope that through this post I was able to demonstrate that whilst enabling ACS for an application is only a first step towards achieving a full end-to-end authentication and authorisation solution for an application, the steps required to complete the solutions are quite straight forward and lean, and that’s the whole point in identity federation – to take away the majority of the work needed, whilst leaving a good level of control in the application.

Storing user’s data

In my post ‘Of Claims and Public Identities’ I have pointed out that web sites that use the ACS usually augment the information provided by the IP (typically a meaningless token) with user inputted personal information which would be stored by the application against the IP’s token.

As part of a demo I’ve been working on I’ve decided to implement this approach and, initially at least, everything seems to work fine (using my Live ID)

Below is the initial code I’ve written (it’s not pretty, but it does the job Smile ) –

First I check that the user has indeed been authenticated (if not, it has not been through the ACS, which should never have happened), next I extract the claims I expect – name identifier and identity provider – before performing a lookup in an Azure table to see if a user with this token (from this particular provider) already exists.

If I find the user in my table I redirect the request to the home page, if I don’t I redirect to the registration page which would ask the user for more details, add it to the table and then re-run the code below to verify.

    //Check that user is authenticated
    if (!User.Identity.IsAuthenticated)
    {
        throw new ApplicationException("User is not authenticated");
    }

    //get user identity
    ClaimsIdentity ci = User.Identity as ClaimsIdentity;
    if (ci == null)
        throw new ApplicationException("Identity is not ClaimsIdentity");

    //read claims from security token token
    Claim id = ci.Claims.FirstOrDefault(claim => claim.ClaimType == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
    Claim provider = ci.Claims.FirstOrDefault(claim => claim.ClaimType == "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");

    if (id == null || provider == null)
        throw new ApplicationException("Security token did not contain expected information");

    //check user identity against user store
    UserLineDataServiceContext context = getUserContext();
    context.IgnoreResourceNotFoundException = true;

    var result = from UserLine u in context.Users where (u.RowKey == id.Value) && (u.PartitionKey == provider.Value) select u;
    UserLine user = null;
    //TODO: lookup user - this causes exception for some reason
    user = result.FirstOrDefault();

    //if user is known return view
    if (user != null)
        return Redirect("/Home/Index");
    else
    {
        //if user is not known return register view.
        UserLineModel userModel = new UserLineModel(provider.Value, id.Value);
        //TODO: should move HOme Controller's Register to the account controller?
        return View("Register", userModel);
    }
}

When I ran this with my Google id, however, I got a DataServiceQueryException on the result.FirstOrDefault() call, which took me a while to figure out.

The inner exception’s message was an xml describing an Invalid Input error with the message – “One of the request inputs is not valid”

Turns out that the Google identity is represented as a Uri (absolutely nothing wrong with that, of course), but that key fields in Azure tables do not allow certain characters, see this for more details.

The solution was to base-64-encode both partition and row key fields before performing the lookup, as well as, naturally, before storing them in the registration controller executing after the user had filled in the registration form and hit ok.

On SQL Azure Reporting

I’ve been preparing a demonstration for a customer about SQL Azure Reporting so I’ve been playing around a little bit and I thought I’d share, at high level, what I’ve done (nothing fancy, I’m afraid, but if you’ve never looked at it, this should give you an idea of what’s involved)

To beginning was to get a data source to work on, and at the moment, that means SQL Azure database(s), which – of course – makes perfect sense, and so I promptly created a SQL Azure Database server, and, using the SQL Azure Migration Wizard, I’ve migrated good old Northwind onto it.

image 

Now that I have a data source with some familiar data, it was time to create a report.
Given that I’m by no means a reporting expert and that this isn’t really the point of the demonstration, I did not try to get too creative and created a simple report of customers by country

I started by opening Visual Studio 2008 and creating a new project of type ‘Report Server Project Wizard’

The first step in the wizard was to define a data source, and it’s great that SQL Azure is an entry in the list of possible types; all that’s needed is to provide the connection string and the UI helps make that easy too

image

It was simply a case of typing in my database server name and credentials and provide the database name. The only other thing I needed to do is set the TrustServercertificate to True under the properties accessed through the Advanced button.

I then used the Query Builder to select the entire Customers Table and carried on with the Wizard specifying Tabular Format, Group By Country and the details fields (you can see I’ve been very creative)

Then, at the last page of the wizard, it was time to specify the deployment location I replaced the default value of http://localhost/ReportServer with the address of my Azure-based SQL Reporting ‘Server’, which I copied from the management portal

image

This, of course, is not necessary at this stage, it is perfectly fine to start working against a local reporting server and deploying the report later either through the management portal or by changing the server property in the report project’s properties and deploying from Visual Studio.

With the wizard complete I could now run my report from Visual Studio and see the results and the only thing I noticed is that I had to provide the credentials to the data source every time I ran the report.

This might be desirable in some cases, but I wanted a more streamlined experience, and so I set the credentials to the database in the data source. the report file itself will be protected through the management portal and the login to that, so these don’t get compromised.

With the data source credentials sorted I now deploy the project straight from visual studio and after minute or so it is visible in the management console. Clicking on the report renders it successfully –

image

So – at this point the report is fully operatoinal, and can be accessed via a publicly available url. access is governed by username/password pairs setup through the admin console and permissions set on the report itself (or a folder0, and that’s probably good enough for many scenarios for departmental reports inside the organisation.

For more public reports, ones available for external parties for example, I think that re-hosting the report in a web role and leveraging the ACS for access control would be a lot more flexible and manageable, and so I moved on to do this as well –

Embedding the report simply meant, in my little example anyway, using the ReportViewer control on an ASP.net page; I’ve configured the ServerReport property of the viewer with the relevant Uri’s and made sure to set the control ProcessingMode property to ‘Remote”.

I then used code to assign the fixed credentials to reporting services. once again – my application is going to be protected by ACS and this code is server side code, so I am comfortable with embedding these in the code (should be configuration, of course…)

At this point I could run my little ASP.net application locally and that would succesfully access the report in reporting services and display it on screen –

image

The last step, then, was to add support for STS.

I’ve made all the necessary configuration in the management portal, and copied the ws-federation metadata url, and then used the add STS reference wizard to make the necessary configuration changes to my application –

image

The result of the wizard was a set of entries added to my web.config, to which I added, under <system.web> the following –

<httpRuntime requestValidationMode=”2.0″/>
<pages validateRequest=”false”/>
<authorization>
  <deny users=”?” />
</authorization>

Running the application now automatically redirects me to the ACS, and – as I have configured to possible identity providers (Windows Live Id and Google) I am presented with a selection screen –

image

Choosing the provider I want I am redirected to the login screen, hosted by the identity provider, and from there back to my application. the second time I will access my reporting application these redirects will happen, but local cookies in all parties will remember me and I won’t need to sign in again, until I sign out or the cookies expire.

The only thing to note is that the ACS configuration includes the url to the application, so once tested locally this needs to change to include the url on Windows Azure but once done, and deployed to Windows Azure, I can now browse to my reporting application, login using, for example, my Windows Live ID, and view a report on SQL Azure Reporting.

Of Claims and Public Identities

I remember sitting in a session delivered a few years ago by Kim Cameron, in which I heard for the first time about ‘the laws of identity’, and I was hooked immediately. I find the topic of identity very interesting and important, and too often looked over.

The cloud often bring this into the conversation and often I find people surprised how comprehensive the Microsoft story around identity is and how powerful the ACS is. I’ve discussed an aspect of this – the ability to federate with the ‘big boys’ – Windows Live ID, Yahoo, Google and Facebook in my previous post.

In his talk, Kim listed 7 ‘laws’ –

  1. User Control and Consent
  2. Minimal Disclosure for Constrained Use
  3. Justifiable Parties
  4. Directed Identity
  5. Pluralism of Operators and Technologies
  6. Human Integration
  7. Consistent Experience Across Contexts

You can read about them here.

In my post I mentioned that the application developer could leverage the ACS to authenticate the user without having to write authentication code, worry about storing username and password, implementing password reset, etc., but suggested the developer would still need to implement a registration screen to get any information that is required about the user and manage a local profile.

This is due to the second law – an identity provider should, in most cases, and certainly in the case of a provider outside the organisation, only disclose the user’s identity, and nothing else. all that’s needed is for someone to say – ‘yep – this user is X as far as I can tell’,

To understand why this is important, consider the difference between these two examples –

When using Windows Live ID as the identity provider, the calling web site (the Relying Party, or RP), receives back a token with two claims – the identity provider (“uri:WindowsLiveID”) and the name identifier (in the Windows Live ID case – a GUID). no personal details are shared, nothing that can be used to hack the account.

The web site is then expected to have details stored against those two facts, which the user had given directly to the web site, as it should be according to the 1st law, and if the web site does not have any such details, it should ask the user directly, and the user – now knowing exactly who is getting these details, can make a conscious call as to whether to share them or not.

In contrast, consider Google’s identity provider – this returns four claims – the identity provider (‘Google’) and the name identifier (a link with a unique identifier in it), similar to Windows Live ID, but also the user’s friendly name (‘Yossi Dahan’ in my case) and email address (‘my gmail address used to sign-in to Google). these are two pieces of information I did not necessarily wish to share with the web site, but they were shared without me realising it (until I debugged the code, that is).

Now – it is true that as a user I needed to first proactively go to the website, and then when got redirected to the Google sign-in page I had to agree for details to be shared, so it’s not exactly horrible, but it does demonstrate the point that identity providers should really provide very little detail and that web site developers, whilst they don’t have to worry about authentication, should really manage user profiles independently and handle user registration against those identities.

%d bloggers like this: