In the previous part of this blog series Windows Azure Pack was configured to use ADFS for authentication for the Tenant Site and the Admin Site. We have done numerous implementations of Windows Azure Pack where ADFS was part of the design. In the first production deployments we struggled with setting the correct claim values for Co-Admins on subscriptions and admin access for the Admin Site based on groups, like the issue described at the and of the previous part of this blog series. Since then we have learned (or at least we tried) and there are a couple of ways that you can use to gain some insight into the actual issued claims by ADFS. Now please understand me correctly, there will probably be more ways to do the same. I just collected the procedures that we stumbled upon during the troubleshooting moments. We have used the following functionalities to look at issued claims.
- ADFS Auditing
- WIF SDK Claim App
There are probably more or better ways to look at the claims issued by ADFS. If you know any, please don’t hesitate to add them to the comments at the end of this blog post.
Active Directory Federation Service provides a built in functionality to log success and failure audits in the event log of the ADFS server. The success audits contain the actual claims provided by ADFS. Besides enabling this functionality in ADFS, auditing rights must also be enabled for the ADFS service account on the server running ADFS.
The first step is to enable auditing rights for the ADFS service account on the server running ADFS. You can configure this with a local policy or a group policy. Open the local or domain policy that will apply to your ADFS server and browse to the Computer Configuration > Policies > Windows Settings > Security Settings > Local Policies > User Rights Assignment entry.
Open the Generate security audits setting and add the domain service account used in the ADFS configuration wizard in part one of this blog series (domain\SVC_ADFS). Update the policy settings on the ADFS server by running the following command
Enable auditing on the ADFS server by running the following command
auditpol.exe /set /subcategory:”Application Generated” /failure:enable /success:enable
Open the ADFS management console. Right-click the root of the entries and select Edit Federation Service Properties.
Select the events tab and enable the Success audits checkmark.
Logon to the Windows Azure Pack portal (Tenant or Admin) that is configured for ADFS. After authenticating to ADFS open the event viewer on the ADFS server and browse to the default Windows Logs > Security entry. The log will contain events with the source AD FS Auditing. For each authentication against ADFS a couple of different events are logged. Event ID 500 and 501 will contain the actual issued claims for that session.
Since the default security log records a lot of events, it might be a good idea to create a custom view and target it on the Source AD FS Auditing or even filter it for Event ID 500 and 501.
If you are not allowed to enable auditing or you just want to do a one time validation for configuring access to the admin portal it is also possible to use a PowerShell cmdlet to request a token. The token will contain the issued claims. Logon the Windows Azure Pack server. It is recommended to use the PowerShell ISE (Integrated Scripting Environment), because it will give you Intellisense, but you can also run the cmdlets in a default PowerShell console.
The following function requests a token from ADFS. Fill in you environment variables at the bottom of this script.
function Get-AdfsToken([string]$adfsAddress, [PSCredential]$credential)
$clientRealm = ‘http://azureservices/AdminSite’
$allowSelfSignCertificates = $true
Add-Type -AssemblyName ‘System.ServiceModel, Version=188.8.131.52, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Add-Type -AssemblyName ‘System.IdentityModel, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b77a5c561934e089′
$identityProviderEndpoint = New-Object -TypeName System.ServiceModel.EndpointAddress -ArgumentList ($adfsAddress + ‘/adfs/services/trust/13/usernamemixed’)
$identityProviderBinding = New-Object -TypeName System.ServiceModel.WS2007HttpBinding -ArgumentList ([System.ServiceModel.SecurityMode]::TransportWithMessageCredential)
$identityProviderBinding.Security.Message.EstablishSecurityContext = $false
$identityProviderBinding.Security.Message.ClientCredentialType = ‘UserName’
$identityProviderBinding.Security.Transport.ClientCredentialType = ‘None’
$trustChannelFactory = New-Object -TypeName System.ServiceModel.Security.WSTrustChannelFactory -ArgumentList $identityProviderBinding, $identityProviderEndpoint
$trustChannelFactory.TrustVersion = [System.ServiceModel.Security.TrustVersion]::WSTrust13
$certificateAuthentication = New-Object -TypeName System.ServiceModel.Security.X509ServiceCertificateAuthentication
$certificateAuthentication.CertificateValidationMode = ‘None’
$trustChannelFactory.Credentials.ServiceCertificate.SslCertificateAuthentication = $certificateAuthentication
$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($credential.Password)
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
$trustChannelFactory.Credentials.SupportInteractive = $false
$trustChannelFactory.Credentials.UserName.UserName = $credential.UserName
$trustChannelFactory.Credentials.UserName.Password = $password #$credential.Password
$rst = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.RequestSecurityToken -ArgumentList ([System.IdentityModel.Protocols.WSTrust.RequestTypes]::Issue)
$rst.AppliesTo = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.EndpointReference -ArgumentList $clientRealm
$rst.TokenType = ‘urn:ietf:params:oauth:token-type:jwt’
$rst.KeyType = [System.IdentityModel.Protocols.WSTrust.KeyTypes]::Bearer
$rstr = New-Object -TypeName System.IdentityModel.Protocols.WSTrust.RequestSecurityTokenResponse
$channel = $trustChannelFactory.CreateChannel()
$token = $channel.Issue($rst, [ref] $rstr)
$tokenString = ([System.IdentityModel.Tokens.GenericXmlSecurityToken]$token).TokenXml.InnerText;
$result = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($tokenString))
# Fill in values
$adfsAddress = ‘https://sts.hyper-v.nu‘
$username = ‘domain\username‘
$password = ‘password‘
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username,$securePassword
$token = Get-AdfsToken -adfsAddress $adfsAddress -credential $credential
The result of this script is base64 encoded.
You can use any online base64 decoder to paste the string and decode it. I used http://www.base64decode.org/ for this screenshot.
The Get-AdfsToken is a quick solution for a one time use for Administrators. But what if you want an end user that does not have access to these servers to look at their claims.
WIF SDK Claim App
This was actually the first feature I used to look at the issued claims and is probably the most user friendly. The Windows Identity Foundation SDK contains a sample claims-based application that runs on a Windows Server 2012 R2 web server and displays the issued claims in a web browser. The claims-based application uses its own relying party on the same ADFS server that Windows Azure Pack uses for authentication. Deploy a Windows Server 2012 R2 server. When the installation is complete add the required roles and features by running the following cmdlet.
Install-WindowsFeature Web-Server, Web-WebServer, Web-Common-Http, Web-Default-Doc, Web-Dir-Browsing, Web-Http-Errors, Web-Static-Content, Web-Health, Web-Http-Logging, Web-Performance, Web-Stat-Compression, Web-Security, Web-Filtering, Web-App-Dev, Web-Net-Ext, Web-Asp-Net, Web-ISAPI-Ext, Web-ISAPI-Filter, Web-Mgmt-Tools, Web-Mgmt-Console, NET-Framework-Features, NET-Framework-Core, NET-Framework-45-ASPNET, Windows-Identity-Foundation, PowerShell-V2
Download WindowsIdentityFoundation-SDK-3.5.msi from http://www.microsoft.com/en-us/download/details.aspx?id=4451. Install WindowsIdentityFoundation-SDK-3.5.msi on the webserver.
After completing the WindowsIdentityFoundation-SDK-3.5.msi installation, import the wildcard certificate we defined in part one of this blog series. It is also possible to use an other web sever certificate. The common name or subject alternate name of the certificate should match the FDDN a user will specify in a URL to access the claims-based application. In this example we will use claims.hyper-v.nu. Create a DNS zone and DNS records in the internal DNS server that resolves the fqdn (in this example claims.hyper-v.nu) to the IP address of the web server.
Copy the contents of C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5\Samples\Quick Start\Web Application\PassiveRedirectBasedClaimsAwareWebApp to C:\Inetpub\Claimapp.
Open the Default.aspx.cs in c:\inetpub\Claimapp with notepad. Locate the second instance of ExpectedClaims and comment out the entire IF statement and its braces. The FOREACH statement should now match this example.
Foreach (claim claim in claimsIdentity.Claims)
//Before showing the claims validate that this is an expected claim
//If it is not in the expected claims list then don’t show it
//if (ExpectedClaims.Contains( claim.ClaimType ) )
writeClaim( claim, table );
Save and close Default.aspx.cs and open web.config from :\inetpub\Claimapp with notepad. Remove the entire <microsoft.identityModel> section. Save and close web.config.
Open the application pools in IIS manager, right-click the DefaultAppPool and select Advanced settings. In the Advanced settings screen change the Load User Profile to True located in the Process Model category.
Save the settings, right-click the DefaultAppPool again and select basic settings. Change the .NET CLR Version to .NET CLR Version v2.0.50727.
Edit the bindings of the default website, add port 443 and select the web server certificate we imported earlier.
Right-click Default Web Site and select Add Application. Set the alias to claimapp and the physical path to c:\inetpub\claimapp. Execute FedUtil.exe located in C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5. Specify the application configuration location to C:\inetpub\claimapp\web.config and set the application URI to the URL for your site (https://claims.hyper-v.nu/claimapp/).
In the security Token Service page select Use an existing STS and specify the metadata URL of the ADFS server (https://sts.hyper-v.nu/FederationMetadata/2007-06/FederationMetadata.xml). Select Disable certificate chain validation in the STS signing certificate chain validation error page of the wizard. On the Security token encryption page select No encryption. Accept the defaults in the Offered claims page and enable the checkmark to Schedule a task to perform daily WS-Federation metadata updates on the Summary page.
Just like we did with configuring Windows Azure Pack we need to create a relying party in ADFS for the claimapp. Logon to the ADFS server and open the ADFS management console. Navigate to AD FS > Trust Relationships. Right-click Relying Party Trusts and select Add Relying Party Trust.
Select Import data about the relying party published online or on a local network on the Select Data Source page and specify the metadata URL for claimapp (https://claims.hyper-v.nu/claimapp/federationmetadata/2007-06/federationmetadata.xml). Accept the generated Display name for the Relying party or enter your own. Accept the default value I do not want to specify multi-factor authentication setting for this relying party trust at this time. We will configure Multi Factor Authentication integration in the next part of this blog series. Select to Permit all users to access this relying party and keep the checkmark enabled to open the Edit Claim Rules dialog for this relying trust when the wizard closes and finish the wizard.
In the Issuance Transform Rules tab of the Edit Claims Rules console click Add Rule. Select the Send Rules Using a Custom Rule in the Rule Template dropdown. Give a descriptive name for the claim rule (for example Pass Through All Claims) and enter the following code in the Custom rule field
=> issue(claim = c);
Submit the Custom Rule and apply the Claim Rules. Browse to the claimapp (https://claims.hyper-v.nu/claimapp). You will be redirected to the ADFS logon portal. After you successfully authenticated in the ADFS portal you will be redirected back to the claimapp, which will display a table view of all the issued claims.
Instead of the custom rule to accept all claims, it is also possible to specify a pass through rule on the relying party passing through a particular claim to see the result of that single rule. In the next and last part of this series Multi Factor Authentication will be integrated with ADFS.
Two fellow MVPs have written a couple of great in-depth posts on ADFS that are related to this blog series.