Controlling or scoping the synchronisation of #AzureAD DRS devices back to your on-premises #ActiveDirectory forest using #AADConnect

Azure Active Directory (Azure AD, AAD) Connect can optionally synchronise Azure AD device objects, registered either via Azure Device Registration Service (Azure DRS); InTune; or Office 365 Mobile Device Management (MDM), back to your on-premises Active Directory Domain Services (AD DS) forest for use by Active Directory Federation Services (AD FS).  (For a quick recap on DRS, see this post.)

One of the primary reasons for doing so is to introduce persistent single sign-on (PSSO), which inevitably helps reduce MFA interaction for users and generally provides a simpler, more enriched user authentication experience across devices and on-premises and cloud-based applications.

One of the issues that I’ve faced when implementing Azure DRS is the inability to control what can be registered.  Once it’s enabled, anyone can register anything as long as they undergo multi-factor authentication (MFA) – a configurable, yet wholly recommended setting.  In the case of my current customers they wanted more granularity.  Specifically, one request was this:

Write-back InTune managed devices and domain-joined, automatically registered devices.

In other words, do not simply write-back any and all bring your own device (BYOD) devices, which is the default.  The reason?  AD FS issues persistent single sign-on cookies to registered devices, which reduces the amount of credential collection and MFA – a positive and beneficial configuration if you trust the device.  Well, they don’t trust all devices, which is why they’re implementing InTune!

Identifying compliant and managed devices

The Azure device object has a few useful properties:

  • deviceTrustType (string)
  • isCompliant (Boolean)
  • isManaged (Boolean)

The trust type will be one of three: Workplace; ServerAd; or AzureAd; and is depicted in https://manage.windowsazure.com as TRUST TYPE.

deviceTrustType value Azure portal value Description
AzureAd Azure AD Joined Windows 10 Azure AD joined machine
ServerAd Domain Joined Windows 7 and Windows 8.1 domain-joined machines
Workplace Workplace Joined BYOD device that has registered either via DRS or InTune/O365MDM
Also used by Windows 10 domain-joined machines

The isCompliant and isManaged attributes describe the TRUST LEVEL column in the Azure portal.  isCompliant can be true, false or null; isManaged can be true or null (not seen a false yet), which mean the following:

isCompliant isManaged TRUST LEVEL
null true Managed
true true Compliant
false true Uncompliant
null null Authenticated

A trust level of managed describes an automatically workplace-joined/registered device, i.e. Windows 7 or Windows 8.1 domain-joined machines.

A trust level of compliant describes a device registered with and managed by InTune and currently in compliance with policy.

A trust level of authenticated describes a device that has been registered via DRS and the user registering the device has successfully proven themselves, which typically means they authenticated with MFA.

Windows 10 domain-joined machines are different to Windows 7 and Windows 8.1 devices.  Whereas Win7 and Win8.1 have a trust level of managed, Windows 10 has authenticated.  Windows 10 also does not have the isManaged Boolean set.

Therefore, to identify managed and compliant devices, the following matrix is used.

Trust Type isManaged/ isCompliant OS Device
ServerAd isManaged == true Windows 7/8.1
Workplace isManaged == NULL Windows 10 Windows 10 AD DS member
Workplace isCompliant == true BYOD – InTune
AzureAd isCompliant == true Windows 10 AAD join

Example device

Assuming you have at least one device registered in Azure AD, and AAD Connect is configured for device write-back, open up the Azure AD Connect synchronisation service manager and search the metaverse for device objects.  Randomly select one, click lineage and take a look at the AAD connector, for example:

aADConnect-ExampleDevice

Here we can see an iPhone that has enrolled with InTune.  The trust type is workplace and it is both compliant and managed.  As we will see in the next section, this device will be written back to the on-premises AD DS and the owner of this device will get Persistent Single Sign-On when accessing AD FS and Azure AD applications.

Filtering the devices

Given the information provided we have what we need to make a decision about whether or not to write-back the object:

  • deviceTrustType == ServerAd && isManaged == true (Trust type == Domain Joined; Trust Level == Managed)
    • Windows 7 or Windows 8.1 domain-joined machine (in-scope)
  • deviceTrustType == Workplace && isCompliant == true (Trust type == Workplace Joined; Trust Level == Compliant)
    • InTune-managed BYOD device (in-scope)
  • deviceTrustType == Workplace && isManaged == NULL & deviceOperatingSystem == Windows 10
    • Windows 10 domain-joined machine (in-scope)

So how best to achieve this in Azure AD Connect?  There are going to be multiple approaches.  I tried to align with documented recommendations and therefore chose to utilise the cloudFiltered attribute, even though I’m filtering on-premises objects and not filtering from the cloud.  I then implementing the following rules.  (I utilise the same naming convention but prefix custom rules with the customer name – for this post I have just written “Customer”.)

  • Customer In from AAD – Device Common.  This rule is used to contribute additional attributes not contributed by the out-of-box rule In from AAD – Device Common.  The new rule shares roughly the same configuration and has a higher precedence and different set of transformations, i.e. link type of join; no scoping filter; one join rule (deviceId == deviceId).  I implemented a precedence of 92 and two transformations:
    • A direct flow type; for target:deviceTrustType, source:deviceTrustType; with an update merge type
    • An expression flow type; for target:cloudFiltered, source:IIF statement; with an update merge type. The IIF statement is:
      • IIF([deviceTrustType] = “ServerAd” && [isManaged] <> True, True, IIF([deviceTrustType] = “Workplace” && IsNullOrEmpty([isManaged]) && [deviceOSVersion] = “Windows 10”, NULL, IIF([deviceTrustType] = “Workplace” && [isCompliant] <> True, True, NULL))))
  • Out to AD – Device Join SOAInAAD.  The out-of-box rule was copied and disabled.
  • Customer Out to AD – Devicze Join SOAInAAD.  This rule is a copy of  Out to AD – Device Join SOAInAAD, with two differences: higher precedence and additional scoping filters:
    • Group one: added cloudFiltered NOTEQUAL true
    • Group two: added cloudFiltered NOTEQUAL true

Applying the configuration

You apply configuration changes using PowerShell.  The following script implements the changes described in the preceding section – it disables Microsoft.OuttoADDeviceJoinSOAInAAD.nnn and creates Customer.OuttoADDeviceJoinSOAInAAD.001 and Customer.InfromAADDeviceCommon.001Customer.OuttoADDeviceJoinSOAInAAD.001 is a copy of the original OOB rule that is disabled that has a minor number of modifications.

Here’s the script.

CreateAzureDrsFilteringRules.ps1


## CreateAzureDrsFilteringRules.ps1
##   Example AAD Connect script to demonstrate how to apply a configuration that will allow
##   you to selectively write-back devices as opposed to accepting all registered devices

[String]$CustomRuleNamePrefix = 'Customer' # No spaces, e.g. "Contoso" or "AbstractSynapse"

[String]$CustomInfromAADDeviceCommonID = '9fe196c8-a80a-4f76-8a23-652d113aca29' # Go ahead and generate a GUID if you don't have one already, e.g. [Guid]::NewGuid().Guid|clip
[String]$CustomInfromAADDeviceCommonTag = '{0}.InfromAADDeviceCommon.001' -f $CustomRuleNamePrefix # Modify your version accordingly

[String]$CustomOutToADDeviceJoinSOAInAadID = '100f52da-c115-4f59-a348-872907e41490'
[String]$CustomOutToADDeviceJoinSOAInAadTag = '{0}.OuttoADDeviceJoinSOAInAAD.001' -f $CustomRuleNamePrefix # No changes to this rule

[String]$AzureADConnectorID = Get-ADSyncConnector | ? { $_.SubType -eq 'Windows Azure Active Directory (Microsoft)' } | select -ExpandProperty Identifier | Select -ExpandProperty Guid
[String]$AddsConnectorID = Get-ADSyncConnector | ? { $_.Type -eq 'AD' -and $_.GlobalParameters["Connector.DeviceContainerDn"] -ne $null} | select -ExpandProperty Identifier | select -ExpandProperty Guid

# Create custom "In from AAD Device Common rule"
Write-Host "Creating 'In from AAD Device Common' Sync Rule"
New-ADSyncRule  `
    -Name ('{0} In from AAD - Device Common' -f $CustomRuleNamePrefix) `
    -Identifier $CustomInfromAADDeviceCommonID `
    -Description 'Rule provides a filtering transformation to only provision certain registered devices.' `
    -Direction 'Inbound' `
    -Precedence 92 `
    -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
    -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
    -SourceObjectType 'device' `
    -TargetObjectType 'device' `
    -Connector $AzureADConnectorID `
    -LinkType 'Join' `
    -SoftDeleteExpiryInterval 0 `
    -ImmutableTag $CustomInfromAADDeviceCommonTag `
    -OutVariable AadDeviceCommonSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AadDeviceCommonSR[0] `
    -Source @('deviceTrustType') `
    -Destination 'deviceTrustType' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AadDeviceCommonSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AadDeviceCommonSR[0] `
    -Source @('deviceTrustType','isManaged','isCompliant') `
    -Destination 'cloudFiltered' `
    -FlowType 'Expression' `
    -ValueMergeType 'Update' `
    -Expression 'IIF([deviceTrustType] = "ServerAd" && [isManaged] <> True, True, IIF([deviceTrustType] = "Workplace" && IsNullOrEmpty([isManaged]) && [deviceOSVersion] = "Windows 10", NULL, IIF([deviceTrustType] = "Workplace" && [isCompliant] <> True, True, IIF([deviceTrustType] = "AzureAd" && [isCompliant] <> True, True, NULL))))' `
    -OutVariable AadDeviceCommonSR | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.JoinCondition' `
    -ArgumentList 'deviceId', 'deviceId', $false `
    -OutVariable joinCondition | Out-Null

Add-ADSyncJoinConditionGroup  `
    -SynchronizationRule $AadDeviceCommonSR[0] `
    -JoinConditions @($joinCondition[0]) `
    -OutVariable AadDeviceCommonSR | Out-Null

Add-ADSyncRule -SynchronizationRule $AadDeviceCommonSR[0] | Out-Null

# Create custom "Out to AD - Device Join SOAInAAD" rule
Write-Host "Creating 'Out to AD Device Join SOAInAAD' Sync Rule"
New-ADSyncRule  `
    -Name ('{0} Out to AD - Device Join SOAInAAD' -f $CustomRuleNamePrefix) `
    -Identifier $CustomOutToADDeviceJoinSOAInAadID `
    -Description 'Supersedes Microsoft.OuttoADJoinSOAInAAD.001 - the only difference is the scoping filter, which descopes cloudFiltered devices' `
    -Direction 'Outbound' `
    -Precedence 94 `
    -PrecedenceAfter '00000000-0000-0000-0000-000000000000' `
    -PrecedenceBefore '00000000-0000-0000-0000-000000000000' `
    -SourceObjectType 'device' `
    -TargetObjectType 'msDS-Device' `
    -Connector $AddsConnectorID `
    -LinkType 'Provision' `
    -SoftDeleteExpiryInterval 0 `
    -ImmutableTag $CustomOutToADDeviceJoinSOAInAadTag `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceId') `
    -Destination 'dn' `
    -FlowType 'Expression' `
    -ValueMergeType 'Update' `
    -Expression '"CN=" & StringFromGuid([deviceId]) & "," & %Microsoft.DeviceWriteBack.Container%' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceId') `
    -Destination 'cn' `
    -FlowType 'Expression' `
    -ValueMergeType 'Update' `
    -Expression 'StringFromGuid([deviceId])' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('cloudAnchor') `
    -Destination 'msDS-CloudAnchor' `
    -FlowType 'Expression' `
    -ValueMergeType 'Update' `
    -Expression 'IIF(IsNullOrEmpty([cloudAnchor]),NULL,CGuid(Word([cloudAnchor],2,"_")))' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('displayName') `
    -Destination 'displayName' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('accountEnabled') `
    -Destination 'msDS-IsEnabled' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('alternativeSecurityId') `
    -Destination 'altSecurityIdentities' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceId') `
    -Destination 'msDS-DeviceID' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceObjectVersion') `
    -Destination 'msDS-DeviceObjectVersion' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceOSType') `
    -Destination 'msDS-DeviceOSType' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('deviceOSVersion') `
    -Destination 'msDS-DeviceOSVersion' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('devicePhysicalIds') `
    -Destination 'msDS-DevicePhysicalIDs' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('registeredOwnerReference') `
    -Destination 'msDS-RegisteredOwner' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('registeredUsersReference') `
    -Destination 'msDS-RegisteredUsers' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncAttributeFlowMapping  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -Source @('isManaged') `
    -Destination 'msDS-IsManaged' `
    -FlowType 'Direct' `
    -ValueMergeType 'Update' `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

# This rule exists to facilitate the filtering of devices - cloudFiltered==true objects are excluded from scope
# and thus not provisioned
New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'cloudCreated','true','EQUAL' `
    -OutVariable scopeCondition1 | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'cloudFiltered','true','NOTEQUAL' `
    -OutVariable scopeCondition2 | Out-Null

Add-ADSyncScopeConditionGroup  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -ScopeConditions @($scopeCondition1[0], $scopeCondition1[0]) `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'cloudCreated','false','EQUAL' `
    -OutVariable scopeCondition1 | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'cloudAnchor','','ISNOTNULL' `
    -OutVariable scopeCondition2 | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'alternativeSecurityId','','ISNOTNULL' `
    -OutVariable scopeCondition3 | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.ScopeCondition' `
    -ArgumentList 'cloudFiltered','true','NOTEQUAL' `
    -OutVariable scopeCondition4 | Out-Null

Add-ADSyncScopeConditionGroup  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -ScopeConditions @($scopeCondition1[0], $scopeCondition2[0], $scopeCondition3[0], $scopeCondition4[0]) `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

New-Object  `
    -TypeName 'Microsoft.IdentityManagement.PowerShell.ObjectModel.JoinCondition' `
    -ArgumentList 'msDS-DeviceID','deviceId',$false `
    -OutVariable joinCondition1 | Out-Null

Add-ADSyncJoinConditionGroup  `
    -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] `
    -JoinConditions @($joinCondition1[0]) `
    -OutVariable AdDeviceJoinSoaInAadSR | Out-Null

Add-ADSyncRule -SynchronizationRule $AdDeviceJoinSoaInAadSR[0] | Out-Null

# Disable Microsoft.OuttoADJoinSOAInAAD.001 - as it's superseded by the custom rule
Write-Host "Disabling OOB sync rule Microsoft.OuttoADJoinSOAInAAD.001"
$MicrosoftOuttoADJoinSOAInAAD = Get-ADSyncRule |? { $_.ImmutableTag -like 'Microsoft.OuttoADJoinSOAInAAD.*' }
$MicrosoftOuttoADJoinSOAInAAD.Disabled = $true
$MicrosoftOuttoADJoinSOAInAAD | Add-ADSyncRule | Out-Null

To apply, simply save and execute it on an AAD Connect server as a member of the ADSyncAdmins group.

Once the configuration has been successfully applied you’ll need to execute a full synchronisation for all connectors.

The end result is that you will have all devices registered in Azure DRS in the metaverse, and a subset of these devices written back to the CN=Registered Devices container in your on-premises AD DS forest.  You can look see what is going to be provisioned versus what will remain filtered in the metaverse by looking at the cloudFiltered attribute of device objects.  NULL means not filtered (and therefore will be provisioned) and TRUE means filtered.

Summary

Azure Device Registration Service and InTune enrolment result in device objects registered in your Azure AD tenant and, optionally, written back to your on-premises Active Directory Domain Services forest by Azure AD Connect Sync.  By default all registered devices are written back regardless of the state of that device.  Some customers will not want all devices written back and will only want compliant devices or compliant devices and automatically-registered (domain-joined) devices written back.  This post illustrates how to achieve this configuration by describing data available to understand differences in registered devices and a practical and working (and successfully deployed, I might add) approach to implementing the necessary filtering in Azure AD Connect.  The result being more control over what devices are written back to on-premises and light-up persistent SSO.  The example also handles the differences with Windows 10 AD DS domain-joined devices, which look different to Windows 7 and Windows 8.1 domain-joined devices.

The script

Just in case of formatting issues or whatever, the PS1 file can be downloaded from here:

I don’t output to the screen, so to check that it’s worked, look for Sync Rules with an immutable tag starting with Customer or whatever you changed $CustomRuleNamePrefix to be and also ensure that the OOB SR with an immutable tag of Microsoft.OuttoADJoinSOAInAAD.* is disabled.

 

Posted in AADSync, Azure AD Connect | Tagged , , , , , , , | Leave a comment

Multi-Factor Authentication and multiple identity providers

When you implement an additional authentication provider in your Active Directory Federation Services (AD FS) identity provider (IdP) you soon start getting all manner of requests from application owners/managers within the business for multi-factor authentication (MFA) configuration.  For the most part it’s nice and easy, each application has one or two requirements such as “MFA when accessing from outside of the corporate network” or “MFA for admins”, so you create a couple of additional authentication rules on the relying party trust for the app.  This is nice and clean because there’s typically a one-to-one relationship between web application and federation service.  For example, consider the following architecture diagram – two SaaS apps, each implemented within the idp.contoso.com federation service (FS) as a relying party trust, or RP:

image

This is, in effect, a type of conditional access.  The AD FS claim rules allow you to implement a number of scenarios, for example:

  • Require MFA when accessing SaaS app #1 from outside the corporate network
  • Require MFA when accessing SaaS app #2 if you are not requesting a token from a registered device, i.e. a Workplace Joined (WPJ) or InTune-managed device
  • Require MFA when accessing SaaS app#1 and/or SaaS app#2 if you are an administrator of the app (designated by group membership in AD DS)

I won’t go over conditional access here – I have another post on that subject coming soon – and Ramiro does an awesome job of describing additional authentication rules in this post.  Suffice to say, there’s a lot of flexibility when it comes to controlling MFA in AD FS 2012 R2 and AD FS 2016 when the application in question trusts your FS directly.

Issues arise when the client needs to make a second hop for authentication however.  That is, when the SaaS app trusts an IdP that is not your FS and that IdP trusts your FS, as is the case in the following scenarios:

  • Azure Active Directory apps and federated users
  • Application or location specific federation services that support your corporate users
  • Partner organisations that trust your corporate users

The latter bullet is not really relevant here, but I am constantly coming across the first two points.  Take the following architecture illustration as an example.

image

What have I drawn?  Four web or SaaS apps and three different identity providers all of which actually support your corporate users.

  • Firstly, external web app #1 and #2 trust a specific federation service that has two claims provider trusts: one for a custom STS or FS that handles external users, hosted in a Linux LDAP directory; the other being your corporate IdP that handles corporate user authentications.
  • Next, SaaS app #1 is an Azure AD pre-integrated app, i.e. it trusts AAD for authentication, however your Azure AD tenant contains both managed (cloud) and federated identities, where the federated identities are your on-premises corporate users and the cloud identities are non-IT staff, e.g. factory workers, that need access to at least one of your SaaS apps.
  • Lastly, SaaS app #2 is a typical federated application that trusts your AD FS IdP.  Here you get SSO to your SaaS apps using your on-premises identities, i.e. your Active Directory Domain Services (AD DS) users.

Integrating MFA now just got a lot more complex (for a subset of your apps).

I want to gloss over Azure AD because I have another post planned for a more detailed discussion around Conditional Access Policy, or CAP.  So for AAD we’ll just say if you want MFA you need to control or configure that within Azure AD – this is Conditional Access Policy in AAD and it’s powerful, granular and the scope of what is included is growing.

And as we discussed at the start of this post, the SaaS app that directly trusts AD FS is obvious and easy – that’s just additional authentication rules.

However, if you have apps that trust an FS that in turn trusts your FS and those application owners want some of the MFA action then you have a bit of a problem, and this is really the reason I wanted to write this post.  To be clear, we’re specifically talking about this section of the architecture:

image

The issue that often confuses people is what the IdP is actually doing.  If we quickly summarise the flow of authentication, it goes like this:

  1. User/client hits the web app and is redirected to the IdP for authentication (the first FS – sts.contososervices.com)
  2. User selects realm and is redirected to the corporate FS/IdP (the second FS – idp.contoso.com).
    • (Users typically interact with the FS to perform Home Realm Discovery (HRD) a.k.a. IdP discovery – in the above case this involves a custom page with a button for external users and another button for corporate users.)
  3. User authenticates with the second FS (idp.contoso.com) and is issued a token for the first FS/IdP and is redirected back to the first IdP
  4. User authenticates with the first FS (sts.contososervices.com) using the token issued by the second FS and is then issued a new token for the web app and redirected back to the web app
  5. User is authenticated using the second security token – the token from the first IdP

All of this happens through java-script initiated browser redirects and HTTP POSTs.  The request for a token to the first IdP from the second IdP (a request for a SAML token for sts.contososervices.com, issued by idp.contoso.com) has no information relating to the actual app the user is hitting.  Only when the first IdP is issuing a token for the app is there any real information available about the app.  When authenticating with the second FS the app is technically the first FS.

So making a decision about whether to invoke MFA or not is not really the responsibility of the second – the corporate – FS.  The corporate FS/IdP cannot accurately make the right decision as it is simply issuing a token for another IdP (although it’s just another relying party to the FS).  What options exist then?  Assuming that you don’t want to discuss the obvious answer which is implement MFA at the first FS.

Options for implementing MFA for an app that indirectly trusts your FS/IdP that integrates with your MFA system

Note – I use the terms federation service (FS) and identity provider (IdP) interchangeably throughout this (and other) posts.  As far as I am concerned they are synonymous.

  • The easiest answer is to require MFA for members of one or more groups.  This is typically done via an equality match on the value (which is a string representation of a SID) in the http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid claim type
    • The benefit here is you lump users into a group and they get MFA
    • The disadvantage here is that the users might be using other applications that do not require MFA and will still be expected to undergo MFA each time they get their SSO token from the corporate AD FS (once per day or once per browser session, depending on usage)
    • You can control whether or not to invoke MFA based on endpoint here too, so you can permit browser-based authentication and bypass MFA for applications that talk directly to WS-Trust endpoints – this may or may not be a good thing as you can stop apps from breaking but obviously negate the security benefit of the secondary authentication
  • Another option is that the application can request MFA itself, i.e. the federation service simply honours the request.  This is typically done within the application itself.  Options are:
    • The application actually inspects the claims in the authentication token and determines whether or not MFA is needed, e.g. it looks to see if there’s an MFA authentication type value (http://schemas.microsoft.com/claims/multipleauthn) in the http://schemas.microsoft.com/claims/authnmethodsreferences claim type and perhaps also takes a look at the value of http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork claim type or maybe checks to see what role the authenticated user holds and, if the logic is such that you determine MFA is required, the app asks the IdP for MFA via the WAUTH (WS-Federation) or RequestedAuthNContext (SAML) request parameter.  This is generally known as step-up authentication.  In summary, your code looks for whether or not you have done MFA (you must issue the AuthNMethodsReferences claim to the app) and requests it if you have not and it feels it is needed.  Again, Ramiro does a quality job of describing this with a .NET MVC example here.
    • If the app simply requires MFA regardless of condition, then it can build the original sign-in request containing the WAUTH request parameter.  You can find a nice succinct discussion on the topic in this TechNet wiki article.
  • Beyond this you need to start venturing into the grey area of support that I like to think of as creative territory.  Sure, it’s fun and you can make it work, but it’s unlikely to be an operationally sound option going forward.
    • For example, you can look to rewrite the URL between the RP and the first FS and add the WAUTH query string, which the FS will honour.  In one customer case I did a basic POC here, because the first FS was running AD FS 2.0 and therefore had IIS locally, so an HTTP Module simply added the WAUTH query string if it wasn’t there.
    • You can also bake a “smart URL” where you craft the request yourself and again the FS will honour it, e.g. https://sts.contososervices.com/adfs/ls/?wa=wsignin1.0&wtrealm=urn:federation:dumpclaims&wauth=http://schemas.microsoft.com/claims/multipleauthn – users hitting this URL will undergo MFA and logon to the app, however you have to be sure that users will access the app via the URL.

What about invoking MFA at the first FS for users coming from the CP trust?  (In other words, what if we look for users authenticating from the second FS and invoke MFA for the app here?)

If you try to do this using additional authentication rules as you would a user in the same realm as the FS (i.e. your AD DS forest or trusted forest) then this won’t work in AD FS 2012 R2.  The request for MFA is ignored.  It is not bounced back to the IdP.  However, AD FS 2016 changes this…

AD FS 2016 exhibits the same behaviour as AD FS 2012 R2 by default.  But this behaviour is configurable.  There are two properties of interest:

  • On the relying party trust, the Boolean property RequestMFAFromClaimsProviders – set this to TRUE and the RP authentication rules will fire for claims provider trusts that support MFA (for CP trusts that do not support MFA, then you will either get an access denied when requesting your token or will get your token without MFA, depending on the access policy)
  • On the claims provider trust, the Boolean property SupportsMFA – set this to TRUE and the redirect to the claims provider will include the WAUTH parameter with the multipleauthn value which will request MFA (just like Azure AD does when your federated domain has SupportsMFA set to true)
    • If your upstream FS is not AD FS or doesn’t support the multipleauthn value in WAUTH you can specify the URI that will trigger MFA using the property CustomMFAUri

So, to answer the original question, upgrade sts.contososervices.com to AD FS 2016 and configure per-app MFA on each in-scope RP trust and make use of the above properties to get idp.contoso.com to perform the MFA.

Summary

Controlling multi-factor authentication via conditional access policy is a very powerful feature of AD FS.  The scope of AD FS conditional access policy must be fully understood, however.  AD FS can make granular decisions to allow or deny access to a trust or require MFA to a trust based on the set of claims available to the policy engine.  The important point here is that this configuration is made at the trust level, which is generally an application and occasionally two or three apps that are typically categorised the same.  If there’s an intermediary federation service, such as Azure AD (in the use case where you utilise federated users to access AAD SaaS apps, for example) or another federation service between your AD FS IdP which handles MFA and the app then you have no ability to determine what application is requesting the token and therefore your decisions only relate to the FS that you are getting a token for, which complicates your ability to define per-app MFA policies.  I have described some options in this space, but in reading them it should be obvious that the answer lies either within the app itself (generally not appropriate other than for administrative step-up) or the intermediary FS.  This is important.  I will cover off conditional access policy in more depth soon, but the purpose of this post want to highlight why your AD FS with MFA server can’t help mandate MFA for app #1 and not app #2 that trust an IdP (FS) that trusts yours.  The good news is that AD FS 2016 can be a big help here…

Posted in AD FS | Tagged , , , , , , , , | 1 Comment

Time for a change

When I first created this blog several years back WordPress asked me for a catchy site title and a brief synopsis for the site tagline.  I hadn’t really thought about this, so looked at the pre-filled strings and largely accepted them.  WordPress, creates new blogs with the site title of “Yet another WordPress blog…”, so I, using enormous levels of creativity, replaced WordPress with identity management and moved on to focus on the brief synopsis that resulted in “thoughts and opinions on Microsoft identity management”.  Groundbreaking imagination.  Smile

At the time I was very busy implementing Forefront Identity Manager, or FIM, with multiple customers.  With the usual bits and pieces that go with that.

As time has moved on my role has naturally evolved, although I stubbornly retain my consultant or SME title and focus and refuse to “mature into an architect”.  In other words I still roll up my sleeves and actually deliver!  Winking smile

But the role has changed.  The fundamental technology has changed that is.  I used to fill the odd gap with AD FS work.  Then I got real busy with some big AD FS projects and, in parallel, started doing a lot of the nasty, non-out-of-box Azure AD/Office 365 directory synchronisation projects, i.e. custom FIM-based DirSync solutions.  This quietly evolved into Azure AD Premium and all things identity and scripting that wasn’t specifically Exchange Online or SharePoint Online.

Now I find myself being one of the “identity guys”.  You want to talk about Azure AD, AD FS, MFA, CAP, AAD Connect, App Proxy, DRS and the glue that no one wants to talk about – you need Paul.  Which is very cool.  And I really enjoy this stuff.  And the next stage is likely more and more emphasis on understanding apps and clients and InTune.  None of which should simply be called “identity management”.  As I’m managing identities less and less, but building out or assisting and facilitating customers build out large and complex hybrid identity federation services infrastructures that light-up as much of the Azure AD stack as possible.

So with that in mind I’m renaming my blog and tweaking my synopsis.  Unfortunately, I still don’t really have much in terms of creativity, so the title is still quite bland.  But at least it reflects what I am doing in my day job today, which is what drives the posts.  Customer questions, problems I have to solve and mistakes my customers make or I make are the general inspiration, for want of a better term, behind my posts.

Yet another identity management blog
Thoughts and opinions on and around Microsoft Identity Management

Becomes:

Hybrid Identity
Thoughts and opinions on and around the subject of hybrid identity in the Microsoft cloud

Hopefully I won’t need to change this again any time soon.

Posted in About | Leave a comment

Active Directory Federation Services (#ADFS) Single Sign On (SSO) and token lifetime settings

To understand Single Sign-On (SSO) and Persistent Single Sign-On (PSSO) in Active Directory Federation Services (AD FS) you must first understand the authentication cookie.  After the initial logon is validated an authentication cookie is written to the client (typically a browser). These cookies are implementation-specific and opaque to intermediaries and the user, including the federation service proxy for example. Subsequent authentication is done using the cookie and does not necessitate recapturing credentials or re-authenticating via the original means, e.g. forms-based or Kerberos/NTLM.

This is important.  The cookie allows for subsequent authentication without recapturing credentials.  In other words, the process is invisible or transparent to the user.  The user is still authenticating, there’s still round trips back to the authentication service, but the user is not bothered – it’s seamless – it’s SSO.

Within the AD FS authentication and token issuance flow the FS writes the authentication cookie to the client as a session cookie by default. The cookie contains a signed token and a set of claims in-line with the claims issued by the Active Directory Domain Services (AD DS) Claims Provider trust (or any other CP in the federated scenario).  Session cookies are bound to the session.  When the parent process, i.e. the browser, is terminated the session is destroyed.  Thus, regardless of the validity of the token, you kill all instances of your browser, for example, and the token is destroyed.

When a client requests a security token for a relying party (RP) from the FS the authentication cookie is used to authenticate to the FS and initialise or bootstrap the set of input claims for token issuance. This cookie provides SSO. An example being a client authenticates with AD FS and gets a token to an application, e.g. Azure AD, and then attempts to access another app, for example an on-premises federated app; the client already has a valid authentication token in the form of the cookie and therefore is able to SSO to the FS and obtain a new security token for the federated app.

Persistent Single Sign-On, or PSSO

PSSO is basically an authentication cookie that persists across sessions. By default, authentication cookies are session-based, which means when the browser closes the cookie is destroyed. PSSO cookies are written to Workplace Joined (WPJ)/registered devices by default, i.e. devices that have registered using Azure Device Registration Services (DRS) and been written back to the on-premises Active Directory Domain Services (AD DS) forest by Azure AD Connect (hybrid model); or registered directly using Windows Server 2012 R2 Device Registration Service (on-premises model).

AD FS will also set PSSO cookies if Keep Me Signed In (KMSI) is selected during the authentication process.

In addition, AD FS emits a PSSO claim type of http://schemas.microsoft.com/2014/03/psso with a value of “true”.  The PSSO claim can be sent to Azure AD so that AAD and SharePoint Online honour the configuration too, i.e. AAD will issue PSSO cookies as a result of this being configured in AD FS.  This is done by adding the following custom claim rule to the Azure AD (urn:federation:MicrosoftOnline) relying party trust:


psso:[Type == "http://schemas.microsoft.com/2014/03/psso"]

 => issue(claim = psso);

SSO (and PSSO) settings

The AD FS 2012 R2 settings for SSO and PSSO are as follows:

  • SSO lifetime is 480 minutes (8 hours) – configurable at the global/AD FS level (SsoLifetime property)
  • PSSO is enabled – global configuration (PersistentSsoEnabled)
  • KMSI is disabled – global configuration (KmsiEnabled)
  • PSSO lifetime for registered devices is 10,080 minutes (seven days) – global configuration (PersistentSsoLifetimeMins)
  • PSSO lifetime for KMSI is 1,440 minutes (24 hours) – global configuration (KmsiLifetimeMins)

All of the above are global settings, read using Get-AdfsProperties and managed using Set-AdfsProperties.

What about the actual tokens issued?

  • Token lifetime is 60 minutes (1 hour) – configurable at the Relying Party (RP) trust level (TokenLifetime)

The RP settings are read using Get-AdfsRelyingPartyTrust and managed using Set-AdfsRelyingPartyTrust.  The default value, when read, is zero, which actually means 60, as the property expects the configured value in minutes.

There’s another RP-specific setting worth calling out too:

  • AlwaysRequireAuthentication – when true (false by default) the SSO cookie is ignored and a credential capture is mandated

Important note

If you authenticate using IWA then the re-authentication (credential capture) is invisible – it’s SSO – so you won’t see much from a UX perspective, but you’ll have a fresh and valid 60-minute token.

Always Require Authentication is particularly useful if you require Multi-Factor Authentication (MFA) for a sensitive app.  Regardless of SSO/PSSO state, if you mandate MFA and Always Require Authentication for an RP then each time you need a token for that app you will MFA.

Changes in Windows Server 2016

Active Directory Federation Services 2016 changes some of the above settings.  There’s a new TechNet document describing the registered devices and non-registered devices SSO settings:

I’ll describe the information here for posterity purposes.

  • Registered devices.  By default, users with registered devices will get SSO for up to a maximum of 90 days if they use their device to access AD FS resources within a 14 day window.  This behaviour is controlled by two new settings/values:
    • Maximum SSO period (PersistentSsoLifetimeInMins – default value is 60,480)
    • Device Usage Window (DeviceUsageWindowInDays – default value is 14)
  • Non-registered devices.  For non-registered devices SSO and PSSO is managed using KMSI settings, as is true of AD FS 2012 R2.
    • If KMSI is disabled the SSO cookie has an 8-hour validity (SsoLifetime property of the RP trust).
    • If KMSI is enabled the SSO cookie has a 24-hour validity (KmsiLifetimeMins – default value is 1,440; property is global; maximum upper value is 7 days)

Summary

AD FS 2012 R2 and AD FS 2016 tokens have a one hour (sixty minute) validity period by default.  This value is configurable on a per-relying party trust basis.  Token issuance requires that the token requestor has been authenticated by AD FS and has authorisation to request a token.  When a user is authenticated by AD FS an authentication cookie is written to the client to provide an SSO period, and subsequent requests to AD FS utilise this SSO period (authentication cookie).  The default value for the SSO period is 8 hours and is configurable at the global AD FS level.  If Keep Me Signed In is enabled and selected by the requestor, the SSO lifetime increases to 24 hours by default.  This value is configurable, globally, up to a maximum of 7 days.  If the requestor is authenticating from a registered device the SSO lifetime increases to 7 days in AD FS 2012 R2 and 90 days in AD FS 2016.  The 90 day validity requires an authentication within a 14 day period known as the device usage window, which means if you don’t access an AD FS application for more than 14 days then credentials will need to be recaptured, i.e. the SSO cookie is invalidated.

Lastly, you can configure a property of an RP trust to force authentication every time a token is requested.  That is, by enabling Always Require Authentication you configure AD FS to ignore SSO cookies for the configured trust(s).  This is useful when you want to deploy and utilise PSSO but need to mandate MFA for high-value assets.

Posted in AD FS | Tagged , , , , , | Leave a comment

Adding a link to the #AzureAD Password Reset web site in your #ADFS forms-based sign-in pages

Implementing Azure Active Directory Password Reset?  Want to make it a bit easier for your users who logon to Azure AD using federated identities to get to the self-service password reset and/or unlock portal?

Take the following customised forms-based authentication (FBA) sign-in page in Active Directory Federation Services (AD FS) 2012 R2, a.k.a. “AD FS 3.0” – typically accessed by users outside of the corporate network, hitting the FS-P (Web Application Proxy, WAP) but also by corpnet users with devices that are not capable of performing Integrated Windows Authentication (IWA, sometimes referred to as WIA).

image

When a user gets their password wrong, the page changes to include the bad username/password verbiage, like so:

image

A requirement that has cropped up, for me at least, a few times is to allow users to navigate to the password reset workflow from here – particularly those customers that have enabled the ~updatepassword endpoint that allows for the neat in-line password change experience.

The result of this requirement is therefore something like this – the FBA sign-in page after a bad password is entered.

image

See how I’ve added “Forgotten your password?  Reset it here”?  Click that URL results in you hitting the Azure Active Directory Password Reset portal and the user ID input box being automatically populated with your username, as entered in the AD FS FBA user name input box:

image

The solution to this requirement is quite neat and easy.  Firstly, the generic Azure AD SSPR portal takes a query string called username to facilitate pre-populating the User ID input.  The client-side JavaScript therefore needs to: look for the error text on the page, and if it’s there, add a new paragraph with an anchor that contains the username already input in the form.

Here’s some proof-of-concept JS to do this:

// Code to look for bad password
var badPasswordError = document.getElementById('errorText');
if (badPasswordError) {
    if (badPasswordError.innerText.startsWith('Incorrect user ID or password')) {
        var introductionDiv = document.getElementById('introduction');
        var userNameAreaDiv = document.getElementById('userNameInput');
        var userNameFromInputBox = userNameAreaDiv.value
        if (userNameFromInputBox != userNamePlaceHolder) {
            introductionDiv.innerHTML = introductionDiv.innerHTML +
                'Forgotten your password?  Reset it <a href="https://passwordreset.microsoftonline.com?username=' + userNameFromInputBox + '">here</a>.';
        }
    }
}

To deploy the code you will need to export the current theme and modify the OnLoad.js script and then update AD FS with the modified script.  This process is documented here:

Note.

The above code snippet has a dependency on a var called userNamePlaceHolder.  You can remove this check or you can implement the placeholder customisation with the following block of additional JS.

// Code to change the example@domain.com placeholder
var userNamePlaceHolder = document.getElementById('userNameInput');
if (userNamePlaceHolder) {
    if ("someone@example.com" == userNamePlaceHolder.getAttribute("placeholder")) {
        userNamePlaceHolder.setAttribute('placeholder', 'user.name@abstractsynapse.com');
    }
}

What the above is doing is replacing the out-of-box someone@example.com with user.name@abstractsynapse.com.

Posted in AD FS | Tagged , , , , , | Leave a comment

Integrated Windows Authentication (IWA) with Enhanced Protection for Authentication (EPA) and Chrome–it now works!

Some time back I posted quite a popular post describing the effect of a bug in Chrome that prevented the use of Enhanced Protection for Authentication, a.k.a. TLS channel binding, when authenticating via Integrated Windows Authentication (IWA).  I cared about this because it meant I had to disable EPA on Active Directory Federation Services (AD FS) farms where Chrome is a supported client.

Well, good news.  The bug has been fixed in Chrome 51.  Chromium 270219 was closed as fixed on March 26th, 2016.

What does this mean?  It means that you (a) don’t have to disable EPA in your AD FS farm; and (b) for those of you who have disabled it you can now turn it back on and close down those security waivers that you have open. 

 

Posted in AD FS, News | Tagged , , , , , | Leave a comment

Azure Multi-Factor Authentication (#AzureMFA) and Active Directory Federation Services (#ADFS)

Today, implementing Azure Multi-Factor Authentication (MFA) in an hybrid identity and access management solution based on Azure Active Directory (Azure AD, AAD) and Active Directory Federation Services (AD FS) more often than not requires that you implement the on-premises Azure MFA Server component.  This is because you typically have relying party (RP) trusts established in your on-premises AD FS federation service.  That is, you have some Software-as-a-Service (SaaS) apps that authenticate via AD FS and not Azure AD.  In order to introduce Azure MFA into the authentication process for these apps you require a secondary authentication provider deployed within AD FS.  Smaller deployments can install the MFA Server component directly on the federation service (FS) servers; larger deployments, i.e. more than two FS, will generally build out a load-balanced Azure MFA Server “farm” and utilise the Azure MFA “adapter” (the secondary authentication provider) to talk to Azure MFA Server via the load-balanced VIP (virtual IPv4 address).

I’ll provide a couple of drawings, to illustrate what I’m talking about.  Firstly, here’s the on-premises scenario – SaaS, AD FS and MFA Server.  In this case Azure MFA Server is mandatory – it’s no different to implementing any other MFA technology.  The cloud can’t really help with this use case today.

image

If you have an hybrid Azure AD and AD FS IdP then this is what it looks like: Azure AD, AD FS and MFA Server authenticating an Azure AD-hosted SaaS app.  In this case Azure MFA (cloud) is not used and again Azure MFA Server (on-premises) is because it’s a hybrid environment – it factors the AAD SaaS (and first-party) apps as well as on-premises apps.

image

If you’re building this now, especially if it’s for AD FS-only purposes, then this pattern is somewhat frustrating, more so if you have users registered with Azure MFA (the cloud service) or even Azure Password Management.  You cannot migrate registration data between cloud and on-premises (or between on-premises and the cloud) so you end up having to go all-out and deploy on-premises MFA Server, user and mobile registration portals, localisations and then manage the end-user communications and helpdesk management as well as the directory synchronisation.  Not ideal.  One example, I had to add eight (8) additional servers to an existing (I previously deployed) twenty (20) node AD FS deployment (8 FS, 8 WAP, 4 SQL) – 28 servers in total!

Which is why Windows Server 2016 Active Directory Federation Services (AD FS 2016) has a new and improved Azure MFA secondary authentication provider.  AD FS 2016 ships with a built-in “connector” for Azure MFA that talks directly to the cloud service and negates the need for any on-premises MFA Server infrastructure.  As long as the identities that AD FS is authenticating are synchronised to your Azure AD everything is in place.  Furthermore, AD FS 2016 actually enables Azure MFA as a primary authentication mechanism!  What does that mean?  It means that if you register the Azure Authenticator app, next time you access an AD FS protected resource you can authenticate by providing your username and the one-time-pin (OTP) code from the app.  No password!  This is one of several key new features arriving in AD FS 2016 that negate the need for users to provide credentials in the form of passwords!  I’ll blog about all of the no-password features in a separate post.  For now my focus is the Azure MFA [cloud] “adapter”.

May 2016 (Windows Server 2016 Technical Preview)

Windows Server 2016 Technical Preview 5 shipped a little while back.  When it shipped the Azure MFA secondary authentication provider was in private preview.  As of the 17th of May, 2016, the preview is public.  This means WS 2016 TP5 gives you everything you need to deploy Azure MFA as a primary or secondary authentication provider!

The official documentation was a few days behind the actual transition from private to public but is there now.  Here’s the URLs:

Configuration/deployment

I won’t re-document the configuration steps here, I’ll just summarise the actual process:

  1. For each federation service server/node in your AD FS farm, you create a certificate that will be used to authenticate with Azure MFA.  AD FS provides a PowerShell cmdlet for this – New-AdfsAzureMfaTenantCertificate
  2. Each certificate is assigned to an Azure AD Service Principal by creating a service principal credential.  This is done via New-MsolServicePrincipalCredential and is no different to assigning a credential to an Azure Web App or Web API, etc.  The app principal ID is a constant – it’s 981f26a1-7f43-403b-a875-f8b09b8cd720.
  3. Lastly, once you’ve created a cert for each FS node and created a service principal credential for each cert you configure the Azure MFA provider using another AD FS PowerShell cmdlet – Set-AdfsAzureMfaTenant.  This operation is performed once per farm and can be run from any node.

Once this configuration is done you’re ready to start configuring additional authentication policies for RP trusts other than urn:federation:MicrosoftOnline (The Azure AD/Office 365/MSOnline) trust.  For the Azure AD trust you configure the SupportsMfa Boolean property of each federated domain (Set-MsolFederatedDomain) and utilise Conditional Access Policy (CAP) to invoke MFA for Azure AD apps (SaaS, PaaS, App Proxy and first-party apps).  When using Azure MFA there is no need to offload MFA to AD FS – you just use the cloud.  This is different – the inverse – from the AD FS 2012 R2 and AAD hyrbid model.

Caveats and limitations

There are some features missing.  You have to enable (or enforce) at the per-user level if you want an in-line registration experience.  Per-app MFA with automatic “in-line” registration is not available.  You obviously cannot only utilise Azure MFA as a primary authentication provider either, so the typical pattern will be to enable in isolation for the extranet and utilise IWA and/or FBA for the intranet.

The per-user model is particularly frustrating.  It effectively mandates the use of app passwords which are difficult to deploy and use.  The alternative is getting users to register themselves and then utilising CAP, however as this will break non-registered users I can’t see this being a suitable option either.  Time will tell how useful this is and whether it is a sufficiently flexible implementation that truly negates the need for an on-premises MFA Server setup.

Summary

Prior to Windows Server 2016 integrating Azure MFA with on-premises SaaS apps that authenticate using AD FS required an on-premises implementation of Azure MFA Server

Windows Server 2016 (starting in public preview in Technical Preview 5) introduces a new and improved secondary authentication provider for Azure MFA that does not require any on-premises components.  The “adapter” talks directly to the cloud service; configuration of all MFA properties are managed in the cloud (it’s a hybrid of on-premises and cloud when you use the on-premises MFA Server).

In addition to negating the need for on-premises MFA components AD FS 2016 also introduces additional authentication scenarios for Azure MFA customers.  Namely, as part of the drive to never require a password when accessing corporate resources from outside of the corporate network, AD FS 2016 supports Azure MFA as a primary authentication provider, which means users can sign-in to AD FS-protected resources using their username, e.g. pawill@contoso.com, and their Azure Authentication one-time-pin (OTP) code.

Posted in AD FS, Azure MFA | Tagged , , , , , | Leave a comment