I’ve received several enquiries lately about performing single sign-on (SSO) from on-premise environments, typically intranets or extranets, to Office 365 software-as-a-service (SaaS) subscriptions. Microsoft’s documentation, specifically for directory integration with Office 365 is targeted at customers that utilize Active Directory (AD) in-house and guidance is provided for provisioning users to Office 365 from AD using a directory synchronization tool, and performing SSO using Active Directory Federation Services (ADFS). In this article I will explain how to realize provisioning of users without needing to have them stored in AD, and how to perform SSO to Office 365 using IBM’s Federated Identity Manager (FIM) instead of ADFS. The applicability of these guidelines may change over time as either IBM or Microsoft update management interfaces however the steps were working in August 2012 when I prepared this article. Note also that IBM’s federation support extends only to the WS-Federation protocol and other published FIM product capabilities. IBM does not control the interfaces to Office 365 or how they may evolve over time. The techniques described in this article are not part of Microsoft Office 365’s official documentation, and this technique has not yet been endorsed by Microsoft as part of their “Works with Office 365 program” although it largely makes use of Microsoft Power Shell tools for configuration. That said, I am persuing that with the Microsoft Office 365 team and will publish any progress.
I would like to acknowledge that several colleagues assisted in developing this material, and specifically recognize Budi Mulyono for his work on this topic.
Let’s start with a diagram depicting the desired runtime state:
Technical Summary
At a high level, these are the technical design and implementation steps that I needed to perform:
- Establish an Office 365 subscription
- Install the Windows Powershell and Microsoft Online Services Module
- Decide on a UPN naming convention
- Decide on a UUID/ImmutableID strategy
- Configure a FIM federation for SSO to Office 365
- Register a domain with Office 365 and register federation endpoints
- Provision users to Office 365
- Test Single Sign-on
Configuration and design details
Establish an Office 365 subscription
Somebody else in my team got me an Office 365 subscription. By the time I got engaged, I had login credentials. Logging in at https://portal.microsoftonline.com I have access to a portal with administrative functions.
Install the Windows PowerShell and Microsoft Online Services Module
I installed the PowerShell on my Windows 7 desktop however Windows Server 2008 R2 is also supported. The Microsoft documentation to do this is entitled: Use Windows PowerShell to manage Office 365.
Two components need to be installed, first the Microsoft Online Services Sign-In Assistant, and then the Microsoft Online Services Module for Windows PowerShell. Both 32 and 64 bit versions are available.
You can then start the Microsoft Online Services Module for Windows Powershell which we will use later to provision users to Office 365.
Documentation for the various cmdlets we will use is available in Microsoft’s Windows PowerShell cmdlets for Office 365.
Common to the use of all cmdlets you must first connect to your domain with the cmdlet Connect-MsolService. This will popup a dialog box for login using the same administrator username and password for your Office 365 subscription:
You should do this now to ensure you have valid credentials.
Decide on a UPN naming convention
Office 365 users are identified by two pieces of information – a UPN (of the format username@domainname) and an ImmutableID (a never-recycled UUID for a user account). The UPN will typically be your local account username appended with @domainname for a registered domain you own. If users in your local registry are already represented in this format then there will be no need to map the username during SSO to append the “@domainname”. At this stage just determine how you are going to logically map a local account username to the UPN format. The identity mapping rule in the FIM federation will need to implement your mapping when we configure the federation later.
Decide on a UUID/ImmutableID strategy
As you’ll see later when provisioning users and when performing SSO to Office 365 there are two important pieces of information that need to be shared and consistent between the on-premise user registry and Office 365’s online registry. These are a UPN (format: username@domainname) and ImmutableID.
The UPN is fairly straight forward and has already been covered.
The ImmutableID requires a little more consideration. This should be a non-recycled unique identifier for the account. AD accounts have this concept built in and transparent to user administration. Tivoli Access Manager accounts also have this same concept built in (in TAM it’s called the principal UUID). Accounts in Office 365 require a unique identifier be set during provisioning, and this same unique identifier must be passed as an attribute in the SAML assertion used during WS-Federation SSO at runtime. The question is – what value should we use for the UUID and where is it stored so that later it can be retrieved at runtime?
There are lots of choices, but I’m going to describe two and you can then decide for yourself what makes sense in your environment:
- Using TAM principal UUID for ImmutableID
- Using the FIM alias service to manage ImmutableID
Using TAM principal UUID for ImmutableID
This technique is only applicable if you use Tivoli Access Manager WebSEAL as your point of contact for FIM and standard user authentication from the TAM registry as otherwise your credentials will not have a principal UUID attribute in them at runtime. The idea is that you need to figure out the principal UUID that TAM assigned to a user account, then use that same UUID when provisioning the Office 365 account, and when performing SSO using WS-Federation in FIM. This technique is attractive because when doing SSO at runtime the UUID will already be in the TAM credential and it’s trivial to write a FIM mapping rule to retrieve it and insert into the SAML assertion. One drawback to this technique is that you don’t really have any way to tell at runtime if a user has actually been provisionined to Office 365 or not before attempting SSO. The presumption is that provisioning is done before a user attempts SSO, otherwise SSO will fail at Office 365 (and the error doesn’t really say “user not provisioned” or anything useful like that). For a TAM domain “default” and a user “shane”, here’s how to find the TAM principal UUID with an ldapsearch:
/opt/IBM/ldap/V6.1/bin/idsldapsearch -L -h localhost -p 389 -D cn=root -w <your_ldap_pwd>
-b "cn=users,secauthority=default" -s one "(&(objectclass=secUser)(principalName=shane))" secUUID
dn: principalName=shane,cn=Users,secAuthority=Default
secUUID: caf8b386-bf20-11df-8429-0050569b79f5
If you are familiar with the format of a TAM credential (e.g. read my Practial TAM Authorization API article) the secUUID will be the value of the AZN_CRED_PRINCIPAL_UUID attribute. We will use this later to both provision the Office 365 account and in a FIM mapping rule for WS-Federation SSO. For now you need to find the principal UUID for each of the users you intend to provision into Office 365.
Using the FIM alias service to manage ImmutableID
Note: Using this technique first requires you to have configured the alias service for FIM. Please see FIM documentation for how to do this for either the LDAP or JDBC alias service implementations.
If you are not using TAM as your point of contact, or you don’t want to use the TAM principal UUID as the Office 365 ImmutableID then you need to store and manage a different UUID for this purpose. FIM provides a generic alias service for just this kind of situation. It is most commonly leveraged in SAML 2.0 federations with persistent name identifiers, however it is equally applicable in account linking scenarios such as this one. I’ve demonstrated it’s use in previous account linking and self-registration articles.
The supported programmatic interface to the FIM alias service is via the API’s made available in the IDMappingExtUtils class that can be called from Javascript and Java mapping rules in the FIM Security Token Service (STS). That means that to populate the alias service you will need to create an STS trust chain for that purpose and call those API’s. Here’s how to do that.
- First create an STS chain with the following structure:
- STSUU (validate)
- Default Map (map)
- STSUU (issue)
-
The chain selection criteria can be set to anything that is going to match the RST request message that we’ll send to the STS. I suggest using the default “Validate” request type (it will match my example RST below), and set:
AppliesTo Address |
http://appliesto/alias |
Issuer Address |
http://issuer/alias |
This screenshot shows the chain configuration from my test environment:
- For the default map module, use the following javascript mapping rule:
// BEGIN Javascript mapping rule
// mapping rule for performing simple store and fetch alias operations
importPackage(Packages.com.tivoli.am.fim.trustserver.sts);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.utilities);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.uuser);
// Figure out the "operation" being performed
var operation = stsuu.getAttributeValueByName("operation");
// store operation
if (operation != null && operation == "store") {
// get username, federation ID, and alias value to store
var username = stsuu.getPrincipalName();
var federationid = stsuu.getAttributeValueByName("federationID");
var alias = stsuu.getAttributeValueByName("alias");
if (username != null && federationid != null && alias != null) {
// store it, first removing any existing values
var existingAliasValues =
IDMappingExtUtils.lookupAliasesForUserAsStringArray(
federationid, username);
if (existingAliasValues != null) {
for (var i = 0; i < existingAliasValues.length; i++) {
IDMappingExtUtils.removeAliasForUser(
federationid, username, existingAliasValues[i]);
}
}
IDMappingExtUtils.addAliasForUser(federationid, username, alias);
}
}
// fetch operation
if (operation != null && operation == "fetch") {
// get username, federation ID
var username = stsuu.getPrincipalName();
var federationid = stsuu.getAttributeValueByName("federationID");
if (username != null && federationid != null) {
// fetch alias value(s) and put in STSUU
var existingAliasValues =
IDMappingExtUtils.lookupAliasesForUserAsStringArray(
federationid, username);
var attr = new Attribute("alisValues", "", existingAliasValues);
stsuu.addAttribute(attr);
}
}
// delete operation
if (operation != null && operation == "delete") {
// get username, federation ID
var username = stsuu.getPrincipalName();
var federationid = stsuu.getAttributeValueByName("federationID");
if (username != null && federationid != null) {
// remove any existing values
var existingAliasValues =
IDMappingExtUtils.lookupAliasesForUserAsStringArray(
federationid, username);
if (existingAliasValues != null) {
for (var i = 0; i < existingAliasValues.length; i++) {
IDMappingExtUtils.removeAliasForUser(
federationid, username, existingAliasValues[i]);
}
}
}
}
// END Javascript mapping rule
- To provision an alias for a user, use the techique described in my Using CURL to send requests to the FIM Security Token Service article, with an RST such as this. Note that you should substitute in your own values for the username and alias(ImmutableID) attributes for each user you wish to provision. The federationID can be any value however it is important that you use the same value consistently for both this step, and for the mapping rule used later for the federation. Here is an example RST to start with:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wst:RequestType xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">http://schemas.xmlsoap.org/ws/2005/02/trust/Validate</wst:RequestType>
<wst:Issuer xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsa:Address xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://issuer/alias</wsa:Address>
</wst:Issuer>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
<wsa:Address>http://appliesto/alias</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:Base xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
<stsuuser:STSUniversalUser
xmlns:stsuuser="urn:ibm:names:ITFIM:1.0:stsuuser">
<stsuuser:Principal>
<stsuuser:Attribute name="name"
type="urn:ibm:names:ITFIM:5.1:accessmanager">
<stsuuser:Value>shane</stsuuser:Value>
</stsuuser:Attribute>
</stsuuser:Principal>
<stsuuser:AttributeList>
<stsuuser:Attribute name="operation">
<stsuuser:Value>store</stsuuser:Value>
</stsuuser:Attribute>
<stsuuser:Attribute name="federationID">
<stsuuser:Value>urn:federation:MicrosoftOnline</stsuuser:Value>
</stsuuser:Attribute>
<stsuuser:Attribute name="alias">
<stsuuser:Value>myalias</stsuuser:Value>
</stsuuser:Attribute>
</stsuuser:AttributeList>
</stsuuser:STSUniversalUser>
</wst:Base>
</wst:RequestSecurityToken>
</soapenv:Body>
</soapenv:Envelope>
Note that by changing the operation attribute value to fetch and re-sending the RST you can test that the alias service value was stored correctly (look for an aliasValue attribute in the returned STSUU. Similarly the mapping rule also supports the delete operation.
By now you should know which technique you are going to use for ImmutableID’s for the federation, and in the case of the alias service approach you should know how to provision those ID’s to the alias service. You can use any unique case-sensitive value you wish for the alias value that is acceptable as an ImmutableID by Microsoft. Using uuidgen is a fairly good way to get a unique id for this purpose. We now have the information we need to provision users to the Office 365 online service.
Configure a FIM federation for SSO to Office 365
Creating the federation is divided into two parts – creating the federation configuration and adding the partner configuration.
Creating a WS-Federation passive profile identity provider federation
Using the FIM management console, create a WS-Federation passive profile federation using a SAML 1.1 token. Establish your point of contact URL in the normal manner with or without a junction depending on whether or not you are using WebSEAL or WebSphere as your point of contact. In my example federation I used the federation name office365 with a point of contact https://profile.ibmidentitydemo.com/FIM/sps.
The identity mapping rule is particularly important as it is responsible for populating the UPN and ImmutableID attributes. This Javascript mapping rule should serve as a good example, and a flag at the top toggles between both ImmutableID techniques mentioned earlier in this article:
// BEGIN Javascript mapping rule for Office 365 federation
importPackage(Packages.com.tivoli.am.fim.trustserver.sts);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.utilities);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.uuser);
// variable to indicate our Office 365 domain. We will append this to the FIM username to form the UPN
var office365Domain = "@federativo.com";
// flag to indicate whether we are using the TAM principal UUID or FIM alias service to get the ImmutableID
var useTAMAsImmutableID = false;
// variable to indicate alias service federationID if we are using alias service
var federationID = "urn:federation:MicrosoftOnline";
// Get the current principal name and add the Office 365 domain name
var origPrincipalName = stsuu.getPrincipalName();
var prinValues = java.lang.reflect.Array.newInstance(java.lang.String, 1);
prinValues[0] = origPrincipalName + office365Domain;
//
// Note that this mapping rule makes use of the following pattern a couple of times:
// if (origPrincipalName != null) {
// do something
// }
// That statement is in there to trick the Javascript validator into not
// validating the "do something" code as Javascript validation tries
// to execute the rule on an empty STSUU.
//
// Clear the stsuu. We don't need any of the existing data.
stsuu.clear();
// Create the new principal attribute that's
// appropriate for a SAML 1.x Assertion.
var principalAttr = new Attribute(
"name",
"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
prinValues);
stsuu.addPrincipalAttribute(principalAttr);
// Add the AuthenticationMethod for SAML 1.x
var authnMethodAttr = new Attribute(
"AuthenticationMethod",
"urn:oasis:names:tc:SAML:1.0:assertion",
"urn:oasis:names:tc:SAML:1.0:am:password");
stsuu.addAttribute(authnMethodAttr);
// determine the ImmutableID value
var alias = null;
if (useTAMAsImmutableID) {
alias = stsuu.getAttributeValueByName("AZN_CRED_PRINCIPAL_UUID");
} else {
// use alias service for ImmutableID. Alias must be pre-provisioned
if (origPrincipalName != null) {
var existingAliasValues = IDMappingExtUtils.lookupAliasesForUserAsStringArray(
federationID, origPrincipalName);
if (existingAliasValues != null && existingAliasValues.length > 0) {
alias = existingAliasValues[0];
}
}
}
if (alias == null || alias.length() <= 0) {
if (origPrincipalName != null) {
throw new STSModuleException("Alias not configured for user: " + origPrincipalName + " and federation: " + federationID);
}
} else {
// set the UPN attribute
var UPNAttr = new Attribute(
"UPN",
"http://schemas.xmlsoap.org/claims",
prinValues);
stsuu.addAttribute(UPNAttr);
// set the ImmutableID attribute
var immutableIDAttr = new Attribute(
"ImmutableID",
"http://schemas.microsoft.com/LiveID/Federation/2008/05",
alias);
stsuu.addAttribute(immutableIDAttr);
}
// END Javascript mapping rule for Office 365 federation
You can tune the security token NotBefore and NotOnOrAfter timeframe to allow for clock skew. During intial testing it is a good idea to make these larger values to avoid this being the reason an assertion is rejected. In my example configuration I set the values to 600 seconds each allowing a 20-minute skew window. Settings for assertion signatures should be left at their default values. It is important to note your final WS-Federation endpoint as it will be used later when verifying your DNS domain with Office 365. In my federation this was https://profile.ibmidentitydemo.com/FIM/sps/office365/wsf
This screen capture shows my federation properties:
Adding Office 365 as a federation partner
Use the wizard in the FIM console to add a partner to your office365 federation. When entering WS-Federation configuration data, use:
- WS-Federation Realm: urn:federation:MicrosoftOnline
- WS-Federation Endpoint: https://login.microsoftonline.com/login.srf
- Maximum request lifetime: -1
The signing key you use for your federation should be a real public/private key pair that you have either purchased from a commercial certificate authority or generated from your own internal CA. Do not use the testkey as shown in these examples.
This screen capture shows my federation partner properties:
After creating the federation and partner and re-loading configuration, don’t forget to run the tfimcfg utility to update TAM policy if you are using WebSEAL as a point of contact.
This completes the FIM configuration requirements for Office 365 integration.
Register a domain with Office 365 and register federation endpoints
The next important step on the path to SSO is to register and verify your domain with Office 365. This has to be a DNS domain that you own. Domain verification is a multi-step process which requires you to prove that you own the domain. Verification of the domain will also result in establishing the SSO endpoints and signing certificate related to our on-premise FIM federation for users from that domain. Users that will SSO from your intranet environment to Office 365 will be members of this domain. You cannot provision those users using the portal web interface, nor can those users be assigned a password and login via the portal. They will only be able to login via SSO.
First you must already own a registered DNS domain and be able to control it’s MX or TXT records. We will use Windows PowerShell commands to register and verify the domain as federated domains cannot be configured via the management portal.
Start by adding the new domain to your Office 365 subscription. From the Microsoft Online Services Module for Windows PowerShell command prompt, login to your domain with Connect-MsolService if you are not already authenticated. Then add the new domain as a federated domain with the command:
New-MsolDomain -authentication federated -name <yourdomain>
For example:
At this point you need to get a piece of randomized information that Microsoft generates and add it to a TXT record or an MX record at your DNS registrar to prove you own the domain. The cmdlet Get-MsolDomainVerificationDns is used for this purpose. Here is an example of retrieving the TXT record information:
You now need to go to your DNS registrar and add a TXT record for the hostname @.<yourdomain> with the value as shown. In my example this was MS=ms92116141. How you do this will be dependent upon the management interface provided by your domain registrar.
After some period of time to allow for DNS propagation (suggested at least 15 minutes) you can attempt to verify your domain with the cmdlet Confirm-MsolDomain. This command takes a lot of parameters, and here is the example that I used for my federation (all on one line – line breaks are only shown here for formatting and readability purposes):
Confirm-MsolDomain
-DomainName federativo.com
-FederationBrandName "Federativo, Inc"
-IssuerUri https://profile.ibmidentitydemo.com/FIM/sps/office365/wsf
-LogOffUri https://profile.ibmidentitydemo.com/FIM/sps/office365/wsf
-PassiveLogOnUri https://profile.ibmidentitydemo.com/FIM/sps/office365/wsf
-PreferredAuthenticationProtocol WsFed
-SigningCertificate MIICBz........Q==
The IssuerUri, LoggOffUri and PassiveLogOnUri are all the same value and point to the WS-Federation endpoint of the on-premise FIM installation we set up earlier. The SigningCertificate is one big long string containing the PEM-formatted public certificate corresponding to the key that is used to sign SAML assertions in the federation. This should be a real public/private key pair that you have purchased or generated with an internal CA. The command returns silently on success:
At the end of the process you should be able to go back into the managment portal at https://portal.microsoftonline.com, list your domains and see a verified domain in your list, as shown here for our domain federativo.com:
Remember: Users that will SSO from your intranet environment to Office 365 will be members of this domain. You cannot provision those users using the portal web interface, nor can those users be assigned a password and login via the portal. They will only be able to login via SSO.
Provision users to Office 365
Using the Windows PowerShell, first make sure you have logged in using the Connect-MsolService cmdlet.
After successful connection you can determine your license (needed for user provisioning), with the cmdlet Get-MsolAccountSku:
You can now provision users, with the cmdlet New-MsolUser, for example (replace with parameters for your domain, user, UPN and ImmutableID, and make the command all on one line. The line breaks are only shown here for readability):
New-MsolUser -userprincipalname $upn
-immutableID $base64
-lastname $sn
-firstname $gn
-Displayname $displayName
-BlockCredential $false
-LicenseAssignment $license
-usageLocation $usagelocation
e.g.
New-MsolUser -userprincipalname shane@federativo.com
-immutableID myalias
-lastname Weeden
-firstname Shane
-Displayname "Shane Weeden"
-BlockCredential $false
-LicenseAssignment "tfim:ENTERPRISEPACK"
-usageLocation AU
Notice that the user is NOT provisioned with a password. They can only login via federation. You now have a user provisioned to Office 365 with a UPN and ImmutableID set and are ready to test SSO.
Test Single Sign-on
Finally we can test that single sign-on from our on-premise FIM environment works to Office 365. Start by visiting the sign-in portal at https://portal.microsoftonline.com/
You will be redirected to login at login.microsoftonline.com. In the User ID field, enter your provisioned user id (e.g. shane@federativo.com) and press TAB to go to the password field. At this point you should see the screen change to indicate that you do not need to provide a password for this user and instead must login via federation:
Pressing the Sign in at Federativo.com link should direct you to your on-premise FIM federation for authentication:
Completing the login at your FIM server should result in being redirected back to Office 365 where you will now be authenticated:
Congratulations, you have successfully configured SSO from IBM Federated Identity Manager to Office 365.
Conclusion
Use of a standardized protocol such as WS-Federation passive profile makes this single sign-on possible. The FIM WS-Federation integration with Office 365 is a little complicated to establish and requires sophisticated use of a set of command-line tools on Windows, but once configured works seamlessly at runtime. Further automation would be useful for account provisioning and reconciliation and I anticipate refinements in this over time.