+ All Categories
Home > Documents > ClaimsBasedWPFWhitepaper

ClaimsBasedWPFWhitepaper

Date post: 14-Oct-2014
Category:
Upload: tamir-dresher
View: 87 times
Download: 6 times
Share this document with a friend
60
Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009 claimsbasedwpf.codeplex.com Page 1 Contents Claims-Based Security ................................................................................................................................... 2 Roles, Permissions and Claims .................................................................................................................. 3 How Are Claims Different?........................................................................................................................ 3 Federated Security Scenarios........................................................................................................................ 7 Federation Basics ...................................................................................................................................... 8 (Sort Of) Federation in a Single Domain ................................................................................................. 10 Federation with a Trusted Domain ......................................................................................................... 11 Claims Transformation ............................................................................................................................ 13 More Federation Scenarios..................................................................................................................... 15 Setting up a Simple Federation Scenario .................................................................................................... 17 Claims Model .......................................................................................................................................... 18 Exposing Federated Security Endpoints.................................................................................................. 19 Generating a Federated Proxy ................................................................................................................ 21 Deferring Proxy Initialization .................................................................................................................. 25 Issuing Claims at the STS ......................................................................................................................... 28 Authorization at the RP Service .............................................................................................................. 32 WPF Clients and Claims............................................................................................................................... 35 Claims-Based UI Models ......................................................................................................................... 36 Client Authorization Service ................................................................................................................... 38 Claims-Based Access Control .................................................................................................................. 40 Caching Security Tokens ............................................................................................................................. 47 ClientCredentials and Issued Tokens ...................................................................................................... 49 Caching Issued Tokens for Multiple Proxies ........................................................................................... 51 SecurityTokenCache................................................................................................................................ 52 CachedClientCredentials ......................................................................................................................... 53 CachedClientCredentialsSecurityTokenManager ................................................................................... 55 CachedIssuedSecurityTokenProvider...................................................................................................... 56 Sharing CachedClientCredentials and Updating Client Claims ............................................................... 58 Session and Token Lifetime .................................................................................................................... 59
Transcript

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 1

Contents Claims-Based Security ................................................................................................................................... 2

Roles, Permissions and Claims .................................................................................................................. 3

How Are Claims Different? ........................................................................................................................ 3

Federated Security Scenarios........................................................................................................................ 7

Federation Basics ...................................................................................................................................... 8

(Sort Of) Federation in a Single Domain ................................................................................................. 10

Federation with a Trusted Domain ......................................................................................................... 11

Claims Transformation ............................................................................................................................ 13

More Federation Scenarios ..................................................................................................................... 15

Setting up a Simple Federation Scenario .................................................................................................... 17

Claims Model .......................................................................................................................................... 18

Exposing Federated Security Endpoints.................................................................................................. 19

Generating a Federated Proxy ................................................................................................................ 21

Deferring Proxy Initialization .................................................................................................................. 25

Issuing Claims at the STS ......................................................................................................................... 28

Authorization at the RP Service .............................................................................................................. 32

WPF Clients and Claims ............................................................................................................................... 35

Claims-Based UI Models ......................................................................................................................... 36

Client Authorization Service ................................................................................................................... 38

Claims-Based Access Control .................................................................................................................. 40

Caching Security Tokens ............................................................................................................................. 47

ClientCredentials and Issued Tokens ...................................................................................................... 49

Caching Issued Tokens for Multiple Proxies ........................................................................................... 51

SecurityTokenCache ................................................................................................................................ 52

CachedClientCredentials ......................................................................................................................... 53

CachedClientCredentialsSecurityTokenManager ................................................................................... 55

CachedIssuedSecurityTokenProvider ...................................................................................................... 56

Sharing CachedClientCredentials and Updating Client Claims ............................................................... 58

Session and Token Lifetime .................................................................................................................... 59

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 2

Summary ..................................................................................................................................................... 60

Claims-based and federated security scenarios are becoming more and more popular in recent years,

and as the tools continue to simplify the developers experience for these scenarios this trend will only

continue. But, it is not merely a trend for there is real value in building claims-based applications and in

supporting various federated security scenarios. These are topics that have been discussed in various

fabulous blogs, whitepapers and webcasts – delivered by very smart people – but sometimes it is hard

to find a single document that tells a concise story to bootstrap knowledge from a particular

perspective. This whitepaper will focus on the impact of these claims-based and federated security

scenarios on WPF clients. You’ll learn about claims, and several common scenarios for claims-based and

federated security that involves WPF applications, and while the server side of this story will be briefly

discussed the focus will be how to develop the client side of that experience.

In terms of technologies used this paper will leverage Windows Presentation Foundation (WPF),

Windows Communication Foundation (WCF), “Geneva” Framework, and also touch on Windows

CardSpace and CardSpace “Geneva”. While the server-side leverages WCF with Geneva Framework,

examples discussed in this whitepaper will include WPF clients that use WCF and others that leverage

WCF with Geneva Framework to illustrate a particular scenario. Note that it is entirely possible to

implement claims-based and federated WPF clients without the help of Geneva Framework since WCF

supports the appropriate protocols – and the server-side can be implemented with any technology so

long as the appropriate protocols are used.

Some of the highlights include:

Understanding federated WCF bindings from the client developer’s perspective

Using claims to restricting access to features at the client

Caching and sharing tokens between proxies at the client

Recovering from session and token expiry in a graceful manner (with a nice proxy wrapper that handles it all!)

If you are WPF developer new to claims-based and federated security concepts, I hope that reading this

paper in its entirety will provide you with a digestible overview of these things along with some key

scenarios relevant to your plight building rich clients. If you are not new to these concepts, skip the

intro and jump to the meat of each implementation scenario.

Claims-Based Security Claims-based and federated security are related subjects that have been described in other whitepapers

and resources so I will not reinvent the wheel here by producing a long-winded section on these

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 3

subjects. In this section I will at least provide you with a short and high-level description of these

concepts to provide you with context if you are completely new to the subject – enough information so

that you can read this whitepaper without your eyes glossing over.

I’ll start with claims-based security – which one of the underpinnings of federated security.

Roles, Permissions and Claims When you build a security model for your applications you typically think role-based. Users supply

credentials, are authenticated, and are granted one or more roles that indicate their rights in the

system. Applications and services use those roles to determine which features the user can view in the

user interface, and services typically also restrict access to operations based on the same roles. There

are a few potential challenges with role-based security:

Roles might be renamed (think localization or co-branding) which means code relying on the name of each role is invalidated.

Roles can carry different meanings to different sets of application users (again, think co-branding or departmental use). For example a different department using the same application may want to reduce the rights of the Administrators group, and create a SuperUser account that has all privileges.

Applications often have to authenticate users from behind the firewall (intranet users using domain accounts) and from outside the firewall (Internet users with accounts in a custom credential store). To avoid writing authorization code against both sets of roles a single set of roles must be used for both users – usually by converting Windows roles to custom roles. If additional credentials are supported for authentication (such as certificates) the same rule applies.

In the past these problems were often solved by creating a permission-based security model whereby all

roles are mapped to a set of permissions and authorization code never looked at roles, only the resulting

permissions granted by the user’s roles. Claims-based security offers the same benefit as permission-

based security in the sense that it is a more granular artifact for authorization – but a claims-based

security model also has many other valuable characteristics.

How Are Claims Different? Discussing claims without discussing federation can be a bit of a chicken and egg problem, since the

value proposition for claims increases significantly when you implement federated scenarios – as does

the value of different types of claims. Put another way your applications can derive value in a claims-

based solution without federating with other partners and applications, but if you federate your

applications almost always rely on claims to authorize access to features and functionality. This is why I

like to first talk about claims in the context of how they benefit simpler application scenarios and then

expand the discussion to full-blown federation.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 4

With that in mind, in this section I will talk about claims from a simple point of view – what can they

contain and how are they different from roles and permissions in the classic sense. Later, I will talk

about claims again within the context of different federated scenarios which will hopefully add clarity.

First of all, a claim contains the following key bits of information:

Property Description

Claim Type Can be any string, often a Uri, indicating the type of claim. The type of claim could be representative of the user’s identity, roles, permissions, or other details such as name, email, age, birth date and so on. There are a number of predefined claim types defined as Uri in System.IdentityModel and Microsoft.IdentityModel (Geneva Framework) in their respective definitions for ClaimTypes. Example: Microsoft.IdentityModel.Claims.ClaimTypes.Role or “http://schemas.microsoft.com/ws/2008/06/identity/claims/role” Example: “Permission” or “http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission”

Claim Value The value for the claim, relevant to the claim type of course. This can be a string, Uri, or any other CLR type relevant to the claim type. Example: Claim Type = “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress” Claim Value = “[email protected]” Example: Claim Type = “http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission” Claim Value = “http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission/create”

Claim Issuer Claims differ from permissions in that they are issued by a trusted authority. If you issue the claims as part of your application, then your application is the authority. Once claims start traversing boundaries we need a more reliable way to “trust” the source of those claims and this is handled with digital signatures. To summarize, claims are typically received by a service in a signed security token where the signature is used to guarantee a) who issued the claims, and b) that the token has not been tampered with en route. The issuer is also represented a string value once the claims have been extracted from a security token.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 5

Example: Claim Type = Microsoft.IdentityModel.Claims.ClaimTypes.Role Claim Value = “BUILTIN\\Administrators” Claim Issuer = “System” Example: Claim Type = “Permission” Claim Value = “CreateCustomers” Claim Issuer = “CN=MySecurityTokenService”

The issuer usually grants a set of claims to an authenticated caller, sometimes referred to as a “claim

set” or more simply a “collection of claims”. Think about authenticating a user based on their username

and password and granting them one or more roles or permissions, for example. The party handling

authentication in a federated scenario is also considered the identity provider (IdP) and is responsible

for issuing identity claims (along with other claims if relevant) for the authenticated user. Ultimately

these claims travel across service boundaries in a security token – more on this later.

I mentioned that claims can sometimes represent roles or more granular artifacts such as permissions.

One example could be that a user is authenticated (don’t worry about where they are authenticated just

yet, we’ll get there) and their name and email are supplied as claims as shown in Figure 1.

Figure 1: Authenticating a user and granting a name and email claim

Another example could be that the same user is granted a set of roles a shown in Figure 2.

Figure 2: Authenticating a user and granting role claims

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 6

How about granting permissions instead of roles, for a more granular artifact useful for authorizing

access to features? Figure 3 illustrates an example of this.

Figure 3: Authenticating a user and granting permission claims

Or, how about granting all of these claims at once, so that the application can know information about

the user, the original roles they were granted, and the ultimate permissions that they have in the

application? Figure 4 illustrates this scenario.

Figure 4: Authenticating a user and granting informational, role and permission claims

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 7

Which scenario is best? Which is the most appropriate? Well, that depends how the application is wired,

and the way that claims are granted. For example, you may always want access to the name and email

claim for information, but only care about the permissions relevant to the current task. It may not be

practical to grant claims relevant to an entire application at once – there could be hundreds of possible

claims, some only relevant for particular services and operations. The point is that claims are a very

flexible artifact, they can be:

Informational: name, email, address, phone number, age

The answer to a question: Are you over 13? Do you belong to the sales department? Do you have a driver’s license?

Describe application rights: roles, permissions, operations you can call

As I mentioned before, claims are issued by a trusted authority. For an application to rely on a set of

claims for authorization, it must first have a trust relationship with the issuer of the claims. This is where

claims-based security adds value beyond a simple permission-based model. A set of claims can be issued

and passed in a security token signed by their issuer to guarantee the claims are not tampered on the

wire. If the signature matches one of the application’s trusted issuer certificates, the claims are

considered valid. This level of guarantee makes federation possible where the issuer may not be part of

the same domain as the application. And this is where we segue into a discussion of federated scenarios.

Federated Security Scenarios At this point you should be comfortable with the concept that a claim can carry information about the

user and his roles or permissions as deemed necessary by the application. So, hopefully the first

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 8

connection has been made that as long as you have a set of claims from a trusted issuer, your

application can authorize access to features and functionality. On to the next set of questions:

Where does the user authenticate?

Who issues the claims?

How are trust relationships established between the application and issuer?

Federation Basics The first, simple way to look at federation is to use a scenario that involves the common participants in a

federated security scenario: the Relying Party, the Identity Provider, the client application and the user

(or, subject) to be authenticated. The Relying Party (RP) can be any web site or service that relies on a

set of claims from a trusted issuer to authorize access. For the purposes of this whitepaper we will focus

on services that back WPF client applications. The Identity Provider (IdP) is the party responsible for

authenticating the user and can grant claims for the user on request. More specifically, if the IdP has a

Security Token Service (STS) it can issue a security token carrying claims for the authenticated user. The

IdP is responsible for using appropriate measures to authenticate the user before granting said claims

and the RP must be able to trust the those measure are adequate. The client application is responsible

for presenting credentials from the user (this can be done in a variety of ways depending on the

credentials used to authenticate to the STS) and calls the STS at the IdP to get a security token carrying

claims for the RP. The client then includes that security token in calls to the RP so that access can be

authorized according to the claims it carries.

This dance involving the user, the client, the STS and the RP, where the RP is a service, is known as active

federation based on WS-Trust protocol. Passive Federation is similar but all communications are handled

through the browser as the RP is a web site and the STS invoked over HTTP, and this is based on WS-

Federation protocol. Figure 5 illustrates the moving parts of active federation.

Figure 5: A simple federation scenario involving a single RP and STS

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 9

From a high level what is happening is this:

1. The client gathers the user’s credentials according to the requirements of the STS 2. The client sends a WS-Trust message called a Request for Security Token (RST) along with those

user credentials. The RST indicates the RP for whom the token will be issued, the type of token, and the claims the RP is requesting the token to contain (although, the STS often knows this based on the RP).

3. The STS determines if it has a trust relationship with the RP before doing anything. Assuming there is trust, the STS authenticates the user against its credential store and determines which claims to grant.

4. The STS issues a security token carrying the claims granted the user and returns it in a Request for Security Token Response (RSTR).

5. The client sends a request message to the RP passing along the security token for authorization. 6. The RP validates the token, verifies that it trusts the issuer, and authorizes the request based on

the security token’s claims. 7. If all goes well, the RP executes the request, possibly returning data.

Typically SAML tokens are issued in a federated security scenario. SAML is an interoperable, XML-based

security token format that contains one or more SAML assertions (equivalent to a claim) and is digitally

signed by the STS. Although this type of security token is not a requirement in federated security

scenarios – it is the most commonly used since most platforms support it.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 10

(Sort Of) Federation in a Single Domain In the last section I discussed the flow of communications between participants in a federated security

scenario, but I didn’t mention domains and this is a very important part of the discussion. The whole

point of federation is to support scenarios where users authenticate to their own domain (or, realm) and

present proof of that authentication in the form of claims. In the scenario shown in Figure 5 the user

authenticates to the IdP and presents the RP with a security token signed by the STS at the IdP which

proves he was authenticated. The RP doesn’t care how authenticate took place – it could be a Windows

credential, a username and password, a certificate or any other valid credential that the IdP can use to

validate the users identity. The RP must only trust the issuer to authenticate users in an acceptable

fashion – hence the trust relationship.

Do the IdP and RP belong to the same domain? Possibly yes, in a simple scenario. Let’s say you are

building an application with a bunch of services (this would be the RP) and you want to decouple the

process of authentication from the service so that you can support multiple different credential types

without affecting how the service handles authorization. By delegating authentication to an STS within

the domain of the RP (let’s call it the RP-STS) you can achieve this. This way, service developers can

focus on authorization of the claims issued by the RP-STS, and the RP-STS has the job of authenticating

users based on any number of supported credential types, and issuing a normalized set of claims

relevant to the RP for each authenticated user. This scenario is illustrated in Figure 6.

Figure 6: Delegating authentication to an RP-STS to support a streamlined support claims-based

authorization model

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 11

In this scenario, the RP-STS is also the IdP since it is responsible for authenticating users and granting

claims to those users.

In fact, this is not federation in the true sense of the word even though federation protocols are in play.

If the IdP were located outside the RP’s domain, we would have federation.

Federation with a Trusted Domain The RP can trust users from another domain removing the need to manage credentials for those users in

its domain. This yields several benefits including:

No need to duplicate user credentials at the RP

Prevention of errors keeping credentials in sync across domains

Prevention of errors introduced with provisioning and de-provisioning users in multiple locations

The flow of communication between user, client, RP and IdP are still the same (see Figure 7) however

the IdP is not owned by the RP. An explicit trust relationship is established between the RP STS (RP-STS)

and the IdP STS (IP-STS) before users can gain access to the RP.

Figure 7: Federation between the RP domain and IP domain

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 12

Why would an RP want to trust users from another domain? There are so many possible scenarios, all

leading to a common result – users from another trusted domain are granted access to features of the

RP. A few reasons for this could include:

Granting access to users of another application access to the RP within a corporate environment to enable Single Sign-On (SSO).

Granting access to users of another domain behind the firewall access to applications in the RP domain, again within a corporate environment.

Granting access to users within a particular department or role of another company access to certain features of applications in the RP domain.

Granting access to any user with a Windows Live account, preventing the need to provision user accounts at the RP.

Security tokens from a trusted issuer may vary in terms of the claims they carry. Sometimes the RP and

IdP can establish an agreed upon set of claims that is meaningful to the RP. For example the IdP might

supply a name claim plus information about the roles of the user in that domain. Perhaps the IdP

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 13

transforms some of its claims into claims that the RP can use to authorize access. For example the IdP

might convert a role from its system into a new claim indicating that role in a way the RP can

understand. It is also possible that the IdP may only supply proof of authentication with some

information about the user, such as in the case of Windows Live – where no roles or granular

permissions exist. A social networking application may not need to differentiate users by their role, they

either have an account or they don’t. In this case a set of claims that identifies the user without specific

roles and permissions is perfectly acceptable.

Claims Transformation In the scenario illustrated in Figure 6, the RP-STS is also the IdP authenticating users. It can therefore

generate claims appropriate for the RP to use to authorize users (roles, permissions, or other) after the

authentication step – no claims transformation required. In a true federation scenario that involves

other trusted issuers the IdP is not owned by the RP and so the claims issued by the IdP may not be

enough for authorization at the RP.

The RP can supply an STS (RP-STS) to transform claims issued by the IdP (from the IP-STS) into claims the

RP can use for authorization. Sometimes the IdP can be wired in advance to send claims that help the RP

perform this transformation, sometimes the RP can make use of whatever claims the IdP issues.

Figure 8 illustrates a scenario where the RP supplies an STS (RP-STS) to handle transformation of trusted

issuer claims. The RP-STS forms a trust relationship with the STS authenticating users from the IP domain

(IP-STS). Security tokens from the IP-STS flow to the RP-STS for transformation and the RP only deals

with tokens from the RP-STS. This makes it possible for the RP to authorize access based only on RP

claims relevant to the application, without having to handle claims from potentially different trusted

issuers.

Figure 8: Transforming claims at the RP-STS

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 14

The RP-STS can also act as IdP to clients in the same domain as shown in Figure 9. Once again, this

ensures that the RP need only worry about claims issued by its RP-STS – trusting that users have been

authenticated by their respective domains.

Figure 9: Authenticating internal users and transforming claims for users from a trusted domain

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 15

If you are familiar with .NET Services, part of Azure Services – Microsoft’s cloud computing platform –

the Access Control Service is an STS in the cloud that an RP can use to do nothing but transform claims

from trusted issuers. It removes the need for an RP to supply an RP-STS to perform this step, pretty cool

stuff.

More Federation Scenarios From a high level, federated security scenarios are not difficult to understand. An application (the RP)

needs some claims to authorize access, and needs to know that they come from a trusted issuer. The

user is authenticated by its domain (the IdP, which could be the RP or some other domain that the RP

trusts). The IdP grants claims and issues a security token with those claims. Ultimately the IdP claims are

transformed into claims that the RP can use for authorization (this can be handled by the RP, or by some

intermediate STS in a more advanced scenario).

Fortunately, the process of passing user credentials for authentication, gathering claims from the IdP,

and traversing whatever chain of STS live between the IdP and the RP is handled automatically by WCF.

The developer is shielded from the complexity most of the time (very little to code, but you do have to

understand a few things).

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 16

So far I discussed only a few simple federation scenarios:

Using an STS at the RP to facilitate delegation of authentication and to support a claims-based security model.

The RP trusting another domain for authentication, and transforming those claims to support a normalized set for authorization – which involves two STS hops.

A combination of both of these.

This model scales well. The RP can have trust relationships with several domains – each an IdP for their

respective users – and merely add additional transformation rules for each based on the claims they

issue.

Another area where this can expand is that there can be a chain of trust that traverses multiple STS (see

Figure 10).

Figure 10: The RP trusting a particular domain, which leads to an STS trust chain

Figure 10 introduces a new type of participant, a Federation Provider (FP), which also hosts an STS (FP-

STS). TO help explain its role, consider the following points:

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 17

An STS issues security tokens based on internal rules, perhaps as the result of authentication, or in order to transform claims received by one issuer into a format expected for a particular request.

An IdP usually has an STS (IP-STS) which authenticates users and issues claims for that user.

An RP can have an STS (RP-STS) that will transform claims into a format that the RP can use for authorization. Sometimes the RP-STS also authenticates users within the RP domain, thus is also an IdP.

An FP is not an IdP or an RP – its role in a federated security scenario is often to transform claims from one issuer to a new set of claims issued by the FP. It is invited to the federation dance because one of the key players invited it. For example, the RP may rely on a particular FP to manage its federation partners. The RP still decides which IdP it trusts, but configures that in a central place at the FP. The .NET Services Access Control Service is an example of an FP playing this type of role.

Back to Figure 10, if the RP expects a token from the RP-STS, and the RP-STS trusts FP-STS, and the FP-

STS trusts IP-STS, we now have a chain of trust that must be resolved to navigate from user credentials

to RP claims. As I already mentioned, fortunately WCF knows how to resolve this chain of trust (the

service policy chain, really) so that developers are shielded and need only generate a proxy and make

calls to the RP’s services. That doesn’t mean there aren’t a few things to understand along the way

which is why I’ll walk through these scenarios in one form or another from the perspective of the client

developer.

Without turning this into a whitepaper about all the possible business scenarios for federation, my goal

up to now has been to provide some background on the reasoning behind claims-based security, despite

federation, and on some of the possible scenarios for federation to help you understand the flow of

communication. In the remainder of this whitepaper I will explain how some of these scenarios are

achieved focusing on the WPF client developer’s perspective – while also discussing relevant aspects of

the relying party and security token services along the way.

Setting up a Simple Federation Scenario In this section I will walk through the process of configuring a WPF client application to communicate

with a WCF service in a federated scenario – that is, the WCF service requires an issued token from an

STS. To provide a complete picture of the moving parts in this scenario I’ll also provide a brief summary

of the relevant configurations at the service and STS. The scenario I’ll use for this example is a Todo List

application (see Figure 11). The Todo List client application uses a WCF proxy to call the TodoListService.

Since the service requires an issued token from RP-STS the proxy first passes credentials to authenticate

to RP-STS, requesting a token for the TodoListService. The proxy then proceeds to make the call to the

TodoListService – which in turn authorizes access according to the claims granted.

Figure 11: Federation between the Todo List client, TodoListService and RP-STS

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 18

Claims Model Before discussing the implementation details it will be helpful to understand what the claims-based

authorization model looks like for this scenario. The TodoListService exposes four operations:

GetItems(), AddItem(), UpdateItem() and DeleteItem(). These items each require a single claim be

granted the caller: Read, Create, Update and Delete, respectively. Figure 12 illustrates this.

Figure 12: Demanding claims for each service operation

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 19

The RP-STS grants some combination of these four claims according to the authenticated user.

Exposing Federated Security Endpoints This section will briefly summarize how the TodoListService exposes an endpoint that requires an issued

token from the RP-STS. Services can use one of two bindings to expose federated security endpoints:

WSFederationHttpBinding: supports WS-Trust February 2005 and WS-SecureConversation February 2005

WS2007FederationHttpBinding: supports the latest versions of the same standards, WS-Trust 1.3 and WS-SecureConversation 1.3.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 20

The service should choose the binding that best suits protocols supported by the STS. In this example,

WS-Trust 1.3 is used by all parties, and so the service model configuration for the TodoListService

endpoint looks as shown in Figure 13.

Figure 13: Exposing a federated security endpoint at the service

<system.serviceModel> <services> <service name="TodoList.TodoListService" behaviorConfiguration="serviceBehavior"> <endpoint address="" binding="ws2007FederationHttpBinding" bindingConfiguration="wsFed" contract="Contracts.ITodoListService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://localhost:8000/TodoListService"/> </baseAddresses> </host> </service> </services> <bindings> <ws2007FederationHttpBinding> <binding name="wsFed"> <security mode="Message"> <message negotiateServiceCredential="false"> <claimTypeRequirements> <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="false"/> <add claimType="http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission" isOptional="false"/> </claimTypeRequirements> <issuerMetadata address="http://localhost:8010/rpsts/mex" /> </message> </security> </binding> </ws2007FederationHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="serviceBehavior"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors>

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 21

</behaviors> </system.serviceModel>

Figure 14 lists the key configurable elements common to both of the federation bindings.

Figure 14: Key elements in a federated binding configuration

Element Description

Token Type In theory federation can involve any number of token types, but the most common is SAML. Currently SAML 1.0 and SAML 1.1 are supported by WCF, each represented by the following Uri respectively: urn:oasis:names:tc:SAML:1.0:assertion, http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1. If the value is omitted, SAML 1.1 is assumed.

Claim Types The service can supply a list of expected optional or required claim types. This information is included in the service metadata and therefore available to the client after proxy generation. The client includes this information in its RST to the STS, and the STS can optionally use it to determine the claims to issue the caller once authenticated. In reality, most STS have a predefined set of rules for granting claims for a particular RP.

Issuer Metadata The service must indicate where the metadata exchange endpoint is for the STS so that clients will know where to send their token request. SvcUtil uses this endpoint during proxy generation to gather endpoints available to the client for authentication to the STS.

Issuer Address The service can optionally specify a particular STS endpoint where the client should authenticate to retrieve an issued token. Even if there are multiple endpoints available at the STS, SvcUtil will generate configuration for the specified endpoint.

As indicated in the listing in Figure 13, the TodoListService expects a SAML 1.1 security token (the

default), to be issued from a WS-Trust 1.3 endpoint at the RP-STS whose metadata can be found at the

Url: http://localhost:8010/rpsts/mex. The service expects a Name claim and (optionally) one or more

permission claims. The service enables metadata exchange at its endpoint so that clients can access this

metadata and generate proxies.

As you can see there isn’t much to implementing a federated endpoint using one of the standard

federated bindings supplied by WCF. All of the heavy lifting for the messaging protocols is handled by

the underlying channel stack.

Generating a Federated Proxy To communicate with a federated service endpoint the client developer will typically generate a proxy to

kick start the process. To generate a proxy, the service must either expose a metadata exchange

endpoint, or it must supply a Web Service Description Language (WSDL) document that contains all of

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 22

the metadata for the service and its endpoints. Ultimately, the client needs a copy of the service

contract associated with the federated endpoint, and any associated data contracts or other serializable

types. In addition, the appropriate configuration for the proxy – which is usually close to what the

service configuration looks like – but for federated scenarios things are a little different at the client.

The client requires additional aspects of the federation binding to be supplied before it can

communicate with the STS. This information is not supplied by the service – it is gathered directly from

the STS. When you generate a proxy using Add Service Reference, SvcUtil calls the metadata exchange

endpoint exposed by the STS and gathers information about its WS-Trust endpoints to produce the

appropriate client configuration. The client can then use this configuration to initialize an instance of the

ClientBase<T> proxy or create the channel directly with ChannelFactory<T>.

Figure 15 shows the binding configuration generated for the Todo List client application based on the

service configuration shown in Figure 13. I have cleaned up the generated configuration to remove

default values that are not relevant to this discussion.

Figure 15: Configuration generated for the TodoListService federated endpoint

<system.serviceModel> <bindings> <ws2007FederationHttpBinding> <binding name="WS2007FederationHttpBinding_ITodoListService"> <security mode="Message"> <message algorithmSuite="Default" issuedKeyType="SymmetricKey" negotiateServiceCredential="false"> <issuer address="http://localhost:8010/rpsts" binding="ws2007HttpBinding" bindingConfiguration="http://localhost:8010/rpsts"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAw5XNSnQJp3fU495G1whJhnYamVAgAAAAAQAAAO4BAAAwggHqMIIBU6ADAgECAhAfYtbn6q4Ytkg7+CbBMULBMA0GCSqGSIb3DQEBBAUAMBAxDjAMBgNVBAMTBVJQU1RTMB4XDTA5MDYyOTA1NDQ1NloXDTM5MTIzMTIzNTk1OVowEDEOMAwGA1UEAxMFUlBTVFMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK+ocZ51zrBpIGC1SjFu4io0qWLhSi53S1Uas0jfInaNvHjyJzEZJhtiRIaXVxfcQiMX1yToPvWzoNDZ8M24x/AM0PCm5JR1TYqUNSVDpLbYjCE23w30IsYqqOpWVFo0Vs3GZ8B4XZYUB/QOv4zNz5/R69eeSAiU2QgWiJ2RrfTPAgMBAAGjRTBDMEEGA1UdAQQ6MDiAEK2EFXxZKjypLl5TT/Q34M6hEjAQMQ4wDAYDVQQDEwVSUFNUU4IQH2LW5+quGLZIO/gmwTFCwTANBgkqhkiG9w0BAQQFAAOBgQBvxxg3XM03jT3UHgnLic4zTPceP/uPz4XP/WrM0ey1tN4s9t15mtAEjYcHEEtTRba8xAic5gZKYsDAgmonFeAbsd286rvtIcGO6R/jqlZ2+2eVAPiY18F5U62uLooboXh3H7+GVuXZRPgouHM5gz1rRWgIBEzRIQjlXvTPLFHdWg==" /> </identity> </issuer> <issuerMetadata address="http://localhost:8010/rpsts/mex" />

<tokenRequestParameters>

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 23

<trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType> <trust:KeySize xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">256</trust:KeySize> <trust:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity" xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <wsid:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" /> <wsid:ClaimType Uri="http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" /> </trust:Claims> <trust:KeyWrapAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p</trust:KeyWrapAlgorithm> <trust:EncryptWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptWith> <trust:SignWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2000/09/xmldsig#hmac-sha1</trust:SignWith> <trust:CanonicalizationAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm> <trust:EncryptionAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm> </trust:SecondaryParameters> </tokenRequestParameters> </message> </security> </binding> </ws2007FederationHttpBinding> <ws2007HttpBinding> <binding name="http://localhost:8010/rpsts" > <security mode="Message"> <message clientCredentialType="UserName" negotiateServiceCredential="false" algorithmSuite="Default" establishSecurityContext="false" /> </security> </binding> </ws2007HttpBinding>

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 24

</bindings> <client> <endpoint address="http://localhost:8000/TodoListService" binding="ws2007FederationHttpBinding" bindingConfiguration="WS2007FederationHttpBinding_ITodoListService" contract="TodoList.ITodoListService" name="default"> <identity> <certificate encodedValue="AwAAAAEAAAAUAAAAMTNxA8ZXL5/j4SM7kjejoa6aZscgAAAAAQAAAOUBAAAwggHhMIIBSqADAgECAhDtugE1LpBwuUuwJHt6AjsKMA0GCSqGSIb3DQEBBAUAMA0xCzAJBgNVBAMTAlJQMB4XDTA5MDcwMTE0NTMxM1oXDTM5MTIzMTIzNTk1OVowDTELMAkGA1UEAxMCUlAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO3BQX7ZOxcKCSzj2SF/Ibldy47RSCRrsSVe7hR2sDeqy5T7RCOgBi6s5oVn44O8twUYHJF5eZH84+gJfQOeaQW08mlrv96s8FaPZr9QZd9/RwbJoaMBlErdSBiKZciqQZUw5JLJDI1VrWzpIGa1FwyoP6eNd8VwxUCPaRwmM9TnAgMBAAGjQjBAMD4GA1UdAQQ3MDWAELmxuUZ/CRnp4cwF6XoiJOShDzANMQswCQYDVQQDEwJSUIIQ7boBNS6QcLlLsCR7egI7CjANBgkqhkiG9w0BAQQFAAOBgQBb0ZfGov5xc/X4f/HqTWxAvbJoALvXeG263h8FA3Ogie1GE4KJozbONEWQVILsT1EUnSEtVZgH0sHt2bBxYViC9BBFGfmISiq8iAWmLMDcBRyj1r13qTcRiq19+6mi+MZ0ycUI/sMATEG+3DDCQEO8GblyY3VGu/cJGgpk1ajFaw==" /> </identity> </endpoint> </client> </system.serviceModel>

Beyond the information the TodoListService provided, the client-side federation binding adds the

following necessary pieces of information:

The issuer address is provided, indicating the endpoint the proxy will call to request tokens for the TodoListService.

A binding configuration is provided for this issuer endpoint, in this case indicating that the client will use WS2007HttpBinding to call the STS.

The WS2007HttpBinding configuration for the STS endpoint indicates that the client must pass a UserName credential to authenticate to the STS.

A base64-encoded copy of the STS certificate is provided as the identity of the STS, removing the need to acquire the STS public key out-of-band in order to encrypt messages to the STS.

Token request parameters include information about the WS-Trust dialect to use, the claim types requested by the service, along with other key signing and encryption details.

This is all very straight-forward so far since proxy generation makes things super easy. But, it is still

important to understand the moving parts so that you can make adjustments to the generated

configuration as needed. Also, keep in mind that the results of proxy generation can vary under some

circumstances. Here is a summary of some extended situations to be aware of:

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 25

Proxy generation will fail if the service or STS are not online. Of course, one would hope they are online

For interoperability with other platforms, you may need to adjust some of the token request parameters such as the key type, key size, or algorithms used for signing and encryption. Proxy generation should produce the correct result, but this is an area to keep in mind if you are having problems.

The setting to negotiate service credentials should be set to false on the federated and http bindings since negotiation is not interoperable. It is a convenient way to dynamically acquire the service certificate at the STS or RP when both sides are WCF.

As with any WCF scenario, you may need to adjust binding elements related to message size, byte array size, and other reader quotas.

If the STS exposes multiple endpoints that satisfy the WS-Trust protocol supported by the service endpoint, the client-side configuration will include some options. SvcUtil currently will default the client configuration to the first compatible STS endpoint it finds and provide alternatives in the commented out in the <alternativeIssuedTokenParameters> element. The client developer should look at this section and determine which endpoint best suits its authentication or protocol requirements. The configuration requirements for each of the commented STS endpoints are generated and ready to use if any of the other alternatives are uncommented.

If the service specifies a particular issuer address in its federation configuration, the client will include configuration for that specific STS endpoint.

As the client developer, your job can be as easy as generating a proxy for a federated endpoint and

supplying the appropriate credentials to authenticate to the STS. This experience is identical to any

other scenario – you simply need to look at the issuer binding to find out what credentials it expects. For

example, the TodoListService scenario calls an STS endpoint that requires a username and password,

and so the proxy initialization code looks like this:

TodoListServiceProxy _Proxy = new TodoListServiceProxy("default"); this._Proxy.ClientCredentials.UserName.UserName = this.Username; this._Proxy.ClientCredentials.UserName.Password = this.Password; this._TodoItems = this._Proxy.GetItems();

The call to GetItems() handles acquiring the issued security token from the STS first, authenticating with

the username and password collected from the user, and then calling the TodoListService passing the

issued token for authorization.

Deferring Proxy Initialization This tip applies to more than WCF proxies, but it rears its head quickly if you initialize your proxies when

you declare them, or in the Windows constructor. Imagine you declare a proxy as a member of the main

window, and initialize it inline like this:

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 26

public partial class MainWindow : Window { private ExceptionHandlingProxy _Proxy = new ExceptionHandlingProxy("default"); public MainWindow() { InitializeComponent(); } }

Let’s say you have a configuration error such as a certificate reference that can’t be found. When the

window is loaded, you’ll get a parsing error like that shown in Figure 16 during construction:

Figure 16: Generic XamlParseException resulting from problems that occur during Window

construction

In fact, you’ll see this type of XamlParseException if any exception occurs as the Window is being

constructed. The exception makes sense the error is limited to loading the XAML document, however it

doesn’t apply to other code such as proxy construction. You can drill down to reach the error by

selecting View Detail…and this will get you to the real exception. Figure 17 illustrates the example where

the certificate reference could not be loaded.

Figure 17: InnerException for the XamlParseException

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 27

The problem is, this extra drilldown step is really annoying when you are trying to debug your

application. The same problem, by the way, occurs for any exceptions that happen during window

construction, not just proxy initialization. As for proxy construction, the same problem exists if you

initialize the proxy in the Window constructor, and if you initialize a ChannelFactory<T> instance instead

of a generated proxy based on ClientBase<T>:

private ChannelFactory<TodoList.ITodoListService> _ChannelFactory = new ChannelFactory<TodoList.ITodoListService>("default");

To work around this, you should defer proxy initialization to the Window.Loaded event (or later) such as

in this example:

public partial class MainWindow : Window { private ExceptionHandlingProxy _Proxy; public MainWindow() { InitializeComponent(); } private void window_Loaded(object sender, RoutedEventArgs e) { _Proxy = new ExceptionHandlingProxy("default");

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 28

this._Proxy.ClientCredentials.UserName.UserName = this.Username; this._Proxy.ClientCredentials.UserName.Password = this.Password; this._Proxy.Open(); } private void window_Closing(object sender, CancelEventArgs e) { _Proxy.Dispose(); } } After making this change you’ll see the correct exception immediately. For the example discussed previously Figure 18 illustrates the certificate reference exception, no extra drilling down required. Figure 18: An example of a proxy initialization exception thrown during the Window.Loaded event

Hopefully this little tip helps you to avoid some potentially painful debugging sessions.

Issuing Claims at the STS The STS in this example is a custom STS created with Geneva Framework – however in reality the STS

will usually be a platform STS such as that provided by “Geneva” Server. At its core, an active STS is

merely a service exposing one or more WS-Trust endpoints according to one of the following versions of

the protocol: the pre-released version WS-Trust February 2005, or the latest release WS-Trust 1.3. The

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 29

RP service determines the protocols it supports by exposing endpoints using either

WSFederationHttpBinding or WS2007FederationHttpBinding. When implemented with Geneva

Framework the STS determines protocol support by exposing endpoints with the correct version of the

WS-Trust contract.

From the client developer’s perspective the RP endpoint indicates the correct STS endpoint, proxy

generation provides the necessary configuration to communicate with the STS, and the client developer

(usually) need only collect user credentials to authenticate to the STS when the proxy requests a token.

In this section I will explain some of the semantics of token issuance, and assuming a SAML 1.1 token is

issued – describing some of the important underlying details that are handled by WCF and Geneva

Framework.

Figure 19 captures the communication flow between the Todo List client, the RP-STS and the

TodoListService. An RST is sent to RP-STS to request a token, which returns an RSTR (if all goes well) with

a SAML 1.1 token. The client sends this SAML 1.1 token to the service to be used for authorization.

Figure 19: Core elements in the RST, RSTR, and SAML 1.1 token

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 30

Although you need not write the core plumbing to achieve this scenario, there are a few underlying

details that I want to touch on so that you are familiar.

The RST includes an AppliesTo element which indicates for whom (which RP) the token should be issued.

The STS uses this information to determine if it has a trust relationship with the RP, and if it does, will

typically encrypt the token with matching RP public key. Encryption is not a requirement, but is

recommended. As I have mentioned, the claim types passed in the RST are a hint to the claims the RP

would like issued, however the STS will typically figure out the right claims to issue based on the context

of the call.

The SAML 1.1 token returned in the RSTR includes the following information:

Issued claims are stored as SAML attributes

Token lifetime information indicates the date and time range for which the token can be used

The subject confirmation is usually “holder-of-key” for active federation scenarios, indicating that a proof key will be used to ensure that the requestor of the token (the client application in this case) is indeed the sender of the token to the RP service. What this means from a high level is that a symmetric key will be supplied by the STS, embedded in the SAML token, and returned separately to the client so that it can sign the message to the RP service with this token. The RP service can verify that the signature of the message matches the proof key inside the token to “prove” that the sender was also the requestor of the token.

The token signature is a hash of the token using the STS private key, to be used to verify that the token contents have not been tampered with

Since the platform handles this protocol exchange, what you really care about is that the STS has a trust

relationship with the RP service so that they will indeed issue the requested token. Figure 20 shows the

listing for the RP-STS implemented with Geneva Framework. The two key overrides are:

GetScope() – which checks that the RP indicated by the AppliesTo element is trusted

GetOutputClaimsIdentity() – which returns a ClaimsIdentity instance with the claims to be issued for the authenticated user

Figure 20: The RP-STS SecurityTokenService implementation

public class RPSTS : SecurityTokenService { public RPSTS(SecurityTokenServiceConfiguration config) : base( config ) { } protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 31

{ IClaimsIdentity claimsIdentity = new ClaimsIdentity(); claimsIdentity.Claims.AddRange(CredentialStore.GetClaimsForUser(principal.Identity.Name)); return claimsIdentity; } protected override Scope GetScope(Microsoft.IdentityModel.Claims.IClaimsPrincipal principal, RequestSecurityToken request) { Scope scope = new Scope(request.AppliesTo.Uri.AbsoluteUri); scope.EncryptingCredentials = this.GetCredentialsForAppliesTo(request.AppliesTo); scope.SigningCredentials = new X509SigningCredentials(CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=RPSTS")); return scope; } private X509EncryptingCredentials GetCredentialsForAppliesTo(EndpointAddress appliesTo) { if (appliesTo == null || appliesTo.Uri ==null || string.IsNullOrEmpty(appliesTo.Uri.AbsolutePath)) { throw new InvalidRequestException("AppliesTo must be supplied in the RST."); } X509EncryptingCredentials creds = null; if (appliesTo.Uri.AbsoluteUri.StartsWith("http://localhost:8000/TodoListService")) { creds = new X509EncryptingCredentials(CertificateUtil.GetCertificate(StoreName.TrustedPeople, StoreLocation.LocalMachine, "CN=RP")); } else throw new InvalidRequestException(String.Format("Invalid relying party address: {0}", appliesTo.Uri.AbsoluteUri)); return creds; }

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 32

}

Authorization at the RP Service At this point I have discussed how to think about the claims-based security model up front, how the RP

service exposes federation endpoints, how the client generates a proxy to communicate with that

service and the indicated STS, and how the STS issues tokens for trusted RPs. At this point, when the

client application attempts to call the RP service, calls will succeed or fail based on the issued claims –

assuming of course that the service enforces these claims.

Geneva Framework supplies features to WCF services for claims-based authorization. Here is a summary

of what can happen at the service:

Enable the federation behavior for the ServiceHost instance

This results in a ClaimsPrincipal created for each request, containing the authenticate user’s claims

Classic .NET role-based security will work to authorize access based on the current principal’s claims

Let’s consider how the TodoListService handles the process. The federation behavior is enabled

declaratively by adding this service behavior:

<federatedServiceHostConfiguration name="TodoList.TodoListService"/>

This requires an extension be registered within the <system.serviceModel> section:

<extensions> <behaviorExtensions> <add name="federatedServiceHostConfiguration" type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </behaviorExtensions> </extensions>

The behavior will look to the <microsoft.identityModel> section for configuration details, and so this

configuration section must be registered:

<configSections>

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 33

<section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </configSections> The actual configuration for the behavior requires only two things for an RP scenario: a list of trusted

issuers and a certificate to be used for decrypting tokens. For the TodoListService the following

configuration is used:

<microsoft.identityModel> <service name="TodoList.TodoListService"> <issuerNameRegistry type="TodoListHost.TrustedIssuerNameRegistry, TodoListHost"/> <serviceCertificate> <certificateReference findValue="CN=RP" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName"/> </serviceCertificate> </service> </microsoft.identityModel>

The TrustedIssuerNameRegistry type checks that the RP-STS is the issuer:

public class TrustedIssuerNameRegistry : IssuerNameRegistry { public override string GetIssuerName(SecurityToken securityToken) { X509SecurityToken x509Token = securityToken as X509SecurityToken; if (x509Token != null) { if (String.Equals(x509Token.Certificate.SubjectName.Name, "CN=RPSTS")) { return x509Token.Certificate.SubjectName.Name; } } throw new SecurityTokenException("Untrusted issuer."); } }

Lastly, since the service relies on the permission claim type for authorization, this must be

indicated as the role claim type so that classic .NET role-based security checks that use

IPrincipal.IsInRole() functionality will work. You do this by removing and re-adding the SAML 1.1

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 34

security token handler with the appropriate parameters to replace the default role claim type

as follows:

<securityTokenHandlers> <remove type="Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> <add type="Microsoft.IdentityModel.Tokens.Saml11.Saml11SecurityTokenHandler, Microsoft.IdentityModel, Version=0.6.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <samlSecurityTokenRequirement > <roleClaimType value="http://claimsbasedwpf.codeplex.com/samples/2009/06/claims/permission"/> </samlSecurityTokenRequirement> </add> </securityTokenHandlers>

This is not a requirement, you can always programmatically check the claims – but it is very

convenient to integrate with role-based security techniques such as permission demands and

IsInRole() checks. The TodoListService implements a permission demand at each operation like

this:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)] public class TodoListService: ITodoListService { [PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Read)] public List<TodoItem> GetItems() {…} [PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Create)] public string CreateItem(TodoItem item) {…} [PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Update)] public void UpdateItem(TodoItem item) {…} [PrincipalPermission(SecurityAction.Demand, Role = Constants.Permissions.Delete)] public void DeleteItem(string id) {…}

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 35

} This will prevent users from calling operations they haven’t been granted the rights to – however it would also be nice if the UI prevented the user from accessing those features proactively. This is the subject of the next section.

WPF Clients and Claims The stability of the protocols for federated security makes it possible for platforms like WCF and Geneva

Framework to hide the details of the implementation from the developer – and that is the way it should

be. There isn’t, however, a well-defined protocol for supplying the client application with access to the

claims issued to the authenticated user – to be used for authorization. This leaves the client developer

to come up with custom ways to restrict access to features and functionality.

Some of the possible ways a developer might think to gain access to claims at the client are summarized

in Figure 21 – but only the last in the list is acceptable in my books.

Figure 21: Possible ways to access issued claims at the client

Approach Discussion Points

Use the issued token When the STS issues a security token for an RP, the client proxy has access to this token. In theory that might make it tempting to use this token for client-side authorization, however there are some flaws in this approach:

The token might be encrypted for the RP. Sure, the client and RP may be part of the same domain, but it is not generally acceptable to distribute private keys to the client machine.

The token format may not be understood by the client. What if the token is not a SAML 1.1 token? Should the client care? Not really, they merely supply the credentials for authentication to the STS and let the STS decide what token is best for the RP.

Even if the token format is known, and the token is not encrypted, the claims issued for the request may not be representative of the rights the user has across all features and functionality. The claim issued may only target a particular set of features for a particular service (among multiple RP services).

Request a display token There is the concept of a DisplayToken that the STS can return with the issued token. Unlike the issued token whose format may not be known to the client, the DisplayToken has a well-defined format. CardSpace uses this to present claims to the user, for example. The problem with the DisplayToken is that it should really be used to present information to the user about a set of claims, but not in a format that could be programmatically reliable. By definition display claims should include a friendly representation of the issued token’s claims without violating any

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 36

privacy.

Return a serialized ClaimsPrincipal

Ok, now we are getting somewhere. The service could, in theory, return the ClaimsPrincipal for the request thread or the claims collection held by the ClaimsPrincipal. There are a few flaws in this approach as well:

The ClaimsPrincipal and claims collection are not directly serializable without some extra effort. Ok, we can work around this one…but…

The result of serialization will includes a lot more information than the client really needs, so the wire is bloated unnecessarily.

The claims issued may differ depending on which RP service is called. Once again, this may not be representative of the entire application (all RP services) and what the user can or cannot do. Different claims may be issued for each service, or even for a particular operation!

Return a customized collection of claims

Provide a special authorization endpoint that triggers the STS to issue a set of claims representative of the user’s rights across the entire set of RP services. The call to this service can then return a simple collection of claims that can be tailored for the application’s requirements. Perhaps some applications only care about a collection of claim type and claim value pairs. Others might want to know the issuer. Others might want additional information.

The advantage of the latter is that you can gather a broader representation of overall application claims

and still return only what the client needs to work with – based on some context. The fact of the matter

is that you need to come up with a model for claims-based authorization that also includes how you

want the client to manage authorization. Then, you can decide how to go about achieving the result. In

the sections to follow I’ll discuss this approach in more detail.

Claims-Based UI Models As I mentioned earlier, there are many different types of claims that could be valuable to an application:

details about the user, roles and permissions, and any number of other details specific to an application

scenario. Obviously knowledge of the claims you are dealing with must play a role in how you control

access to an application. Ultimately, the RP is responsible for transforming claims into a format that is

useful both to the service, and to the client, to restrict access to features and functionality.

Before I discuss the Todo List application scenario, I’ll describe a richer scenario that involves an

application with multiple RP services. Figure 22 illustrates such a scenario where the RP exposes services

to control access to Customers, Orders and Reports. The RP-STS is responsible for issuing tokens for

users that interact with these services, but may issue different claims depending on the service being

called based on the value of AppliesTo in the RST.

Figure 22: A scenario where the RP exposes multiple services and the RP-STS issues tokens for each of

them accordingly

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 37

What would the claims-based security model look like for this service? Here are some options:

All users may be globally assigned Create, Read, Update and Delete claims that apply to all application resources

The application may grant CRUD rights differently per resource, and so perhaps permission claims expand to CreateCustomers, CreateOrders, CreateReports, and similar for Read, Update and Delete rights.

Assuming the latter case, as Figure 22 illustrates, the claims issued by the RP-STS will depend which

service is being called, which means it will be challenging for the UI to filter access to the entire set of

application features.

One way to decouple the idea of claims issued for a particular RP service, and claims issued for the UI, is

to create a client authorization service of some sort that is a specific RP service that returns client-

friendly claims for authorization according to the application’s needs. This service would be known to

the RP-STS as an AppliesTo that triggers returning some sort of global set of claims for the authenticated

user. Figure 23 illustrates this model.

Figure 23: Using an authorization service to return claims for the authenticated user

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 38

The sections to follow will explain how this model is implemented for the Todo List application discussed

throughout this whitepaper.

Client Authorization Service Implementing a client authorization service to facilitate supplying claims to the client application is a

completely custom implementation for reasons I already discussed. That means it is up to you to decide

what kinds of requests this service should support so that the appropriate claims can be returned. For

the Todo List application there is only a single RP service – the TodoListService. That means a simple

GetClaims() function will probably suffice since there isn’t the need to differentiate sets of functionality

so that the RP-STS can issue different claims according to context.

For this example I created a service contract called IClientAuthorization as follows:

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 39

[ServiceContract(Namespace=Constants.Namespaces.ServiceContractNS)] public interface IClientAuthorization { [OperationContract] List<ClientClaim> GetClaims(); }

GetClaims() will return a collection of ClientClaim instances – another custom type I created for this

scenario as shown here:

[DataContract(Namespace = Constants.Namespaces.DataContractNS)] public class ClientClaim { [DataMember(Order=1, IsRequired=true)] public string ClaimType { get; set; } [DataMember(Order = 2, IsRequired = true)] public string ClaimValue { get; set; } }

You can easily add the issuer or some form of lifetime information to each claims as appropriate to your

application scenario, and I discussed some of these options earlier.

The implementation of ClientAuthorizationService is shown in Figure 24. When the client calls

GetClaims() the proxy will first call the RP-STS passing a username and password for authentication as it

does for the TodoListService. This will result in a set of claims for the application as a whole, not

specifically targeting the TodoListService. GetClaims() accesses these issued claims through the

ClaimsPrincipal attached to the request thread – and builds a collection of ClientClaim instances to

return.

Figure 24: Implementation of IClientAuthorization

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)] public class ClientAuthorizationService: IClientAuthorization { #region IClientAuthorization Members public List<ClientClaim> GetClaims() {

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 40

List<ClientClaim> clientClaims = new List<ClientClaim>(); ClaimsPrincipal p = Thread.CurrentPrincipal as ClaimsPrincipal; foreach (IClaimsIdentity identity in p.Identities) { if (identity.Claims != null) { foreach (Claim c in identity.Claims) { if (c.ClaimType.Contains(Constants.ClaimTypes.Permission)) { clientClaims.Add(new ClientClaim { ClaimType = Constants.ClaimTypes.Permission, ClaimValue = c.Value }); } } } } return clientClaims; } }

Even if the claims returned by the RP-STS are the same for the ClientAuthorizationService and the

TodoListService now – this could change in future if additional RP services are added to the application.

The ClientAuthorizationService could be used to gather additional claims later on. Thus, decoupling the

service used to return claims to the client from a particular RP service is a good idea. This allows the STS

to evolve the set of global claims as new RP services are brought online.

Claims-Based Access Control Once you have designed your claims-based model, and created an authorization service that can be

used to gather the right claims for the client, the next step is to use those claims to restrict access to

features and functionality in your WPF clients. A big part of this is hiding, showing or disabling UI

elements according to the appropriate claims. This can be done programmatically by writing code to

hide, show, enable or disable – or declaratively through WPF bindings.

Consider the claims required to enable features in the Todo List Client shown in Figure 25. The following

lists each claim and the rights it grants the user in the UI:

Read – this claim is required for the user to view the application

Create – this claim allows the new item panel to be visible, otherwise it will be collapsed

Update – this claim allows editing grid items, otherwise the user cannot select text or enter any new values in any column

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 41

Delete – this claim controls if the Delete Task button is shown or hidden

Figure 25: Claims authorization in the Todo List client

To explain how bindings can be used to for claims-based security, let me dissect how the DataGrid

visibility is controlled based on the presence of the Read claim. Here is the Binding statement for the

control’s IsReadOnly property:

IsReadOnly="{Binding Path=ClientClaims, Converter={StaticResource

ClaimsToIsReadOnlyConverter}, ElementName=window, Mode=Default}"

The Path and ElementName respectively indicate that the public ClientClaims property exposed by the

window associated with this XAML file will be used in the property binding. To support this the window

exposes a ClientClaims property defined as follows:

public List<ClientClaim> ClientClaims { get; set; }

The Converter is the key to adequately converting the bound property, a List<ClientClaim> type, into a

true or false value for the IsReadOnly property. When the property is evaluated, the IValueConverter

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 42

type specified in the Converter property is invoked. More specifically, the Convert() function of the

ClaimsToIsReadOnlyConverter shown in Figure 26 is called.

Figure 26: The implementation of ClaimsToIsReadOnlyConverter

class ClaimsToIsReadOnlyConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return true; List<ClientClaim> actualClaims = (List<ClientClaim>)value; var results = from c in actualClaims where c.ClaimType == Constants.ClaimTypes.Permission && c.ClaimValue == Constants.Permissions.Update select c.ClaimValue; if (results != null && results.Count() > 0) return false; return true; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }

The logic works as follows:

If the ClientClaims property is null, true is returned indicating that Update rights have not been granted yet since there are no claims to work with.

If there is a collection of claims, the collection is queried to see if the Update claim exists and if so Convert() returns false indicating Update rights have been granted.

If not, Convert() returns true since Update rights were not granted.

The result is that the IsReadOnly property of the DataGrid is controlled without writing any additional

code. The next trick, however, is updating the value when claims have changed. This is done by

implementing INotifyPropertyChanged on the window (which owns the ClientClaims property).

Whenever the ClientClaims are reevaluated if the PropertyChanged event is fired then the DataGrid will

reevaluate by calling the converter again. Figure 27 shows the window listing showing only aspects

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 43

relevant to this discussion. Specifically, the PropertyChanged event is triggered before the window is

loaded since claims haven’t been collected yet, and this will hide appropriate items; and again each time

the proxies are initialized. If there were additional code to handle session and token expiry this would

also be a good place to trigger the PropertyChanged event for the ClientClaims property.

Figure 27: Triggering the PropertyChanged event to bind the UI to the ClientClaims property

public partial class MainWindow : Window, INotifyPropertyChanged { private TodoListServiceProxy _Proxy = new TodoListServiceProxy("todolistdefault"); private ClientAuthorizationProxy _ClientAuthProxy = new ClientAuthorizationProxy("clientauthdefault"); public List<ClientClaim> ClientClaims { get; set; } public MainWindow() { InitializeComponent(); NotifyPropertyChanged("ClientClaims"); } private void InitializeProxies() { try { this._ClientAuthProxy.ClientCredentials.UserName.UserName = this.Username; this._ClientAuthProxy.ClientCredentials.UserName.Password = this.Password; this._ClientAuthProxy.Open(); this._Proxy.ClientCredentials.UserName.UserName = this.Username; this._Proxy.ClientCredentials.UserName.Password = this.Password; this._Proxy.Open(); this.ClientClaims = this._ClientAuthProxy.GetClaims(); NotifyPropertyChanged("ClientClaims"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } #region INotifyPropertyChanged Members

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 44

public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string property) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property)); } #endregion }

The ClaimsToIsReadOnly converter is defined in the <Window.Resources> section along with the

ClaimsToVisibilityConverter as shown here:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:TodoList_WpfClient_TodoList="clr-namespace:WpfClient.TodoList" xmlns:TodoList_WpfClient_Utilities="clr-namespace:WpfClient.Utilities" xmlns:TodoList_WpfClient_Security="clr-namespace:WpfClient.Security" xmlns:TodoList_WpfClient_DesignTime="clr-namespace:WpfClient.DesignTime" x:Class="WpfClient.MainWindow" Title="Todo List Client" x:Name="window" d:DesignHeight="498" Background="{DynamicResource WindowBackgroundBrush}" MinWidth="671" d:DesignWidth="672" Closing="window_Closing" Loaded="window_Loaded"> <Window.Resources> <TodoList_WpfClient_Security:ClaimsToIsReadOnlyConverter x:Key="ClaimsToIsReadOnlyConverter"/> <TodoList_WpfClient_Security:ClaimsToVisibilityConverter x:Key="ClaimsToVisibilityConverter"/> <TodoList_WpfClient_Utilities:StatusToTextConverter x:Key="StatusToTextConverter"/> </Window.Resources> … </Window>

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 45

The namespace for the component is defined earlier in the XAML document, as is required for all

components. The ClaimsToIsReadOnlyConverter is used by the IsReadOnly property of the DataGrid, and

by the IsReadOnly property of the description TextBox shown when an item is selected in the DataGrid.

The ClaimsToVisibilityConverter is used to set the Visibility property of the DataGrid, the Delete button,

and the new item panel which is encapsulated in a <Border> element. The following show these

snippets of the XAML document without the irrelevant properties and clutter.

DataGrid bindings:

<my:DataGrid x:Name="TodoDataGrid" IsReadOnly="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToIsReadOnlyConverter}, ElementName=window, Mode=Default}" Visibility="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToVisibilityConverter}, ConverterParameter=Read, ElementName=window, Mode=Default}" >

Description TextBox bindings:

<TextBox IsReadOnly="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToIsReadOnlyConverter}, ElementName=window, Mode=Default}" /> Delete Button bindings:

<Button Visibility="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToVisibilityConverter}, ConverterParameter=Delete, ElementName=window, Mode=Default}" />

New item Border bindings:

<Border Margin="8,8,8,3.04" BorderThickness="1,1,1,1" CornerRadius="4,4,4,4" BorderBrush="{DynamicResource NormalBorderBrush}" Padding="4,4,4,4" Visibility="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToVisibilityConverter}, ConverterParameter=Create, ElementName=window, Mode=Default}"> <Grid x:Name="NewItemPanel">…<Grid> </Border>

The ClaimsToVisibilityConverter is also an IValueConverter that has a slightly different implementation.

In this case, the WPF binding passes a parameter indicating a comma delimited list of required claims for

visibility to be true, and the Convert() implementation uses this parameter to search the ClientClaims

collection:

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 46

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Visibility visibilitySetting = Visibility.Visible; if (value == null) return Visibility.Collapsed; List<ClientClaim> actualClaims = (List<ClientClaim>)value; string claimsParameter = (string)parameter; List<string> requestedClaims = new List<string>(claimsParameter.Split(',')); requestedClaims.ForEach(x=>{ var results = from c in actualClaims where c.ClaimType == Constants.ClaimTypes.Permission && c.ClaimValue.ToLower().Contains(x.ToLower()) select c.ClaimValue; if (results == null || results.Count() == 0) visibilitySetting = Visibility.Collapsed; }); return visibilitySetting; }

In this implementation, the parameter passed from the WPF binding is not the actual claim Uri – only

the suffix “Create”, “Read”, “Update”, or “Delete” is required. This is used to match up the suffix of the

claim value – but you can pattern this as appropriate to your application.

The parameter is passed to the converter parameter as shown here:

<Button Visibility="{Binding Path=ClientClaims, Converter={StaticResource ClaimsToVisibilityConverter}, ConverterParameter=Delete, ElementName=window, Mode=Default}" />

Using declarative bindings to control access to features is something unique to WPF. Although quite

useful, that is not to say that imperative authorization checks aren’t also useful. There are many reasons

why an imperative check may be necessary:

For requirements that cannot be easily represented as a single claim. Perhaps the user has rights to create a report, but this is only allowed during business hours. The Create Report button could be disabled or hidden if the user doesn’t have the CreateReport right. If they do have the CreateReport right, the code still must check the time of day before proceeding.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 47

To control access to functionality in dependent assemblies. Client code could use a library that enforces claims-based security rules using .NET role-based security techniques discussed earlier for the TodoListService. For example declarative permission demands, imperative permission demands, or IsInRole() checks could be performed.

To support imperative demands you should initialize a security principal with the appropriate claims-

based “roles” at the client. At the server, ASP.NET and WCF handle initialize every request thread with a

security principal so that we can do role-based permission demands. By default, the client thread does

not have a security principal, unless you explicitly construct one and provide it with the appropriate

roles.

After gathering the claims from the service, you can easily construct a GenericPrincipal and attach it to

the client thread as follows:

this.ClientClaims = this._ClientAuthProxy.GetClaims(); var results = from item in this.ClientClaims where item.ClaimType == Constants.ClaimTypes.Permission select item.ClaimValue; GenericIdentity clientIdentity = new GenericIdentity(this.Username, "CN=RPSTS"); GenericPrincipal clientPrincipal = new GenericPrincipal(clientIdentity, results.ToArray()); Thread.CurrentPrincipal = clientPrincipal;

Permission claims are assigned to the roles collection of the GenericPrincipal, which makes it possible to

check claims with IsInRole(). For example, to check if the user has a particular claim:

Thread.CurrentPrincipal.IsInRole(Constants.Permissions.Delete);

Of course Geneva Framework provides a ClaimsPrincipal type that could be used instead of a

GenericPrincipal – with first class support for claims. This requires you to add Geneva Framework

assemblies as a dependency at the client.

Caching Security Tokens Inevitably you will encounter situations that require reusing or caching security tokens in your WPF

clients. Some possible reasons include:

To avoid prompting the user for credentials more than once to initialize multiple proxies for multiple RP services

To avoid unnecessary trips to the STS to request a security token for multiple proxies for multiple RP services

To avoid extra prompting or STS trips when recreating proxies if the channel times out or is faulted for some reason

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 48

Since tokens are issued for a particular RP, they can only be shared across RP services if the following is

true:

Assuming tokens are encrypted for the RP, all RP services must share the same service certificate.

The STS must be aware of all relevant RP addresses used in the AppliesTo of the RST (discussed earlier) and be able to issue a token with claims that will be useful across all RP services. A workaround to this would be to always use an authentication service to issue a token relevant to all RP services as indicated in Figure 28 for the Todo List application.

If tokens are issued for one RP service and to be received by another, the service must allow tokens that potentially have an AudienceUri indicating one of the other RP services.

Assuming you can get past these obstacles, read on and I will discuss how to share tokens among related

proxies. As part of the discussion I’ll cover handling session timeouts and token expiry as well.

Figure 28: Sharing tokens between the ClientAuthorizationService and the TodoListService

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 49

ClientCredentials and Issued Tokens Every proxy (channel) exposes a ClientCredentials property which is used to supply information required

to authenticate to the service. For example, you might set the Windows, UserName or ClientCertificate

properties of the ClientCredentials type before making the first call to the service. The following code

illustrates setting the UserName credential:

this._ClientAuthProxy = new ClientAuthorizationProxy("clientauthdefault"); this._ClientAuthProxy.ClientCredentials.UserName.UserName = this.Username; this._ClientAuthProxy.ClientCredentials.UserName.Password = this.Password;

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 50

In federated security scenarios, you will still set the appropriate Windows, UserName or ClientCertificate

credentials if the STS endpoint expects to authenticate users with one of those credential types (keeping

in mind that there are other authentication options such as HttpDigest or another IssuedToken from a

chained STS). In addition, the ClientCredentials type exposes an IssuedToken property which supplies

information about the STS to authenticate to such as its address and binding requirements. This

information can be initialized programmatically, however when you generate a proxy as I’ve discussed,

this information is initialized from the federated binding configuration.

Figure 29 illustrates how the issued token is acquired. The proxy has a reference to a ClientCredentials

instance and supplies the user’s credentials required to authenticate to the STS. The runtime triggers the

CreateSecurityTokenManager() method of ClientCredentials to construct a SecurityTokenManager –

specifically the ClientCredentialsSecurityTokenManager. A SecurityTokenManager type is responsible for

provisioning, authenticating and serializing tokens. In this scenario, we are interested in overriding how

tokens are provisioned. The SecurityTokenManager type supplies an implementation for

CreateSecurityTokenProvider() to construct the appropriate token provider responsible for supplying

tokens for all outgoing calls. In this case, the IssuedSecurityTokenProvider is used to request an issued

token from the STS indicated in the ClientCredentials configuration. This token is then used for the call

to the service.

Figure 29: Requesting an issued token through the IssuedSecurityTokenProvider

The IssuedToken property of ClientCredentials is an instance of the IssuedTokenClientCredential type.

This type has a property to control caching tokens: CachedIssuedTokens. By default, this property is set

to true, which means that tokens will be cached until they expire, and reused to call the service instead

of re-authenticating to the STS. This reduces overhead for an individual proxy, but does not provide the

ability to share that cached token across multiple proxies. To share an issued token across proxies, you

can create a custom IssuedSecurityTokenProvider that will draw a token from a shared cache it one

exists, rather than calling the STS for each proxy.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 51

Caching Issued Tokens for Multiple Proxies To produce a shared token cache at the client a custom IssuedSecurityTokenProvider can be created to

check the local token cache prior to calling the STS to request a token. To install a custom

IssuedSecurityTokenProvider, you must also create a custom ClientCredentialsSecurityTokenManager,

and ClientCredentials type. The custom ClientCredentials type can be shared between proxies if the

configuration requirements are identical, and this will lead both proxies to the same custom

IssuedSecurityTokenProvider – thus the same cached token if one exists in the token cache. The

architecture for this scenario is illustrated in Figure 30.

Figure 30: Sharing CachedClientCredentials between two proxies

Here is a summary of the new types created:

SecurityTokenCache: a custom type that holds a SecurityToken reference and issued an event when the token has changed so the client application can respond if interested

CachedClientCredentials: a custom ClientCredentials type that holds a reference to the SecurityTokenCache and overrides CreateSecurityTokenManager() to create a custom CachedClientCredentialsSecurityTokenManager

CachedClientCredentialsSecurityTokenManager: a custom ClientCredentialsSecurityTokenManager type that holds a reference of the SecurityTokenCache

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 52

and overrides CreateSecurityTokenProvider() to create a custom CachedIssuedSecurityTokenProvider

CachedIssuedSecurityTokenProvider: a custom IssuedSecurityTokenProvider that holds a reference to the SecurityTokenCache and overrides GetTokenCore() to customize how the issued token is acquired – returning the cached token if valid, otherwise calling the STS for a new token and updating the cache

Another possible view of the architecture in Figure 30 is shown in Figure 31. In my view this is a better

way to share the token cache between two proxies using the same customized object model just

summarized. In this case each proxy gets a new instance of the CachedClientCredentials,

CachedClientCredentialsSecurityTokenManager, and CachedIssuedSecurityTokenManager – however

the same SecurityTokenCache. This is a bit more pure since the item the proxies are really sharing is the

issued token, not the other configuration settings – and though they may be the same for all proxies

calling a group of RP services, it is probably best to decouple those elements that may differ.

Figure 31: Using a new CachedClientCredentials for each proxy, still sharing the token cache

In the next sections I’ll explain the code that implements this scenario.

SecurityTokenCache There are many possible views for how to create a custom security token cache. You might have a very

complicated client application that calls many different groups of services and requires caching issued

tokens for each group. Or, you may just have an application that calls a number of related RP services

and each can share the issued token. The latter is the more common scenario in my experience, and so I

have kept this implementation very simple. Figure 32 shows the code for a custom SecurityTokenCache.

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 53

Figure 32: SecurityTokenCache implementation

public class SecurityTokenCache { private SecurityToken _Token; public SecurityToken Token { get { return _Token; } set { _Token = value; if (TokenUpdated != null) { TokenUpdated(this, null); } } } public event EventHandler TokenUpdated; }

I’ve made some assumptions in this implementation:

The client application is responsible for constructing however many of these SecurityTokenCache instances it requires for the number of issued tokens it needs to create for related groups of proxies. This will usually be just one.

The client application may want to know when the token is updated by the CachedIssuedSecurityTokenProvider – and so the TokenUpdated event is fired whenever this happens. For example, the client may want to update its client-side claims collection (discussed earlier).

This type is supplied to the CachedClientCredentials type when constructed.

CachedClientCredentials The CachedClientCredentials type is responsible for creating a custom SecurityTokenManager, which will

in turn create a custom IssuedSecurityTokenProvider for this scenario. The implementation of the

CachedClientCredentials type is shown in Figure 33. It holds a reference to the SecurityTokenCache and

passes so that the custom CachedClientCredentialsSecurityTokenManager has access to it once

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 54

constructed. The CreateSecurityTokenManager() override is the heart of this custom implementation –

shown highlighted in Figure 33.

Figure 33: CachedClientCredentials implementation

public class CachedClientCredentials: ClientCredentials { public SecurityTokenCache TokenCache { get; private set; } public CachedClientCredentials(SecurityTokenCache tokenCache): base() { this.TokenCache = tokenCache; } public CachedClientCredentials(SecurityTokenCache tokenCache, ClientCredentials clientCredentials) : base(clientCredentials) { this.TokenCache = tokenCache; } public CachedClientCredentials(CachedClientCredentials clientCredentials): base(clientCredentials) { this.TokenCache = clientCredentials.TokenCache; } public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager() { return new CachedClientCredentialsSecurityTokenManager((CachedClientCredentials)this.Clone()); } protected override ClientCredentials CloneCore() { return new CachedClientCredentials(this); } }

The client code will construct a new CachedClientCredentials type and pass in the original

ClientCredentials type to preserve any settings initialized from configuration. Figure 34 shows the code

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 55

to create the token cache, create a proxy, remove the original ClientCredentials behavior, create a new

CachedClientCredentialsBehavior passing the token cache and original behavior, and then set up the

UserName credentials to supply credentials to call the STS.

Figure 34: Initializing the CachedClientCredentials type

this.TokenCache = new SecurityTokenCache(); this._Proxy = new TodoListServiceProxy("todolistdefault"); ClientCredentials oldCreds = this._Proxy.Endpoint.Behaviors.Remove<ClientCredentials>(); CachedClientCredentials newCreds = new CachedClientCredentials(this.TokenCache, oldCreds); this._Proxy.Endpoint.Behaviors.Add(newCreds); this._Proxy.ClientCredentials.UserName.UserName = this.Username; this._Proxy.ClientCredentials.UserName.Password = this.Password; this._Proxy.Open(); Shortly I’ll describe how to share this between proxies and respond to updated tokens.

CachedClientCredentialsSecurityTokenManager As I mentioned, the CachedClientCredentials type creates a custom SecurityTokenManager – the

CachedClientCredentialsSecurityTokenManager. The heart of this implementation is the override to

CreateSecurityTokenProvider() – which returns a new CachedIssuedSecurityTokenProvider to the

runtime. Figure 35 shows this implementation.

Figure 35: CachedClientCredentialsSecurityTokenManager implementation

public class CachedClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager { public CachedClientCredentialsSecurityTokenManager(CachedClientCredentials clientCredentials): base(clientCredentials) { } public override System.IdentityModel.Selectors.SecurityTokenProvider CreateSecurityTokenProvider(System.IdentityModel.Selectors.SecurityTokenRequirement tokenRequirement) { if (!this.IsIssuedSecurityTokenRequirement(tokenRequirement)) return base.CreateSecurityTokenProvider(tokenRequirement); IssuedSecurityTokenProvider provider = base.CreateSecurityTokenProvider(tokenRequirement) as IssuedSecurityTokenProvider;

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 56

CachedIssuedSecurityTokenProvider cachedProvider = new CachedIssuedSecurityTokenProvider(provider, (CachedClientCredentials)this.ClientCredentials); return cachedProvider; } }

One of the things you’ll notice in the implementation is that a custom provider is only created if the

token requirement is for an issued token. This can be verified by calling the

IsIssuedSecurityTokenRequirement() method of the SecurityTokenManager type. Note that the

CachedIssuedSecurityTokenProvider is passed a reference to the CachedClientCredentials instance so

that it has access to the cached token.

CachedIssuedSecurityTokenProvider A partial listing of the CachedIssuedSecurityTokenProvider is shown in Figure 36. I have omitted some of

the noise related to implementing ICommunicationObject and IDisposable. The GetTokenCore() override

is where the magic happens. The code checks to see if there is a valid security token in the cache, and if

so returns it. If a valid token doesn’t exist, the base functionality to retrieve a token is called and the

token cache is updated. The token is considered valid if its ValidTo property is greater than the current

UTC time.

Figure 36: CachedIssuedSecurityTokenProvider implementation

public class CachedIssuedSecurityTokenProvider: IssuedSecurityTokenProvider, ICommunicationObject, IDisposable { private CachedClientCredentials ClientCredentials { get; set; } private IssuedSecurityTokenProvider InnerProvider {get; set;} public CachedIssuedSecurityTokenProvider(IssuedSecurityTokenProvider provider, CachedClientCredentials clientCredentials):base() { this.InnerProvider = provider; this.ClientCredentials = clientCredentials; this.CacheIssuedTokens = provider.CacheIssuedTokens; this.IdentityVerifier = provider.IdentityVerifier; this.IssuedTokenRenewalThresholdPercentage = provider.IssuedTokenRenewalThresholdPercentage; this.IssuerAddress = provider.IssuerAddress; this.IssuerBinding = provider.IssuerBinding;

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 57

foreach (IEndpointBehavior item in provider.IssuerChannelBehaviors) this.IssuerChannelBehaviors.Add(item); this.KeyEntropyMode = provider.KeyEntropyMode; this.MaxIssuedTokenCachingTime = provider.MaxIssuedTokenCachingTime; this.MessageSecurityVersion = provider.MessageSecurityVersion; this.SecurityAlgorithmSuite = provider.SecurityAlgorithmSuite; this.SecurityTokenSerializer = provider.SecurityTokenSerializer; this.TargetAddress = provider.TargetAddress; foreach (XmlElement item in provider.TokenRequestParameters) this.TokenRequestParameters.Add(item); } protected override System.IdentityModel.Tokens.SecurityToken GetTokenCore(TimeSpan timeout) { SecurityToken securityToken = null; if (this.ValidTokenInCache(this.ClientCredentials.TokenCache.Token)) { securityToken = this.ClientCredentials.TokenCache.Token; } else { securityToken = this.InnerProvider.GetToken(timeout); this.ClientCredentials.TokenCache.Token = securityToken; } return securityToken; } private bool ValidTokenInCache(SecurityToken token) { if (token == null) return false; return (DateTime.UtcNow <= token.ValidTo.ToUniversalTime()); } }

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 58

Sharing CachedClientCredentials and Updating Client Claims Figure 31 illustrated a design where each proxy has its own CachedClientCredentials reference, but

shared the SecurityTokenCache. Figure 37 illustrates the code to achieve this by initializing each instance

of the CachedClientCredentials with the same SecurityTokenCache instance. The code also illustrates

gathering client claims (as discussed earlier) and hooking the TokenUpdated event so that client claims

can be refreshed if a new token is retrieved.

Figure 37: Sharing the token cache between proxies

this.TokenCache = new SecurityTokenCache(); this._Proxy = new TodoListServiceProxy("todolistdefault"); ClientCredentials oldCreds = this._Proxy.Endpoint.Behaviors.Remove<ClientCredentials>(); CachedClientCredentials newCreds = new CachedClientCredentials(this.TokenCache, oldCreds); this._Proxy.Endpoint.Behaviors.Add(newCreds); this._Proxy.ClientCredentials.UserName.UserName = this.Username; this._Proxy.ClientCredentials.UserName.Password = this.Password; this._Proxy.Open(); _ClientAuthProxy = new ClientAuthorizationProxy("clientauthdefault"); ClientCredentials oldCreds = this._ClientAuthProxy.Endpoint.Behaviors.Remove<ClientCredentials>(); newCreds = new CachedClientCredentials(this.TokenCache, oldCreds); this._ClientAuthProxy.Endpoint.Behaviors.Add(newCreds); this._ClientAuthProxy.ClientCredentials.UserName.UserName = this.Username; this._ClientAuthProxy.ClientCredentials.UserName.Password = this.Password; this._ClientAuthProxy.Open();

this.ClientClaims = this._ClientAuthProxy.GetClaims(); NotifyPropertyChanged("ClientClaims"); this.TokenCache.TokenUpdated += new EventHandler(TokenCache_TokenUpdated);

When the application is first loaded and each proxy is initialized, the first call to retrieve a token will

populate the cache. In this scenario, the ClientAuthorizationProxy instance calls the GetClaims()

operation to retrieve client claims based on the issued token. After retrieving the claims the first time,

the client subscribes to the TokenUpdated event exposed by the SecurityTokenCache so that future

updates to the token will also update the local claims. Here is the event handler that does the work:

void TokenCache_TokenUpdated(object sender, EventArgs e) {

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 59

try { this.ClientClaims = this._ClientAuthProxy.GetClaims(); NotifyPropertyChanged("ClientClaims"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } Note that after updating the ClientClaims property to keep it in sync with the current cached security

token, a UI update is triggered with NotifyPropertyChanged – so that the XAML will rebind properties

based on the latest claims.

Session and Token Lifetime At this point I’ve discussed how to share a cached token at the client with multiple proxies and provided

one approach to synchronizing client-side claims with the latest token – but there is one more issue that

can bite you with federated security scenarios and that is secure session timeout and faulted channels.

Any time a transport session exists it is subject to timeout at the server when the channel is inactive. In

addition, any uncaught exceptions thrown at the service will fault the service channel which results in

the proxy becoming unusable. The interesting thing is that the user may not necessarily be interested in

knowing about timeouts or communication exceptions – and just prefer that the application create a

new channel so they can keep working with the application. Based on the implementation I have

discussed thus far we know that an invalid token will trigger another call to the STS to retrieve a fresh

issued token – but what happens in these other two scenarios?

As for session timeout, the client may not be aware of it until trying to make a call to the service – at

which point the call fails. Since there is no way to confirm he failed call was the result of a timeout, what

we want is for the failed call to be retried after recreating the proxy. If the retry fails we have a bigger

communication issue, but if it succeeds a new token is issued, the cache is updated, the client claims are

updated, the UI is rebound and all is well in the world.

If an exception is thrown by the service, and that exception is not a CommunicationException, the user

should be presented with the error but the next attempt to use the proxy will fail because the channel is

in a faulted state. The user doesn’t need to know about this as they were already informed of the

originating exception that caused the faulted channel. So, what we want is to recreate the proxy when

the channel is faulted and if the next call executes properly – once again things are well in the world.

I’m providing only a high-level summary of this issue because I have discussed it in greater detail in a

separate whitepaper and in a few short webcasts at http://wcfguidanceforwpf.codeplex.com. In

addition, I have created an ExceptionHandlingWCFProxyGenerator at

Building Claims-Based WPF Applications Michele Leroux Bustamante, June 2009

claimsbasedwpf.codeplex.com Page 60

http://wcfproxygenerator.codeplex.com that automates creating a special proxy base class that handles

these specific issues and allows you to shield the user from unnecessary exceptions. The side-benefit is

that the same proxy also helps us to recreate gather a new issued token when the channel is faulted.

Summary In this whitepaper I attempted to summarize a very big topic from the perspective of the WPF developer

that could be new to federated security scenarios – so that you could have a single place to get up to

speed before discussing specific implementations that would be useful to your federated WPF clients. I

talked about the process of building your first federated client, how to build a claims-based

authorization model at the client with some ideas for a possible implementation, and talked about some

other common issues around sharing issued tokens and dealing with all of these features together in a

single application. The reference samples for this discussion are located on the web site at

http://claimsbasedwpf.codeplex.com so that you can take a closer look. Enjoy!


Recommended