ADFS + OpenID Connect email claim and external ADFS - active-directory

I'm having difficulties setting up ADFS with OpenID Connect on Windows Server 2016.
I've setup AD for testing and I can successfully authenticate, however the email claim is not in the id token.
Additionally I've setup an external ADFS in the Claims Provider trust. It is displayed as an option, however upon logging in I get the error:
MSIS9642: The request cannot be completed because an id token is required but the server was unable to construct an id token for the current user.
Anybody have suggestions on how to fix this?

The root cause of MSIS9642 is that the new OpenID Connect Application Group features in ADFS 2016 need to issue an access token to your application. This token must include the users identity. In order to issue the token the subsystem must understand which claim in the inbound claims is used to uniquely identify the user.
A new property called AnchorClaimType has been added to the Claim Provider Trust model.
When ADFS is first installed it registers a built in Claim Provider Trust for AD AUTHORITY and sets the value for AnchorClaimType to
foo://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname
You can see this by using the powershell command get-adfsclaimsprovidertrust.
This is why OpenID works for when authenticating against Active Directory.
When you create a new Claim Provider Trust the system does not set an AnchorClaimType. The OpenID system can't issue a token because it does not know which inbound claim constitutes the unique user identity. This is why OpenID does not work when authenticating against an external Claim Provider trust.
In order to resolve this problem you need to take a few actions:
a) Verify that you are running Windows Server 2016 RTM Unfortunately the powershell attribute to set AnchorClaimType does not exist in the CTP, and the property cannot be set using the UI.
b) Choose a claim from the inbound token that represents the users identity and identify the claim type. In our case we were federating with an Azure Active Directory and chose name, and the type is foo://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
c) Set the AnchorTypeClaim for the Claim Provider Trust to the type selected by using powershell
set-adfsclaimsprovidertrust -targetidentifier identifier -AnchorClaimType http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
(get identifier from powershell get-adfsclaimsprovidertrust)
d) Create at least one inbound rule that passes through the value for the primary input claim, in our case Name
Hope this helps

To solve the problem with the missing AnchorClaimType parameter for additional added Claim Provider Trusts (CPT) a workaround for Windows Server 2016 TP5 (until end of support) can be used.
Workaround:
If CPT is already existing, delete the CPT.
Use the powershell command Add-AdfsClaimsProviderTrust
Either parameter wise (see Technet Description)
Or using a Metadata URL + the Parameter -AnchorClaimType "yourAnchorClaimValue".
Create at least one inbound rule that passes through the value for the primary input claim
In my case the following PS command solved the problem:
[String]$ClaimProviderTrustName = "YourCPTName"
[String]$MetaDataURL = "https://..."
[String]$AnchorClaimType = "YourAnchorClaimValue"
Add-AdfsClaimsProviderTrust -Name $ClaimProviderTrustName -MetadataUrl $MetaDataURL -AnchorClaimType $AnchorClaimType

I work at Microsoft. My customer had this same error. This is how we fixed it. We used Claims x-ray. We had them do a login with an identity from Active Directory and then do a login with an identity that uses an external claims provider trust.
When we compared the Claims X-Ray output, the value for anchorclaimtype didn't look right on the claims provider trust test login. We made a change in the claims provider to issue http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress as the anchor claim type and it resolved the problem.
v-michall#microsoft.com

Related

Azure B2C - Can't get a B2C user flow to work with another Azure AD instance as a custom identity provider

I have set up a B2C instance OK and managed to get a basic Blazor (server) app working with it a using the Microsoft Identity Platform (using AD groups for permissions - it was a hassle but works).
However, I'm trying to use an external Azure AD as a custom identity provider in the user flow, so that I am not just restricted to just email/id/social accounts, but can have guest accounts from other directories use the app without having to manage their sign-in's. To do that I performed a web app registration in the AD tenant that I wanted to use to authenticate those accounts against (as suggested in a couple of tutorials).
The application I registered in the external AD has a Redirect URI in the format "https://{My B2C Directory Name}.b2clogin.com/{My B2C Directory Name}.onmicrosoft.com/oauth2/authresp", which matches the name of my B2C instance, and I have added the client id and secret generated from that app registration and put the details into the custom identity provider I have created for the sign-in flow, as per the instructions here (including the mappings etc.):
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow
I also found a slightly older tutorial here, which is pretty similar (different mappings) that I've tried to follow (and adapt the bits that are out-of-date).
https://medium.com/the-new-control-plane/connecting-azure-ad-b2c-to-azure-ad-via-the-b2c-custom-identity-provider-42fbc2832e32
However when I run the user flow I get "AADSTS900971: No reply address provided." - this happens even when I run the flow directly from the User Flows tab in B2C with a 'Reply URL' explicitly set to "http://jwt.ms" (just to capture the token contents).
I'm confused about the reply URL being missing because they exist in both registered apps. Also, it's not saying they're mismatched, just that one isn't set at all (but appears to be).
It feels like I'm missing something simple - does anyone have any idea what that might be?
Ok so I did a couple of things to resolve this:
Re-registered the application in the AD I want to authenticate with (following this tutorial again: https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow)
I was careful to ensure that the redirect URI in the format:
https://{B2C Instance Name}.b2clogin.com/{B2C Instance Name}.onmicrosoft.com/oauth2/authresp
was all lower case.
I also had to change from just a 'sign-in' user flow to the 'sign-up, sign-in' one, and then applied the custom identity provider to that flow. Apparently you need that even for users from another AD to be able to complete their invite process (otherwise you just end up with a user doesn't exist error - even if you've invited/added them to the B2C users list).
I also elected to 'Grant admin consent for Default directory' under the API Permissions tab for the application being registered in the external AD (to be used for the custom identity provider).
The flow seems to work now. The only thing that would be useful would be to have an invite only sign-up, sign-in flow so that you could invite specific people without breaking the invite process.
If anyone knows how to do that please do post something.

Azure Active Directory override claim value in OIDC id-token

I'm looking for a way to ensure that the unique_name claim value is always the user email for a specific app in AAD which is accessed via SSO. I've noticed that for users which have an AAD account, the unique_name claim value is the email address, but for external users (invited to the directory) it is prefixed by "live.com#" (e.g. live.com#email#domain.com). The application uses the OIDC protocol. I've been trying different things from Microsoft docs:
Setting a different source for the unique_name claim
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-saml-claims-customization
(I'm not sure if this applies, given the app uses OIDC, not SAML)
(By the way, that documentation is a bit out of date because I found that the "User Attributes & Claims" section is now in SSO https://learn.microsoft.com/en-us/answers/questions/248748/cant39-find-the-34user-attributes-amp-claims34-sec.html)
Building a claim-mapping policy
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping
None of these worked.
I know this would be trivial in AAD B2C with custom policies. Is it possible to override the unique_name claim with the email claim in AAD?
Update 07/01/2022
After further investigation I found that the unique_name claim is only supported in v1 tokens https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens, https://github.com/MicrosoftDocs/azure-docs/issues/62971 (the current version is v2) and it's transformed into "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" by the ADAL client. This customization I'm after is for a legacy .NET app which uses ADAL on the v1 AAD endpoint.
The only way I can think of how to achieve this is via a claim-mapping policy, however I haven't been able to do make it work. Any ideas would be appreciated.
I believe I've found an answer to my question and the conclusion is that it's not possible to override the unique_name claim, because it's a restricted claim. The closest I got was to map the mail claim to a custom claim (e.g. unique_name2). This is what I did:
https://learn.microsoft.com/en-gb/azure/active-directory/develop/active-directory-claims-mapping
I installed the Azure AD PowerShell Module public preview (I had to uninstall the AD Powershell module first because I already had a later version)
I followed the instructions on the document above to connect to AAD and view any existing claim mapping policies
Connect-AzureAD -Confirm
Get-AzureADPolicy
I followed the instructions on https://learn.microsoft.com/en-us/answers/questions/80295/claim-transformation-in-azure-id-token-upn-data-to.html to build a claim mapping policy, although I had to adapt it because it wasn't working. I ran the following commands:
3.1 I used the search command to search for my app (called "test").
GET-AzureADServicePrincipal -SearchString Test
This outputs a table like the following:
ObjectId AppId DisplayName
3.2 I ran the following command to create a new mapping policy which maps the "mail" claim to the "unique_name2" claim. (When I tried mapping to "unique_name" it works, but the "unique_name" claim doesn't get overridden, because it's a restricted claim)
$Policy=New-AzureADPolicy -Definition
#('{"ClaimsMappingPolicy":{"Version":1,"IncludeBasicClaimSet":"true",
"ClaimsSchema":[{"Source":"user","ID":"mail"},{"Source":"transformation","ID":"DataJoin","TransformationId":"JoinTheData","JwtClaimType":"unique_name2"}],"ClaimsTransformations":[{"ID":"JoinTheData","TransformationMethod":"Join","InputClaims":[{"ClaimTypeReferenceId":"mail","TransformationClaimType":"string1"}],
"InputParameters":
[{"ID":"string2","Value":""},{"ID":"separator","Value":""}],"OutputClaims":[{"ClaimTypeReferenceId":"DataJoin","TransformationClaimType":"outputClaim"}]}]}}')
-DisplayName "MapMailToUniqueName2" -Type "ClaimsMappingPolicy"
3.3 I ran the following command to add the custom policy to my app service principal (copying the id from step 3.1). This means the policy will be applied only to that specific app.
Add-AzureADServicePrincipalPolicy -Id {my app service principal id}
-RefObjectId $Policy.Id
That's all. When I tested SSO on my app, I had an additional claim "unique_name2" which contains the value of the "mail" claim.
There probably is a more efficient way of creating the mapping policy in step 3.2 but the way described in the step 3 link wasn't working for me so I came up with a concatenation to a blank string.
It doesn't seem possible to override the unique_naim claim because it's a restricted claim. I later found that this is actually documented here https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/active-directory/develop/reference-claims-mapping-policy-type.md. I wish I had found this earlier.

Kerberos Delegation and Authentication: Impact due to Domain change

[I'm fairly new to Kerberos Protocol]
We have a customer, who back in 2020 was using a domain let's call it customdom.itm, which has a user account krb-test-cd setup for Kerberos delegation and this domain is part of a domain Active Directory forest itm.
Since they're a large corporation with many users across different countries, they also have another huge domain AD forest with many child domains (and domain controllers) as part of this forest let's call it top.abc. Here the domain relevant to us is aust.top.abc, which has krb-test-aust user account setup for Kerberos.
Since the forests itm and top.abc are different, the same servicePrincipalName for both krb users is safely set to HTTP/testloadbalancer.com, while their userPrincipalName is of course different, i.e.:
krb-test-cd uPN is HTTP/testloadbalancer.com#CUSTOMDOM.ITM
krb-test-aust uPN is HTTP/testloadbalancer.com#AUST.TOP.ABC
And since https://testloadbalancer.com is part of the intranet sites at the customer, their browsers do not challenge the users to enter their AD credentials.
Now here's the problem:
Last month the customer decided to migrate the users in customdom.itm to a new domain can.top.abc which is part of forest top.abc. The user krb-test-cd and some other accounts were not migrated, however, and customdom.itm still exists in its own forest.
Due to the migration, these users are now challenged everytime to enter their AD credentials and are granted access only with the old domain name, i.e.
customdom\michael and password
I have setup a new user account krb-test-can in the domain can.top.abc for Kerberos delegation with setspn and the SPN HTTP/testloadbalancer.com, and the first time, I got this error:
The operation failed because SPN value provided for addition/modification is not unique forest-wide.
Next, I tried ktpass with SPN HTTP/testloadbalancer.com#CAN.TOP.ABC, and I get another error:
Failed to set property 'servicePrincipalName' to 'host/<host name>' on
Dn 'CN=<CN Name>,CN=Users,DC=<DC Name>,DC=<DC Name>,DC=abc': 0x13.
WARNING: Unable to set SPN mapping data.
Later, I finally understood that the SPN is already set to user krb-test-aust.
My question is:
How can I still successfully assign the SPN HTTP/testloadbalancer.com and eventually UPN HTTP/testloadbalancer.com#CAN.TOP.ABC to the user krb-test-can without affecting Kerberos delegation to user krb-test-aust?
Or is there a workaround on how I can use only the user krb-test-aust to delegate Kerberos authentications to the users now residing in domain can.top.abc without the need for user krb-test-can at all?
Any help is highly appreciated.
Thanks in advance!
[Some background]
We have an Access Management software on our side where we have configured many Identity Providers, 2 Policy Enforcement Points and Kerberos authentication for SingleSignOn for each of the above 2 domains.
We only need to inject the uPN and the password of the krb users into the respective PEPs and the software doesn't require a keytab file.
We are identity providers and the customer uses some links like https://testloadbalancer.com/xyz/efg_idp/entityid to make an IdP initiated login and is redirected to the target application.
Taking a hint from Steve, I finally found the solution to my own problem:
Since the domains can.top.abc and aust.top.abc and other domains are part of the same forest top.abc, they SHOULD have a cross-domain trust (I'm not sure if the trust is default or needs to be setup separately).
Hence, I only need the user account krb-test-aust to delegate Kerberos authentications to the users residing in all domains under top.abc.
The uPN of krb-test-aust remains HTTP/testloadbalancer.com#AUST.TOP.ABC.

Is there something special I need to do to ensure the AAD Connect synchronized extension attribute is included in the token claims?

I have a query about adding extension claims to an idToken using MSAL.JS 1.1.3 – it seems to work where an extension attribute was added into an AAD application but not via AAD Connect.
Is there something I need to do to ensure the AAD Connect synchronized extension attribute is included in the token?
I have the following setup with 2 extension attributes (extension_{id}customOptionalclaimApplicationUserCode (added in AAD) and extension{id}_stuExtensionAttriibute1 (added to AAD via AAD Connect from on-prem AD):
PowerShell displaying attributes
Customer wants to use manifest so I set this up.
The decoded token only includes extn.CustomOptionalClaimApplicationUserCode as per this image
The customer prefers to use manifest optional claims as per the example for least privilege per application instead of claims mapping policy.
To repeat the question: Is there something special I need to do to ensure the AAD Connect synchronized extension attribute is included in the token claims?
From your screenshot, extension_{id}_customOptionalclaimApplicationUserCode 's id is different from extension_{id}_stuExtensionAttriibute1's id.
One of the two ids is the id of the app itself. The other one is the app id used by AD Connect.
Now, the fact is that, from the API , one extension attribute is lying on an application.
However, you can only get the extension claim value from its application in a token. In other words, the resource id should be the same as the id in the extension, then you can get the extension value. For more details, you may refer to: Configuring optional claims
So, the result you got is expected. The extension synced from on premise will not be included in token.

Kerberos/SPNEGO : multiple SPNs for the same AD account

Deploying the same HTTP based application on several web servers (srv1, srv2, etc). Protecting the application with SPNEGO auth. The servers are Linux and AD doesn't know of their existence, i.e. they are not joined to the domain. I've got the whole SPNEGO working smoothly on a single host. Now moving on to the subsequent hosts.
Most guides I've found will tell you that you need
An account in AD
A SPN
A keytab (generated on the AD server and then
moved to the Linux host)
While I believe that (2) + (3) will always need to be per-server, I'm somewhat uncertain about (1). Can I do with only one account? I would really like to not having all these accounts in AD if I can do with only one.
This blog has a good recipe for how it can be done: The first invocation of ktpass (for srv1) should be as described in the all the guides you find on the internet, however subsequent invocations (for srv2, srv3, etc) should be using the -setpass and -setupn options.
However I've found that when one uses the ktpass.exe tool the account's userPrincipalName attribute changes to become as given by princ argument from the last invocation of ktpass. So the name of the srv, e.g. srv3 is coded into the name and the name of the account will therefore basically change with each invocation of ktpass. When the web server performs the final step in the SPNEGO chain of events, which is to contact AD using the keytab as credentials, it will look for an account in AD with a userPrincipalName equal to the SPN and this step will therefore fail. (source, scroll to last post, list item 3). Contradicting this is that I'm using Tomcat and thereby JAAS and as far as I understand I can hardcode the principal name to use in my jaas.conf file thereby effectively ignoring the principal name from the keytab.
Can multiple app servers + single account in AD ever work and if so how?
In short, yes it will work and I will tell you how. First of all let's clarify some things and some statements not properly described in your question or the comments:
You have three machines which serve the same DNS name, this means that you either have a DNS round-robin: service.example.com will returned a shuffled list if IPs or a load-balancer (hard of sort) will only one IP for the A record depending on the load. For Kerberos, both setups are equal in the outcome.
Now, you cannot say that the AD does not know the existence of a service or a server if you require Kerberos authentication. It will and must know otherwise it cannot create service tickets for your clients which they pass on to the server. Additionally, Tomcat will not contact the KDC to accept the security context because the service ticket is encrypted with the account's long-term key.
Here is the approach: You have already figured out that one SPN can be bound to one machine, multiple bindings are not allowed. This is the case when you have the machine name bound to the machine account (srv1$, etc.). You need a service account. The service account is a regular account without password expiration, e.g., my-service#EXAMPLE.COM. For this account, you will bind your CNAME or A record. Have you Tomcat authenticator to accept all securty contexts with this service account and it will work.
How to create this magical service account on a Unix-like OS?
Use mskutil to
create the service account,
create a keytab for that service account,
bind your SPN to that service account and have the keytab updated.
After that you will have a keytab suitable for your use. Verify with an LDAP query (e.g., with Softerra's LDAP browser or else) that the account exists, the SPN (servicePrincipalName) is bound to that account and you are done.
Important: if any of your clients use MIT Kerberos or Heimdal, you must set rdns = false your your krb5.conf.
Godspeed!

Resources