#ADFS, IWA and the WIASupportedUserAgents property

Recently, I’ve found myself answering several questions and writing emails and some change control paperwork on the topic of Integrated Windows Authentication (IWA) in AD FS.  I’m going to commit the behaviour to this blog for posterity and easy (lazy) reference.

Active Directory Federation Services (AD FS) 2012 R2 or 2016 implicitly support IWA and Forms Based Authentication (FBA).  The federation service servers authenticate clients based on the set of permitted handlers configured in the system.  When AD FS receives a request to a token endpoint (and the SSO cookie is not present or is present and is not valid) it grabs the user agent of the client and performs a comparison to see if any of the strings defined in the WIASupportedUserAgents property match against the user agent.

The comparison is a String contains comparison in 2012 R2 and 2016 by default, however 2016 also supports regex match should you configure it (by adding strings prefixed by an equals and a tilde =~, the same character set used to describe regex in the claims language, e.g. =~Windows\s*NT.*Edge).

If there is a match then the IWA handler is invoked.  If there is not a match the FBA handler is invoked.  Whether or not FBA is enabled in global authentication policy is irrelevant here.  Let me re-state that.  FBA is available and will be used without you specifically selecting (enabling) it within the global authentication policy.  IWA fails back to FBA regardless of the global authentication policy configuration. 

However, if AD FS is provided with instructions on what handler to invoke, e.g the WAUTH parameter in WS-Fed or the RequestedAuthnContext in SAML2P then the global authentication policy is inspected and the request will fail if FBA is not enabled.  This is important if any of your clients request a specific authentication handler.  And be sure to remember that Modern Authentication applications, i.e. applications that authenticate to Azure AD using ADAL, are such applications.  If a modern authentication app sends prompt=login to Azure AD for a federated domain AAD will translate that into &wauth=urn:oasis:names:tc:SAML:1.0:am:password&wfresh=0 which will fail if FBA is not enabled.

Note that AD FS 2016, and AD FS 2012 R2 with the July 2016 update rollup (kb3172614), have new functionality to natively support prompt=login without translating it into WS-Fed, so that IWA can still be used.  This is configured on your federated domain in Azure AD using Set-MsolFederationSettings.

Enabling IWA beyond Internet Explorer

Out of the box AD FS 2012 R2 has a bunch of user agents defined to support IWA using Internet Explorer and the Windows 8.1 authentication broker.  Other IWA-capable browsers are excluded by default.  By default Chrome and Firefox, for example, don’t work – you have to configure them to do IWA, hence we fallback to FBA as that is a better user experience.

AD FS 2016 also supports Microsoft Edge by default, and has a string for OneDrive for Business (OD4B) Next Generation Sync Client (NGSC) .

Many customers need to enable IWA for Edge and Chrome in 2012 R2 and Chrome in 2016.  So let’s look at the WIASupportedUserAgents  property by OS.

WIASupportedUserAgents in 2012 R2

A default installation of AD FS 2012 R2 has the following values defined in the WIASupportedUserAgents property:

  • MSAuthHost/1.0/In-Domain
  • MSIE 6.0
  • MSIE 7.0
  • MSIE 8.0
  • MSIE 9.0
  • MSIE 10.0
  • Trident/7.0
  • MSIPC
  • Windows Rights Management Client

The AD FS documentation provides an update to this default list.

  • MSIE 6.0
  • MSIE 7.0; Windows NT
  • MSIE 8.0
  • MSIE 9.0
  • MSIE 10.0; Windows NT 6
  • Windows NT 6.3; Trident/7.0, Windows NT 6.3; Win64; x64; Trident/7.0
  • Windows NT 6.3; WOW64; Trident/7.0
  • Windows NT 6.2; Trident/7.0
  • Windows NT 6.2; Win64; x64; Trident/7.0
  • Windows NT 6.2; WOW64; Trident/7.0
  • Windows NT 6.1; Trident/7.0
  • Windows NT 6.1; Win64; x64; Trident/7.0
  • Windows NT 6.1; WOW64; Trident/7.0
  • MSIPC
  • Windows Rights Management Client

Which accidently removes the authentication broker!  (MSAuthHost/1.0/In-Domain)

WIASupportedUserAgents in 2016

A default installation of AD FS 2012 R2 has the following values defined in the WIASupportedUserAgents property:

  • MSAuthHost/1.0/In-Domain
  • MSIE 6.0
  • MSIE 7.0
  • MSIE 8.0
  • MSIE 9.0
  • MSIE 10.0
  • Trident/7.0
  • MSIPC
  • Windows Rights Management Client
  • MS_WorkFoldersClient
  • =~Windows\s*NT.*Edge

MS_WorkFoldersClient is new, as is the last entry for Microsoft Edge.  The last entry demonstrates new functionality in AD FS 2016 that supports regex match – remember that the default behaviour in 2016 is the same as the only behaviour in 2012 R2, which is to perform a contains string comparison.  Anything that starts with an equals and tilde is a regex.

Adding support for Chrome and Firefox and Opera

The easiest way to include Chrome and Firefox and Opera into the mixture is to add the following string:

  • Mozilla/5.0 (Windows NT

Do be sure to include “(Windows NT” otherwise you’ll match Android and iOS devices, which is really bad, trust me.

Truthfully, this string will probably match most variants of IE too, however unless you want to strip out of box values out and test it, the least painful option is to just add it at the end.

Summary

If you want Chrome or browsers other than Internet Explorer to perform Integrated Windows Authentication you have to update your WIASupportedUserAgents  property to identify and support the browser.  You must be mindful that you only want to match browsers that are actually capable of doing IWA – you must not include mobile devices, for example.  And you also need to understand that if you don’t configure the browser to perform IWA then you will not fallback to forms, but will instead see the browsers native credential input box.  I generally add “Mozilla/5.0 (Windows NT” without the quotes to the existing value.  That keeps the change control paperwork simple.

I discuss more information on the settings needed for Chrome and Firefox in this post if you are interested:

However it is important to note that Chrome now supports Enhanced Protection for Authentication (Channel Binding encryption) so you DO NOT NEED TO TURN IT OFF as many older blogs will recommend (including my referenced post).  More information on that change to Chrome here:

You don’t have to enable forms based authentication for intranet in the global authentication provider to support fallback to FBA for non-IWA browsers.  However you do need to enable FBA if you are an Azure AD/Office 365 user or you have any relying parties that might specify an authentication mechanism using WAUTH or RequestedAuthnContext.

Lastly, please do test changes to WIASupportedUserAgents in a lab before unleashing on your production network.

If you choose to bin the out of box configuration and implement a minimal configuration please let us know via the comments.

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

#ADFS and #MFA load balancer guidance

When designing Active Directory Federation Services (AD FS) my actual involvement with the networking guys who handle the load balancer configuration is generally limited to a few calls and emails.  We provide some requirements in the forms of availability and persistence or stickiness and they do what needs to be done.  Truthfully, you get a high-level architectural view of the various solutions which helps with subsequent designs as you have a bit of experience of pitfalls from past engagements.  This post is nothing prescriptive.  I’m no expert with BigIP, Cisco or Brocade.  But I do know AD FS and I do want to talk about a nuance that can change the basic persistence recommendations.

Recommended practices

AD FS.  Generally you’ll have an internal (corpnet) Virtual IPv4 (VIP) for federation service (FS) servers and an external (Internet-facing) VIP for the Federation Service Proxy (FS-P).  The VIP often supports both HTTP and HTTPS protocols, although most deployments are 95-100% HTTPS.  The persistence or stickiness setting is generally NONE.  AD FS is stateless and does not require client session affinity.  The load balancer needs to do what it does to keep the TLS connection alive, but beyond that there is nothing application specific that requires affinity.

MFA Server.  This topology can vary a little, but I typically have two corpnet VIPs – one for the SDK WS, one for the user portal; and one external VIP for the user and mobile portals.  These VIPs only really need to be HTTPS but I like to use both HTTP and HTTPS and redirect HTTP to HTTPS.  These VIPs require session-based affinity, and generally we use source-IP.

Let me draw what an AD FS IdP with MFA Server looks like to summarise the above.

image

What am I trying to convey here? 

  • DC#1-WAP and DC#1-PTL(EXT) are the external (Internet-facing) VIPs – WAP is the FS-P and PTL(EXT) is the User and Mobile portals
  • DC#1-FS, DC#1-MFA and DC#1-PTL(INT) are the internal (corpnet) VIPs for AD FS, MFA (SDK WS) and User portal
  • The MFA-related VIPs have persistence/affinity/stickiness; the AD FS VIPs do not

Deviation from typical in a complex deployment

This is all well and good for most deployments.  However I have recently seen an intermittent issue with SMS OTP in this topology that resulted in us having to introduce source-IP based stickiness at the corpnet FS VIP.  Here’s why…

In my deployments there are too many FS servers to consider installing the MFA Server onto, so I always use the MFA Server secondary authentication provider, a.k.a. “AD FS Adapter”.  This talks to the SDK WS, which is load balanced.

The FS-P (WAP) talks to any AD FS server via the FS VIP.  Payload is sent, response received and job done.  FS-P uses connection pooling and can and will talk to one of several FS servers and with round-robin distribution and no stickiness the FS can and will change.

Now, when a user is authenticating from the Internet and is therefore interacting with the FS-P and MFA is invoked and the user’s chosen MFA mechanism is one-way SMS there can be a valid delay between the initial authentication with username and password and the OTP being entered.  What we saw was that sometimes the FS-P would send the user-input OTP to a different FS than the one that the primary authentication happened with.  This is not at all desirable.  The FS that performed the primary authentication invoked the MFA and as such is the only server that can accept the OTP because only the MFA Server that initiated the OTP from the cloud service holds the OTP – the OTP is not written to the data file.  When the FS-P uses a different connection to another FS the FS sends the OTP to a different MFA Server which rejects it because it has no knowledge of that OTP, so authentication fails.

The solution is to turn on stickiness at the FS VIP so that the FS-P servers maintain connections with the same FS.  The FS servers are already seeing consistent and persistent sessions with the MFA Server servers.

Let me try and draw this just to be super clear.

image

  • First, the blue arrow shows our on-premises federated user authenticate via FS-P (WAP01) which talks to FS01 which in turn talks to MFA01 which invokes one-way OTP.
  • Secondly, the red arrow shows our user enter her OTP at WAP01 which communicates with FS03 which in turn communicates with MFA03 which has no knowledge of the OTP.

Summary

If you are using a deployment of AD FS 2012 R2 or AD FS 2016 and Azure MFA Server and the secondary authentication provider for MFA Server known as the AD FS adaptor and you allow one-way SMS as a means of authentication then you have to ensure SSL/TLS stickiness/affinity/persistence between FS-P (WAP) and FS (AD FS) in addition to stickiness on the MFA SDK web services VIP.

Impact

I first encountered this issue with a hybrid IdP (Azure AD Premium and AD FS as described above) with a significant user base (~100,000 active users across multiple geographies) and a medium to large number of federated applications (~100) during the post go-live phase of rolling out the MFA Server and Conditional Access Policies.  Our concern at the time was that implementing stickiness on the FS VIP could result in multiple FS-P servers getting a sticky session to a single FS which would result in an uneven load distribution.  We weren’t too bothered that the FS servers themselves couldn’t handle it – they can, we just didn’t want to see big spikes on a small set of the servers.  Our concerns were unfounded.  We had the luxury of Azure AD Connect Health giving us a rich set of easily digestible performance data and we monitored it closely when we made the change to the VIP and for a week or so after and we saw no ill effects.  Hence the reason I’m happy to post this recommendation as I’ve since deployed another similar hybrid IdP and am now designing another and have to commit this recommendation to those designs.

I hope this helps.  (And I hope it makes sense.)  Smile

Posted in AD FS, Azure MFA | Tagged , , , , , , , , , | 2 Comments

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