Table of Contents
Recently I tried to setup a Citrix DaaS environment with OnPrem VDA’s and FAS for a working Azure AD B2B scenario. Every B2B customer’s UPN suffix is created OnPrem with the matching Shadow Account.
Update – August 23, 2023
It is now possible to use cip_sid (SID) OR cip_upn (UPN) Attribute when using SAML 2.0 as the Identity Provider for DaaS – so the following mentioned post for the usage of a NetScaler isn’t a requirement, anymore.
You are able to delete cip_sid which will enter it in cursive:
Start using the Citrix Cloud SAML SSO Azure AD Gallery Enterprise Application and modify the Attributes & Claims, delete the cip_sid Claim:
That’s it, now I am able to login to DaaS via SAML 2.0 to Azure AD with my employee tenant and the mapping to my OnPrem LAB Shadow Account AD User is working fine – no error because of no SID-Check:
Begin of the older Post – Background Information
When starting a HDX Session via a B2B Azure AD User with a matching Shadow Account, SSO to the VDA never works. On FAS, you’re getting the following Eventlog issue with Event ID 103:
[S103] Server [CC:XXXXXXXX] requested UPN [email@example.com] SID S-1-5-21-984895203-1488828186-1863578951-5285, but lookup returned SID S-1-5-21-977221278-2530229883-4204116001-9174. [correlation: cc#967472c8-4342-489b-9589-044a24ca57d1]
Citrix FAS is checking the SID of my Azure AD User and comparing that to my OnPrem Shadow Account AD User. Logically, the SID’s in this B2B situation never match. There is no difference when using the DaaS Azure Active Directory Connector (which is using OAuth and OpenID Connect) or SAML 2.0.
When doing a typical sync from OnPrem AD to Azure AD with AD-Connect or Cloud Sync to only one tenant, everything will work fine as the synced SID’s are matching.
Looks like the issue is present since years (Eg Citrix Discussions) but never found any listed solution for this.
But why is it working fine with OnPrem CVAD + NetScaler with SAML to AAD + FAS and some B2B customers? The difference is the authentication-process!
When using OnPrem NetScaler as the frontend, NetScaler inspects the UPN and finds the corresponding shadow account, the shadow account’s SID is used and the session launches fine on the VDA with SSO.
When using Workspace Service / DaaS as the frontend, the session is created for the principal account, not the shadow one, and as FAS will lookup the user account by UPN and get the SID of the shadow account, this will NOT match the SID of the Workspace Service session. As a security precaution, FAS will not perform SSO to the VDA and the above mentioned eventlog S103 will take place.
The goal would be to disable the SID-Check, as this breaks the architecture of how shadow accounts work. When doing SAML 2.0 / Azure Active Directory (OAuth & OpenID Connect) / Citrix Gateway or Adaptive Authentication mechanisms with your DaaS Tenant, you can see the mandatory for SID.
I’ve already written about Citrix Gateway as an IdP for Citrix DaaS and the needed requirements – SID included!
Finding the Way
To push the missing Shadow Account SID to Citrix DaaS, it was clear to use OnPrem NetScaler / Adaptive Authentication / NetScaler on Azure / NetScaler “on whatever” connected as the “Citrix Gateway” Authentication Method in DaaS Identity and Access Management and also enabled in Workspace Configuration.
I’ve setup the OAuth IdP Policy and Profile, bound to an AAA vServer and bound also a SAML Azure AD Auth-Policy, which is configured as an Enterprise Application in Azure AD.
The way it should work for the authentication to take place than looks like customer.cloud.com -> NetScaler OAuth Policy -> Azure AD SAML Policy -> NetScaler OAuth Policy -> customer.cloud.com but I got authentication errors from Citrix Workspace Service.
Checking my ns.log brought me to the following important notes:
"OAUTHIDP: CC IDTOKEN: user: <firstname.lastname@example.org>'s claims are: sub:\, name:, upn:, email:, ctx_auth_alias:, cip_domain:, cip_forest: sid:, oid:, amr:["external"], nonce:6380854677429951, familyname:, givename:, domain: , groups len 0" "OAUTHIDP: CC IDTOKEN: user: <email@example.com>'s claims are insufficient to perform federation with Citrix Cloud, domain len 0, displayname_len 0 cip_domain_len 0, cip_forest_len 0, sid_len 0, oid_len 0, upn_len 0, email_len 0 "
The claims like name, upn, email and also SID are empty. As we’re doing OAuth followed by SAML, no authentication takes place on the NetScaler(OnPremAD)-side, how should the required attributes ever reach Citrix Cloud?
One Solution is to deploy two extended Attributes to your Azure Active Directory, containing guestShadowUPN and guestUserOnPremSID. It’s described in detail in this Article, but as you have to do the mapping to every user from every B2B customer, that’s no option and to much manual work to do for any enterprise organizations.
My preferred way in this situation is the following:
You have to insert a OnlyUsername.xml No-Auth LDAPS LoginSchema in between the flow, so it looks like customer.cloud.com -> NetScaler OAuth Policy -> NetScaler OnlyUsername LoginSchema -> Azure AD SAML Policy -> NetScaler OAuth Policy -> customer.cloud.com
The user needs to enter their sAMAccountName or UserPrincipalName for extracting all required OnPrem-AD attributes, also containing the SID of the matching OnPrem Shadow Account, which is than send (after the successful login to Azure AD) to Citrix Cloud via the OAuth IDTOKEN, SSO to your VDA is working fine and Error S103 on FAS Servers is gone!
Here’s a successful test with my Option Consulting AAD User logging in to my HDXLAB AAD Tenant and this is how the user experience looks like. NetScaler sends the required SID of my HDXLAB OnPrem Shadow Account to Workspace Service.
Now this is how the ns.log looks like:
"SAMLIDP: LOGIN SUCCESS; Core <0>, adding SAML/OAUTH entry with action <oAuthProf_DaaS> in session for user <firstname.lastname@example.org>" "OAUTHIDP: CC IDTOKEN: user: <email@example.com>'s claims are: sub:HDXLAB\julian.jakob, name:Julian Jakob, upn:firstname.lastname@example.org, email:email@example.com, ctx_auth_alias:Julian Jakob, cip_domain:hdxlab.ch, cip_forest:hdxlab.ch sid:S-1-5-21-9772812045-2530229883-4204116001-9255, oid:b33376b0-3aed-4e1f-ejj2-eed0d4520a86, amr:["password","external"], nonce:638085492672781615, familyname:Jakob, givename:Julian, domain: HDXLAB, groups len 1"
Here are the CLI commands containing this setup.
#Adding OAuth Profile add authentication OAuthIDPProfile oAuthProf_DaaS -clientID 12345 -clientSecret 12345 -redirectURL "https://accounts.cloud.com/core/login-cip" -issuer "https://aaa.customer.com" -audience (clientid) -skewTime 10 -encryptToken ON -sendPassword ON add authentication OAuthIdPPolicy OAuthPol_DaaS -rule true -action oAuthProf_DaaS #Adding Azure AD SAML Profile and PolicyLabel add authentication samlAction META_saml_server_OPTION -metadataUrl "https://login.microsoftonline.com/b6bc373-b78cfcacd6/federationmetadata/2007-06/federationmetadata.xml?appid=e36ca49e-e8-e7665cb4a5" -samlSigningCertName wildcard.customer.com -samlIssuerName "https://aaa.customer.com" -enforceUserName OFF -logoutURL "https://login.microsoftonline.com/b67373-b82-4b0-b235-70dcccd6/saml2" -logoutBinding REDIRECT -metadataRefreshInterval 3600 add authentication Policy META_Saml_Server_Azure_OPTION -rule true -action META_saml_server_OPTION add authentication policylabel AuthPol_SAML_AAD_Option -loginSchema LSCHEMA_INT bind authentication policylabel AuthPol_SAML_AAD_Option -policyName META_Saml_Server_Azure_OPTION -priority 100 -gotoPriorityExpression NEXT #Adding LDAPS NoAuth Group Extraction Action and Policy add authentication ldapAction srv_ldaps_lb_GroupExtractionNoAuth -serverIP 10.10.10.10 -serverPort 636 -ldapBase "DC=contoso,DC=local" -ldapBindDn firstname.lastname@example.org -ldapLoginName UserPrincipalName -groupAttrName memberOf -subAttributeName CN -secType SSL -authentication DISABLED -passwdChange DISABLED -nestedGroupExtraction ON -groupNameIdentifier sAMAccountName -groupSearchAttribute memberOf -groupSearchSubAttribute CN add authentication Policy AuthPol_LDAPS_NoAuth -rule true -action srv_ldaps_lb_GroupExtractionNoAuth #Creating OnlyUsername Loginschema add authentication loginSchema LoginSchema_OnlyUsername -authenticationSchema "/nsconfig/loginschema/LoginSchema/OnlyUsername.xml" add authentication loginSchemaPolicy lschema_OnlyUsername -rule true -action LoginSchema_OnlyUsername #Creating AAA vServer and binding Policies add authentication vserver AAA_vServer_DaaS SSL 0.0.0.0 bind authentication vserver AAA_vServer_DaaS -policy lschema_OnlyUsername -priority 100 -gotoPriorityExpression END bind authentication vserver AAA_vServer_DaaS -policy AuthPol_LDAPS_NoAuth -priority 70 -nextFactor AuthPol_SAML_AAD_Option -gotoPriorityExpression NEXT bind authentication vserver AAA_vServer_DaaS -policy oAuthPol_DaaS -priority 90 -gotoPriorityExpression NEXT #Binding your cert to vpnglobal is a requirement bind vpn global -certkeyName wildcard.customer.com
I hope this Post will save you some time troubleshooting FAS SSO issues with B2B / Shadow Accounts when using Azure AD or any IdP where Citrix DaaS checks the SID as a requirement.
Hopefully there will be an option to disable SID-Check in DaaS, soon – as mostly the Unique User Identifier (Name ID) in the world of IdP’s primary focusses on UserPrincipalName.
By the way, you can also use OAuth to Azure AD instead of a SAML Policy, if you want to stay permanent on OAuth 🙂
Thanks to Nathanael Davison for the open minded discussion about this issue.