First, make sure the integration is in fact set up correctly. To use Okta as an IdP for inbound federation to AAD for guest users, you'll want to create a new custom app in Okta:
Create a web platform app using the SAML 2.0 signin method with these settings:
- Single sign on URL:
https://login.microsoftonline.com/login.srf
- Use this for recipient URL and destination URL: yes
- Audience URI (SP Entity ID):
https://login.microsoftonline.com/{your AAD tenant id}/
- Default RelayState: leave this blank, but it might be possible to put something here to create a smart link maybe
- Name ID format: unspecified
- Application username: Email (or okta username if your okta usernames are emails)
If you stopped here, your users wouldn't be able to sign in (they will get some generic access denied depending on how your application is deployed), and buried in the HTTP transcript of the sign-in attempt will be this:
AADSTS5000819: SAML Assertion is invalid. Email address claim is missing or does not match domain from an external realm.
To resolve this issue, you must add an attribute statement:
- Name:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
- Name format:
Unspecified
- Value:
user.email
The documentation implies you also have to add this other attribute to tell it what to use for email addresses, but it doesn't appear to be actually needed:
- Name:
emailaddress
- Name format:
Unspecified
- Value:
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
Click on "show advanced settings" and make it look like this (note to the future, check that RSA and SHA256 are still safe, and update to something better if it becomes available):
Email address is somewhat special here and may not necessarily be an actual email. For best results, I'd suggest using an alias domain, especially if you already use AAD, because of what follows.
Save your app and grab the metadata file:
On the AAD side, you should set up your external IdP in External Identities (https://portal.azure.com/#blade/Microsoft_AAD_IAM/CompanyRelationshipsMenuBlade/IdentityProviders). In this part, you register an email address domain with the Microsoft sign-in portal and make it so that attempts to sign in with email addresses in that domain send users through Okta.
There are many pitfalls. It has to be a domain nobody is currently using for AAD, and has to match several rules (https://docs.microsoft.com/en-us/azure/active-directory/external-identities/direct-federation) - pertinently, either you need the domain name to match the domain name part of the passive authentication endpoint, or you need to set up a TXT record in the domain like DirectFedAuthUrl=https://fabrikamconglomerate.com/adfs
(where the URL is the same as the one you have configured in "passive authentication endpoint").
If you do have a verified domain, you can still use it for federation without removing it, but you can't have the same domain be both managed (AAD is the system of record) and federated (Okta is the system of record) because the users would conflict with each other. You have to use powershell to set it up. See below.
Create a SAML identity provider with "Domain name of federating IdP" set to the domain name you'd like users to use to log in.
Load the metadata from your file. The issuer URI should look like "http://www.okta.com/exkoMK3x9R3njKrTag24".
Set the metadata endpoint URL, or your integration will mysteriously stop working in about 10 years when the certificate expires. The URL is the same one you clicked the link to get the metadata file from earlier.
Your users access your application (directly, through the App Service's URL or whatever, not through Okta's passive authentication endpoint) and it redirects them to the Microsoft account login page. They enter their email, with the domain name being the one you set in "Domain name of federating IdP". They get sent through Okta, sign in there, and get redirected back.
Probably, they will get "AADSTS50020: User account ... from identity provider ... does not exist in tenant ... and cannot access the application ... in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account". This is happening because you need to provision them somehow, as AAD does not infer their existence from the initial inbound SAML attesting their identity.
Irritatingly, you cannot enable user flows for self-service sign up with a SAML IdP (they only allow it for Microsoft Account and other AAD instances right now). So currently, for each user, you have to invite them as a guest user in your AAD instance (https://portal.azure.com/#blade/Microsoft_AAD_IAM/UsersManagementMenuBlade/MsGraphUsers and select "New guest user"). When you invite them, you can provision them whatever app access and AAD groups they will need to use things in your environment. Use the email address they will type at the Microsoft login prompt.
The first time they sign in, they will be sent through the consent flow. Beware; if you've enabled "security defaults" in AAD, they'll also be asked to set up Microsoft Authenticator even if they are already authenticated with MFA in Okta.
And that's it! Now your users can be sent through Okta instead of having an additional password for your AAD environment.
If you have a verified domain and you don't have any users that need to use it to log in using AAD as the IdP, you can use it for federation, and it's a little easier - you don't have to invite guest users and get them to accept the invitation. But you have to use powershell. I think you might still need the deprecated MSOnline module for this, so you have to do it in Powershell 5 (not newer afaik). If you don't have it:
install-module -name MSOnline -Scope CurrentUser
Log in:
Connect-MsolService
Set $cert
to the certificate in Base64 without spaces or newlines:
$cert = "MIIDq..........."
Then set up federation:
Set-MsolDomainAuthentication
-DomainName example.net \
-FederationBrandName "Example.net Okta Users"
-Authentication Federated
-PreferredAuthenticationProtocol Samlp
-SigningCertificate $cert
-MetadataExchangeUri "https://okta.example.net/app/xxxxxxxxxxxxxxxxxxxx/sso/saml/metadata"
-PassiveLogOnUri "https://okta.example.net/app/dev-99999999_applicationname/xxxxxxxxxxxxxxxxxxxx/sso/saml"
-IssuerUri "http://www.okta.com/xxxxxxxxxxxxxxxxxxxx"
-LogOffUri "https://okta.example.net/login/signout"
The URIs in the command come from the same place you'd get them for the external identities method above. You do need to specify the logout URI; I recommend putting the /login/signout URL for Okta or anything you want really.
If you get some error Set-MsolDomainAuthentication : Unable to complete this action. Try again later.
, what actually happened is it rejected your parameters. Often it's because the certificate is invalid (you deleted some characters while removing all the newlines maybe, or didn't remove spaces, or included the ----- headers). Or, it's because a necessary parameter like LogOffUri wasn't specified or your MetadataExchangeUri isn't working.
The cmdlets Get-MsolDomain
and Get-MsolDomainFederationSettings
are useful for checking your work. In the former, you should see your domain name's authentication changed from "Managed" to "Federated". Users who try to log into it at the login.microsoftonline.com screen will now be sent to the PassiveLogOnUri with a SAMLRequest to get a SAML assertion.
You have to add users still. It won't infer their existence based on a valid SAML assertion.
New-MsolUser -UserPrincipalName someone@example.net -ImmutableId someone@example.net -DisplayName "Test User" -UsageLocation US
The ImmutableId parameter is the thing that's in the immutable ID from SAML and doesn't have to be the UPN. This can be handy if you want to use something other than a valid email address as the immutable ID from the Okta side, and have it map to a valid UPN in AAD.
If you can verify your domain, this may be the better option, as then your SP-initiated sign-on will work without using a tenanted login URL in cases where it would otherwise be required (for B2B). But with this option the users are in AAD as normal users, not B2B guest users. If you use this, the AADSTS50107: Requested federation realm object does not exist, when integrating Okta as an IdP for AAD
error shouldn't appear. Why? If you're doing B2B, it only looks for the issuer ("federation realm object") in the tenant it thinks the user's logging into. If you use a verified domain, you're the only user of that domain in AAD, so there can be no ambiguity and it can just look up the appropriate AAD tenant to use.
Unfortunately, this method doesn't let you do an IdP-initiated signon either. It has a different failure mode: it discards your RelayState parameter and just sends the user to the Office 365 portal at https://www.office.com/?auth=2&home=1
. I have a ticket open with Microsoft about this on the off chance there's an undocumented secret incantation to make it send you to the right place.