This article describes a simple SSO pattern for authenticating and authorizing users from an external AD and to your application without requiring federation.
the Challenge
You need to authenticate external users to use your application, these users belong to an organization using Azure Active Directory with specific login policies (such as password strength and expiry, multi factor authentication, etc). Your requirements (if you choose to accept them) are:
- You are required to provide SSO to these users using their home AD tenant and policies
- The solution does not include SAML based federation between directories (yours and theirs)
- The solution does not require any changes on the external AD tenant (no new AAD applications, client secrets, etc)
the Solution
Using an IDAM/IDaaS platform (such as Okta in this case), along with an AAD application (in your AD tenant in your Azure subscription), you can create a local AD app using this magic property to accomplish all of the above requirements (requiring zero changes on the third-party AD).
This is what it looks like using the az
cli:
the --available-to-other-tenants
property is Microsoft's way of allowing you to implicitly trust other AAD/Office 365 tenants, meaning the authentication request is passed to the target AD tenant from your application.
Here is a context diagram which explains the interactions in the context of a Jamstack application (using a library such as Auth.js).
- Overview
- PlantUML
@startuml
!define C4Puml https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/master
!includeurl C4Puml/C4_Context.puml
!includeurl C4Puml/C4_Component.puml
!includeurl C4Puml/C4_Container.puml
'left to right direction
!define Rel_NoRank(e_from,e_to, e_label=" ") Rel_(e_from,e_to, e_label, "-[norank]->")
!$imgroot = "https://github.com/avensolutions/plantuml-cloud-image-library/raw/main/images"
!unquoted procedure $AzureActiveDirectory($alias, $label, $techn, $descr="", $stereo="Azure Active Directory")
rectangle "==$label\n\n<img:$imgroot/azure/AzureActiveDirectory.png{scale=0.75}>\n//<size:12>[$techn]</size>//" <<$stereo>> as $alias #white
!endprocedure
!unquoted procedure $Okta($alias, $label, $techn, $descr="", $stereo="Okta")
rectangle "==$label\n\n<img:$imgroot/okta/okta.png{scale=1}>\n//<size:12>[$techn]</size>//" <<$stereo>> as $alias #white
!endprocedure
Person(user, User\n<i>UserAgent (Browser) )
Person(admin, Application Admin)
note right
Create users in the Okta org with the same email as the users email address in their AD (external AD)
end note
rectangle "Application Environment" <<boundary>> as app{
$AzureActiveDirectory(localad, Local AD Tenant, Azure Active Directory)
$Okta(okta, Local Okta Org, Okta)
}
$AzureActiveDirectory(otherad, Azure AD Tenant\n<i>(External AD), Azure Active Directory)
Lay_D(user, okta)
Lay_R(okta, localad)
Lay_R(localad, otherad)
Lay_D(okta, admin)
Rel_U(okta, user, access code)
Rel_D(user, okta, authorize request)
Rel_R(okta, localad, routes to)
Rel_R(localad, otherad, forwards to)
Rel_U(admin, okta, creates users)
@enduml
Setup and Configuration
The following flowchart explains the steps involved in setting this up. The highlighted nodes are part of normal application lifecycle operations as users get created and deactivated.
Authorisation flow
The authorization flow for a public client (SPA) using PKCE (Proof Key for Code Exchange) is shown here:
Next up
Code!
Stay tuned...
if you have enjoyed this post, please consider buying me a coffee ☕ to help me keep writing!