In the previous installments of this series we looked at how Windows Azure Pack authenticates users and how it’s configured out of the box for federation. This time around we’re going to look at how you can configure federation with a third party IdP.
Microsoft designed Windows Azure Pack the right way. It supports federation with industry protocols out of the box. You can’t say that for many services, and you certainly can’t say that those services support it natively for all versions – more often than not you have to pay extra for it.
Windows Azure Pack supports federation, and actually uses it to authenticate users by default. This little fact makes it easy to federate to a 3rd party IdP.
If we searched around we will find lots of resources on federating to ADFS, as that’s Microsoft’s federation product, and there are a number of good (German content) walkthroughs on how you can get it working. If you want to use ADFS go read one or all of those articles as everything we talk about today will be about using a non-Microsoft federation service.
Before we begin though I’d like to point out that Microsoft does have some resources on using 3rd party IdPs, but unfortunately the information is a bit thin in some places.
Federation is a complex beast and we should be clear about what is required to get it working. In no particular order you need the following:
- STS that supports the WS-Federation (passive) protocol
- STS that supports WS-Federation wrapped JSON Web Tokens (JWT)
- Optional: STS that supports WS-Trust + JWT
If you plan to use the public APIs with federated accounts then you will need a STS that supports WS-Trust + JWT.
If you don’t have a STS that can support these requirements then you should really consider taking a look at ADFS, or if you’re looking for customization, Thinktecture Identity Server. Both are top notch IdPs (edit: insert pitch about the IdP my company builds and sells as well [edit-edit: our next version natively supports JWT] -- sorry, this concludes the not-so-regularly-scheduled product placement).
Another option is to roll your own IdP. Don’t do this. No seriously, don’t. It’s a complicated mess. You’re way better off using the Thinktecture server and extending it to fit your needs.
Supposing though that you already have an IdP and want to support JWT though, here’s how we can do it. In this context the IdP is the overarching identity providing system and the STS is simply the service issuing tokens.
Skip this next section if you just want to see how to configure Windows Azure Pack. That’s the main part that’s lacking in the MSDN documentation.
JWT via IdentityModel
First off, you need to be using .NET 4.5, and you need to be using the the 4.5 IdentityModel stack. You can’t use the original 3.5 bits.
At this point I’m going to assume you’ve got a working IdP already. There are lots of articles out there explaining how to build one. We’re just going to mod the STS.
Before making any code changes though you need to add the JWT token handler, which is easily installed via Nuget (I Nuget):
PM> Install-Package System.IdentityModel.Tokens.Jwt
This will need to be added to the project that exposes your STS configuration class.
Next, we need to inject the token handler into the STS pipeline. This can easily be done by adding an entry to the web.config system.identityModel section:
Or if you want to hardcode it you can add it to your SecurityTokenServiceConfiguration class.
There are of course other (potentially better) ways you can add it in, but this serves our purpose for the sake of a sample.
By adding the JWT token handler into the STS pipeline we can begin issuing JWTs to any relying parties that request one. This poses a problem though because passive requests don’t have a requested token type tacked on. Active (WS-Trust) requests do, but not passive. So we need to specify that a JWT should be minted instead of a SAML token. This can be done in the GetScope method of the STS class.
All we really needed to do was specify the TokenType as WIF will use that to determine which token handler should be used to mint the token. We know this is the value to use because it’s exposed by the GetTokenTypeIdentifiers() method in the JWTSecurityTokenHandler class.
Did I mention the JWT library is open source?
So now at this point if we made a request for token to the STS we could receive a WS-Federation wrapped JWT.
If the idea of using a JWT instead of a SAML token appeals to you, you can configure your app to use the JWT token handler similar to Dominick’s sample.
If you were submitting a WS-Trust RST to the STS you could use client code along the lines of:
When the GetScope method is called the request.TokenType should be set to whatever you passed in at the client. For more information on service calls you can take a look at the whitepaper Claims-Based Identity in Windows Azure Pack (docx). A future installment of this series might have more information about using services.
Lastly, we need to sign the JWT. The only caveat to using the JWT token handler is that the minimum RSA key size is 2048 bits. If you’re using a key smaller than that then please upgrade it. We’re going to overlook the fact that the MSDN article shows how to bypass minimum key sizes. Seriously. Don’t do it. I don’t want to have to explain why (putting paranoia aside for a moment, 1024 is being deprecated by Windows and related services in the near future anyway).
Issuing Tokens to Windows Azure Pack
So now we’re at a point where we can mint a JWT token. The question we need to ask now is what claims should this token contain? Looking at Part 1 we see that the Admin Portal requires UPN and Group claims. The tenant portal only requires the UPN claim.
Lucky for us the JWT token handler is smart. It knows to transform certain known XML-token-friendly-claim-types to JWT friendly claim types. In our case we can use http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn in our ClaimsIdentity to map to the UPN claim, and http://schemas.xmlsoap.org/claims/Group to map to our Group claim.
Then we need to determine where to send the token, and who to address it to. Both the tenant and admin sites have Federation Metadata documents that specify this information for us. If you’ve got an IdP that can parse the metadata then all you need to do is point it to https://yourtenantsite/FederationMetadata/2007-06/FederationMetadata.xml for the tenant configuration or https://youradminsite/FederationMetadata/2007-06/FederationMetadata.xml for the admin configuration.
Of course, this information will also map up to the configuration elements we looked at in Part 2. That’ll tell us the Audience URI and the Reply To for both sites.
Finally we have everything we need to mint the token, address it, and send it on its way.
Configuring Windows Azure Pack to Trust your Token
The tokens been sent and once it hits either the tenant or admin site it’ll promptly be ignored and you’ll get an ugly error message saying “nope, not gonna happen, bub.”
We therefore need to configure Windows Azure Pack to trust our token. Looking at MSDN we see some somewhat useful information telling us what we need to modify, but frankly, its missing a bunch of information so we’re going to ignore it.
First things first: if your IdP publishes a Federation Metadata document then you can just configure everything via PowerShell:
You can replace the target “Admin” with “Tenant” if you want to configure the Tenant Portal. The only caveat with doing it this way is that the metadata document needs to be accessible from the server. I’ve submitted a feature request that they also support local file paths too; hopefully they listen! Since the parameter takes the full URL you can put the metadata document somewhere public if its not normally accessible. You will only need the metadata accessible while applying this configuration.
If the cmdlet completed successfully then you should be able to log in from your own IdP. That’s all there is to it for you. I would recommend seriously considering going this route instead of configuring things manually.
Otherwise, lets carry on.
Since we can’t import our federation metadata (since we probably don’t have any), we need to configure things manually. To do that we need to modify settings in the database.
Looking back to Part 2 we see all the configuration elements that enable our federated trust to the default IdPs. We’ll need to update a few settings across the Microsoft.MgmtSvc.Store and Microsoft.MgmtSvc.PortalConfigStore databases.
As per the MSDN documentation it says to modify the settings in the PortalConfigStore database.
It’s wrong. It’s incomplete as that’s only part of the process.
The PortalConfigStore database contains the settings used by the Tenant and Admin Portals to validate and request tokens. We need to modify these settings to use our custom IdP. To do so locate the Authentication.IdentityProvider setting in the [Config].[Settings] table. The namespace we need to choose is dependent on which site we want to configure. In our case we select the Admin namespace. As we saw last time it looks something like:
We need to substitute our STS information here. The Realm is whatever your STS issuer is, and the Endpoint is where ever your WS-Federation endpoint is located. The Certificate should be a base 64 encoded representation of your signing certificate (remember, just the public key).
In my experience I’ve had to do an IISRESET on the portals to get the settings refreshed. I might just be impatient though.
Once those values are replaced you can try logging in. You should be redirected to your IdP and if you issue the token properly it’ll hit the portal and you should be logged in. Unfortunately this’ll actually fail with a non-useful error message.
Who can guess why? So far I’ve stated that the MSDN documentation is missing information. What have we missed? Hopefully if you’ve read the first two parts of this series you’re yelling at the screen telling me to get on with it already because you’ve caught on to what I’m saying.
We haven’t configured the API services to trust our STS! Oops.
With that being said, we now have proof that Windows Azure Pack flows the token to the services from the Portal and, more importantly, the services validate the token. Cool!
Anyway, now to configure the APIs. Warning: complicated.
In the Microsoft.MgmtSvc.Store database locate the Settings table and then locate the Authentication.IdentityProvider.Secondary element in the AdminAPI namespace. We need to update it with the exact same values as we put in to the configuration element in the other database.
If you’re only wanting to configure the Tenant Portal you’d want to modify the Authentication.IdentityProvider.Primary configuration element. Be careful with the Primary/Secondary elements as they can get confusing.
If you’re configuring the Admin Portal you’ll need to update the Authentication.IdentityProvider.Secondary configuration element in the TenantAPI namespace to use the configuration you specified for the Admin Portal as well. As I said previously, I think this is because the Admin Portal calls into the Tenant API. The Admin Portal will use an admin-trusted token – therefore the TenantAPI needs to trust the admin’s STS.
Now that you’ve completed configuration you can do an IISRESET and try logging in. If you configured everything properly you should now be able to log in from your own IdP.
For those rock star Ops people who understand identity this guide was likely pretty easy to follow, understand, and implement. For everyone else though, this was probably a pain in the neck. Here are some troubleshooting tips.
Review the Event Logs
It’s surprising how many people forget that a lot of applications will write errors to the Windows Event Log. Windows Azure Pack has quite a number of logs that you can review for more information. If you’re trying to track down an issue in the portals look in the MgmtSvc-*Site where * is Tenant or Admin. Errors will get logged there. If you’re stuck mucking about the APIs look in the MgmtSvc-*API where * is Tenant, Admin, or TenantPublic.
Enable Development Mode
You can enable developer mode in sites by modifying a value in the web.config. Unprotect the web.config by calling:
And then locate the appSetting named Microsoft.Azure.Portal.Configuration.PortalConfiguration.DevelopmentMode and set the value to true. Be sure to undo and re-protect the configuration when you’re done. You should then get a neat error tracing window show up in the portals, and more diagnostic information will be logged to the event logs. Probably not wise to do this in a production environment.
Use the PowerShell CmdLets
There are a quite a number of PowerShell cmdlets available for you to learn about the configuration of Windows Azure Pack. If you open the Windows Azure Pack Administration PowerShell console you can see that there are two modules that get loaded that are full of cmdlets:
PS C:\Windows\system32> get-command -Module MgmtSvcConfig
CommandType Name ModuleName
----------- ---- ----------
Cmdlet Add-MgmtSvcAdminUser MgmtSvcConfig
Cmdlet Add-MgmtSvcDatabaseUser MgmtSvcConfig
Cmdlet Add-MgmtSvcResourceProviderConfiguration MgmtSvcConfig
Cmdlet Get-MgmtSvcAdminUser MgmtSvcConfig
Cmdlet Get-MgmtSvcDatabaseSetting MgmtSvcConfig
Cmdlet Get-MgmtSvcDefaultDatabaseName MgmtSvcConfig
Cmdlet Get-MgmtSvcEndpoint MgmtSvcConfig
Cmdlet Get-MgmtSvcFeature MgmtSvcConfig
Cmdlet Get-MgmtSvcFqdn MgmtSvcConfig
Cmdlet Get-MgmtSvcNamespace MgmtSvcConfig
Cmdlet Get-MgmtSvcNotificationSubscriber MgmtSvcConfig
Cmdlet Get-MgmtSvcResourceProviderConfiguration MgmtSvcConfig
Cmdlet Get-MgmtSvcSchema MgmtSvcConfig
Cmdlet Get-MgmtSvcSetting MgmtSvcConfig
Cmdlet Initialize-MgmtSvcFeature MgmtSvcConfig
Cmdlet Initialize-MgmtSvcProduct MgmtSvcConfig
Cmdlet Install-MgmtSvcDatabase MgmtSvcConfig
Cmdlet New-MgmtSvcMachineKey MgmtSvcConfig
Cmdlet New-MgmtSvcPassword MgmtSvcConfig
Cmdlet New-MgmtSvcResourceProviderConfiguration MgmtSvcConfig
Cmdlet New-MgmtSvcSelfSignedCertificate MgmtSvcConfig
Cmdlet Protect-MgmtSvcConfiguration MgmtSvcConfig
Cmdlet Remove-MgmtSvcAdminUser MgmtSvcConfig
Cmdlet Remove-MgmtSvcDatabaseUser MgmtSvcConfig
Cmdlet Remove-MgmtSvcNotificationSubscriber MgmtSvcConfig
Cmdlet Remove-MgmtSvcResourceProviderConfiguration MgmtSvcConfig
Cmdlet Reset-MgmtSvcPassphrase MgmtSvcConfig
Cmdlet Set-MgmtSvcCeip MgmtSvcConfig
Cmdlet Set-MgmtSvcDatabaseSetting MgmtSvcConfig
Cmdlet Set-MgmtSvcDatabaseUser MgmtSvcConfig
Cmdlet Set-MgmtSvcFqdn MgmtSvcConfig
Cmdlet Set-MgmtSvcIdentityProviderSettings MgmtSvcConfig
Cmdlet Set-MgmtSvcNotificationSubscriber MgmtSvcConfig
Cmdlet Set-MgmtSvcPassphrase MgmtSvcConfig
Cmdlet Set-MgmtSvcRelyingPartySettings MgmtSvcConfig
Cmdlet Set-MgmtSvcSetting MgmtSvcConfig
Cmdlet Test-MgmtSvcDatabase MgmtSvcConfig
Cmdlet Test-MgmtSvcPassphrase MgmtSvcConfig
Cmdlet Test-MgmtSvcProtectedConfiguration MgmtSvcConfig
Cmdlet Uninstall-MgmtSvcDatabase MgmtSvcConfig
Cmdlet Unprotect-MgmtSvcConfiguration MgmtSvcConfig
Cmdlet Update-MgmtSvcV1Data MgmtSvcConfig
As well as the MgmtSvcConfig module which is moreso for daily administration.
Read the Windows Azure Pack Claims Whitepaper
See here: Claims-Based Identity in Windows Azure Pack (docx).
Visit the Forums
When in doubt take a look at the forums and ask a question if you’re stuck.
Lastly, you can contact me (email@example.com) with any questions. I may not have answers but I might be able to find someone who can help.
In the first two parts of this series we looked at how authentication works, how it’s configured, and now in this installment we looked at how we can configure a third party IdP to log in to Windows Azure Pack. If you’re trying to configure Windows Azure Pack to use a custom IdP I imagine this part is the most complicated to figure out and hopefully it was documented well enough. I personally spent a fair amount of time fiddling with settings and most of the information I’ve gathered for this series has been the result of lots of trial and error. With any luck this series has proven useful to you and you have more luck with the configuration than I originally did.
Next time we’ll take a look at how we can consume the public APIs using a third party IdP for authentication.
In the future we might take a look at how we can authenticate requests to a service called from a Windows Azure Pack add-on, and how we can call into Windows Azure Pack APIs from an add-on.