ACS v2 support two fundamental types of client identities– I like to call them “enterprise
identities” (WS-*) and “web identities” (Google, LiveID, OpenId in general…).
I also see two different “mind sets” when it comes to application design using the
above identity types:
Enterprise identities – often the fact that a client can present a token from a trusted
identity provider means he is a legitimate user of the application. Trust relationships
and authorization details have been negotiated out of band (often on paper).
Web identities – the fact that a user can authenticate with Google et al does not
necessarily mean he is a legitimate (or registered) user of an application. Typically
additional steps are necessary (like filling out a form, email confirmation etc).
Sometimes also a mixture of both approaches exist, for the sake of this post, I will
focus on the web identity case.
I got a number of questions how to implement the web identity scenario and after some
conversations it turns out it is the old authentication vs. authorization problem
that gets in the way.
Many people use the IsAuthenticated property on IIdentity to make
security decisions in their applications (or deny user=”?” in ASP.NET terms). That’s
a very natural thing to do, because authentication was done inside the application
and we knew exactly when the IsAuthenticated condition is true. Been there,
done that. Guilty ;)
The fundamental difference between these “old style” apps and federation is, that
authentication is not done by the application anymore. It is done by a third party
service, and in the case of web identity providers, in services that are not under
our control (nor do we have a formal business relationship with these providers).
Now the issue is, when you switch to ACS, and someone with a Google account authenticates,
indeed IsAuthenticated is true – because that’s what he is! This does not
mean, that he is also authorized to use the application. It just proves he was able
to authenticate with Google. Now this obviously leads to confusion.
How can we solve that?
Easy answer: We have to deal with authentication and authorization separately. Job
done ;)
For many application types I see this general approach:
Application uses ACS for authentication (maybe both enterprise and web identities,
we focus on web identities but you could easily have a dual approach here)
Application offers to authenticate (or sign in) via web identity accounts like LiveID,
Google, Facebook etc.
Application also maintains a database of its “own” users. Typically you want to store
additional information about the user
In such an application type it is important to have a unique identifier for your users
(think the primary key of your user database). What would that be?
Most web identity provider (and all the standard ACS v2 supported ones) emit a NameIdentifier claim.
This is a stable ID for the client (scoped to the relying party – more on that later).
Furthermore ACS emits a claims identifying the identity provider (like the original
issuer concept in WIF). When you combine these two values together, you can be sure
to have a unique identifier for the user, e.g.:
Facebook-134952459903700\799880347
You can now check on incoming calls, if the user is already registered and if yes,
swap the ACS claims with claims coming from your user database. One claims would maybe
be a role like “Registered User” which can then be easily used to do authorization
checks in the application. The WIF claims authentication manager is a perfect place
to do the claims transformation.
If the user is not registered, show a register form. Maybe you can use some claims
from the identity provider to pre-fill form fields. (see here where
I show how to use the Facebook API to fetch additional user properties). After successful
registration (which may include other mechanisms like a confirmation email), flip
the bit in your database to make the web identity a registered user.
This is all very theoretical. In the next post I will show some code and provide a
download link for the complete sample.
More on NameIdentifier
Identity providers “guarantee” that the name identifier for a given user in your application
will always be the same. But different applications (in the case of ACS – different
ACS namespaces) will see different name identifiers. This is by design to protect
the privacy of users because identical name identifiers could be used to create “profiles”
of some sort for that user. In technical terms they create the name identifier approximately
like this:
name identifier = Hash((Provider Internal User ID) + (Relying Party Address))
Why is this important to know? Well – when you change the name of your ACS namespace,
the name identifiers will change as well and you will will lose your “connection”
to your existing users.
Oh an btw – never use any other claims (like email address or name) to form a unique
ID – these can often be changed by users.