Windows Azure Pack with ADFS and Windows Azure Multi-Factor Authentication – Part 3

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
  • Get-AdfsToken
  • 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.

ADFS Auditing

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.

01 GPO

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

gpupdate /force

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.

02 Federation Service

Select the events tab and enable the Success audits checkmark.

03 Success audits

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.

04 Event ID 501

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.

Get-AdfsToken

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′
Add-Type -AssemblyName ‘System.IdentityModel, Version=4.0.0.0, 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

    if ($allowSelfSignCertificates)
{
$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)
[System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($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))
return $result
}

# 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
$token

The result of this script is base64 encoded.

05 Token

You can use any online base64 decoder to paste the string and decode it. I used http://www.base64decode.org/ for this screenshot.

06 Decode

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

07 Install-WindowsFeature

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.

08 WIF SDK Setup

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.

09 Copy

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.

10 identityModel

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.

11 Load User Profile

Save the settings, right-click the DefaultAppPool again and select basic settings. Change the .NET CLR Version to .NET CLR Version v2.0.50727.

12 ApplicationPool

Edit the bindings of the default website, add port 443 and select the web server certificate we imported earlier.

13 Site Binding

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/).

14 FedUtil

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.

15 Relying Party

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

c:[]
=> issue(claim = c);

16 All Claims

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.

17 ClaimApp

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.

More Information

Two fellow MVPs have written a couple of great in-depth posts on ADFS that are related to this blog series.

Steve Syfuhs – http://www.syfuhs.net/post/2014/02/07/Windows-Azure-Pack-Authentication-Part-3-Using-a-Third-Party-IdP.aspx

Jorge de Almeida Pinto – http://jorgequestforknowledge.wordpress.com/2013/07/08/enabling-auditing-of-issued-claims-in-adfs-v2-x-and-adfs-v3-x/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">