<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" version="2.0">
  <channel>
    <title>Joe Kaplan - Identity Federation</title>
    <link>http://www.joekaplan.net/</link>
    <description>.NET. LDAP. Geekery.</description>
    <language>en-us</language>
    <copyright>Joseph E. Kaplan</copyright>
    <lastBuildDate>Wed, 20 May 2009 21:11:13 GMT</lastBuildDate>
    <generator>newtelligence dasBlog 1.9.6264.0</generator>
    <managingEditor>blog@joekaplan.net</managingEditor>
    <webMaster>blog@joekaplan.net</webMaster>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=923c8f3c-0adc-4885-8a78-c1c82871e136</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,923c8f3c-0adc-4885-8a78-c1c82871e136.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,923c8f3c-0adc-4885-8a78-c1c82871e136.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=923c8f3c-0adc-4885-8a78-c1c82871e136</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I've nearly given up blogging as you can probably tell by the gaps between my posts
these days and am uncertain whether I'll ever get that going again or not.  Who
knows?  However, something weird happened today related to ADFS that I thought
might be worth a post that I decided to document for posterity.
</p>
        <p>
Essentially, a colleague of mine at work was trying to install the ADFS Web Agent
on a Win2K3 R2 server using the standard method of "Add/Remove Windows Components". 
The OS was running on a VM with the image built through some mechanism that neither
of us had any firsthand knowledge of.  Essentially, when he tried to run the
install, he got an error saying basically that the install failed.  In the event
log was something similar pointing to a log file that showed the command line used
to run MSIEXEC which in turn included a command line option to provide additional
logging in another file.  That file was very short by MSI logging standards and
basically recorded the error "1620" which translates to "not a valid intaller package":
</p>
        <p>
C:\&gt;err 1620<br />
# for decimal 1620 / hex 0x654 :<br />
  ERROR_INSTALL_PACKAGE_INVALID                                
winerror.h<br />
# This installation package could not be opened.  Contact the<br />
# application vendor to verify that this is a valid Windows<br />
# Installer package.<br />
# 1 matches found for "1620"
</p>
        <p>
This is weird!  The file c:\windows\adfs.msi is not a valid installer package?
</p>
        <p>
So, I grabbed the file and copied it to a machine that has Orca installed.  Orca
is a tool used by MSI developers to examine the contents of MSI databases (you can
get it for free with the Platform SDK tools).  When I opened the database, it
was clear that this was not actually an MSI database at all but instead an MSI patch
database (usually a .MSP file).  Strange.  When I went back to the server
and looked, there was also a file called ADFS.MSP in C:\windows with the same size. 
They turned out to be the same file with different names!  So, something had
dorked up the file images associated with the ADFS component.
</p>
        <ul>
          <li>
To solve the problem, I first did a file search for adfs.msi and found a copy in an
R2 source directory locally</li>
          <li>
I then copied this file into the c:\windows directory (after renaming the other file
"just in case")</li>
          <li>
Then, upon running Add/Remove Windows Components, the installer started to work correctly. 
I WAS reprompted to provide the location of "adfs.msi" and put in the file path to
the R2 source where I found it.  After that, all was well.  Hopefully the
patch got applied correctly as well (I didn't check).  
</li>
        </ul>
        <p>
I have no idea how the box got dorked up or if this affects anyone by my colleagues
on our servers, but I thought it was worth mentioning. This is the first time I've
ever had any success troubleshooting an ADFS installer issue, so it seemed worthy
of a post.  :)
</p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=923c8f3c-0adc-4885-8a78-c1c82871e136" />
      </body>
      <title>A Wierd ADFS Installer Issue and a Solution</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,923c8f3c-0adc-4885-8a78-c1c82871e136.aspx</guid>
      <link>http://www.joekaplan.net/AWierdADFSInstallerIssueAndASolution.aspx</link>
      <pubDate>Wed, 20 May 2009 21:11:13 GMT</pubDate>
      <description>&lt;p&gt;
I've nearly given up blogging as you can probably tell by the gaps between my posts
these days and am uncertain whether I'll ever get that going again or not.&amp;nbsp; Who
knows?&amp;nbsp; However, something weird happened today related to ADFS that I thought
might be worth a post that I decided to document for posterity.
&lt;/p&gt;
&lt;p&gt;
Essentially, a colleague of mine at work was trying to install the ADFS Web Agent
on a Win2K3 R2 server using the standard method of "Add/Remove Windows Components".&amp;nbsp;
The OS was running on a VM with the image built through some mechanism that neither
of us had any firsthand knowledge of.&amp;nbsp; Essentially, when he tried to run the
install, he got an error saying basically that the install failed.&amp;nbsp; In the event
log was something similar pointing to a log file that showed the command line used
to run MSIEXEC which in turn included a command line option to provide additional
logging in another file.&amp;nbsp; That file was very short by MSI logging standards and
basically recorded the error "1620" which translates to "not a valid intaller package":
&lt;/p&gt;
&lt;p&gt;
C:\&amp;gt;err 1620&lt;br&gt;
# for decimal 1620 / hex 0x654 :&lt;br&gt;
&amp;nbsp; ERROR_INSTALL_PACKAGE_INVALID&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
winerror.h&lt;br&gt;
# This installation package could not be opened.&amp;nbsp; Contact the&lt;br&gt;
# application vendor to verify that this is a valid Windows&lt;br&gt;
# Installer package.&lt;br&gt;
# 1 matches found for "1620"
&lt;/p&gt;
&lt;p&gt;
This is weird!&amp;nbsp; The file c:\windows\adfs.msi is not a valid installer package?
&lt;/p&gt;
&lt;p&gt;
So, I grabbed the file and copied it to a machine that has Orca installed.&amp;nbsp; Orca
is a tool used by MSI developers to examine the contents of MSI databases (you can
get it for free with the Platform SDK tools).&amp;nbsp; When I opened the database, it
was clear that this was not actually an MSI database at all but instead an MSI patch
database (usually a .MSP file).&amp;nbsp; Strange.&amp;nbsp; When I went back to the server
and looked, there was also a file called ADFS.MSP in C:\windows with the same size.&amp;nbsp;
They turned out to be the same file with different names!&amp;nbsp; So, something had
dorked up the file images associated with the ADFS component.
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
To solve the problem, I first did a file search for adfs.msi and found a copy in an
R2 source directory locally&lt;/li&gt;
&lt;li&gt;
I then copied this file into the c:\windows directory (after renaming the other file
"just in case")&lt;/li&gt;
&lt;li&gt;
Then, upon running Add/Remove Windows Components, the installer started to work correctly.&amp;nbsp;
I WAS reprompted to provide the location of "adfs.msi" and put in the file path to
the R2 source where I found it.&amp;nbsp; After that, all was well.&amp;nbsp; Hopefully the
patch got applied correctly as well (I didn't check).&amp;nbsp; 
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I have no idea how the box got dorked up or if this affects anyone by my colleagues
on our servers, but I thought it was worth mentioning. This is the first time I've
ever had any success troubleshooting an ADFS installer issue, so it seemed worthy
of a post.&amp;nbsp; :)
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=923c8f3c-0adc-4885-8a78-c1c82871e136" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,923c8f3c-0adc-4885-8a78-c1c82871e136.aspx</comments>
      <category>Identity Federation</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=c59ba3ad-6e76-4aa0-9117-e52626283b4e</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=c59ba3ad-6e76-4aa0-9117-e52626283b4e</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
It seems like I've deteriorated into semi-annual blog posts.  Sigh.  At
least the discussion groups at <a href="http://www.directoryprogramming.net">www.directoryprogramming.net</a> continue
to flourish and we are seeing a nice uptick in activity on the ADFS board there. 
I think the writing may be on the wall for me as a blogger, but who knows.  Perhaps
I'll get back on the wagon.
</p>
        <p>
Anyway, thanks to all the people who came to see my talks at DEC this year. 
I hope you enjoyed visiting my town and you got a lot out of the conference itself. 
DEC is one of my favorites and I'm happy to see it continue to do well.  I got
a lot of nice feedback on both of my talks and I'm always interested to hear what
you thought.
</p>
        <p>
My first talk this year was on customizing ADFS.  To my knowledge, this type
of stuff has never been talked about publicly before, so the session was a bit of
an experiment.  I essentially tried to cover all the different types of things
you should and could do to ADFS V1 or 1.1 (2003 R2 server or 2008 server) to make
it do different things.  It started off discussing some cosmetics to apply to
the pages displayed by the FS, moved through ADAM account store tweaks and then covered
custom claim transformation modules and some advanced hacks/mods such as non-Windows
authentication.  Some of that stuff has been discussed here on this blog and
nearly all of it was based on stuff we've actually done at work, so basically real
world experience.  My fear was that the subject matter would be a little over
the audience's head since it wasn't introductory at all (assumed you already knew
ADFS pretty well) and covered some developer stuff which might have seemed alien to
many of the audience members who tend to be more of the IT Pro sort.  Still,
I think it was valuable stuff.
</p>
        <p>
I mentioned a sample custom claim transform module that I'd post the source to. 
I'll follow that up in a separate post shortly.
</p>
        <p>
The audiences for all the ADFS talks were pretty small by comparison to the big AD
talks, but this doesn't surprise me.  Not only is ADFS still a new thing, but
to a great extent it is a luxury for most DEC attendees to be able to go to those
sessions.  ADFS largely assumes that both the directory and the identity provisioning
stuff is all a solved problem and that we have rich repositories filled with security
principals whose identity is trustworthy and stuffed with useful metadata that can
be converted to claims.  For many, that is not (yet) a solved problem at all
and federated identity is still largely wishful thinking.  Still, you have to
start somewhere.
</p>
        <p>
My second talk was essentially the talk I've done at DEC now for 3 years, although
this time modified heavily to cover the new .NET 3.5 stuff in System.DirectoryServices.AccountManagement. 
I was really dreading this talk and didn't finish the slides for it before the conference,
so between the last minute PPT work and the anxiety, I only got about an hour of sleep.
</p>
        <p>
Still, the talk seemed to go off pretty well.  I was in the big room this time
and in one of the last slots of the conference, so I had low expectations for turnout. 
It seemed like I had a pretty good crowd though (not the Dean and Joe show, but I'll
take it!) and people seemed to be into it.  I think it was probably the best
version of that talk I've done to date, so all in all I'm quite happy.  I miss
having <a href="http://www.dunnry.com/blog/">Ryan</a> there to bounce stuff around
with, but so it goes.
</p>
        <p>
Thanks also to <a href="http://blogs.msdn.com/donovanf/">Donovan</a> for inviting
me up for his case studies talk and giving me an opportunity to talk about some of
the real world stuff we are doing with federation at work and talking about the process
and legal stuff as much as the technical aspects.  People clearly have as many
if not more questions about those things than the engineering parts.
</p>
        <p>
I think I finally get CardSpace now, especially as it applies to the enterprise, and
am looking forward to having a chance to get it running internally.  That should
be interesting.  Stuart, I need some bits I can actually deploy.  :) 
Thanks to <a href="http://pamelaproject.com/">Pamela Dingle</a> for coming to DEC
and bringing both the CardSpace love and the non-MS platform perspective.  I'm
anxious to go to a conference where everyone knows her and no one knows me at all
and see how I do.
</p>
        <p>
She's got a nice <a href="http://eternaloptimist.wordpress.com/2008/03/10/dec-2008-this-ones-for-you-wook/">follow
up</a> on the Wook Lee Challenge this year which I played a tiny role in.
</p>
        <p>
        </p>
        <p>
          <a href="http://www.joekaplan.net/content/binary/Customizing-ADFS.zip">Customizing-ADFS.zip
(1.06 MB)</a>
        </p>
        <p>
          <a href="http://www.joekaplan.net/content/binary/DotNet-DS-Programming.zip">DotNet-DS-Programming.zip
(1.27 MB)</a>
        </p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=c59ba3ad-6e76-4aa0-9117-e52626283b4e" />
      </body>
      <title>DEC 2008 Wrap Up and Materials</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</guid>
      <link>http://www.joekaplan.net/DEC2008WrapUpAndMaterials.aspx</link>
      <pubDate>Wed, 12 Mar 2008 04:41:56 GMT</pubDate>
      <description>&lt;p&gt;
It seems like I've deteriorated into semi-annual blog posts.&amp;nbsp; Sigh.&amp;nbsp; At
least the discussion groups at &lt;a href="http://www.directoryprogramming.net"&gt;www.directoryprogramming.net&lt;/a&gt; continue
to flourish and we are seeing a nice uptick in activity on the ADFS board there.&amp;nbsp;
I think the writing may be on the wall for me as a blogger, but who knows.&amp;nbsp; Perhaps
I'll get back on the wagon.
&lt;/p&gt;
&lt;p&gt;
Anyway, thanks to all the people who came to see my talks at DEC this year.&amp;nbsp;
I hope you enjoyed visiting my town and you got a lot out of the conference itself.&amp;nbsp;
DEC is one of my favorites and I'm happy to see it continue to do well.&amp;nbsp; I got
a lot of nice feedback on both of my talks and I'm always interested to hear what
you thought.
&lt;/p&gt;
&lt;p&gt;
My first talk this year was on customizing ADFS.&amp;nbsp; To my knowledge, this type
of stuff has never been talked about publicly before, so the session was a bit of
an experiment.&amp;nbsp; I essentially tried to cover all the different types of things
you should and could do to ADFS V1 or 1.1 (2003 R2 server or 2008 server) to make
it do different things.&amp;nbsp; It started off discussing some cosmetics to apply to
the pages displayed by the FS, moved through ADAM account store tweaks and then covered
custom claim transformation modules and some advanced hacks/mods such as non-Windows
authentication.&amp;nbsp; Some of that stuff has been discussed here on this blog and
nearly all of it was based on stuff we've actually done at work, so basically real
world experience.&amp;nbsp; My fear was that the subject matter would be a little over
the audience's head since it wasn't introductory at all (assumed you already knew
ADFS pretty well) and covered some developer stuff which might have seemed alien to
many of the audience members who tend to be more of the IT Pro sort.&amp;nbsp; Still,
I think it was valuable stuff.
&lt;/p&gt;
&lt;p&gt;
I mentioned a sample custom claim transform module that I'd post the source to.&amp;nbsp;
I'll follow that up in a separate post shortly.
&lt;/p&gt;
&lt;p&gt;
The audiences for all the ADFS talks were pretty small by comparison to the big AD
talks, but this doesn't surprise me.&amp;nbsp; Not only is ADFS still a new thing, but
to a great extent it is a luxury for most DEC attendees to be able to go to those
sessions.&amp;nbsp; ADFS largely assumes that both the directory and the identity provisioning
stuff is all a solved problem and that we have rich repositories filled with security
principals whose identity is trustworthy and stuffed with useful metadata that can
be converted to claims.&amp;nbsp; For many, that is not (yet) a solved problem at all
and federated identity is still largely wishful thinking.&amp;nbsp; Still, you have to
start somewhere.
&lt;/p&gt;
&lt;p&gt;
My second talk was essentially the talk I've done at DEC now for 3 years, although
this time modified heavily to cover the new .NET 3.5 stuff in System.DirectoryServices.AccountManagement.&amp;nbsp;
I was really dreading this talk and didn't finish the slides for it before the conference,
so between the last minute PPT work and the anxiety, I only got about an hour of sleep.
&lt;/p&gt;
&lt;p&gt;
Still, the talk seemed to go off pretty well.&amp;nbsp; I was in the big room this time
and in one of the last slots of the conference, so I had low expectations for turnout.&amp;nbsp;
It seemed like I had a pretty good crowd though (not the Dean and Joe show, but I'll
take it!) and people seemed to be into it.&amp;nbsp; I think it was probably the best
version of that talk I've done to date, so all in all I'm quite happy.&amp;nbsp; I miss
having &lt;a href="http://www.dunnry.com/blog/"&gt;Ryan&lt;/a&gt; there to bounce stuff around
with, but so it goes.
&lt;/p&gt;
&lt;p&gt;
Thanks also to &lt;a href="http://blogs.msdn.com/donovanf/"&gt;Donovan&lt;/a&gt; for inviting
me up for his case studies talk and giving me an opportunity to talk about some of
the real world stuff we are doing with federation at work and talking about the process
and legal stuff as much as the technical aspects.&amp;nbsp; People clearly have as many
if not more questions about those things than the engineering parts.
&lt;/p&gt;
&lt;p&gt;
I think I finally get CardSpace now, especially as it applies to the enterprise, and
am looking forward to having a chance to get it running internally.&amp;nbsp; That should
be interesting.&amp;nbsp; Stuart, I need some bits I can actually deploy.&amp;nbsp; :)&amp;nbsp;
Thanks to &lt;a href="http://pamelaproject.com/"&gt;Pamela Dingle&lt;/a&gt; for coming to DEC
and bringing both the CardSpace love and the non-MS platform perspective.&amp;nbsp; I'm
anxious to go to a conference where everyone knows her and no one knows me at all
and see how I do.
&lt;/p&gt;
&lt;p&gt;
She's got a nice &lt;a href="http://eternaloptimist.wordpress.com/2008/03/10/dec-2008-this-ones-for-you-wook/"&gt;follow
up&lt;/a&gt; on the Wook Lee Challenge this year which I played a tiny role in.
&lt;/p&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/Customizing-ADFS.zip"&gt;Customizing-ADFS.zip
(1.06 MB)&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/DotNet-DS-Programming.zip"&gt;DotNet-DS-Programming.zip
(1.27 MB)&lt;/a&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=c59ba3ad-6e76-4aa0-9117-e52626283b4e" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,c59ba3ad-6e76-4aa0-9117-e52626283b4e.aspx</comments>
      <category>General;Identity Federation;LDAP</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=5c846938-9aba-467a-b1b0-99bbef193bcb</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=5c846938-9aba-467a-b1b0-99bbef193bcb</wfw:commentRss>
      <slash:comments>1</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I was lucky enough to attend DEC again this year and was even more lucky to have been
asked to speak due to an unfortunate last minute cancellation.  This year, I
presented on a variation of the same type of stuff that Ryan and I <a href="http://dunnry.com/blog/DEC2006WrapUpAndPresentationMaterial.aspx">presented
on at DEC 2006</a>.  This year, I had to fly solo as Ryan could not attend. 
:(
</p>
        <p>
Here's what we did differently this time around:
</p>
        <ul>
          <li>
No PowerShell (DEC already had 2 PowerShell sessions, so why bother?) 
</li>
          <li>
Focus on some new Longhorn LDAP and AD features (Fine Grained Password Policy) 
</li>
          <li>
A "slideware" overview of what's coming in .NET 3.5 "Orcas" with the new System.DirectoryServices.AccountManagement
namespace (formerly known as the Principal API).</li>
        </ul>
        <p>
I'd like to thank all of those who attended.  I hope you enjoyed the talk and
hope that some of you got free books.  I apologize if I could not accomodate
all of you.  :(  Thanks to the <a href="http://www.awprofessional.com/index.asp?rl=1">Addison-Wesley</a> marketing
team for providing the books for your enjoyment.
</p>
        <p>
For those of you interested in the Snippet Compiler tool I used in my demos, you can
find it <a href="http://www.sliver.com/dotnet/SnippetCompiler/">here</a>.
</p>
        <p>
The slides and code for the demos are attached and I did get around to converting
them to VB for all of you VB people (I'm a VB.NET guy too; I really don't know why
I coded all the demos in C# :)).  
</p>
        <p>
Note that my application of the "in chain" matching rule turns out to be incorrect
usage.  Don't do it like that!  Read more <a href="http://www.joekaplan.net/InChainMatchingRuleShouldNotBeUsedForTransitiveLinkExpansion.aspx">here</a>. 
I feel silly.
</p>
        <p>
Note that if you are confused about which API to use, S.DS or S.DS.P, I discussed
that in some detail <a href="http://www.joekaplan.net/ATaleOfTwoLDAPStacks.aspx">here</a>. 
There is really no right answer, but hopefully that helps.  
</p>
        <p>
To ask us any specific questions about LDAP programming, please use the book's <a href="http://www.directoryprogramming.net">discussion
forum</a>.  This is the only place that Ryan and I both use together.
</p>
        <p>
As always, DEC is a treat and I really enjoyed all the conversations and interaction
and am happy to see ADFS gaining a little traction.  Now, about that hot chicken...
</p>
        <a href="http://www.joekaplan.net/content/binary/DEC2007.zip">DEC2007.zip (536.52
KB)</a>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=5c846938-9aba-467a-b1b0-99bbef193bcb" />
      </body>
      <title>DEC 2007 Follow Up</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</guid>
      <link>http://www.joekaplan.net/DEC2007FollowUp.aspx</link>
      <pubDate>Fri, 27 Apr 2007 16:58:34 GMT</pubDate>
      <description>&lt;p&gt;
I was lucky enough to attend DEC again this year and was even more lucky to have been
asked to speak due to an unfortunate last minute cancellation.&amp;nbsp; This year, I
presented on a variation of the same type of stuff that Ryan and I &lt;a href="http://dunnry.com/blog/DEC2006WrapUpAndPresentationMaterial.aspx"&gt;presented
on at DEC 2006&lt;/a&gt;.&amp;nbsp; This year, I had to fly solo as Ryan could not attend.&amp;nbsp;
:(
&lt;/p&gt;
&lt;p&gt;
Here's what we did differently this time around:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
No PowerShell (DEC already had 2 PowerShell sessions, so why bother?) 
&lt;li&gt;
Focus on some new Longhorn LDAP and AD features (Fine Grained Password Policy) 
&lt;li&gt;
A "slideware" overview of what's coming in .NET 3.5 "Orcas" with the new System.DirectoryServices.AccountManagement
namespace (formerly known as the Principal API).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
I'd like to thank all of those who attended.&amp;nbsp; I hope you enjoyed the talk and
hope that some of you got free books.&amp;nbsp; I apologize if I could not accomodate
all of you.&amp;nbsp; :(&amp;nbsp; Thanks to the &lt;a href="http://www.awprofessional.com/index.asp?rl=1"&gt;Addison-Wesley&lt;/a&gt; marketing
team for providing the books for your enjoyment.
&lt;/p&gt;
&lt;p&gt;
For those of you interested in the Snippet Compiler tool I used in my demos, you can
find it &lt;a href="http://www.sliver.com/dotnet/SnippetCompiler/"&gt;here&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The slides and code for the demos are attached and I did get around to converting
them to VB for all of you VB people (I'm a VB.NET guy too; I really don't know why
I coded all the demos in C# :)).&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Note that my application of the "in chain" matching rule turns out to be incorrect
usage.&amp;nbsp; Don't do it like that!&amp;nbsp; Read more &lt;a href="http://www.joekaplan.net/InChainMatchingRuleShouldNotBeUsedForTransitiveLinkExpansion.aspx"&gt;here&lt;/a&gt;.&amp;nbsp;
I feel silly.
&lt;/p&gt;
&lt;p&gt;
Note that if you are confused about which API to use, S.DS or S.DS.P, I discussed
that in some detail &lt;a href="http://www.joekaplan.net/ATaleOfTwoLDAPStacks.aspx"&gt;here&lt;/a&gt;.&amp;nbsp;
There is really no right answer, but hopefully that helps.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
To ask us any specific questions about LDAP programming, please use the book's &lt;a href="http://www.directoryprogramming.net"&gt;discussion
forum&lt;/a&gt;.&amp;nbsp; This is the only place that Ryan and I both use together.
&lt;/p&gt;
&lt;p&gt;
As always, DEC is a treat and I really enjoyed all the conversations and interaction
and am happy to see ADFS gaining a little traction.&amp;nbsp; Now, about that hot chicken...
&lt;/p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/DEC2007.zip"&gt;DEC2007.zip (536.52
KB)&lt;/a&gt;&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=5c846938-9aba-467a-b1b0-99bbef193bcb" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,5c846938-9aba-467a-b1b0-99bbef193bcb.aspx</comments>
      <category>General;Identity Federation;LDAP</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=d41ff2af-fa75-4252-8f93-37c88b019dbe</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,d41ff2af-fa75-4252-8f93-37c88b019dbe.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,d41ff2af-fa75-4252-8f93-37c88b019dbe.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=d41ff2af-fa75-4252-8f93-37c88b019dbe</wfw:commentRss>
      <slash:comments>2</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I've been using ADFS day in and day out for almost a year now and have run into a
fairly stunning number of issues.  Most of these are simple problems with simple
solutions, but they can cause hours or days of frustration to resolve.  One of
the main themes that seems to pervade these little doses of IT misery is based on
a misunderstanding of how cookies work and how ADFS uses cookies.  This post
is my attempt to gather some of the lessons I've learned and hopefully make your time
with ADFS a little easier.  
</p>
        <p>
          <font size="5">How Cookies Work in Web Browsers</font>
        </p>
        <p>
Although many of us have a basic, foggy notion about what cookies do in web applications,
many of us don't know all of the little rules that govern their behavior.  I'll
try to run that down now based on my own understanding, which I believe is at least
technically accurate enough for the purposes of the rest of this article.  Feel
free to correct me if you know better or refer to the RFCs if you want more precision.
</p>
        <p>
Basically, a cookie is a small piece of textual data that is sent by a web server
to a web browser.  The job of the browser is to return this piece of data back
to the server when it makes another request to the server, based on the rules that
I'll detail shortly.  Cookies are generally used in web applications as a means
of maintaining state in the web server across multiple requests made by the browser.
</p>
        <p>
Cookies are implemented as well-known HTTP headers in the request data sent by the
browser and the response data sent.  The request header name is called <font face="Courier New"><strong>Cookie</strong></font> and
the response header name is <strong><font face="Courier New">Set-Cookie</font></strong>. 
The server always initiates the usage of cookies by returning a response with a <font face="Courier New"><strong>Set-Cookie</strong></font> header
in it that contains the data and some other instructions.  The browser's job
is to then add a <font face="Courier New"><strong>Cookie</strong></font> header on
all subsequent requests to the server that contains exactly the data the server sent
in the <strong><font face="Courier New">Set-Cookie</font></strong> header.  However,
exactly when the browser will add the <font face="Courier New"><strong>Cookie</strong></font> header
to its request is based on the other instructions in the <font face="Courier New"><strong>Set-Cookie</strong></font> header
and the rules for cookie behavior.  
</p>
        <p>
First, let's examine the "other instructions".  The <strong><font face="Courier New">Set-Cookie</font></strong> header
may contain these bits of info:
</p>
        <ul>
          <li>
Name 
</li>
          <li>
Value 
</li>
          <li>
Domain (optional) 
</li>
          <li>
Path (optional) 
</li>
          <li>
Expires (optional) 
</li>
          <li>
Secure flag (optional) 
</li>
          <li>
HttpOnly flag (optional)</li>
        </ul>
        <p>
          <font size="4">Cookie Name and Value</font>
        </p>
        <p>
The Cookie Name is just what it sounds like.  Since a web app may use many different
cookies, this is how they can be distinguished.  The Cookie Value is just some
textual data.  It could be anything.  Oftentimes, it may contain some data
that has been encrypted that the browser can't even interpret it--only the server
can.  This is not a problem.  The data is for the server anyway.  The
browser's job is to dutifully sending it back to the server, not understand it.
</p>
        <p>
          <font size="4">Cookie Expires, Secure and HttpOnly</font>
        </p>
        <p>
I'll skip ahead to the last three, as they easy to explain and not terribly problematic
(for our purposes).  The optional Expires field may contain a
date/time value that tells the browser when to stop sending the cookie back to the
server.  This one is interesting because it alone determines whether a cookie
is a <strong>session cookie</strong> or a <strong>file-based cookie</strong>.  
</p>
        <p>
If there is no expiration specified, the cookie defaults to a session cookie and will
be returned as long as the browser session continues.  In IE and FireFox (and
probably most other browsers on Windows), the session is basically the duration that
the browser <em>process</em> is open.  Note that a browser process may span many
windows and may originate in a non-obvious place like an email or an instant messenger
client, so you may sometimes be surprised to see a cookie being sent back to a server
even after a window was closed (or seemingly all windows for that matter!).  
</p>
        <p>
If the expiration date is set, then the cookie is "file-based" and will be written
to disk.  It will survive across multiple browser processes being launched and
stopped.  The browser will continue to send it until the expiration time is exceeded.
</p>
        <p>
The Secure flag instructs the browser to only return the cookie back to the server
if the request uses the HTTPS (SSL) protocol.  This flag is often used when the
cookie contains sensitive data that could be useful by a bad guy if it was intercepted
on the wire.  I consider it to be bad practice to ever use any kind of forms-based
authentication cookie without the Secure flag set, although this then requires SSL. 
If you are serious about security, you use SSL for secure websites.  End of story.
</p>
        <p>
The HttpOnly flag is the new guy and not all browsers support it (although many do). 
It basically says that this cookie will only be sent on the wire.  The browser
will not allow the cookie to show up in the browser's Document Object Model (DOM)
cookie collection.  This prevents scripts from being able to view and manipulate
the cookie and can help prevent some kinds of cross-site scripting (XSS) attacks.
</p>
        <p>
          <font size="4">Cookie Domain</font>
        </p>
        <p>
For our purposes, The Cookie Domain and Path are the most interesting values, as this
is the heart of all of the trouble in ADFS, so we cover them last.  The domain
specifies which hosts the browser will replay the cookie to.  If the domain is
not specified, the browser will only replay the cookie to host that sent the <font face="Courier New"><strong>Set-Cookie</strong></font> header
in the first place.  Here, the host is determined by the host portion of the
URL.  For example, in <a href="http://www.joekaplan.net/default.aspx">http://www.joekaplan.net/default.aspx</a>,
the host is <font face="Courier New" color="#0000ff">www.joekaplan.net</font>.  
</p>
        <p>
Domains can also be specified hierarchically.  I could set the cookie domain
to <font face="Courier New" color="#0000ff">.joekaplan.net</font> and then my browser
would return the cookie to any host within the <font face="Courier New" color="#0000ff">joekaplan.net</font> DNS
namespace such as <font face="Courier New" color="#0000ff">www.joekaplan.net</font>, <font face="Courier New" color="#0000ff">blogs.joekaplan.net</font> and <font face="Courier New" color="#0000ff">junk.joekaplan.net</font> (neither
of which exist in the latter two examples, but that isn't important).
</p>
        <p>
One might wonder if one can set a cookie for a domain other than the domain of the
host.  The answer is "it depends", but usually no.  As one can imagine,
this could (and has!) lead to all kinds of crazy hacking where one site writes over
another site's cookies.  In IE, the ability to do this is locked down by the
various security levels and assigned by zone.  I haven't seen many legitimate
uses for such a design personally, so I'm happy this is restricted now.
</p>
        <p>
Another important picky detail about the domain value is that it doesn't consider
the port component of the hostname in the URL.  Basically, this means that it
treats the host <font face="Courier New" color="#0000ff">www.joekaplan.net:80</font> and <font face="Courier New" color="#0000ff">www.joekaplan.net:8080</font> as <strong><em>the
same host</em></strong>, even if those map to completely different websites on your
web server.  This can lead to surprises.
</p>
        <p>
          <font size="4">Cookie Path</font>
        </p>
        <p>
The final modifier is the Path.  The path value is like the domain, except that
is specifies the scope in the path hierarchy that the cookie will be replayed to. 
Here, the path is the part of the URL that comes after the host name.  If the
path is set to <font face="Courier New" color="#0000ff">/</font>, then the cookie
will be replayed to any part of the hierarchy of the website.  If the path is
set to <font face="Courier New" color="#0000ff">/site</font>, then the cookie will
only be replayed to requests for URLs at or below <font face="Courier New" color="#0000ff">/site</font>.
</p>
        <p>
One tricky detail with the path value is that it is <strong><em>case-sensitive</em></strong>. 
If you set the path to <font face="Courier New" color="#0000ff">/site</font> and your
URL is <font face="Courier New" color="#0000ff">http://www.joekaplan.net/Site/default.aspx</font> (note
the capital "S"), guess what?  The cookie will not be returned with the request! 
This little gotcha can easily confound IIS developers, as IIS is generally quite happy
to treat folders and virtual directories as case-insensitive.  Developers on
"other" web server platforms where URLs (and file names) are case-sensitive tend to
be a little more hip to this. :)
</p>
        <p>
          <font size="5">A Brief Tour of the ADFS Cookies</font>
        </p>
        <p>
ADFS, like most other HTTP authentication protocols that don't use the built in HTTP
authentication specifications (Basic, Digest, Negotiate, Kerberos, NTLM, etc.), use
cookies for a lot of stuff.  There are three primary cookies to know about:
</p>
        <ul>
          <li>
Authentication/Token cookie (<font face="Courier New" color="#0000ff">_WebSsoAuth</font>) 
</li>
          <li>
Home Realm Cookie (<font face="Courier New" color="#0000ff">_LSRealm</font>) 
</li>
          <li>
Logout Cookie (<font face="Courier New" color="#0000ff">_LSCleanup</font>)</li>
        </ul>
        <p>
Let's take each in turn.
</p>
        <p>
          <font size="4">Authentication/Token Cookie</font>
        </p>
        <p>
The <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie is certainly
the most important one, as that is the cookie that represents the user's login to
a federation resource.  This cookie is always a session cookie, in that the Expires
field is never set, so it goes away when the browser process does.  Expiration
of this cookie is handled by timestamps set in the data in the cookie itself and is
a programmatic feature of the protocol and is not related to how the browser treats
cookie expiration.  
</p>
        <p>
The cookie basically contains the SAML 1.1 token issued by a federation server that
allows access to a specific resource.  The format of the cookie for ADFS is in
some crazy compressed format that MS came up with that isn't documented, but that
is supposedly cool because the WS-Federation PRP spec doesn't actually contain any
details about what this should look like and leaves it implementation specific. 
These cookies are only consumed by the server/app that issued them (hopefully!), so
this isn't a big deal.  Sometimes you may see a <font face="Courier New" color="#0000ff">_WebSsoAuth0</font> cookie
or additional numbering.  This can happen if the SAML token is very large and
the compressed size of the XML in the SAML token may exceed the allowed size of a
cookie.  ADFS breaks these up for you to overcome this.  This is a picky
and unimportant detail for our discussion, but I figured I'd mention it in case you
saw it and were curious.
</p>
        <p>
The <font face="Courier New"><strong>Set-Cookie</strong></font> header for this cookie
usually looks something like this (data clipped for brevity):
</p>
        <p>
          <font face="Courier New" color="#0000ff">Set-Cookie: _WebSsoAuth=eNrNWWuTqkgS9acYzpfd6LV5qAhGd8cWDxUVFMVnbMREASWiQ.....CdagA;
path=/adfs/ls/; secure; HttpOnly</font>
        </p>
        <p>
In this case, this is the authentication cookie issued by the federation server itself. 
In ADFS, each federation server the user logs in to (at least one, maybe two if there
is an account partner/resource partner set up) will issue a <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie. 
Additionally, each application that the user visits will issue a <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie
that is specific to the application.  
</p>
        <p>
Each <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie is <em>different</em> and <em>is
intended only for the app/server that issued it</em>.  However, you probably
noticed how they all have the same name.  Because they all have the same
name, they must different by <em>domain</em> and <em>path</em> so that the browser
will know which cookie to send to which app/server.  If you've ever configured
an ADFS token-based or claims-based application, you will have noticed that you are
required to specify the domain and path for the cookie when configuring the application. 
All it takes is a naive mistake with this configuration to cause the browser to start
sending the wrong cookie to the wrong place and ADFS chaos ensues.
</p>
        <p>
          <font size="4">Home Realm Cookie</font>
        </p>
        <p>
The <font face="Courier New" color="#0000ff">_LSRealm</font> cookie is a file-based
cookie issued by the resource federation server after you have successfully logged
in that specifies the federation server URI (usually <font face="Courier New">urn:federation:foo</font> or
something) of the server that authenticated you.  This is done so that the resource
federation server doesn't have to prompt you for your home organization again if it
has multiple potential partners.  We already discussed a little bit about these
values and home realm discovery query strings in a <a href="http://www.joekaplan.net/NoteToUrnfederationself.aspx">post</a> last
summer, so I won't get into much more detail.  These things rarely cause trouble
except that you have to clear your cookies if you want to get a resource federation
server to prompt you for your home realm again after you log in the first time. 
They are issued for one month as I recall; I'm too lazy to check for certain right
now.  :)
</p>
        <p>
          <font size="4">Logout Cookie</font>
        </p>
        <p>
The <font face="Courier New" color="#0000ff">_LSCleanup</font> cookie assists in the
logout process by keeping track of which federation servers and applications you've
visited.  When ADFS does a log out, it attempts to "drain the swamp" by logging
you out of <strong>everything</strong>.  To do this, the built-in logout page
attempts to visit the logout URL for every federation server you've visited and every
app you've visited.  These logout URLs set the <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie
value for the cookies they issued to "null".  This cookie is also a session cookie. 
These cookies can get screwed up just like the authentication cookies, but usually
this fact never comes up because once the authentication cookies are hosed, things
tend to break before you ever try to log out.
</p>
        <p>
          <font size="5">Common ADFS Problems Caused by Incorrect Cookie Settings</font>
        </p>
        <p>
There are two ADFS problems I've seen frequently that cause much pain that are related
to cookie problems and probably some variants that I'm forgetting:
</p>
        <ul>
          <li>
Invalid SAML Audience 
</li>
          <li>
Infinite Loop Detected</li>
        </ul>
        <p>
          <font size="4">Invalid SAML Audience</font>
        </p>
        <p>
This occurs when one ADFS application get's another ADFS application's cookie. 
Like I said before, each application has an application-specific cookie and there
is a piece of data in the SAML token called the <em>audience</em> that says exactly
where the cookie was supposed to go.  This is identified by the application's
configured URL in the trust policy and the web site.  
</p>
        <p>
There are a variety of ways you can create this error.  One easy way is by setting
the cookie domain to something like <font face="Courier New" color="#0000ff">.domain.com</font> and
having your apps set up with host names like <font face="Courier New" color="#0000ff">app1.domain.com</font> and <font face="Courier New" color="#0000ff">app2.domain.com</font>. 
Since these are both basically children of <font face="Courier New" color="#0000ff">.domain.com</font>,
the browser will happily send a <font face="Courier New" color="#0000ff">_WebSsoAuth</font> cookie
to <strong>app2</strong> that <strong>app1</strong> issued if the paths are the same. 
Since the path is often set to <font face="Courier New" color="#0000ff">/</font>,
this is not uncommon.  You can also have pseudo-chaos that is even more difficult
to debug if the path's aren't the same but contain some overlap in hierarchy.
</p>
        <p>
I'm still trying to figure out a reason why you would ever set the cookie domain in
an ADFS system.  Generally, you only ever want an ADFS cookie going back to the
host that issued it, so why not leave it at the default setting of "null"?  In
my experience, nothing but trouble ever comes from setting the domain.  If you
think of a use case for setting this, please let me know.
</p>
        <p>
I also mentioned overlapping paths.  These are a significant cause of trouble. 
Let's say you have an app at the root of the site <font face="Courier New" color="#0000ff">/</font> and
an app below that <font face="Courier New" color="#0000ff">/Site</font>.  Basically,
if they have the same host name, <em>you can't do that in ADFS</em>.  They need
to be strict siblings like <font face="Courier New" color="#0000ff">/Site1</font> and <font face="Courier New" color="#0000ff">/Site2</font>.  
</p>
        <p>
Overlapping paths can show up in subtle ways as well.  Sometimes, you may not
think that overlapping paths are a problem because you aren't aware that you have
overlapping domains.  If you use the <em><strong>ADFS Step-by-Step Guide</strong></em> to
put together your ADFS demo lab, you may find yourself using host names like this
to try out different application styles and using different TCP/IP ports for SSL to
split them up in IIS:
</p>
        <ul>
          <li>
            <font face="Courier New" color="#0000ff">https://apps.domain.com:8081/</font>
          </li>
          <li>
            <font face="Courier New" color="#0000ff">https://apps.domain.com:8082/</font>
          </li>
        </ul>
        <p>
While this makes things easy from the IIS perspective in terms of setting up multiple
sites and it makes it easier to deal with few host names (and less DNS and certificate
mucking around), remember that <strong><em>the browser treats those two hosts as being
the same thing for cookie purposes</em></strong>.  You now have overlapping domains. 
Therefore, it simply falls to the cookie path to make a distinction between them. 
If you use a path like:
</p>
        <ul>
          <li>
            <font face="Courier New" color="#0000ff">/claimsapp</font>
          </li>
          <li>
            <font face="Courier New" color="#0000ff">/tokenapp</font>
          </li>
        </ul>
        <p>
...for each one, you are fine, but if you naively put one or both of those apps in
the root, you will have problems if you try to do SSO between them with the previously
mentioned error.
</p>
        <p>
          <font size="4">Infinite Loop Errors and the Like</font>
        </p>
        <p>
The other problem we can easily create is the "infinite loop" problem or "same token
request within the last 20 seconds" problem on the federation server.  I've seen
this happen when we have a capitalization different between the browser URL and the
cookie path.  As we explained above, <strong><em>the cookie path is treated as
case sensitive by the browser</em></strong>, but IIS is generally very happy to treat
a URL folder or virtual directory path as case-insensitive.  The problem then
manifests itself in ADFS with an interaction like this:
</p>
        <ol>
          <li>
User logs into federation server after being redirected to do so by the application
they were trying to access 
</li>
          <li>
Federation server generates SAML token for resource application and redirects browser
to POST the token to that URL (<font face="Courier New" color="#0000ff">app.domain.com/site/</font>) 
</li>
          <li>
Resource application receives the token via POST, verifies it and issues the browser
a cookie with the token contents for use with subsequent visits.  The path in
the <strong><font face="Courier New">Set-Cookie</font></strong> header is <font face="Courier New" color="#0000ff">/Site</font>. 
App redirects the user back to itself with the exact URL originally requested. 
</li>
          <li>
Browser receives the cookie, but it is using a URL like <font face="Courier New" color="#0000ff">https://app.domain.com/<strong>s</strong>ite/default.aspx</font>,
not <font face="Courier New" color="#0000ff">https://app.domain.com/<strong>S</strong>ite/default.aspx</font>. 
Since those are clearly different (note the capital S!), <strong><em>the browser does
not include the authentication cookie in the request</em></strong>. 
</li>
          <li>
App receives the request from the browser without the cookie and decides "this guy
should log in if I'm going to allow access, so go back to my federation server, get
a token and I'll see you later" and redirects the user back to the federation server
with instructions to sign in. 
</li>
          <li>
Federation server is clever and notices that it just issued a token for this user
for this app a second ago or so and throws an error because it knows that something
just isn't right.</li>
        </ol>
        <p>
This one is a little subtle, but easily fixable.
</p>
        <p>
          <font size="5">Conclusion</font>
        </p>
        <p>
Cookies are at the heart and soul of how ADFS works (with the Passive Requester Profile
anyway), so understanding the rules that govern their behavior in the browser and
how ADFS expects you to use them will save you potentially hours or days of troubleshooting
misery.  
</p>
        <p>
          <font size="4">Tools for Viewing Headers and Cookies</font>
        </p>
        <p>
When working with ADFS (or really just about any web app I've found), having a way
to view the raw request and response headers associated with all of the traffic generated
by the browser and server is invaluable.  There are great plugins for IE (<a href="http://www.blunck.info/">ieHttpHeaders</a>) and
FireFox (<a href="http://livehttpheaders.mozdev.org/">Live Headers</a>) that do a
fine job of this and are free.  
</p>
        <p>
          <em>
            <font size="1">(edited March 4, 2007, to add some additional formatting, fix some
wording and add the section on viewing headers)</font>
          </em>
        </p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=d41ff2af-fa75-4252-8f93-37c88b019dbe" />
      </body>
      <title>Keep Your Cookies Straight When Using ADFS</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,d41ff2af-fa75-4252-8f93-37c88b019dbe.aspx</guid>
      <link>http://www.joekaplan.net/KeepYourCookiesStraightWhenUsingADFS.aspx</link>
      <pubDate>Sun, 04 Mar 2007 18:09:13 GMT</pubDate>
      <description>&lt;p&gt;
I've been using ADFS day in and day out for almost a year now and have run into a
fairly stunning number of issues.&amp;nbsp; Most of these are simple problems with simple
solutions, but they can cause hours or days of frustration to resolve.&amp;nbsp; One of
the main themes that seems to pervade these little doses of IT misery is based on
a misunderstanding of how cookies work and how ADFS uses cookies.&amp;nbsp; This post
is my attempt to gather some of the lessons I've learned and hopefully make your time
with ADFS a little easier.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
&lt;font size=5&gt;How Cookies Work in Web Browsers&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Although many of us have a basic, foggy notion about what cookies do in web applications,
many of us don't know all of the little rules that govern their behavior.&amp;nbsp; I'll
try to run that down now based on my own understanding, which I believe is at least
technically accurate enough for the purposes of the rest of this article.&amp;nbsp; Feel
free to correct me if you know better or refer to the RFCs if you want more precision.
&lt;/p&gt;
&lt;p&gt;
Basically, a cookie is a small piece of textual data that is sent by a web server
to a web browser.&amp;nbsp; The job of the browser is to return this piece of data back
to the server when it makes another request to the server, based on the rules that
I'll detail shortly.&amp;nbsp; Cookies are generally used in web applications as a means
of maintaining state in the web server across multiple requests made by the browser.
&lt;/p&gt;
&lt;p&gt;
Cookies are implemented as well-known HTTP headers in the request data sent by the
browser and the response data sent.&amp;nbsp; The request header name is called &lt;font face="Courier New"&gt;&lt;strong&gt;Cookie&lt;/strong&gt;&lt;/font&gt; and
the response header name is &lt;strong&gt;&lt;font face="Courier New"&gt;Set-Cookie&lt;/font&gt;&lt;/strong&gt;.&amp;nbsp;
The server always initiates the usage of cookies by returning a response with a &lt;font face="Courier New"&gt;&lt;strong&gt;Set-Cookie&lt;/strong&gt;&lt;/font&gt; header
in it that contains the data and some other instructions.&amp;nbsp; The browser's job
is to then add a &lt;font face="Courier New"&gt;&lt;strong&gt;Cookie&lt;/strong&gt;&lt;/font&gt; header on
all subsequent requests to the server that contains exactly the data the server sent
in the &lt;strong&gt;&lt;font face="Courier New"&gt;Set-Cookie&lt;/font&gt;&lt;/strong&gt; header.&amp;nbsp; However,
exactly when the browser will add the &lt;font face="Courier New"&gt;&lt;strong&gt;Cookie&lt;/strong&gt;&lt;/font&gt; header
to its request is based on the other instructions in the &lt;font face="Courier New"&gt;&lt;strong&gt;Set-Cookie&lt;/strong&gt;&lt;/font&gt; header
and the rules for cookie behavior.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
First, let's examine the "other instructions".&amp;nbsp; The &lt;strong&gt;&lt;font face="Courier New"&gt;Set-Cookie&lt;/font&gt;&lt;/strong&gt; header
may contain these bits of info:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Name 
&lt;li&gt;
Value 
&lt;li&gt;
Domain (optional) 
&lt;li&gt;
Path (optional) 
&lt;li&gt;
Expires (optional) 
&lt;li&gt;
Secure flag (optional) 
&lt;li&gt;
HttpOnly flag (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;font size=4&gt;Cookie Name and Value&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The Cookie Name is just what it sounds like.&amp;nbsp; Since a web app may use many different
cookies, this is how they can be distinguished.&amp;nbsp; The Cookie Value is just some
textual data.&amp;nbsp; It could be anything.&amp;nbsp; Oftentimes, it may contain some data
that has been encrypted that the browser can't even interpret it--only the server
can.&amp;nbsp; This is not a problem.&amp;nbsp; The data is for the server anyway.&amp;nbsp; The
browser's job is to&amp;nbsp;dutifully sending it back to the server, not understand it.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Cookie Expires, Secure and HttpOnly&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
I'll skip ahead to the last three, as they easy to explain and not terribly problematic
(for our purposes).&amp;nbsp; The&amp;nbsp;optional Expires field&amp;nbsp;may contain&amp;nbsp;a
date/time value that tells the browser when to stop sending the cookie back to the
server.&amp;nbsp; This one is interesting because it alone determines whether a cookie
is a &lt;strong&gt;session cookie&lt;/strong&gt; or a &lt;strong&gt;file-based cookie&lt;/strong&gt;.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
If there is no expiration specified, the cookie defaults to a session cookie and will
be returned as long as the browser session continues.&amp;nbsp; In IE and FireFox (and
probably most other browsers on Windows), the session is basically the duration that
the browser &lt;em&gt;process&lt;/em&gt; is open.&amp;nbsp; Note that a browser process may span many
windows and may originate in a non-obvious place like an email or an instant messenger
client, so you may sometimes be surprised to see a cookie being sent back to a server
even after a window was closed (or seemingly all windows for that matter!).&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
If the expiration date is set, then the cookie is "file-based" and will be written
to disk.&amp;nbsp; It will survive across multiple browser processes being launched and
stopped.&amp;nbsp; The browser will continue to send it until the expiration time is exceeded.
&lt;/p&gt;
&lt;p&gt;
The Secure flag instructs the browser to only return the cookie back to the server
if the request uses the HTTPS (SSL) protocol.&amp;nbsp; This flag is often used when the
cookie contains sensitive data that could be useful by a bad guy if it was intercepted
on the wire.&amp;nbsp; I consider it to be bad practice to ever use any kind of forms-based
authentication cookie without the Secure flag set, although this then requires SSL.&amp;nbsp;
If you are serious about security, you use SSL for secure websites.&amp;nbsp; End of story.
&lt;/p&gt;
&lt;p&gt;
The HttpOnly flag is the new guy and not all browsers support it (although many do).&amp;nbsp;
It basically says that this cookie will only be sent on the wire.&amp;nbsp; The browser
will not allow the cookie to show up in the browser's Document Object Model (DOM)
cookie collection.&amp;nbsp; This prevents scripts from being able to view and manipulate
the cookie and can help prevent some kinds of cross-site scripting (XSS) attacks.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Cookie Domain&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
For our purposes, The Cookie Domain and Path are the most interesting values, as this
is the heart of all of the trouble in ADFS, so we cover them last.&amp;nbsp; The domain
specifies which hosts the browser will replay the cookie to.&amp;nbsp; If the domain is
not specified, the browser will only replay the cookie to host that sent the &lt;font face="Courier New"&gt;&lt;strong&gt;Set-Cookie&lt;/strong&gt;&lt;/font&gt; header
in the first place.&amp;nbsp; Here, the host is determined by the host portion of the
URL.&amp;nbsp; For example, in&amp;nbsp;&lt;a href="http://www.joekaplan.net/default.aspx"&gt;http://www.joekaplan.net/default.aspx&lt;/a&gt;,
the host is &lt;font face="Courier New" color=#0000ff&gt;www.joekaplan.net&lt;/font&gt;.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Domains can also be specified hierarchically.&amp;nbsp; I could set the cookie domain
to &lt;font face="Courier New" color=#0000ff&gt;.joekaplan.net&lt;/font&gt; and then my browser
would return the cookie to any host within the &lt;font face="Courier New" color=#0000ff&gt;joekaplan.net&lt;/font&gt; DNS
namespace such as &lt;font face="Courier New" color=#0000ff&gt;www.joekaplan.net&lt;/font&gt;, &lt;font face="Courier New" color=#0000ff&gt;blogs.joekaplan.net&lt;/font&gt; and &lt;font face="Courier New" color=#0000ff&gt;junk.joekaplan.net&lt;/font&gt; (neither
of which exist in the latter two examples, but that isn't important).
&lt;/p&gt;
&lt;p&gt;
One might wonder if one can set a cookie for a domain other than the domain of the
host.&amp;nbsp; The answer is "it depends", but usually no.&amp;nbsp; As one can imagine,
this could (and has!) lead to all kinds of crazy hacking where one site writes over
another site's cookies.&amp;nbsp; In IE, the ability to do this is locked down by the
various security levels and assigned by zone.&amp;nbsp; I haven't seen many legitimate
uses for such a design personally, so I'm happy this is restricted now.
&lt;/p&gt;
&lt;p&gt;
Another important picky detail about the domain value is that it doesn't consider
the port component of the hostname in the URL.&amp;nbsp; Basically, this means that it
treats the host &lt;font face="Courier New" color=#0000ff&gt;www.joekaplan.net:80&lt;/font&gt; and &lt;font face="Courier New" color=#0000ff&gt;www.joekaplan.net:8080&lt;/font&gt; as &lt;strong&gt;&lt;em&gt;the
same host&lt;/em&gt;&lt;/strong&gt;, even if those map to completely different websites on your
web server.&amp;nbsp; This can lead to surprises.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Cookie Path&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The final modifier is the Path.&amp;nbsp; The path value is like the domain, except that
is specifies the scope in the path hierarchy that the cookie will be replayed to.&amp;nbsp;
Here, the path is the part of the URL that comes after the host name.&amp;nbsp; If the
path is set to &lt;font face="Courier New" color=#0000ff&gt;/&lt;/font&gt;, then the cookie will
be replayed to any part of the hierarchy of the website.&amp;nbsp; If the path is set
to &lt;font face="Courier New" color=#0000ff&gt;/site&lt;/font&gt;, then the cookie will only
be replayed to requests for URLs at or below &lt;font face="Courier New" color=#0000ff&gt;/site&lt;/font&gt;.
&lt;/p&gt;
&lt;p&gt;
One tricky detail with the path value is that it is &lt;strong&gt;&lt;em&gt;case-sensitive&lt;/em&gt;&lt;/strong&gt;.&amp;nbsp;
If you set the path to &lt;font face="Courier New" color=#0000ff&gt;/site&lt;/font&gt; and your
URL is &lt;font face="Courier New" color=#0000ff&gt;http://www.joekaplan.net/Site/default.aspx&lt;/font&gt; (note
the capital "S"), guess what?&amp;nbsp; The cookie will not be returned with the request!&amp;nbsp;
This little gotcha can easily confound IIS developers, as IIS is generally quite happy
to treat folders and virtual directories as case-insensitive.&amp;nbsp; Developers on
"other" web server platforms where URLs (and file names) are case-sensitive tend to
be a little more hip to this. :)
&lt;/p&gt;
&lt;p&gt;
&lt;font size=5&gt;A Brief Tour of the ADFS Cookies&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
ADFS, like most other HTTP authentication protocols that don't use the built in HTTP
authentication specifications (Basic, Digest, Negotiate, Kerberos, NTLM, etc.), use
cookies for a lot of stuff.&amp;nbsp; There are three primary cookies to know about:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Authentication/Token cookie (&lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt;) 
&lt;li&gt;
Home Realm Cookie (&lt;font face="Courier New" color=#0000ff&gt;_LSRealm&lt;/font&gt;) 
&lt;li&gt;
Logout Cookie (&lt;font face="Courier New" color=#0000ff&gt;_LSCleanup&lt;/font&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Let's take each in turn.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Authentication/Token Cookie&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie is certainly
the most important one, as that is the cookie that represents the user's login to
a federation resource.&amp;nbsp; This cookie is always a session cookie, in that the Expires
field is never set, so it goes away when the browser process does.&amp;nbsp; Expiration
of this cookie is handled by timestamps set in the data in the cookie itself and is
a programmatic feature of the protocol and is not related to how the browser treats
cookie expiration.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
The cookie basically contains the SAML 1.1 token issued by a federation server that
allows access to a specific resource.&amp;nbsp; The format of the cookie for ADFS is in
some crazy compressed format that MS came up with that isn't documented, but that
is supposedly cool because the WS-Federation PRP spec doesn't actually contain any
details about what this should look like and leaves it implementation specific.&amp;nbsp;
These cookies are only consumed by the server/app that issued them (hopefully!), so
this isn't a big deal.&amp;nbsp; Sometimes you may see a &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth0&lt;/font&gt; cookie
or additional numbering.&amp;nbsp; This can happen if the SAML token is very large and
the compressed size of the XML in the SAML token may exceed the allowed size of a
cookie.&amp;nbsp; ADFS breaks these up for you to overcome this.&amp;nbsp; This is a picky
and unimportant detail for our discussion, but I figured I'd mention it in case you
saw it and were curious.
&lt;/p&gt;
&lt;p&gt;
The &lt;font face="Courier New"&gt;&lt;strong&gt;Set-Cookie&lt;/strong&gt;&lt;/font&gt; header for this cookie
usually looks something like this (data clipped for brevity):
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New" color=#0000ff&gt;Set-Cookie: _WebSsoAuth=eNrNWWuTqkgS9acYzpfd6LV5qAhGd8cWDxUVFMVnbMREASWiQ.....CdagA;
path=/adfs/ls/; secure; HttpOnly&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
In this case, this is the authentication cookie issued by the federation server itself.&amp;nbsp;
In ADFS, each federation server the user logs in to (at least one, maybe two if there
is an account partner/resource partner set up) will issue&amp;nbsp;a &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie.&amp;nbsp;
Additionally, each application that the user visits will issue a &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie
that is specific to the application.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Each &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie is &lt;em&gt;different&lt;/em&gt; and &lt;em&gt;is
intended only for the app/server that issued it&lt;/em&gt;.&amp;nbsp; However, you probably
noticed&amp;nbsp;how they all have the same name.&amp;nbsp; Because they all have the same
name, they must different by &lt;em&gt;domain&lt;/em&gt; and &lt;em&gt;path&lt;/em&gt; so that the browser
will know which cookie to send to which app/server.&amp;nbsp; If you've ever configured
an ADFS token-based or claims-based application, you will have noticed that you are
required to specify the domain and path for the cookie when configuring the application.&amp;nbsp;
All it takes is a naive mistake with this configuration to cause the browser to start
sending the wrong cookie to the wrong place and ADFS chaos ensues.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Home Realm Cookie&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The &lt;font face="Courier New" color=#0000ff&gt;_LSRealm&lt;/font&gt; cookie is a file-based
cookie issued by the resource federation server after you have successfully logged
in that specifies the federation server URI (usually &lt;font face="Courier New"&gt;urn:federation:foo&lt;/font&gt; or
something) of the server that authenticated you.&amp;nbsp; This is done so that the resource
federation server doesn't have to prompt you for your home organization again if it
has multiple potential partners.&amp;nbsp; We already discussed a little bit about these
values and home realm discovery query strings in a &lt;a href="http://www.joekaplan.net/NoteToUrnfederationself.aspx"&gt;post&lt;/a&gt; last
summer, so I won't get into much more detail.&amp;nbsp; These things rarely cause trouble
except that you have to clear your cookies if you want to get a resource federation
server to prompt you for your home realm again after you log in the first time.&amp;nbsp;
They are issued for one month as I recall; I'm too lazy to check for certain right
now.&amp;nbsp; :)
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Logout Cookie&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The &lt;font face="Courier New" color=#0000ff&gt;_LSCleanup&lt;/font&gt; cookie assists in the
logout process by keeping track of which federation servers and applications you've
visited.&amp;nbsp; When ADFS does a log out, it attempts to "drain the swamp" by logging
you out of &lt;strong&gt;everything&lt;/strong&gt;.&amp;nbsp; To do this, the built-in logout page
attempts to visit the logout URL for every federation server you've visited and every
app you've visited.&amp;nbsp; These logout URLs set the &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie
value for the cookies they issued to "null".&amp;nbsp; This cookie is also a session cookie.&amp;nbsp;
These cookies can get screwed up just like the authentication cookies, but usually
this fact never comes up because once the authentication cookies are hosed, things
tend to break before you ever try to log out.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=5&gt;Common ADFS Problems Caused by Incorrect Cookie Settings&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
There are two ADFS problems I've seen frequently that cause much pain that are related
to cookie problems and probably some variants that I'm forgetting:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
Invalid SAML Audience 
&lt;li&gt;
Infinite Loop Detected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;font size=4&gt;Invalid SAML Audience&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
This occurs when one ADFS application get's another ADFS application's cookie.&amp;nbsp;
Like I said before, each application has an application-specific cookie and there
is a piece of data in the SAML token called the &lt;em&gt;audience&lt;/em&gt; that says exactly
where the cookie was supposed to go.&amp;nbsp; This is identified by the application's
configured URL in the trust policy and the web site.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
There are a variety of ways you can create this error.&amp;nbsp; One easy way is by setting
the cookie domain to something like &lt;font face="Courier New" color=#0000ff&gt;.domain.com&lt;/font&gt; and
having your apps set up with host names like &lt;font face="Courier New" color=#0000ff&gt;app1.domain.com&lt;/font&gt; and &lt;font face="Courier New" color=#0000ff&gt;app2.domain.com&lt;/font&gt;.&amp;nbsp;
Since these are both basically children of &lt;font face="Courier New" color=#0000ff&gt;.domain.com&lt;/font&gt;,
the browser will happily send a &lt;font face="Courier New" color=#0000ff&gt;_WebSsoAuth&lt;/font&gt; cookie
to &lt;strong&gt;app2&lt;/strong&gt; that &lt;strong&gt;app1&lt;/strong&gt; issued if the paths are the same.&amp;nbsp;
Since the path is often set to &lt;font face="Courier New" color=#0000ff&gt;/&lt;/font&gt;, this
is not uncommon.&amp;nbsp; You can also have pseudo-chaos that is even more difficult
to debug if the path's aren't the same but contain some overlap in hierarchy.
&lt;/p&gt;
&lt;p&gt;
I'm still trying to figure out a reason why you would ever set the cookie domain in
an ADFS system.&amp;nbsp; Generally, you only ever want an ADFS cookie going back to the
host that issued it, so why not leave it at the default setting of "null"?&amp;nbsp; In
my experience, nothing but trouble ever comes from setting the domain.&amp;nbsp; If you
think of a use case for setting this, please let me know.
&lt;/p&gt;
&lt;p&gt;
I also mentioned overlapping paths.&amp;nbsp; These are a significant cause of trouble.&amp;nbsp;
Let's say you have an app at the root of the site &lt;font face="Courier New" color=#0000ff&gt;/&lt;/font&gt; and
an app below that &lt;font face="Courier New" color=#0000ff&gt;/Site&lt;/font&gt;.&amp;nbsp; Basically,
if they have the same host name, &lt;em&gt;you can't do that in ADFS&lt;/em&gt;.&amp;nbsp; They need
to be strict siblings like &lt;font face="Courier New" color=#0000ff&gt;/Site1&lt;/font&gt; and &lt;font face="Courier New" color=#0000ff&gt;/Site2&lt;/font&gt;.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Overlapping paths can show up in subtle ways as well.&amp;nbsp; Sometimes, you may not
think that overlapping paths are a problem because you aren't aware that you have
overlapping domains.&amp;nbsp; If you use the &lt;em&gt;&lt;strong&gt;ADFS Step-by-Step Guide&lt;/strong&gt;&lt;/em&gt; to
put together your ADFS demo lab, you may find yourself using host names like this
to try out different application styles and using different TCP/IP ports for SSL to
split them up in IIS:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;font face="Courier New" color=#0000ff&gt;https://apps.domain.com:8081/&lt;/font&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;font face="Courier New" color=#0000ff&gt;https://apps.domain.com:8082/&lt;/font&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
While this makes things easy from the IIS perspective in terms of setting up multiple
sites and it makes it easier to deal with few host names (and less DNS and certificate
mucking around), remember that &lt;strong&gt;&lt;em&gt;the browser treats those two hosts as being
the same thing for cookie purposes&lt;/em&gt;&lt;/strong&gt;.&amp;nbsp; You now have overlapping domains.&amp;nbsp;
Therefore, it simply falls to the cookie path to make a distinction between them.&amp;nbsp;
If you use a path like:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;font face="Courier New" color=#0000ff&gt;/claimsapp&lt;/font&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;font face="Courier New" color=#0000ff&gt;/tokenapp&lt;/font&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
...for each one, you are fine, but if you naively put one or both of those apps in
the root, you will have problems if you try to do SSO between them with the previously
mentioned error.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Infinite Loop Errors and the Like&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
The other problem we can easily create is the "infinite loop" problem or "same token
request within the last 20 seconds" problem on the federation server.&amp;nbsp; I've seen
this happen when we have a capitalization different between the browser URL and the
cookie path.&amp;nbsp; As we explained above, &lt;strong&gt;&lt;em&gt;the cookie path is treated as
case sensitive by the browser&lt;/em&gt;&lt;/strong&gt;, but IIS is generally very happy to treat
a URL folder or virtual directory path as case-insensitive.&amp;nbsp; The problem then
manifests itself in ADFS with an interaction like this:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
User logs into federation server after being redirected to do so by the application
they were trying to access 
&lt;li&gt;
Federation server generates SAML token for resource application and redirects browser
to POST the token to that URL (&lt;font face="Courier New" color=#0000ff&gt;app.domain.com/site/&lt;/font&gt;) 
&lt;li&gt;
Resource application receives the token via POST, verifies it and issues the browser
a cookie with the token contents for use with subsequent visits.&amp;nbsp; The path in
the &lt;strong&gt;&lt;font face="Courier New"&gt;Set-Cookie&lt;/font&gt;&lt;/strong&gt; header is &lt;font face="Courier New" color=#0000ff&gt;/Site&lt;/font&gt;.&amp;nbsp;
App redirects the user back to itself with the exact URL originally requested. 
&lt;li&gt;
Browser receives the cookie, but it is using a URL like &lt;font face="Courier New" color=#0000ff&gt;https://app.domain.com/&lt;strong&gt;s&lt;/strong&gt;ite/default.aspx&lt;/font&gt;,
not &lt;font face="Courier New" color=#0000ff&gt;https://app.domain.com/&lt;strong&gt;S&lt;/strong&gt;ite/default.aspx&lt;/font&gt;.&amp;nbsp;
Since those are clearly different (note the capital S!), &lt;strong&gt;&lt;em&gt;the browser does
not include the authentication cookie in the request&lt;/em&gt;&lt;/strong&gt;. 
&lt;li&gt;
App receives the request from the browser without the cookie and decides "this guy
should log in if I'm going to allow access, so go back to my federation server, get
a token and I'll see you later" and redirects the user back to the federation server
with instructions to sign in. 
&lt;li&gt;
Federation server is clever and notices that it just issued a token for this user
for this app a second ago or so and throws an error because it knows that something
just isn't right.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
This one is a little subtle, but easily fixable.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=5&gt;Conclusion&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Cookies are at the heart and soul of how ADFS works (with the Passive Requester Profile
anyway), so understanding the rules that govern their behavior in the browser and
how ADFS expects you to use them will save you potentially hours or days of troubleshooting
misery.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Tools for Viewing Headers and Cookies&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
When working with ADFS (or really just about any web app I've found), having a way
to view the raw request and response headers associated with all of the traffic generated
by the browser and server is invaluable.&amp;nbsp; There are great plugins for IE (&lt;a href="http://www.blunck.info/"&gt;ieHttpHeaders&lt;/a&gt;)&amp;nbsp;and
FireFox (&lt;a href="http://livehttpheaders.mozdev.org/"&gt;Live Headers&lt;/a&gt;) that do a
fine job of this and are free.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;&lt;font size=1&gt;(edited March 4, 2007, to add some additional formatting, fix some
wording and add the section on viewing headers)&lt;/font&gt;&lt;/em&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=d41ff2af-fa75-4252-8f93-37c88b019dbe" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,d41ff2af-fa75-4252-8f93-37c88b019dbe.aspx</comments>
      <category>Identity Federation</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=169bd6ce-29e3-4f5b-9efb-1ed1383a841c</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,169bd6ce-29e3-4f5b-9efb-1ed1383a841c.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,169bd6ce-29e3-4f5b-9efb-1ed1383a841c.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=169bd6ce-29e3-4f5b-9efb-1ed1383a841c</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
So, let's say you are doing some ADFS programming and you see this big cookie being
passed to your app called _WebSsoAuth.  Let's say your app is a token app, so
the only trace of the claims from the federation is in the resulting Windows token
with the user and group info (see <a href="http://www.joekaplan.net/DiscoveringTheUsersNameAndGroupsInTheirWindowsToken.aspx">here</a> if
you are not certain how to see this info).  Let's ignore for a second that it
is possible to have a hybrid token/claims app that uses the IIS web service extension
to build a Windows token for you, but also uses the HTTP Module to build a <font face="Courier New">SingleSignOnIdentity</font> so
you can read the claims (this is possible!).  You are just really curious about
what's in that cookie.  It looks like it contains a big blob of Base64 data. 
Can we see the actual SAML token somehow?
</p>
        <p>
Sadly, the answer is "no", not really.
</p>
        <p>
          <font size="3">Digging a Little Deeper</font>
        </p>
        <p>
At this point, you may choose to stop reading or explore the futility in this a little
further.
</p>
        <p>
So, the data is clearly Base64-encoded, but a quick decode of it doesn't give
us any angle brackets.  We wonder what gives, so we open up <a href="http://www.aisto.com/roeder/dotnet/">Reflector</a> and
start looking at the reverse engineered source code to find out what's up.
</p>
        <p>
As we dig around, we soon discover that the <font face="Courier New">WebSsoAuthenticationModule</font> uses
a class called <font face="Courier New">LargeCookieWriter</font> to read the data
and decode it.  Interestingly, the <font face="Courier New">Decode</font> method
actually calls into an unmanaged code function in another DLL installed by ADFS to
do some sort of decompression/unmunging.  Interesting...I wonder what sort of
magic goes on in there?  Must be cool if it had to be unmanaged C++!  But
anyway, we are not deterred by this as a little reflection code allows us call <font face="Courier New">LargeCookieWriter</font> as
well without any details of what's going on down deep.
</p>
        <p>
So, we call the <font face="Courier New">Decode</font> method and we get a string
back with readable characters.  Sort of.  Some of the data in there looks
like our claims (I see a familar UPN and email address for example), but some of it
is Base64 and none of it is XML.  Where's my SAML token???
</p>
        <p>
At this point it gets a little wacky, as apparently there is some sort of XML in this
data somehow as ADFS has implemented its own set of <font face="Courier New">XmlReader</font> classes
to read this stuff.  This is all handled internally by the constructor on the <font face="Courier New">SamlToken</font> class. 
The implementation is a little hard to follow though and we can't easily call the
constructor on <font face="Courier New">SamlToken</font> ourselves because it also
needs the <font face="Courier New">TrustPolicy</font> and I don't have one in memory
right now and I'm getting a little tired of reflection code, so I give up.  Perhaps
I'm just too easily deterred, but I really just wanted to see angle brackets and it
does not look like there are any to be seen.  Sigh.
</p>
        <p>
The moral of the story is that when the product team says that the _WebSsoAuth cookie
is a proprietary format that is not intended to be read without their code, they mean
it.  If you want the claims, you are supposed to go through the HTTP Module to
get a <font face="Courier New">SingleSignOnIdentity</font>.  This gives them
some flexibility to change how things work under the hood in future releases and other
products.  Is this a violation of the WS-Fed spec?  Apparently not, as it
doesn't specify the token format used in the cookie.
</p>
        <p>
          <font size="3">Should the Only Consumers of Claims Data Be .NET 2.0 Apps?</font>
        </p>
        <p>
Would it be nice to have a way to get the claims as XML based on the cookie? 
I think so.  The real use case in my book is allowing access to the claims for
apps that aren't using .NET 2.0.  This could be a .NET 1.x app or could be another
app platform altogether running on IIS.  It seems reasonable to me that I should
be able to grab the cookie and call a web service method on the federation server
to get a nice text-based XML SAML token to examine.  This just opens up the platform
to allow a greater ecology of applications to participate.
</p>
        <p>
Another option is to capture the redirected form post from the resource federation
server when the user initially returns from logging in.  This does actually contain
the XML-based SAML token.  The problem is that this event only happens once and
triggers an immediate redirect, so there is an excellent chance you won't be able
to grab it.
</p>
        <p>
Still another option (which I think may be the best one) would be for the ADFS
IIS web service extension (the part of ADFS that makes the token app work) to populate
custom HTTP headers/server variables that contained the claims data.  Then any
app platform running on IIS from ASP 3.0 to Rails to consume the claims.  The
Windows token would be a secondary consideration, and in this case you could treat
that token the same way you treat the anonymous user token in other forms authentication
IIS apps (by ignoring it for purposes of authorization).
</p>
        <p>
          <em>(ps: if anyone wants to see the reflection code required to actually call the
Decode method and get the underlying string data, let me know and I'll post it.)</em>
        </p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=169bd6ce-29e3-4f5b-9efb-1ed1383a841c" />
      </body>
      <title>Decoding the _WebSsoAuth Cookie? No.</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,169bd6ce-29e3-4f5b-9efb-1ed1383a841c.aspx</guid>
      <link>http://www.joekaplan.net/DecodingTheWebSsoAuthCookieNo.aspx</link>
      <pubDate>Tue, 08 Aug 2006 05:29:36 GMT</pubDate>
      <description>&lt;p&gt;
So, let's say you are doing some ADFS programming and you see this big cookie being
passed to your app called _WebSsoAuth.&amp;nbsp; Let's say your app is a token app, so
the only trace of the claims from the federation is in the resulting Windows token
with the user and group info (see &lt;a href="http://www.joekaplan.net/DiscoveringTheUsersNameAndGroupsInTheirWindowsToken.aspx"&gt;here&lt;/a&gt; if
you are not certain how to see this info).&amp;nbsp; Let's ignore for a second that it
is possible to have a hybrid token/claims app that uses the IIS web service extension
to build a Windows token for you, but also uses the HTTP Module to build a &lt;font face="Courier New"&gt;SingleSignOnIdentity&lt;/font&gt; so
you can read the claims (this is possible!).&amp;nbsp; You are just really curious about
what's in that cookie.&amp;nbsp; It looks like it contains a big blob of Base64 data.&amp;nbsp;
Can we see the actual SAML token somehow?
&lt;/p&gt;
&lt;p&gt;
Sadly, the answer is "no", not really.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=3&gt;Digging a Little Deeper&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
At this point, you may choose to stop reading or explore the futility in this a little
further.
&lt;/p&gt;
&lt;p&gt;
So, the data is clearly&amp;nbsp;Base64-encoded, but a quick decode of it doesn't give
us any angle brackets.&amp;nbsp; We wonder what gives, so we open up &lt;a href="http://www.aisto.com/roeder/dotnet/"&gt;Reflector&lt;/a&gt; and
start looking at the reverse engineered source code to find out what's up.
&lt;/p&gt;
&lt;p&gt;
As we dig around, we soon discover that the &lt;font face="Courier New"&gt;WebSsoAuthenticationModule&lt;/font&gt; uses
a class called &lt;font face="Courier New"&gt;LargeCookieWriter&lt;/font&gt; to read the data
and decode it.&amp;nbsp; Interestingly, the &lt;font face="Courier New"&gt;Decode&lt;/font&gt; method
actually calls into an unmanaged code function in another DLL installed by ADFS to
do some sort of decompression/unmunging.&amp;nbsp; Interesting...I wonder what sort of
magic goes on in there?&amp;nbsp; Must be cool if it had to be unmanaged C++!&amp;nbsp; But
anyway, we are not deterred by this as a little reflection code allows us call &lt;font face="Courier New"&gt;LargeCookieWriter&lt;/font&gt; as
well without any details of what's going on down deep.
&lt;/p&gt;
&lt;p&gt;
So, we call the &lt;font face="Courier New"&gt;Decode&lt;/font&gt; method and we get a string
back with readable characters.&amp;nbsp; Sort of.&amp;nbsp; Some of the data in there looks
like our claims (I see a familar UPN and email address for example), but some of it
is Base64 and none of it is XML.&amp;nbsp; Where's my SAML token???
&lt;/p&gt;
&lt;p&gt;
At this point it gets a little wacky, as apparently there is some sort of XML in this
data somehow as ADFS has implemented its own set of &lt;font face="Courier New"&gt;XmlReader&lt;/font&gt; classes
to read this stuff.&amp;nbsp; This is all handled internally by the constructor on the &lt;font face="Courier New"&gt;SamlToken&lt;/font&gt; class.&amp;nbsp;
The implementation is a little hard to follow though and we can't easily call the
constructor on &lt;font face="Courier New"&gt;SamlToken&lt;/font&gt; ourselves because it also
needs the &lt;font face="Courier New"&gt;TrustPolicy&lt;/font&gt; and I don't have one in memory
right now and I'm getting a little tired of reflection code, so I give up.&amp;nbsp; Perhaps
I'm just too easily deterred, but I really just wanted to see angle brackets and it
does not look like there are any to be seen.&amp;nbsp; Sigh.
&lt;/p&gt;
&lt;p&gt;
The moral of the story is that when the product team says that the _WebSsoAuth cookie
is a proprietary format that is not intended to be read without their code, they mean
it.&amp;nbsp; If you want the claims, you are supposed to go through the HTTP Module to
get a &lt;font face="Courier New"&gt;SingleSignOnIdentity&lt;/font&gt;.&amp;nbsp; This gives them
some flexibility to change how things work under the hood in future releases and other
products.&amp;nbsp; Is this a violation of the WS-Fed spec?&amp;nbsp; Apparently not, as it
doesn't specify the token format used in the cookie.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=3&gt;Should the Only Consumers of Claims Data Be .NET 2.0 Apps?&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Would it be nice to have a way to get the claims as XML based on the cookie?&amp;nbsp;
I think so.&amp;nbsp; The real use case in my book is allowing access to the claims for
apps that aren't using .NET 2.0.&amp;nbsp; This could be a .NET 1.x app or could be another
app platform altogether running on IIS.&amp;nbsp; It seems reasonable to me that I should
be able to grab the cookie and call a web service method on the federation server
to get a nice text-based XML SAML token to examine.&amp;nbsp; This just opens up the platform
to allow a greater ecology of applications to participate.
&lt;/p&gt;
&lt;p&gt;
Another option is to capture the redirected form post from the resource federation
server when the user initially returns from logging in.&amp;nbsp; This does actually contain
the XML-based SAML token.&amp;nbsp; The problem is that this event only happens once and
triggers an immediate redirect, so there is an excellent chance you won't be able
to grab it.
&lt;/p&gt;
&lt;p&gt;
Still another option (which&amp;nbsp;I think may be the best one) would be for the ADFS
IIS web service extension (the part of ADFS that makes the token app work) to populate
custom HTTP headers/server variables that contained the claims data.&amp;nbsp; Then any
app platform running on IIS from ASP 3.0 to Rails to consume the claims.&amp;nbsp; The
Windows token would be a secondary consideration, and in this case you could treat
that token the same way you treat the anonymous user token in other forms authentication
IIS apps (by ignoring it for purposes of authorization).
&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;(ps: if anyone wants to see the reflection code required to actually call the
Decode method and get the underlying string data, let me know and I'll post it.)&lt;/em&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=169bd6ce-29e3-4f5b-9efb-1ed1383a841c" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,169bd6ce-29e3-4f5b-9efb-1ed1383a841c.aspx</comments>
      <category>Identity Federation</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=fcdf0808-8196-4eb7-b171-8b54a5790687</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,fcdf0808-8196-4eb7-b171-8b54a5790687.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,fcdf0808-8196-4eb7-b171-8b54a5790687.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=fcdf0808-8196-4eb7-b171-8b54a5790687</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <font size="4">Authentication Mechanisms in SDS</font>
        </p>
        <p>
In ADSI and System.DirectoryServicess (SDS), we are really only given two choices
for how authentication will be performed when we do a bind operation.  We can
specify the "Secure" flag (AuthenticationTypes.Secure), and ADSI will attempt to the
bind to the remote directory using Windows SSPI authentication with the Negotiate
protocol.  As you may know, the Negotiate protocol is the primary authentication
protocol in Windows since Windows 2000 and selects between using Kerberos (always
preferred) and NTLM (there for backwards compatibility).  For the really nerdy
of you out there, the Negotiate protocol is implemented by Microsoft's LDAP API using
the GSS-SPNEGO SASL mechanism.
</p>
        <p>
The other option in SDS is to NOT specify AuthenticationTypes.Secure and it will
attempt to use an LDAP simple bind instead<sup><font size="1">*</font></sup>. 
The only authentication mechanism defined by LDAP specification is the simple bind,
so every directory implements it.  As such, it ends up being the cross-platform
lowest common denominator approach.  The problem with the simple bind is that
it is totally insecure by itself.  Simple bind passes the user's plaintext credentials
on the network, so unless some sort of channel encryption is provided (like SSL, the
defacto cross-platform standard here as well), anyone who can sniff the wire traffic
can recover the user's password.  This sort of thing is generally frowned on
my security enthusiasts.  :(
</p>
        <p>
          <font size="4">Digest Authentication in LDAP API/SDS.P</font>
        </p>
        <p>
There is, however, a richer set of authentication mechanisms supported by Microsoft's
LDAP API than what ADSI/SDS get to use, and these are available to System.DirectoryServices.Protocols
(SDS.P).  One of the most interesting of these is the Digest authentication protocol. 
Digest is a <a href="http://www.faqs.org/rfcs/rfc2831.html">standard</a>, secure authentication
protocol that does not involve the exchange of plaintext credentials and is implemented
by AD and ADAM, as well as some other LDAP directories.  In SDS.P, we can use
it very easily.  Here is a short sample illustrating this (and no, there is no
ADAM instance at adam.joekaplan.net; dream on...):
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">public</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">static</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">bool</span> DoDigestAuth(NetworkCredential
cred)<br />
{ 
<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
const</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">int</span> LDAP_INVALID_CREDENTIALS <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> 49;<br />
    LdapConnection conn <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span><br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">       
new</span> LdapConnection(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"adam.joekaplan.net:389"</span>);<br />
    conn.AuthType <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> AuthType.Digest;<br />
    conn.Credential <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> cred;<br /><br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
try</span><br />
    {<br />
        conn.Bind();<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">       
return</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">true</span>;<br />
    }<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
catch</span> (LdapException ex)<br />
    {<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">       
if</span> (ex.ErrorCode == LDAP_INVALID_CREDENTIALS)<br />
        {<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">           
return</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">false</span>;<br />
        }<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">       
else</span><br />
        {<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">           
throw</span>;<br />
        }<br />
    }<br />
}</span>
        </p>
        <p>
Here, we have a trivial little sample that shows how this might be done.  Note
that we probably wouldn't create a new <font face="Courier New">LdapConnection</font> object
each time this is called and we'd also try to find a way to clean that up with a proper
Dispose, but the point here is to illustrate that all we really have to do is set
the <font face="Courier New">AuthType</font> property to <font face="Courier New">AuthType.Digest</font> and
it works.  The credentials are specified with a standard <font face="Courier New">NetworkCredential</font> object. 
For Digest, just specify the username and password, not the domain parameter.
</p>
        <p>
          <font size="4">Why is This Important?</font>
        </p>
        <p>
Digest authentication is really interesting particularly for ADAM, as it is supported
by ADAM principals.  Before ADAM supported Digest auth (as of SP1), the only
way to authenticate an ADAM principal was with a simple bind.  That wasn't secure
(see above), so SSL was required to make it secure.  However, SSL certs are not
always easy to procure and take extra effort to install, so many people were missing
this and were insecure.  :(
</p>
        <p>
The other cool thing with Digest is that since it is implemented through Microsoft's
SSPI model, it can also be used as a mechanism for getting channel encryption and
signing, just like negotiate auth supports today<sup><font size="1">**</font></sup>. 
This means that not only can you get secure binding for free, but all of your network
traffic after the bind can be encrypted and signed for free (no SSL required). 
This is done through setting the <font face="Courier New">Signing</font> and <font face="Courier New">Sealing</font> properties
to true on the <font face="Courier New">LdapSessionOptions</font> class.  This,
in turn, allows a way to do LDAP password operations on ADAM principals without using
SSL and without having to change the setting in ADAM to turn off the requirement for
a secure channel on password ops (another big security frown sandwich there...).  
</p>
        <p>
One great scenario for this feature would be to use it with <strong>ADFS for their
ADAM Account Store support</strong>.  This would allow secure authentication
without requiring SSL out of the box and would also provide free channel encryption.  <strong>Product
team please take note!</strong></p>
        <p>
          <sub>* There is a feature in Windows Server 2003 SP1 ADSI will try Digest auth
when the Secure flag is specified and ADSI detects that the server supports Digest
but not negotiate auth.  However, this doesn't help us for AD or ADAM, since
they both support negotiate auth.  The problem with negotiate auth for ADAM is
that it is only used for authenticating Windows users, not ADAM principals.</sub>
        </p>
        <p>
          <sub>** There appears to be a bug in the implementation in ADAM SP1 where the channel
encryption feature only works if the network traffic is NOT on the loopback
(localhost) port.  This is different from the way negotiate channel encryption
works.  This appears to be an oversight in the implementation and not an underlying
issue in the design, so presumably this will get fixed someday.</sub>
        </p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=fcdf0808-8196-4eb7-b171-8b54a5790687" />
      </body>
      <title>Another SDS.P-only feature: Digest authentication</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,fcdf0808-8196-4eb7-b171-8b54a5790687.aspx</guid>
      <link>http://www.joekaplan.net/AnotherSDSPonlyFeatureDigestAuthentication.aspx</link>
      <pubDate>Mon, 07 Aug 2006 15:31:20 GMT</pubDate>
      <description>&lt;p&gt;
&lt;font size=4&gt;Authentication Mechanisms in SDS&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
In ADSI and System.DirectoryServicess (SDS), we are really only given two choices
for how authentication will be performed when we do a bind operation.&amp;nbsp; We can
specify the "Secure" flag (AuthenticationTypes.Secure), and ADSI will attempt to the
bind to the remote directory using Windows SSPI authentication with the Negotiate
protocol.&amp;nbsp; As you may know, the Negotiate protocol is the primary authentication
protocol in Windows since Windows 2000 and selects between using Kerberos (always
preferred) and NTLM (there for backwards compatibility).&amp;nbsp; For the really nerdy
of you out there, the Negotiate protocol is implemented by Microsoft's LDAP API using
the GSS-SPNEGO SASL mechanism.
&lt;/p&gt;
&lt;p&gt;
The other option&amp;nbsp;in SDS is to NOT specify AuthenticationTypes.Secure and it will
attempt to use an LDAP simple bind instead&lt;sup&gt;&lt;font size=1&gt;*&lt;/font&gt;&lt;/sup&gt;.&amp;nbsp;
The only authentication mechanism defined by LDAP specification is the simple bind,
so every directory implements it.&amp;nbsp; As such, it ends up being the cross-platform
lowest common denominator approach.&amp;nbsp; The problem with the simple bind is that
it is totally insecure by itself.&amp;nbsp; Simple bind passes the user's plaintext credentials
on the network, so unless some sort of channel encryption is provided (like SSL, the
defacto cross-platform standard here as well), anyone who can sniff the wire traffic
can recover the user's password.&amp;nbsp; This sort of thing is generally frowned on
my security enthusiasts.&amp;nbsp; :(
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Digest Authentication in LDAP API/SDS.P&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
There is, however, a richer set of authentication mechanisms supported by Microsoft's
LDAP API than what ADSI/SDS get to use, and these are available to System.DirectoryServices.Protocols
(SDS.P).&amp;nbsp; One of the most interesting of these is the Digest authentication protocol.&amp;nbsp;
Digest is a &lt;a href="http://www.faqs.org/rfcs/rfc2831.html"&gt;standard&lt;/a&gt;,&amp;nbsp;secure&amp;nbsp;authentication
protocol that does not involve the exchange of plaintext credentials and is implemented
by AD and ADAM, as well as some other LDAP directories.&amp;nbsp; In SDS.P, we can use
it very easily.&amp;nbsp; Here is a short sample illustrating this (and no, there is no
ADAM instance at adam.joekaplan.net; dream on...):
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;public&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;static&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;bool&lt;/span&gt; DoDigestAuth(NetworkCredential
cred)&lt;br&gt;
{ 
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
const&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;int&lt;/span&gt; LDAP_INVALID_CREDENTIALS &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; 49;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; LdapConnection conn &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; 
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
new&lt;/span&gt; LdapConnection(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"adam.joekaplan.net:389"&lt;/span&gt;);&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; conn.AuthType &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; AuthType.Digest;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; conn.Credential &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; cred;&lt;br&gt;
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
try&lt;/span&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; conn.Bind();&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
return&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;true&lt;/span&gt;;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
catch&lt;/span&gt; (LdapException ex)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
if&lt;/span&gt; (ex.ErrorCode == LDAP_INVALID_CREDENTIALS)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
return&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;false&lt;/span&gt;;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
else&lt;/span&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
throw&lt;/span&gt;;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
}&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
Here, we have a trivial little sample that shows how this might be done.&amp;nbsp; Note
that we probably wouldn't create a new &lt;font face="Courier New"&gt;LdapConnection&lt;/font&gt; object
each time this is called and we'd also try to find a way to clean that up with a proper
Dispose, but the point here is to illustrate that all we really have to do is set
the &lt;font face="Courier New"&gt;AuthType&lt;/font&gt; property to &lt;font face="Courier New"&gt;AuthType.Digest&lt;/font&gt; and
it works.&amp;nbsp; The credentials are specified with a standard &lt;font face="Courier New"&gt;NetworkCredential&lt;/font&gt; object.&amp;nbsp;
For Digest, just specify the username and password, not the domain parameter.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Why is This Important?&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Digest authentication is really interesting particularly for ADAM, as it is supported
by ADAM principals.&amp;nbsp; Before ADAM supported Digest auth (as of SP1), the only
way to authenticate an ADAM principal was with a simple bind.&amp;nbsp; That wasn't secure
(see above), so SSL was required to make it secure.&amp;nbsp; However, SSL certs are not
always easy to procure and take extra effort to install, so many people were missing
this and were insecure.&amp;nbsp; :(
&lt;/p&gt;
&lt;p&gt;
The other cool thing with Digest is that since it is implemented through Microsoft's
SSPI model, it can also be used as a mechanism for getting channel encryption and
signing, just like negotiate auth supports today&lt;sup&gt;&lt;font size=1&gt;**&lt;/font&gt;&lt;/sup&gt;.&amp;nbsp;
This means that not only can you get secure binding for free, but all of your network
traffic after the bind can be encrypted and signed for free (no SSL required).&amp;nbsp;
This is done through setting the &lt;font face="Courier New"&gt;Signing&lt;/font&gt; and &lt;font face="Courier New"&gt;Sealing&lt;/font&gt; properties
to true on the &lt;font face="Courier New"&gt;LdapSessionOptions&lt;/font&gt; class.&amp;nbsp; This,
in turn, allows a way to do LDAP password operations on ADAM principals without using
SSL and without having to change the setting in ADAM to turn off the requirement for
a secure channel on password ops (another big security frown sandwich there...).&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
One great scenario for this feature would be to use it with &lt;strong&gt;ADFS for their
ADAM Account Store support&lt;/strong&gt;.&amp;nbsp; This would allow secure authentication
without requiring SSL out of the box and would also provide free channel encryption.&amp;nbsp; &lt;strong&gt;Product
team please take note!&lt;/strong&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;sub&gt;* There is a feature in Windows Server 2003 SP1&amp;nbsp;ADSI will try Digest auth
when the Secure flag is specified and ADSI detects that the server supports Digest
but not negotiate auth.&amp;nbsp; However, this doesn't help us for AD or ADAM, since
they both support negotiate auth.&amp;nbsp; The problem with negotiate auth for ADAM is
that it is only used for authenticating Windows users, not ADAM principals.&lt;/sub&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;sub&gt;** There appears to be a bug in the implementation in ADAM SP1 where the channel
encryption feature&amp;nbsp;only works&amp;nbsp;if the network traffic is NOT on the loopback
(localhost) port.&amp;nbsp; This is different from the way negotiate channel encryption
works.&amp;nbsp; This appears to be an oversight in the implementation and not an underlying
issue in the design, so presumably this will get fixed someday.&lt;/sub&gt;
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=fcdf0808-8196-4eb7-b171-8b54a5790687" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,fcdf0808-8196-4eb7-b171-8b54a5790687.aspx</comments>
      <category>Identity Federation;LDAP;Windows Security</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=871baf79-22b3-4e6f-bdd4-026a770bf9e0</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,871baf79-22b3-4e6f-bdd4-026a770bf9e0.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,871baf79-22b3-4e6f-bdd4-026a770bf9e0.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=871baf79-22b3-4e6f-bdd4-026a770bf9e0</wfw:commentRss>
      <slash:comments>5</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
When I was at TechEd this year, I ran into a few people asking how to integrate <a href="http://www.rsasecurity.com/node.asp?id=1156">SecurID</a> authentication
into ADFS.  As it currently stands, Microsoft has no direct support for this,
or any other authentication mechanism besides AD/ADAM password authentication and
client certificate auth.  Hopefully in the future, Microsoft will make the account
store and authentication mechanisms a first class extensibility point, but for now,
we must hack.  :)
</p>
        <p>
As you can probably tell by the article title, I have gotten this working in my organization's
environment, even though it is not supported by MS.  So, how do we make this
work?  First, I'll explain the overall principles of how we glue these things
together and then I'll share how I actually did it.  In part 2 of the posting,
I'll talk about some other ways that we might get this working.
</p>
        <p>
          <font size="4">Basic Principles</font>
        </p>
        <p>
In Windows Server 2003 and Active Directory 2003, Microsoft implemented an important
and useful extension to the Kerberos authentication protocol called <a href="http://technet2.microsoft.com/WindowsServer/en/library/0d3a0555-9a68-4cc6-ad55-fd076d037fcf1033.mspx?mfr=true">"Service
for User" (S4U), aka Protocol Transition</a>.  S4U adds the ability for a service
to authenticate a client with a non-Kerberos protocol and then transition to a Kerberos-based
identity for accessing Windows resources on local or remote services (hence "protocol
transition").  Protocol transition is something that can be done automatically
by services like IIS when a user authenticates with Basic, Digest or NTLM, but it
can also be called programmatically and used by non-Windows authentication systems
such as SecurID.
</p>
        <p>
To use S4U, your code must execute on a Windows Server 2003 machine (or higher) AND
your Active Directory must be 2003 forest functional level.  If you are doing
ADFS, you already have the former.  However, since ADFS supports both Windows
2000 Server and Windows Server 2003 AD, you might not have the latter.  If you
don't, you are basically SOL for now.  Upgrade!
</p>
        <p>
When using S4U programmatically, you are basically calling the Windows API <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/lsalogonuser.asp">LsaLogonUser</a> with
the S4U option specified.  When usings the S4U option, LsaLogonUser only requires
you to know the user's UPN, not their password, in order to get a Windows logon token
for them.  There are some restrictions on how this token can be used, but that
really isn't too important for this discussion.  <a href="http://www.pluralsight.com/blogs/keith/default.aspx">Keith</a> covers
this stuff in detail in his <a href="http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook/HomePage.html">book</a> and
several <a href="http://msdn.microsoft.com/msdnmag/issues/03/04/SecurityBriefs/">articles</a> anyway. 
One other nice thing is that .NET (as of 1.1 and higher) has a very easy way to call
LsaLogonUser for S4U with the WindowsIdentity constructor that just takes the UPN. 
It is that simple.
</p>
        <p>
So, now we know we need to use S4U to get a Windows token for the user and we'll need
their UPN to call this.  How does the Windows token then help us with ADFS? 
Well, the ADFS logon service (LS) has a method that allows a user to authenticate
given their Windows token.  In fact, the /ls/auth/integrated directory on the
federation server does just this.  It is set to use Windows Integrated authentication
(IWA) in IIS.  IIS logs the user on with IWA, passes the corresponding Windows
token to ASP.NET.  Then, the ADFS HTTP Module grabs that and calls the correct
method to log you into ADFS and now you have a federation token.  
</p>
        <p>
An interesting side note is that ADFS itself uses protocol transition in the web agent
for token-based applications to achieve similar things (assuming again that you have
2003 AD; otherwise it uses its custom authentication package).  
</p>
        <p>
So, our basic approach with SecurID authentication is to:
</p>
        <ol>
          <li>
Authenticate the user with SecurID using one of RSA's supported methods for web authentication 
</li>
          <li>
 Get the user's UPN somehow (perhaps an LDAP query?) 
</li>
          <li>
Use S4U to get a Windows token for the user 
</li>
          <li>
Use the appropriate method on the LS to authenticate with ADFS using the token</li>
        </ol>
        <p>
This is cake!  :)
</p>
        <p>
          <font size="4">How I Did This</font>
        </p>
        <p>
Ok, I cheated a little bit.  In addition to using RSA's SecurID/ACE Server product
in our company, we also use their <a href="http://www.rsasecurity.com/node.asp?id=1186">ClearTrust</a> web
SSO product (now apparently called Access Manager, but I didn't get that memo). 
ClearTrust already basically let's me do steps 1-3 above using their standard
product when the protocol transition/S4U feature is enabled.  Since it runs as
an ISAPI Filter/ISAPI Extension (via a wildcard map in IIS), it runs before any .NET
code executes.  All I had to do was configure ClearTrust on my /ls/auth/integrated
directory, and ADFS doesn't know the difference between IIS having used IWA or some
other thing.  The LS just grabs the Windows token from where it expects to find
it in ASP.NET and proceeds with logon.  It really is cake and actually worked
the first time I tried it.
</p>
        <p>
The hardest part was instructing ClearTrust to NOT execute on any resources other
than the /ls/auth/integrated directory, which essentially came down to a lengthy URL
exclusion list in the ClearTrust configuration.  Anyone who has used ClearTrust
probably already knows how to do that.  I will probably experiment with alternate
directory structures to simplify this a bit more in the future.
</p>
        <p>
Since ClearTrust is a web SSO product, it also contains a signout function that is
implemented by navigating to a page.  We integrated this into the ADFS signout
by using the same technique it does: we referred to the ClearTrust signout page in
a hidden image tag on the normal signout page.  That results in our ClearTrust
cookie being cleared.
</p>
        <p>
One thing I should make clear is that we did this customization on the FS, not the
FS-P (Federation Service Proxy).  I don't see a reason why you couldn't
do this there too, but you'd need to mess with it a bit more as the FS-P is configured
out of the box to do its own forms auth instead of redirecting to the /ls/auth/integrated
directory.
</p>
        <p>
One important caveat with SecurID auth is that it basically requires forms authentication,
especially to support PIN operations.  This basically implies that you can't
use the "basic" authentication support built in to ADFS here.  As such, you should
probably turn off that option in your ADFS configuration.  That may also mean
that you some SharePoint/Office integration stuff breaks though.  I'm not sure
how to reconcile that problem.  Forms auth and Office don't like each other.
</p>
        <p>
          <font size="4">Summary</font>
        </p>
        <p>
So anyway, that's the basic concept.  If anyone needs additional details, let
me know and I'll try to expand on this.
</p>
        <p>
In Part 2 of this post, I'll try to expand on how one might accomplish if one were
not so blessed with a working ClearTrust infrastructure in place to do the hard part. 
Note that I've never actually done this part, so some of it might just be wild supposition
and it might take some help from someone else out there who really wants this to make
it happen.  We'll see.  In the meantime, I hope the blueprint was helpful.
</p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=871baf79-22b3-4e6f-bdd4-026a770bf9e0" />
      </body>
      <title>Integrating SecurID Authentication into ADFS--Part 1</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,871baf79-22b3-4e6f-bdd4-026a770bf9e0.aspx</guid>
      <link>http://www.joekaplan.net/IntegratingSecurIDAuthenticationIntoADFSPart1.aspx</link>
      <pubDate>Tue, 01 Aug 2006 18:08:18 GMT</pubDate>
      <description>&lt;p&gt;
When I was at TechEd this year, I ran into a few people asking how to integrate &lt;a href="http://www.rsasecurity.com/node.asp?id=1156"&gt;SecurID&lt;/a&gt; authentication
into ADFS.&amp;nbsp; As it currently stands, Microsoft has no direct support for this,
or any other authentication mechanism besides AD/ADAM password authentication and
client certificate auth.&amp;nbsp; Hopefully in the future, Microsoft will make the account
store and authentication mechanisms a first class extensibility point, but for now,
we must hack.&amp;nbsp; :)
&lt;/p&gt;
&lt;p&gt;
As you can probably tell by the article title, I have gotten this working in my organization's
environment, even though it is not supported by MS.&amp;nbsp; So, how do we make this
work?&amp;nbsp; First, I'll explain the overall principles of how we glue these things
together and then I'll share how I actually did it.&amp;nbsp; In part 2 of the posting,
I'll talk about some other ways that we might get this working.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Basic Principles&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
In Windows Server 2003 and Active Directory 2003, Microsoft implemented an important
and useful extension to the Kerberos authentication protocol called &lt;a href="http://technet2.microsoft.com/WindowsServer/en/library/0d3a0555-9a68-4cc6-ad55-fd076d037fcf1033.mspx?mfr=true"&gt;"Service
for User" (S4U), aka Protocol Transition&lt;/a&gt;.&amp;nbsp; S4U adds the ability for a service
to authenticate a client with a non-Kerberos protocol and then transition to a Kerberos-based
identity for accessing Windows resources on local or remote services (hence "protocol
transition").&amp;nbsp; Protocol transition is something that can be done automatically
by services like IIS when a user authenticates with Basic, Digest or NTLM, but it
can also be called programmatically and used by non-Windows authentication systems
such as SecurID.
&lt;/p&gt;
&lt;p&gt;
To use S4U, your code must execute on a Windows Server 2003 machine (or higher) AND
your Active Directory must be 2003 forest functional level.&amp;nbsp; If you are doing
ADFS, you already have the former.&amp;nbsp; However, since ADFS supports both Windows
2000 Server and Windows Server 2003 AD, you might not have the latter.&amp;nbsp; If you
don't, you are basically SOL for now.&amp;nbsp; Upgrade!
&lt;/p&gt;
&lt;p&gt;
When using S4U programmatically, you are basically calling the Windows API &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/lsalogonuser.asp"&gt;LsaLogonUser&lt;/a&gt; with
the S4U option specified.&amp;nbsp; When usings the S4U option, LsaLogonUser only requires
you to know the user's UPN, not their password, in order to get a Windows logon token
for them.&amp;nbsp; There are some restrictions on how this token can be used, but that
really isn't too important for this discussion.&amp;nbsp; &lt;a href="http://www.pluralsight.com/blogs/keith/default.aspx"&gt;Keith&lt;/a&gt; covers
this stuff in detail in his &lt;a href="http://www.pluralsight.com/wiki/default.aspx/Keith.GuideBook/HomePage.html"&gt;book&lt;/a&gt; and
several &lt;a href="http://msdn.microsoft.com/msdnmag/issues/03/04/SecurityBriefs/"&gt;articles&lt;/a&gt; anyway.&amp;nbsp;
One other nice thing is that .NET (as of 1.1 and higher) has a very easy way to call
LsaLogonUser for S4U with the WindowsIdentity constructor that just takes the UPN.&amp;nbsp;
It is that simple.
&lt;/p&gt;
&lt;p&gt;
So, now we know we need to use S4U to get a Windows token for the user and we'll need
their UPN to call this.&amp;nbsp; How does the Windows token then help us with ADFS?&amp;nbsp;
Well, the ADFS logon service (LS) has a method that allows a user to authenticate
given their Windows token.&amp;nbsp; In fact, the /ls/auth/integrated directory on the
federation server does just this.&amp;nbsp; It is set to use Windows Integrated authentication
(IWA)&amp;nbsp;in IIS.&amp;nbsp; IIS logs the user on with IWA, passes the corresponding Windows
token to ASP.NET.&amp;nbsp; Then, the ADFS HTTP Module grabs that and calls the correct
method to log you into ADFS and now you have a federation token.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
An interesting side note is that ADFS itself uses protocol transition in the web agent
for token-based applications to achieve similar things (assuming again that you have
2003 AD; otherwise it uses its custom authentication package).&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
So, our&amp;nbsp;basic approach&amp;nbsp;with SecurID authentication is to:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Authenticate the user with SecurID using one of RSA's supported methods for web authentication 
&lt;li&gt;
&amp;nbsp;Get the user's UPN somehow (perhaps an LDAP query?) 
&lt;li&gt;
Use S4U to get a Windows token for the user 
&lt;li&gt;
Use the appropriate method on the LS to authenticate with ADFS using the token&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
This is cake!&amp;nbsp; :)
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;How I Did This&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Ok, I cheated a little bit.&amp;nbsp; In addition to using RSA's SecurID/ACE Server product
in our company, we also use their &lt;a href="http://www.rsasecurity.com/node.asp?id=1186"&gt;ClearTrust&lt;/a&gt; web
SSO product (now apparently called Access Manager, but I didn't get that memo).&amp;nbsp;
ClearTrust already basically let's me do steps&amp;nbsp;1-3 above using their standard
product when the protocol transition/S4U feature is enabled.&amp;nbsp; Since it runs as
an ISAPI Filter/ISAPI Extension (via a wildcard map in IIS), it runs before any .NET
code executes.&amp;nbsp; All I had to do was configure ClearTrust on my /ls/auth/integrated
directory, and ADFS doesn't know the difference between IIS having used IWA or some
other thing.&amp;nbsp; The LS just grabs the Windows token from where it expects to find
it in ASP.NET and proceeds with logon.&amp;nbsp; It really is cake and actually worked
the first time I tried it.
&lt;/p&gt;
&lt;p&gt;
The hardest part was instructing ClearTrust to NOT execute on any resources other
than the /ls/auth/integrated directory, which essentially came down to a lengthy URL
exclusion list in the ClearTrust configuration.&amp;nbsp; Anyone who has used ClearTrust
probably already knows how to do that.&amp;nbsp; I will probably experiment with alternate
directory structures to simplify this a bit more in the future.
&lt;/p&gt;
&lt;p&gt;
Since ClearTrust is a web SSO product, it also contains a signout function that is
implemented by navigating to a page.&amp;nbsp; We integrated this into the ADFS signout
by using the same technique it does: we referred to the ClearTrust signout page in
a hidden image tag on the normal signout page.&amp;nbsp; That results in our ClearTrust
cookie being cleared.
&lt;/p&gt;
&lt;p&gt;
One thing I should make clear is that we did this customization on the FS, not the
FS-P (Federation Service Proxy).&amp;nbsp; I don't see&amp;nbsp;a reason why you couldn't
do this there too, but you'd need to mess with it a bit more as the FS-P is configured
out of the box to do its own forms auth instead of redirecting to the /ls/auth/integrated
directory.
&lt;/p&gt;
&lt;p&gt;
One important caveat with SecurID auth is that it basically requires forms authentication,
especially to support PIN operations.&amp;nbsp; This basically implies that you can't
use the "basic" authentication support built in to ADFS here.&amp;nbsp; As such, you should
probably turn off that option in your ADFS configuration.&amp;nbsp; That may also mean
that you some SharePoint/Office integration stuff breaks though.&amp;nbsp; I'm not sure
how to reconcile that problem.&amp;nbsp; Forms auth and Office don't like each other.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Summary&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
So anyway, that's the basic concept.&amp;nbsp; If anyone needs additional details, let
me know and I'll try to expand on this.
&lt;/p&gt;
&lt;p&gt;
In Part 2 of this post, I'll try to expand on how one might accomplish if one were
not so blessed with a working ClearTrust infrastructure in place to do the hard part.&amp;nbsp;
Note that I've never actually done this part, so some of it might just be wild supposition
and it might take some help from someone else out there who really wants this to make
it happen.&amp;nbsp; We'll see.&amp;nbsp; In the meantime, I hope the blueprint was helpful.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=871baf79-22b3-4e6f-bdd4-026a770bf9e0" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,871baf79-22b3-4e6f-bdd4-026a770bf9e0.aspx</comments>
      <category>Identity Federation;Windows Security</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=0de1cf66-d371-445d-9b27-65e65157782d</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,0de1cf66-d371-445d-9b27-65e65157782d.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,0de1cf66-d371-445d-9b27-65e65157782d.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=0de1cf66-d371-445d-9b27-65e65157782d</wfw:commentRss>
      <slash:comments>4</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
One of the things you run into with ASP.NET apps doing authorization using Windows
security is that you often need a way to find out the authenticated user's identity
and security group memberships for troubleshooting.  This is especially useful
under ADFS, where your Windows token can go through a mapping process based on claims
received from an external organization and bear no resemblance to an actual user in
your AD forest.
</p>
        <p>
This is just an ugly sample page in ASP.NET (using VB.NET, but I'll do C# on request
if that's really important; we're talking about 10 lines of code here guys...)
that dumps out the authenticated user's groups and name.
</p>
        <p>
The core function looks like this:
</p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Private</span>
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Sub</span> Page_Load(
_<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
ByVal</span> sender <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">As</span> System.<span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Object</span>,
_<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
ByVal</span> e <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">As</span> System.EventArgs
_<br />
    ) <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Handles</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">MyBase</span>.Load<br /><br />
_nameLabel.Text <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> User.Identity.Name 
<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Dim</span> groupSidHtml <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">As</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">New</span> System.Text.Stringbuilder<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Dim</span> sids <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">As</span> System.Security.Principal.IdentityReferenceCollection <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> _<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">   
DirectCast</span>(User.Identity, System.Security.Principal.WindowsIdentity).Groups<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Dim</span> names <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">As</span> System.Security.Principal.IdentityReferenceCollection <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> _<br />
    sids.Translate(<span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">GetType</span>(System.Security.Principal.NTAccount))<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">For</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Each</span> name <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">as</span> System.Security.Principal.NTAccount <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">In</span> names<br />
    groupSidHTML.AppendFormat(<span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4">"&lt;p&gt;{0}&lt;/p&gt;"</span>,
name.ToString())<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Next</span><br /><br />
_groupLabel.Text <span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">=</span> groupSidHTML.ToString()<br /><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">End</span><span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">Sub</span></span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">If
I were a little less lazy, I probably would have added the imports declarations on
the page instead of using the full type names and would have used a repeater and some
formatting, but this was quick and dirty.  Feel free to improve it.  The
working page can be downloaded at the link at the bottom of the page.</font>
            </span>
          </span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">
                <strong>Caveat</strong>
              </font>
            </span>
          </span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">Even
though token apps allow you to run on prior versions of the .NET framework, this page
uses a bunch of .NET 2.0-specific code in it (IdentityReferenceCollection and such),
so you must configure the app for .NET 2.0 to use this.  I'm simply not at all
interested in writing all the p/invoke stuff to crack the user's token and translate
their SIDs into names simply to create a .NET 1.1 solution.  Sorry.  There
is lazy and then there is just wasting time...</font>
            </span>
          </span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">Also,
you must be configured for Windows authentication in ASP.NET, but that should be obvious
I hope.</font>
            </span>
          </span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">I
hope this helps someone figure out what ADFS is actually putting into their Windows
token!</font>
            </span>
          </span>
        </p>
        <p>
          <span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
            <span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent">
              <font face="Verdana" color="#000000" size="2">(Update,
changed the file to a .zip to avoid error mentioned in the comment)</font>
            </span>
          </span>
        </p>
        <a href="http://www.joekaplan.net/content/binary/default.zip">default.zip (.63 KB)</a>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=0de1cf66-d371-445d-9b27-65e65157782d" />
      </body>
      <title>Discovering the User's Name and Groups in Their Windows Token</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,0de1cf66-d371-445d-9b27-65e65157782d.aspx</guid>
      <link>http://www.joekaplan.net/DiscoveringTheUsersNameAndGroupsInTheirWindowsToken.aspx</link>
      <pubDate>Mon, 31 Jul 2006 19:44:06 GMT</pubDate>
      <description>&lt;p&gt;
One of the things you run into with ASP.NET&amp;nbsp;apps doing authorization using Windows
security is that you often need a way to find out the authenticated user's identity
and security group memberships for troubleshooting.&amp;nbsp; This is especially useful
under ADFS, where your Windows token can go through a mapping process based on claims
received from an external organization and bear no resemblance to an actual user in
your AD forest.
&lt;/p&gt;
&lt;p&gt;
This is just an ugly sample page in ASP.NET (using VB.NET, but I'll do C# on request
if that's&amp;nbsp;really important; we're talking about 10 lines of code here guys...)
that dumps out the authenticated user's groups and name.
&lt;/p&gt;
&lt;p&gt;
The core function looks like this:
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Private&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Sub&lt;/span&gt; Page_Load(
_&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
ByVal&lt;/span&gt; sender &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;As&lt;/span&gt; System.&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Object&lt;/span&gt;,
_&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
ByVal&lt;/span&gt; e &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;As&lt;/span&gt; System.EventArgs
_&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; ) &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Handles&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;MyBase&lt;/span&gt;.Load&lt;br&gt;
&lt;br&gt;
_nameLabel.Text &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; User.Identity.Name 
&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Dim&lt;/span&gt; groupSidHtml &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;As&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;New&lt;/span&gt; System.Text.Stringbuilder&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Dim&lt;/span&gt; sids &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;As&lt;/span&gt; System.Security.Principal.IdentityReferenceCollection &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; _&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;
DirectCast&lt;/span&gt;(User.Identity, System.Security.Principal.WindowsIdentity).Groups&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Dim&lt;/span&gt; names &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;As&lt;/span&gt; System.Security.Principal.IdentityReferenceCollection &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; _&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; sids.Translate(&lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;GetType&lt;/span&gt;(System.Security.Principal.NTAccount))&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;For&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Each&lt;/span&gt; name &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;as&lt;/span&gt; System.Security.Principal.NTAccount &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;In&lt;/span&gt; names&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; groupSidHTML.AppendFormat(&lt;span style="FONT-SIZE: 11px; COLOR: #666666; FONT-FAMILY: Courier New; BACKGROUND-COLOR: #e4e4e4"&gt;"&amp;lt;p&amp;gt;{0}&amp;lt;/p&amp;gt;"&lt;/span&gt;,
name.ToString())&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Next&lt;/span&gt;
&lt;br&gt;
&lt;br&gt;
_groupLabel.Text &lt;span style="FONT-SIZE: 11px; COLOR: red; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;=&lt;/span&gt; groupSidHTML.ToString()&lt;br&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;End&lt;/span&gt; &lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;Sub&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;If
I were a little less lazy, I probably would have added the imports declarations on
the page instead of using the full type names and would have used a repeater and some
formatting, but this was quick and dirty.&amp;nbsp; Feel free to improve it.&amp;nbsp; The
working page can be downloaded at the link at the bottom of the page.&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;&lt;strong&gt;Caveat&lt;/strong&gt;&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;Even
though token apps allow you to run on prior versions of the .NET framework, this page
uses a bunch of .NET 2.0-specific code in it (IdentityReferenceCollection and such),
so you must configure the app for .NET 2.0 to use this.&amp;nbsp; I'm simply not at all
interested in writing all the p/invoke stuff to crack the user's token and translate
their SIDs into names simply to create a .NET 1.1 solution.&amp;nbsp; Sorry.&amp;nbsp; There
is lazy and then there is just wasting time...&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;Also,
you must be configured for Windows authentication in ASP.NET, but that should be obvious
I hope.&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;I
hope this helps someone figure out what ADFS is actually putting into their Windows
token!&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;span style="FONT-SIZE: 11px; COLOR: black; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;span style="FONT-SIZE: 11px; COLOR: blue; FONT-FAMILY: Courier New; BACKGROUND-COLOR: transparent"&gt;&lt;font face=Verdana color=#000000 size=2&gt;(Update,
changed the file to a .zip to avoid error mentioned in the comment)&lt;/font&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/default.zip"&gt;default.zip (.63 KB)&lt;/a&gt;&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=0de1cf66-d371-445d-9b27-65e65157782d" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,0de1cf66-d371-445d-9b27-65e65157782d.aspx</comments>
      <category>Identity Federation;Windows Security</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=50d67e32-dce2-4e83-bb29-7b29726db244</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,50d67e32-dce2-4e83-bb29-7b29726db244.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,50d67e32-dce2-4e83-bb29-7b29726db244.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=50d67e32-dce2-4e83-bb29-7b29726db244</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
One of the tricks you can do with the Active Directory Federation Services (ADFS)
home realm discovery process is get a user to skip the home realm discovery page completely
if you embed a query string in the application URL that tells ADFS what realm to use. 
The query string is:
</p>
        <p>
          <font face="Courier New">whr=xxxxxx</font>
        </p>
        <p>
where xxxxxx is the federation URI of the partner (which they tell you when you set
up your federation, or you create when you are setting up your test lab).  That
typically looks like:
</p>
        <p>
          <font face="Courier New">urn:federation:myorganization</font>
        </p>
        <p>
Thus, the whole url might look like:
</p>
        <p>
          <font face="Courier New">http://www.joekaplan.net/?whr=urn:federation.myorganization</font>
        </p>
        <p>
          <font size="1">
            <em>(no, this site is not federation-enabled and won't be any time
soon...)</em>
          </font>
        </p>
        <p>
Using these home realm query strings is very handy, not only for getting your own
organization's users to the target app quicker by allowing them to skip a page that
may potentially have many choices, but also just for testing.  The query string
overrides the persistent cookie you may have that identifies your home realm, so you
can use this to avoid having to delete your cookies all the time.
</p>
        <p>
However, if you want to refer to the resource partner's account store with this trick,
you don't use the resource partner's federation URI.  Instead, you use the "built-in"
URI:
</p>
        <p>
          <font face="Courier New">urn:federation:self</font>
        </p>
        <p>
I'm sure this is probably documented somewhere (or maybe not; the ADFS docs have a
ways to go...), but I had to figure it out the hard way and I thought I'd share.
</p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=50d67e32-dce2-4e83-bb29-7b29726db244" />
      </body>
      <title>Note to urn:federation:self</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,50d67e32-dce2-4e83-bb29-7b29726db244.aspx</guid>
      <link>http://www.joekaplan.net/NoteToUrnfederationself.aspx</link>
      <pubDate>Mon, 31 Jul 2006 17:57:52 GMT</pubDate>
      <description>&lt;p&gt;
One of the tricks you can do with the Active Directory Federation Services (ADFS)
home realm discovery process is get a user to skip the home realm discovery page completely
if you embed a query string in the application URL that tells ADFS what realm to use.&amp;nbsp;
The query string is:
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;whr=xxxxxx&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
where xxxxxx is the federation URI of the partner (which they tell you when you set
up your federation, or you create when you are setting up your test lab).&amp;nbsp; That
typically looks like:
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;urn:federation:myorganization&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Thus, the whole url might look like:
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;http://www.joekaplan.net/?whr=urn:federation.myorganization&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
&lt;font size=1&gt;&lt;em&gt;(no, this site is not federation-enabled and won't be any time soon...)&lt;/em&gt;&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
Using these home realm query strings is very handy, not only for getting your own
organization's users to the target app quicker by allowing them to skip a page that
may potentially have many choices, but also just for testing.&amp;nbsp; The query string
overrides the persistent cookie you may have that identifies your home realm, so you
can use this to avoid having to delete your cookies all the time.
&lt;/p&gt;
&lt;p&gt;
However, if you want to refer to the resource partner's account store with this trick,
you don't use the resource partner's federation URI.&amp;nbsp; Instead, you use the "built-in"
URI:
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;urn:federation:self&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
I'm sure this is probably documented somewhere (or maybe not; the ADFS docs have a
ways to go...), but I had to figure it out the hard way and&amp;nbsp;I thought I'd share.
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=50d67e32-dce2-4e83-bb29-7b29726db244" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,50d67e32-dce2-4e83-bb29-7b29726db244.aspx</comments>
      <category>Identity Federation</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=49b93ec2-969d-4017-9d99-e382708ac5b9</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,49b93ec2-969d-4017-9d99-e382708ac5b9.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,49b93ec2-969d-4017-9d99-e382708ac5b9.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=49b93ec2-969d-4017-9d99-e382708ac5b9</wfw:commentRss>
      <slash:comments>3</slash:comments>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
          <font size="4">The Problem</font>
        </p>
        <p>
So, let's say you are building a claims-based application for Active Directory Federation
Services (ADFS) and you want to use VS.NET 2005 to do this.  Alternately,
let's say you are customizing some of the built in pages that come with the federation
server or federation server proxy and want to use VS.NET for that.
</p>
        <p>
As things stand today, there is a minor annoying friction point here as the ADFS installer
doesn't provide a nice clean way to set a reference to the code you need in System.Web.Security.SingleSignOn
and such.  Even if you are developing on a 2003 box (which a lot of serious web
devs do, although I still stumble along with XP as its the company standard) and actually
install ADFS, you don't see these assemblies in the .NET tab on the add reference
UI.  The problem is that the ADFS installer puts the assemblies directly in the
GAC and doesn't leave a copy on the "normal" file system (which the compilers actually
need), nor does it bother to create a registry key under: 
</p>
        <p>
          <font face="Courier New">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx</font>
        </p>
        <p>
(which is the little bit of magic that gets your assembly in the .NET tab). 
Multiply this by a large team working on a claims app, and this can be a drag.
</p>
        <p>
          <font size="4">The Solution</font>
        </p>
        <p>
So, what's a dev to do?  Well, you can definitely copy the assemblies yourself,
GAC them and create a registry key and probably do all that with a little batch file
if you are so inclined.  Or, you can go over the top and create your own MSI
installer package that does this for you.
</p>
        <p>
As a little experiment to see if I could do this quickly and learn how to do some
new stuff in MSI that  thought I'd need later on, I did the latter.  It
took about an hour over lunch one day.
</p>
        <p>
I'd love to just distribute the msi package directly, but I won't consider doing that
unless MS gives me permission to do so (which I don't expect to get).  So, that
won't happen.  However, I will tell you how to build your own (fairly) painlessly. 
For this, you will need:
</p>
        <p>
          <font size="4">Ingredients</font>
        </p>
        <ul>
          <li>
One copy of <a href="http://wix.sourceforge.net/">Windows Installer XML (WiX) 2.0</a> (3.0
might work as well, but I didn't bother trying) 
</li>
          <li>
One copy of the ADFS.msi package that comes with R2 (dig around on the install media
for it), or alternately, the 3 assemblies that come with ADFS that are installed the
GAC that you hoisted from a normal install 
</li>
          <li>
One copy of my sample WiX file that will create the MSI (see link at bottom)</li>
        </ul>
        <p>
          <font size="4">Recipe</font>
        </p>
        <ol>
          <li>
Place the vsadfs.wxs file in a new clean directory on your file system 
</li>
          <li>
Use dark.exe from the WiX distro to reverse engineer ADFS.msi in order to get its
files into a handy .cab format, ignoring the error from Dark (<font face="Courier New">&lt;wixpath&gt;\dark
-x bin -out adfs.wxs adfs.msi</font>) 
</li>
          <li>
Grab the three assembly files (S.Web.Security.SSO *) from the cab file and stick them
in a directory called "files" underneath the directory where you put my vsadfs.wxs
file 
</li>
          <li>
Run these two commands from the command prompt, filling in the path to your WiX binary
install where appropriate (unless you have that on your path, obviously): 
<ol><li><font face="Courier New">&lt;wixpath&gt;\candle vsadfs.wxs</font></li><li><font face="Courier New">&lt;wixpath&gt;\light -out vsadfs.msi vsadfs.wixobj &lt;wixpath&gt;\wixui.wixlib
-loc &lt;wixpath&gt;\WixUI_en-us.wxl</font></li></ol></li>
        </ol>
        <p>
You now have a working msi installer that will install (and uninstall and repair!) these
things for you.  You may notice that the UI thing I used, WiXUI, shows a license
screen and shows the CPL license there.  That's because we didn't supply a license.rtf
file in our directory, so WiXUI picked up the one that comes with WiX in its directory. 
Feel free to change it.  If you want to eliminate that dialog completely, you
are on your own.
</p>
        <p>
          <font size="4">Caveats</font>
        </p>
        <ul>
          <li>
            <strong>This will NOT give you a working version of an ADFS-enabled web server! 
You cannot use this to actually generate a SingleSignOnIdentity object to be used
for testing.</strong>  You would need some sort of a mock object thingy to do
that for you (may that's next?).  All it really does is let set a reference in
VS.NET and compile the code, but that's better than what you had before. 
</li>
          <li>
Additionally, my current installer uses different component GUIDs for the version
of the files that go in the GAC.  I'm not sure if it would be the right thing
to reuse the component GUIDs from the real ADFS install which puts these components
in the GAC or not.  The only time this might be an issue was if you ran this
installer on a box that already has ADFS installed.  I'm not sure what would
happen, but right now it seems better to keep them separate.  
</li>
          <li>
The installer allows specify the install location if you want to change it and
breaks the VS.NET and GAC install stuff into separate features so that you don't have
to install both if you don't want.  This can be customized via the feature tree
dialog. 
</li>
          <li>
The installer does not bother to check if you have VS.NET 2005 or even .NET 2.0 installed. 
This would be a good sanity check for mass market usage, but I'm hoping the users
of this are not too insane.  
</li>
          <li>
The installer defaults to a "per-machine" install instead of per-user. 
</li>
          <li>
Repackaging Microsoft's installer is probably a violation of something and may void
your warranty or get you in some sort of trouble that I had not fully considered when
offering this suggestion.</li>
        </ul>
        <p>
          <font size="4">The Future</font>
        </p>
        <p>
I'm hoping that future versions of ADFS will have this scenario already considered
so that things like this won't be necessary.  Perhaps we can convince the team
to ship this as part of the product or as a free download/sample solution?  I
wouldn't be surprised to see these assemblies in a future version of the .NET Framework
as well, which would mean that we'd get this for free another way.  Until then...
</p>
        <p>
(Updated 2 Aug 2006 changed to zip file)
</p>
        <a href="http://www.joekaplan.net/content/binary/vsadfs.zip">vsadfs.zip (1.54 KB)</a>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=49b93ec2-969d-4017-9d99-e382708ac5b9" />
      </body>
      <title>How to Get VS.NET Integration for ADFS Claims-Based Apps</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,49b93ec2-969d-4017-9d99-e382708ac5b9.aspx</guid>
      <link>http://www.joekaplan.net/HowToGetVSNETIntegrationForADFSClaimsBasedApps.aspx</link>
      <pubDate>Mon, 31 Jul 2006 05:18:01 GMT</pubDate>
      <description>&lt;p&gt;
&lt;font size=4&gt;The Problem&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
So, let's say you are building a claims-based application for Active Directory Federation
Services (ADFS) and you want to use VS.NET 2005&amp;nbsp;to do this.&amp;nbsp; Alternately,
let's say you are customizing some of the built in pages that come with the federation
server or federation server proxy and want to use VS.NET for that.
&lt;/p&gt;
&lt;p&gt;
As things stand today, there is a minor annoying friction point here as the ADFS installer
doesn't provide a nice clean way to set a reference to the code you need in System.Web.Security.SingleSignOn
and such.&amp;nbsp; Even if you are developing on a 2003 box (which a lot of serious web
devs do, although I still stumble along with XP as its the company standard) and actually
install ADFS, you don't see these assemblies in the .NET tab on the add reference
UI.&amp;nbsp; The problem is that the ADFS installer puts the assemblies directly in the
GAC and doesn't leave a copy on the "normal" file system (which the compilers actually
need), nor does it bother to create a&amp;nbsp;registry key under: 
&lt;/p&gt;
&lt;p&gt;
&lt;font face="Courier New"&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
(which is the little bit of magic that gets your assembly in the .NET tab).&amp;nbsp;
Multiply this by a large team working on a claims app, and this can be a drag.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;The Solution&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
So, what's a dev to do?&amp;nbsp; Well, you can definitely copy the assemblies yourself,
GAC them and create a registry key and probably do all that with a little batch file
if you are so inclined.&amp;nbsp; Or, you can go over the top and create your own MSI
installer package that does this for you.
&lt;/p&gt;
&lt;p&gt;
As a little experiment to see if I could do this quickly and learn how to do some
new stuff in MSI that&amp;nbsp; thought I'd need later on, I did the latter.&amp;nbsp; It
took about an hour over lunch one day.
&lt;/p&gt;
&lt;p&gt;
I'd love to just distribute the msi package directly, but I won't consider doing that
unless MS gives me permission to do so (which I don't expect to get).&amp;nbsp; So, that
won't happen.&amp;nbsp; However,&amp;nbsp;I will tell you how to build your own (fairly) painlessly.&amp;nbsp;
For this, you will need:
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Ingredients&lt;/font&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
One copy of &lt;a href="http://wix.sourceforge.net/"&gt;Windows Installer XML (WiX)&amp;nbsp;2.0&lt;/a&gt;&amp;nbsp;(3.0
might work as well, but I didn't bother trying) 
&lt;li&gt;
One copy of the ADFS.msi package that comes with R2 (dig around on the install media
for it), or alternately, the 3 assemblies that come with ADFS that are installed the
GAC that you hoisted from a normal install 
&lt;li&gt;
One copy of my sample WiX file that will create the MSI (see link at bottom)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;font size=4&gt;Recipe&lt;/font&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
Place the vsadfs.wxs file in a new clean directory on your file system 
&lt;li&gt;
Use dark.exe from the WiX distro to reverse engineer ADFS.msi in order to get its
files into a handy .cab format, ignoring the error from&amp;nbsp;Dark&amp;nbsp;(&lt;font face="Courier New"&gt;&amp;lt;wixpath&amp;gt;\dark
-x bin -out adfs.wxs adfs.msi&lt;/font&gt;) 
&lt;li&gt;
Grab the three assembly files (S.Web.Security.SSO *) from the cab file and stick them
in a directory called "files" underneath the directory where you put my vsadfs.wxs
file 
&lt;li&gt;
Run these two commands from the command prompt, filling in the path to your WiX binary
install where appropriate (unless you have that on your path, obviously): 
&lt;ol&gt;
&lt;li&gt;
&lt;font face="Courier New"&gt;&amp;lt;wixpath&amp;gt;\candle vsadfs.wxs&lt;/font&gt; 
&lt;li&gt;
&lt;font face="Courier New"&gt;&amp;lt;wixpath&amp;gt;\light -out vsadfs.msi vsadfs.wixobj &amp;lt;wixpath&amp;gt;\wixui.wixlib
-loc &amp;lt;wixpath&amp;gt;\WixUI_en-us.wxl&lt;/font&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
You now have a working msi installer that will install (and uninstall and repair!)&amp;nbsp;these
things for you.&amp;nbsp; You may notice that the UI thing I used, WiXUI, shows a license
screen and shows the CPL license there.&amp;nbsp; That's because we didn't supply a license.rtf
file in our directory, so WiXUI picked up the one that comes with WiX in its directory.&amp;nbsp;
Feel free to change it.&amp;nbsp; If you want to eliminate that dialog completely, you
are on your own.
&lt;/p&gt;
&lt;p&gt;
&lt;font size=4&gt;Caveats&lt;/font&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;This will NOT give you a working version of an ADFS-enabled web server!&amp;nbsp;
You cannot use this to actually generate a SingleSignOnIdentity object to be used
for testing.&lt;/strong&gt;&amp;nbsp; You would need some sort of a mock object thingy to do
that for you (may that's next?).&amp;nbsp; All it really does is let set a reference in
VS.NET and compile the code, but that's better than what you had before. 
&lt;li&gt;
Additionally, my current installer uses different component GUIDs for the version
of the files that go in the GAC.&amp;nbsp; I'm not sure if it would be the right thing
to reuse the component GUIDs from the real ADFS install which puts these components
in the GAC or not.&amp;nbsp; The only time this might be an issue was if you ran this
installer on a box that already has ADFS installed.&amp;nbsp; I'm not sure what would
happen, but right now it seems better to keep them separate.&amp;nbsp; 
&lt;li&gt;
The installer allows specify the install&amp;nbsp;location if you want to change it and
breaks the VS.NET and GAC install stuff into separate features so that you don't have
to install both if you don't want.&amp;nbsp; This can be customized via the feature tree
dialog. 
&lt;li&gt;
The installer does not bother to check if you have VS.NET 2005 or even .NET 2.0 installed.&amp;nbsp;
This would be a good sanity check for mass market usage, but I'm hoping the users
of this are not too insane.&amp;nbsp; 
&lt;li&gt;
The installer defaults to a "per-machine" install instead of per-user. 
&lt;li&gt;
Repackaging Microsoft's installer is probably a violation of something and may void
your warranty or get you in some sort of trouble that I had not fully considered when
offering this suggestion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;font size=4&gt;The Future&lt;/font&gt;
&lt;/p&gt;
&lt;p&gt;
I'm hoping that future versions of ADFS will have this scenario already considered
so that things like this won't be necessary.&amp;nbsp; Perhaps we can convince the team
to ship this as part of the product or as a free download/sample solution?&amp;nbsp; I
wouldn't be surprised to see these assemblies in a future version of the .NET Framework
as well, which would mean that we'd get this for free another way.&amp;nbsp; Until then...
&lt;/p&gt;
&lt;p&gt;
(Updated 2 Aug 2006 changed to zip file)
&lt;/p&gt;
&lt;a href="http://www.joekaplan.net/content/binary/vsadfs.zip"&gt;vsadfs.zip (1.54 KB)&lt;/a&gt;&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=49b93ec2-969d-4017-9d99-e382708ac5b9" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,49b93ec2-969d-4017-9d99-e382708ac5b9.aspx</comments>
      <category>Identity Federation;WiX</category>
    </item>
    <item>
      <trackback:ping>http://www.joekaplan.net/Trackback.aspx?guid=ac8e564c-1b19-44e7-9ab0-6a4ef74bd614</trackback:ping>
      <pingback:server>http://www.joekaplan.net/pingback.aspx</pingback:server>
      <pingback:target>http://www.joekaplan.net/PermaLink,guid,ac8e564c-1b19-44e7-9ab0-6a4ef74bd614.aspx</pingback:target>
      <dc:creator>Joe Kaplan</dc:creator>
      <wfw:comment>http://www.joekaplan.net/CommentView,guid,ac8e564c-1b19-44e7-9ab0-6a4ef74bd614.aspx</wfw:comment>
      <wfw:commentRss>http://www.joekaplan.net/SyndicationService.asmx/GetEntryCommentsRss?guid=ac8e564c-1b19-44e7-9ab0-6a4ef74bd614</wfw:commentRss>
      <body xmlns="http://www.w3.org/1999/xhtml">
        <p>
I've been telling myself I was going to finally get into the blogging racket for almost
2 years now, but even though I've had the hosting all put together for that entire
time, it took me forever to actually get it together.  I just had to do it myself
instead of using another site...
</p>
        <p>
Anyway, this blog will probably resemble the blogs I already read, in that it will
be mostly technical with a focus on building software using Microsoft's .NET platform. 
It will probably lean heavily on my specialties, .NET LDAP programming and application
security, but will likely also feature other stuff I'm into like application architecture,
agile development, identity federation, cryptography, and setup development in MSI
using <a href="http://wix.sourceforge.net">WiX</a>.  
</p>
        <p>
Speaking of .NET and LDAP, if you've ever heard of me before, it is most likely because
you might have stumbled across one of the myriad usenet posts I've made over the last
4-5 years on the Microsoft newsgroups, or perhaps I answered your question directly. 
Micrsosoft has actually designated me an <a href="http://mvp.support.microsoft.com">MVP</a> in
this area, and I've even written a <a href="http://www.directoryprogramming.net">book</a> about
this with my intrepid co-author, <a href="http://www.dunnry.com/blog">Ryan Dunn</a>,
to further our aim of providing resources for the .NET community in this obscure,
but stranglely difficult and increasingly important aspect of software development.
</p>
        <p>
That's all for now.  Maybe some real content next time, eh?
</p>
        <img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=ac8e564c-1b19-44e7-9ab0-6a4ef74bd614" />
      </body>
      <title>About time...</title>
      <guid isPermaLink="false">http://www.joekaplan.net/PermaLink,guid,ac8e564c-1b19-44e7-9ab0-6a4ef74bd614.aspx</guid>
      <link>http://www.joekaplan.net/AboutTime.aspx</link>
      <pubDate>Sun, 30 Jul 2006 15:25:14 GMT</pubDate>
      <description>&lt;p&gt;
I've been telling myself I was going to finally get into the blogging racket for almost
2 years now, but even though I've had the hosting all put together for that entire
time, it took me forever to actually get it together.&amp;nbsp; I just had to do it myself
instead of using another site...
&lt;/p&gt;
&lt;p&gt;
Anyway, this blog will probably resemble the blogs I already read, in that it will
be mostly technical with a focus on building software using Microsoft's .NET platform.&amp;nbsp;
It will probably lean heavily on my specialties, .NET LDAP programming and&amp;nbsp;application
security, but will likely also feature other stuff I'm into like application architecture,
agile development, identity federation, cryptography, and setup development in MSI
using &lt;a href="http://wix.sourceforge.net"&gt;WiX&lt;/a&gt;.&amp;nbsp; 
&lt;/p&gt;
&lt;p&gt;
Speaking of .NET and LDAP, if you've ever heard of me before, it is most likely because
you might have stumbled across one of the myriad usenet posts I've made over the last
4-5 years on the Microsoft newsgroups, or perhaps I answered your question directly.&amp;nbsp;
Micrsosoft has actually designated me an &lt;a href="http://mvp.support.microsoft.com"&gt;MVP&lt;/a&gt; in
this area, and I've even written a &lt;a href="http://www.directoryprogramming.net"&gt;book&lt;/a&gt; about
this with my intrepid co-author, &lt;a href="http://www.dunnry.com/blog"&gt;Ryan Dunn&lt;/a&gt;,
to further our aim of providing resources for the .NET community in this obscure,
but stranglely difficult and increasingly important aspect of software development.
&lt;/p&gt;
&lt;p&gt;
That's all for now.&amp;nbsp; Maybe some real content next time, eh?
&lt;/p&gt;
&lt;img width="0" height="0" src="http://www.joekaplan.net/aggbug.ashx?id=ac8e564c-1b19-44e7-9ab0-6a4ef74bd614" /&gt;</description>
      <comments>http://www.joekaplan.net/CommentView,guid,ac8e564c-1b19-44e7-9ab0-6a4ef74bd614.aspx</comments>
      <category>General;LDAP;Windows Security;WiX;Identity Federation;Cryptography;Agile;Application Architecture;Software Engineering</category>
    </item>
  </channel>
</rss>