<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Ramiro Calderon's blog</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/ramical/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/ramical/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:48:57 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Under the hood tour on Multi-Factor Authentication in ADFS – Part 2: MFA aware Relying Parties</title>
      <link>https://docs.microsoft.com/archive/blogs/ramical/under-the-hood-tour-on-multi-factor-authentication-in-adfs-part-2-mfa-aware-relying-parties</link>
      <pubDate>Tue, 18 Feb 2014 08:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Ramiro Calderon - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/ramical/2014/02/18/under-the-hood-tour-on-multi-factor-authentication-in-adfs-part-2-mfa-aware-relying-parties/</guid>
      <description><![CDATA[Last time, we discussed how to author the policy to enable Multi-Factor Authentication (MFA) in AD...]]></description>
      <content:encoded><![CDATA[<p>Last time, we discussed how to author the <a href="https://blogs.msdn.com/b/ramical/archive/2014/01/30/under-the-hood-tour-on-multi-factor-authentication-in-ad-fs-part-1-policy.aspx">policy</a> to enable Multi-Factor Authentication (MFA) in AD FS. In this post, I want to talk about the developer experience when building relying party applications.</p>
<h1>What's the scenario?</h1>
<p>Let's say you created your ASP.NET MVC 5 application <a href="https://www.cloudidentity.com/blog/2014/02/12/USE-THE-ON-PREMISES-ORGANIZATIONAL-AUTHENTICATION-OPTION-ADFS-WITH-ASP-NET-IN-VISUAL-STUDIO-2013/">authenticated with on premises identity with AD FS</a>. Now, you want to allow access to <em>some</em> (if you wanted <em>all</em> of the operations, you can govern that from AD FS MFA&nbsp;policy) of the administrative operations only if the user performed two factor authentication:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7178.Blog-MFARPHomePage-Figure1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7178.Blog_2D00_MFARPHomePage_2D00_Figure1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7178.Blog-MFARPHomePage-Figure1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7178.Blog_2D00_MFARPHomePage_2D00_Figure1.png" alt="" border="0" /></a></p>
<p><br />Let's start off by letting the application know whether MFA was performed</p>
<h1>Step 1: Send the MFA claim to the Relying Party</h1>
<p>As I mentioned in <a href="https://blogs.msdn.com/b/ramical/archive/2014/01/30/under-the-hood-tour-on-multi-factor-authentication-in-ad-fs-part-1-policy.aspx">part 1 of this tour</a>, AD FS will generate a claim of type <span style="font-family: Consolas;"><strong>https://schemas.microsoft.com/claims/authnmethodsreferences</strong></span> that indicates the methods used when authenticating the user. If we send this claim in the relying party token, we can pivot the authorization of the administrative operations in the application's code.</p>
<p>Let's go to AD FS management console, and pass through the claim to the relying party's issuance transform rules:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7356.Blog-MFARPRulePassthruAMR-Figure2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7356.Blog_2D00_MFARPRulePassthruAMR_2D00_Figure2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7356.Blog-MFARPRulePassthruAMR-Figure2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7356.Blog_2D00_MFARPRulePassthruAMR_2D00_Figure2.png" alt="" border="0" /></a></p>
<p><br />One thing that I always do to make sure I get the right claims after setting the rules is try the flow with <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ff966510(v=vs.85).aspx">Fiddler</a>. Since this is a ws-federation application, the token must be in the <span style="font-family: Consolas;"><strong>wresult</strong></span> parameter in the last redirect from AD FS to your relying party's endpoint:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7230.Blog-AmrClaimInToken-Figure3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7230.Blog_2D00_AmrClaimInToken_2D00_Figure3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/7230.Blog-AmrClaimInToken-Figure3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/7230.Blog_2D00_AmrClaimInToken_2D00_Figure3.png" alt="" border="0" /></a></p>
<p><br />With that, now we are ready to go to the application code.</p>
<h1>Step 2: Authorize access based on MFA</h1>
<p>The claims set by AD FS are accessible through <a href="https://msdn.microsoft.com/en-us/library/system.web.httpcontext.user(v=vs.110).aspx">HttpContext.User</a> as a <a href="https://msdn.microsoft.com/en-us/library/system.security.claims.claimsidentity(v=vs.110).aspx">ClaimsIdentity</a> object in the ASP.NET web application. This means that you can put conditional logic to grant or deny access based on any claim. For this post, I chose to encapsulate the logic using <a href="https://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx">ASP.NET MVC Filtering</a>, creating a custom attribute that derives from the platform's built-in <a href="https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.98).aspx">AuthorizeAttribute</a> which looks for a specific claim and value in the ClaimsIdentity, and redirect to an Access Denied view (which I will leave as an exercise for you <span style="font-family: Wingdings;">J</span>) when failing. The code of the attribute is very simple:</p>
<p style="background: white;"><span style="color: blue; font-family: Consolas; font-size: 8pt;">public<span style="color: black;">&nbsp;<span style="color: blue;">class<span style="color: black;">&nbsp;<span style="color: #2b91af;">AuthorizeClaimAttribute<span style="color: black;">&nbsp;:&nbsp;<span style="color: #2b91af;">AuthorizeAttribute</span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public<span style="color: black;">&nbsp;<span style="color: blue;">string<span style="color: black;">&nbsp;ClaimType&nbsp;{&nbsp;<span style="color: blue;">get<span style="color: black;">;&nbsp;<span style="color: blue;">set<span style="color: black;">;&nbsp;} </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public<span style="color: black;">&nbsp;<span style="color: blue;">string<span style="color: black;">&nbsp;ClaimValue&nbsp;{&nbsp;<span style="color: blue;">get<span style="color: black;">;&nbsp;<span style="color: blue;">set<span style="color: black;">;&nbsp;} </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">protected<span style="color: black;">&nbsp;<span style="color: blue;">override<span style="color: black;">&nbsp;<span style="color: blue;">bool<span style="color: black;">&nbsp;AuthorizeCore(<span style="color: #2b91af;">HttpContextBase<span style="color: black;">&nbsp;httpContext) </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var<span style="color: black;">&nbsp;user&nbsp;=&nbsp;httpContext.User; </span></span></span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//Check&nbsp;if&nbsp;the&nbsp;user&nbsp;is&nbsp;not&nbsp;authenticated</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(user&nbsp;==&nbsp;<span style="color: blue;">null<span style="color: black;">) </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw<span style="color: black;">&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">AuthenticationException<span style="color: black;">( </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: #a31515;">"User&nbsp;is&nbsp;not&nbsp;authenticated."<span style="color: black;">); </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//If&nbsp;authenticated,&nbsp;validate&nbsp;this&nbsp;is&nbsp;a&nbsp;claims&nbsp;identity</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(!(user.Identity&nbsp;<span style="color: blue;">is<span style="color: black;">&nbsp;System.Security.Claims.<span style="color: #2b91af;">ClaimsIdentity<span style="color: black;">)) </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw<span style="color: black;">&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">AuthenticationException<span style="color: black;">(<span style="color: blue;">string<span style="color: black;">.Format( </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: #a31515;">"Identity&nbsp;is&nbsp;not&nbsp;ClaimsIdentity.&nbsp;Actual&nbsp;type&nbsp;=&nbsp;'<span style="color: mediumseagreen;">{0}<span style="color: #a31515;">'. "<span style="color: black;">, </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user.Identity.GetType().Name)); </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">var<span style="color: black;">&nbsp;userClaimsIdentity&nbsp;=&nbsp;user.Identity&nbsp;<span style="color: blue;">as<span style="color: black;">&nbsp;System.Security.Claims.<span style="color: #2b91af;">ClaimsIdentity<span style="color: black;">; </span></span></span></span></span></span></span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">bool<span style="color: black;">&nbsp;claimsFound&nbsp;= </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span style="color: blue;">from<span style="color: black;">&nbsp;c&nbsp;<span style="color: blue;">in<span style="color: black;">&nbsp;userClaimsIdentity.Claims </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">where<span style="color: black;">&nbsp;(c.Type&nbsp;==&nbsp;ClaimType&nbsp;&amp;&amp;&nbsp;c.Value&nbsp;==&nbsp;ClaimValue) </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">select<span style="color: black;">&nbsp;c).Any(); </span></span></span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(claimsFound) </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return<span style="color: black;">&nbsp;<span style="color: blue;">false<span style="color: black;">; </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return<span style="color: black;">&nbsp;<span style="color: blue;">base<span style="color: black;">.AuthorizeCore(httpContext); </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;">&nbsp;</p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">protected<span style="color: black;">&nbsp;<span style="color: blue;">override<span style="color: black;">&nbsp;<span style="color: blue;">void<span style="color: black;">&nbsp;HandleUnauthorizedRequest(<span style="color: #2b91af;">AuthorizationContext<span style="color: black;">&nbsp;filterContext) </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filterContext.Result&nbsp;=&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">RedirectToRouteResult<span style="color: black;">( </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">RouteValueDictionary<span style="color: black;">&nbsp; </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<span style="color: #a31515;">"action"<span style="color: black;">,&nbsp;<span style="color: #a31515;">"Index"<span style="color: black;">&nbsp;}, </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<span style="color: #a31515;">"controller"<span style="color: black;">,&nbsp;<span style="color: #a31515;">"AccessDenied"<span style="color: black;">&nbsp;}, </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;<span style="color: #a31515;">"errorDetail"<span style="color: black;">,&nbsp;<span style="color: blue;">string<span style="color: black;">.Format( </span></span></span></span></span></p>
<p style="background: white;"><span style="color: #a31515; font-family: Consolas; font-size: 8pt;"> "You&nbsp;require&nbsp;the&nbsp;claim&nbsp;'<span style="color: mediumseagreen;">{0}<span style="color: #a31515;">'&nbsp;with&nbsp;value&nbsp;'<span style="color: mediumseagreen;">{1}<span style="color: #a31515;">'"<span style="color: black;">,&nbsp; </span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: blue;">this<span style="color: black;">.ClaimType,&nbsp;<span style="color: blue;">this<span style="color: black;">.ClaimValue) </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> } </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">} </span></p>
<p>&nbsp;</p>
<p>Then, you add this attribute to the methods you want in the controllers:</p>
<p style="background: white;"><span style="color: blue; font-family: Consolas; font-size: 8pt;">public<span style="color: black;">&nbsp;<span style="color: blue;">class<span style="color: black;">&nbsp;<span style="color: #2b91af;">AdminOperationController<span style="color: black;">&nbsp;:&nbsp;<span style="color: #2b91af;">Controller</span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;GET:&nbsp;/AdminOperation/</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;[<span style="color: #2b91af;">AuthorizeClaim<span style="color: black;">( </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClaimType&nbsp;=&nbsp;<span style="color: #a31515;">"https://schemas.microsoft.com/claims/authnmethodsreferences"<span style="color: black;">,&nbsp; </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClaimValue&nbsp;=&nbsp;<span style="color: #a31515;">"https://schemas.microsoft.com/claims/multipleauthn"<span style="color: black;">)] </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public<span style="color: black;">&nbsp;<span style="color: #2b91af;">ActionResult<span style="color: black;">&nbsp;Index() </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return<span style="color: black;">&nbsp;View(); </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">} </span></p>
<p>&nbsp;</p>
<p>When you try this out, you will see the following when attempting to get to the admin operation:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/5381.Blog-AccessDenied-Figure4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/5381.Blog_2D00_AccessDenied_2D00_Figure4.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/5381.Blog-AccessDenied-Figure4.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/5381.Blog_2D00_AccessDenied_2D00_Figure4.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>Nice! So &hellip; we are getting an access denied experience for the admin operations if MFA is not issued by AD FS. Now, how do we go about by proving that when MFA is performed, access is indeed allowed?</p>
<h2>One more way to engage MFA</h2>
<p>In <a href="https://blogs.msdn.com/b/ramical/archive/2014/01/30/under-the-hood-tour-on-multi-factor-authentication-in-ad-fs-part-1-policy.aspx">part 1</a>, I described how we can author policy in the server to engage MFA. Well, that is <em>one </em>way to do it.</p>
<p>In addition to this server driven behavior, we also allow request driven behavior for MFA for WS-Federation (via wauth <a href="https://docs.oasis-open.org/wsfed/federation/v1.2/os/ws-federation-1.2-spec-os.html">request parameter</a>) and SAML 2.0 (via requestedauthncontext <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">request parameter</a>). The value that must be supplied is <span style="font-family: Consolas;"><strong>https://schemas.microsoft.com/claims/multipleauthn</strong></span>.</p>
<p>If you give a try from the browser <span style="font-family: Consolas;"><strong>https://&lt;&lt;farmName&gt;&gt;/adfs/ls?wa=wsiginin1.0&amp;wtrealm=&lt;&lt;rpidentifier&gt;&gt;&amp;wauth= https://schemas.microsoft.com/claims/multipleauthn</strong></span>, you should see that MFA is engaged.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/0676.Blog-MFAEngaged-Figure5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/0676.Blog_2D00_MFAEngaged_2D00_Figure5.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/0676.Blog-MFAEngaged-Figure5.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/0676.Blog_2D00_MFAEngaged_2D00_Figure5.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>As a result, then we can get access to the admin operation</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/2514.Blog-MFAAdminAccess-Figure6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/2514.Blog_2D00_MFAAdminAccess_2D00_Figure6.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/2514.Blog-MFAAdminAccess-Figure6.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/2514.Blog_2D00_MFAAdminAccess_2D00_Figure6.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>Excellent!!. However, this will work for the few users who not only foretells he needs MFA but also happens to know enough of the underlying protocols to know how to craft a request just the right way to get to get his admin task done. It does not sound very user friendly, does it?</p>
<p>Let's make this flow a bit more straightforward.</p>
<h1>Step 3: Smoothen the authentication flow with a Step-up experience</h1>
<p>One way to improve the admittedly subpar flow I described above is to have the user provide the second factor whenever he attempts an admin operation as opposed to show him an access denied page, which is commonly known as step-up authentication.</p>
<p>Since the AuthorizeClaimAttribute is too generic for any claim type/value that may or may not be related to authentication types, we can derive from AuthorizeClaimAttribute and specialize it to add some STS fallback semantics. Assuming that you configured your ASP.NET Application with <a href="https://www.asp.net/visual-studio/overview/2013/creating-web-projects-in-visual-studio">Visual Studio 2013 web project templates</a>, then the STS parameters are discoverable via web.config settings.</p>
<p>The code looks like this:</p>
<p style="background: white;"><span style="color: blue; font-family: Consolas; font-size: 8pt;">public<span style="color: black;">&nbsp;<span style="color: blue;">class<span style="color: black;">&nbsp;<span style="color: #2b91af;">AuthorizeAuthenticationMethodAttribute<span style="color: black;">&nbsp;:&nbsp;<span style="color: #2b91af;">AuthorizeClaimAttribute</span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">protected<span style="color: black;">&nbsp;<span style="color: blue;">override<span style="color: black;">&nbsp;<span style="color: blue;">void<span style="color: black;">&nbsp;HandleUnauthorizedRequest(<span style="color: #2b91af;">AuthorizationContext<span style="color: black;">&nbsp;filterContext) </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//Validate&nbsp;Assumptions&nbsp;on&nbsp;the&nbsp;input&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(<span style="color: blue;">string<span style="color: black;">.IsNullOrEmpty(<span style="color: #2b91af;">ConfigurationManager<span style="color: black;">.AppSettings[<span style="color: #a31515;">"ida:FederationMetadataLocation"<span style="color: black;">])) </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw<span style="color: black;">&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">ConfigurationErrorsException<span style="color: black;">( </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: #a31515;">"The&nbsp;'ida:FederationMetadata'&nbsp;configuration&nbsp;parameter&nbsp;is&nbsp;missing" </span></span></p>
<p style="background: white;"><span style="color: #a31515; font-family: Consolas; font-size: 8pt;"> <span style="color: black;">);</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">Uri<span style="color: black;">&nbsp;federationMetadataLocation&nbsp;=&nbsp;<span style="color: blue;">null<span style="color: black;">; </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(!<span style="color: #2b91af;">Uri<span style="color: black;">.TryCreate( </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: #2b91af;">ConfigurationManager<span style="color: black;">.AppSettings[<span style="color: #a31515;">"ida:FederationMetadataLocation"<span style="color: black;">],&nbsp; </span></span></span></span></span></p>
<p style="background: white;"><span style="color: #2b91af; font-family: Consolas; font-size: 8pt;"> UriKind<span style="color: black;">.Absolute,&nbsp; </span></span></p>
<p style="background: white;"><span style="color: blue; font-family: Consolas; font-size: 8pt;"> out<span style="color: black;">&nbsp;federationMetadataLocation) </span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> ) </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw<span style="color: black;">&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">ConfigurationErrorsException<span style="color: black;">( </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: #a31515; font-family: Consolas; font-size: 8pt;"> "The&nbsp;'ida:FederationMetadataLocation'&nbsp;configuration&nbsp;parameter&nbsp;is&nbsp;not&nbsp;a&nbsp;valid&nbsp;absolute&nbsp;URI" </span></p>
<p style="background: white;"><span style="color: #a31515; font-family: Consolas; font-size: 8pt;"> <span style="color: black;">); </span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">if<span style="color: black;">&nbsp;(<span style="color: blue;">string<span style="color: black;">.IsNullOrEmpty(<span style="color: #2b91af;">ConfigurationManager<span style="color: black;">.AppSettings[<span style="color: #a31515;">"ida:Realm"<span style="color: black;">])) </span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">throw<span style="color: black;">&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">ConfigurationErrorsException<span style="color: black;">( </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;"> <span style="color: #a31515;">"The&nbsp;'ida:Realm'&nbsp;configuration&nbsp;parameter&nbsp;is&nbsp;missing" </span></span></p>
<p style="background: white;"><span style="color: #a31515; font-family: Consolas; font-size: 8pt;"> <span style="color: black;">); </span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//redirect&nbsp;to&nbsp;the&nbsp;STS,&nbsp;requesting&nbsp;MFA,&nbsp;and&nbsp;replying&nbsp;back&nbsp;to&nbsp;the&nbsp;original&nbsp;URL&nbsp;afterwards</span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">string<span style="color: black;">&nbsp;stsUrl&nbsp;=&nbsp;<span style="color: #2b91af;">String<span style="color: black;">.Format( </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #a31515;">"https://<span style="color: mediumseagreen;">{0}<span style="color: #a31515;">/adfs/ls?wa=wsignin1.0&amp;wtrealm=<span style="color: mediumseagreen;">{1}<span style="color: #a31515;">&amp;wauth=<span style="color: mediumseagreen;">{2}<span style="color: #a31515;">&amp;wreply=<span style="color: mediumseagreen;">{3}<span style="color: #a31515;">"<span style="color: black;">, </span></span></span></span></span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;federationMetadataLocation.Host, </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">HttpUtility<span style="color: black;">.UrlEncode(<span style="color: #2b91af;">ConfigurationManager<span style="color: black;">.AppSettings[<span style="color: #a31515;">"ida:Realm"<span style="color: black;">]), </span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">HttpUtility<span style="color: black;">.UrlEncode(<span style="color: #a31515;">"https://schemas.microsoft.com/claims/multipleauthn"<span style="color: black;">), </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #2b91af;">HttpUtility<span style="color: black;">.UrlEncode(filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri)); </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;filterContext.Result&nbsp;=&nbsp;<span style="color: blue;">new<span style="color: black;">&nbsp;<span style="color: #2b91af;">RedirectResult<span style="color: black;">(stsUrl); </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">} </span></p>
<p>&nbsp;</p>
<p>Then, we revisit our privileged operation to use our <span style="background-color: yellow;">new attribute</span> instead:</p>
<p style="background: white;"><span style="color: blue; font-family: Consolas; font-size: 8pt;">public<span style="color: black;">&nbsp;<span style="color: blue;">class<span style="color: black;">&nbsp;<span style="color: #2b91af;">AdminOperationController<span style="color: black;">&nbsp;:&nbsp;<span style="color: #2b91af;">Controller</span></span></span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;</span><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: green;">//&nbsp;GET:&nbsp;/AdminOperation/</span></span></p>
<pre><code><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;[<span style="color: #2b91af;"><span style="background-color: yellow;">AuthorizeAuthenticationMethod</span><span style="color: black;">( </span></span></span></code></pre>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClaimType&nbsp;=&nbsp;<span style="color: #a31515;">"https://schemas.microsoft.com/claims/authnmethodsreferences"<span style="color: black;">,&nbsp; </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClaimValue&nbsp;=&nbsp;<span style="color: #a31515;">"https://schemas.microsoft.com/claims/multipleauthn"<span style="color: black;">)] </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">public<span style="color: black;">&nbsp;<span style="color: #2b91af;">ActionResult<span style="color: black;">&nbsp;Index() </span></span></span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;{ </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: blue;">return<span style="color: black;">&nbsp;View(); </span></span></span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">&nbsp;&nbsp;&nbsp;&nbsp;} </span></p>
<p style="background: white;"><span style="color: black; font-family: Consolas; font-size: 8pt;">} </span></p>
<p>&nbsp;</p>
<p>After you try this out from scratch, you will notice the following flow:</p>
<ol>
<li>User attempts to access to your application</li>
<li>User is redirected to AD FS&nbsp;for authentication</li>
<li>After supplying credentials, the user can access your application. Note that so far, the user has used one factor of authentication</li>
<li>User now attempts to perform an operation that requires multi factor authentication</li>
<li>User is redirected to&nbsp; AD FS for authentication, straight to the second factor challenge. The reason why the first factor is skipped is because the user acquired a single sign on (SSO) cookie that captured a bunch of details, including the fact that a first factor was performed. (The inner details of SSO are a separate topic altogether which deserves a whole post on its own <span style="font-family: Wingdings;">J</span>).</li>
<li>After authenticating with the second factor, the user can now access the operation attempted in step 4.</li>
<li>Subsequent attempts to access other operations that require MFA access will be seamless as the user now has all the data in the claims identity.</li>
</ol>
<h2>Conclusion:</h2>
<ul>
<li>By flowing the <span style="font-family: Consolas;"><strong>authenticationmethodsreferences</strong></span> claim to your relying party, you can use it in your application.</li>
<li>Using the protocol parameters, you can engage MFA from the authentication request.</li>
<li>Using ASP.NET MVC Authorization Filters, you can make your application require MFA to access parts of the application and handle authorization failures by redirecting back to AD FS.</li>
</ul>
<h2>Now, your turn:</h2>
<p>How would you go about decoupling the example above so that:</p>
<ul>
<li>In AD FS, you compute a level of assurance claim based on the authentication type(s).</li>
<li>In the Application code, make the authorization and step-up decisions based upon the level of assurance.</li>
<li>Tweak the level of assurance calculation so that Smart Card Authentication is also considered a strong level of assurance, even if it is the first factor</li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>Under the hood tour on Multi-Factor Authentication in ADFS – Part 1: Policy</title>
      <link>https://docs.microsoft.com/archive/blogs/ramical/under-the-hood-tour-on-multi-factor-authentication-in-adfs-part-1-policy</link>
      <pubDate>Thu, 30 Jan 2014 22:41:00 GMT</pubDate>
      <dc:creator><![CDATA[Ramiro Calderon - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/ramical/2014/01/30/under-the-hood-tour-on-multi-factor-authentication-in-adfs-part-1-policy/</guid>
      <description><![CDATA[One of the new features we introduced in AD FS in Windows Server 2012 R2 is Multi-Factor...]]></description>
      <content:encoded><![CDATA[<p>One of the new features we introduced in <a href="https://technet.microsoft.com/en-us/library/hh831502.aspx">AD FS</a> in Windows Server 2012 R2 is <a href="https://technet.microsoft.com/en-us/library/dn280937.aspx">Multi-Factor Authentication (MFA)</a> for WS-Federation, SAML-P and OAuth protocols.</p>
<p>In this post, I want to talk about some of the ways in which you can configure AD FS to implement several MFA policies to accomplish different authentication requirements.</p>
<p>Before authoring the policies, you need to <a href="https://technet.microsoft.com/en-us/library/dn280936.aspx">prepare your AD FS farm for MFA</a> by registering and enabling at least one MFA authentication provider (such as <a href="https://www.windowsazure.com/en-us/services/multi-factor-authentication/?WT.mc_id=azurebg_us_sem_bing_br_mfa_solutions_mfa">Windows Azure Multi-Factor Authentication</a> or the built-in X509 Client Cert authentication). Once you configure your farm, then you are ready for the next step and decide how to engage MFA in your applications.</p>
<p>Engaging MFA for a relying party is very similar to way you configure its authorization requirements: using the <a href="https://technet.microsoft.com/en-us/library/ee913582(v=ws.10).aspx">claims engine</a> and the <a href="https://technet.microsoft.com/en-us/library/ee913585(v=ws.10).aspx">claims pipeline</a>.</p>
<ul>
<li>The claims pipeline now includes a new stage before issuing the token to determine whether or not MFA should be engaged.</li>
<li>There is a new claim rule set property called 'AdditionalAuthenticationRules' in both the <a href="https://technet.microsoft.com/en-us/library/dn479377.aspx">ADFS global properties</a> as well as the <a href="https://technet.microsoft.com/en-us/library/dn479407.aspx">Relying Party trust object</a>.</li>
</ul>
<p>The way all the pieces above work together is depicted in the following diagram:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/2744.Blog-MFAClaimsFlow.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/2744.Blog_2D00_MFAClaimsFlow.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/2744.Blog-MFAClaimsFlow.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/2744.Blog_2D00_MFAClaimsFlow.png" alt="" border="0" /></a></p>
<p>The sequence goes as follows:</p>
<ol>
<li>The authentication request comes from the browser after a POST action, containing the user credentials (either fresh or SSO), information about the device ( "Device Context" in the diagram), and the request ( "Request Context" in the diagram)</li>
<li>The first factor authentication layer (Box A above) validates the identity of the user and the device. The first stage of authentication is completed, and the user attributes, user credentials, device, and request information are converted to claims.</li>
<li>The claim acceptance rules of the "Active Directory" claims provider trust (box B) is executed. The output set of this stage is used in subsequent stages (Issuance, Authorization, and &hellip; MFA <span style="font-family: Wingdings;">J</span>).</li>
<li>Both the Global and Relying Party MFA AdditionalAuthenticationRules claim rule sets are executed. (Box C). If the output claim set of either rule set contains a claim of type "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" and value "https://schemas.microsoft.com/claims/multipleauthn", then MFA will engage.</li>
<li>If MFA is engaged, AD FS will call the logic of the MFA provider (box D) to render the web pages to challenge the user, who in turn POSTS the response back. The system also augments the claim set from step 2 above, indicating that MFA was performed (the MFA provider could potentially add more claims to the claim set depending upon its implementation).</li>
<li>The Authorization (Box E) and Issuance Transform Rules (Box F) are executed the same way as previous versions of AD FS to produce a permit/deny decision and RP claims respectively.</li>
</ol>
<p>Now, everything boils down to what claims can we use and what the language allows us to do. While the <a href="https://technet.microsoft.com/en-us/library/dn280937.aspx">full list of available claims</a> is quite large, I would like to focus on the claims that can give us the most interesting MFA pivots:</p>
<ul>
<li>
<div><strong>https://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid: </strong></div>
<p>You can use this claim to find out if the user belongs to a specific group.&nbsp;</p>
</li>
<li>
<div><strong>https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser: </strong></div>
<p>When the value of this claim is true, it means that the user who authenticated is the one who originally registered the device.</p>
<p>When the value of this claim is false, it means that the user who authenticated is NOT the one who originally registered the device, which could be OK in the case of shared devices.</p>
<p>If you only care about the device itself being registered, you might want to use the EXISTS operator against this claim</p>
</li>
<li>
<div><strong>https://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path: </strong></div>
<p>Since MFA is supported only for browser applications, you can use this claim to tell apart browser from non-browser requests, in case you have both kinds of protocols in the same relying party trust, which is the typical case when issuing tokens to a federation provider STS.</p>
</li>
<li>
<div><strong>https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork: </strong></div>
<p>When the value is false, it means the request came through a web application proxy. When true, it means the request came directly to the STS.</p>
</li>
<li>
<div><strong>https://schemas.microsoft.com/2012/12/certificatecontext/* (multiple claim types): </strong></div>
<p>Claims that represent different fields and extensions of the X509 client certificate when used as an authentication method.</p>
<p>One interesting use case here is to use the EKU claim (https://schemas.microsoft.com/2012/12/certificatecontext/extension/eku) to ascertain whether the user used a smart card (the exact EKU depends upon the PKI infrastructure of the customer).</p>
</li>
<li>
<div><strong>https://schemas.microsoft.com/claims/authnmethodsreferences: </strong></div>
<p>Used to indicate all authentication methods used to authenticate the user.</p>
<p>When the MFA rules are executed, this claim will contain the authentication method performed in the first stage.</p>
<p>When the Authorization or Issuance Transform rule set is executed, this claim will contain both the first and second factor authentication methods, if the MFA occurred (step 5 above).<strong> </strong></p>
</li>
</ul>
<p>Knowing that, we can now leverage the whole power of the AD FS <a href="https://blogs.technet.com/b/askds/archive/2011/10/07/ad-fs-2-0-claims-rule-language-primer.aspx">Claims Language</a> and Pipeline to go over some MFA Policy examples.</p>
<p>Let's start with the three options we streamlined in the AD FS Management Snap-in: Users/Groups, Devices, and Locations. I pasted the MMC UI for reference:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/5661.Blog-MFAUI.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/5661.Blog_2D00_MFAUI.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/63/31/5661.Blog-MFAUI.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-63-31/5661.Blog_2D00_MFAUI.png" alt="" border="0" /></a></p>
<h3>Scenario 1: MFA for certain groups or users</h3>
<p>Per the table above, we use the Group SID claim:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid", Value == "&lt;&lt;Group SID&gt;&gt;"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<p>Note that the issuance condition (right side of the =&gt; operator). That will always be the same whenever you want to tell AD FS to perform MFA.</p>
<h3>Scenario 2: MFA for unregistered (non <a href="https://technet.microsoft.com/en-us/library/dn280945.aspx"><span style="color: #0563c1; text-decoration: underline;">workplace joined</span></a>) devices</h3>
<p>We can do this in two different ways. One is to require MFA if the user who registered the device is NOT the one who is authenticated using the "isregistereduser" claim (this is the one that the MMC snap-in uses):</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<p>Alternatively, if you don't need to discriminate between the user who registered the device from the one who is authenticated (for example, shared devices), then you can use the EXISTS operator and check for any of the claims associated with the workplace joined state. I personally use the registrationid claim:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">NOT EXISTS([Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser"]) </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<h3>Scenario 3: MFA for users coming from outside the corporate network</h3>
<p>In this case, we use the "insidecorporatenetworkclaim" the same way we used a Boolean claim in the example above</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<p style="margin-left: 36pt;">&nbsp;</p>
<p>As I mentioned above, the three scenarios above are the ones exposed in the AD FS management UI.</p>
<p>Now, let's take a look at a few more that require using the claims language directly:</p>
<h4><strong>Scenario 4: Tweak the MMC scenarios to be AND instead of OR </strong></h4>
<p>Each check box in the management UI corresponds to one rule, which means a logical OR operation when they are put together. For example, when you check both "Unregistered devices" and "Extranet", the rule set will generate two rules, that will look like this:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<p>&nbsp;</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<p>For scenarios that require a logical AND operation between the conditions above me, you should combine them in a single rule and use the &hellip; AND &hellip; operator. If you want to enable MFA only when coming from extranet with a device that is not joined to the workplace, then the rule might look like this:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &amp;&amp; </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">[Type == "https://schemas.microsoft.com/2012/01/devicecontext/claims/isregistereduser", Value == "false"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<h3>Scenario 5: MFA and Office 365/Azure Active Directory</h3>
<p>Given that MFA is plugged into the authentication pipeline for browser applications, if the MFA claim rules generate the claim that will engage MFA over WS-Trust will cause the request to fail with the following message in the ADFS Admin event log channel, with event ID 325. As you can see, this looks pretty much like an access denied failure.</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">The Federation Service could not authorize token issuance for caller 'DOMAIN\User' </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">The caller is not authorized to request a token for the relying party 'urn:dumptoken'. See event 501 with the same Instance ID for caller identity. </span></p>
<p>This becomes a problem for federation provider scenarios, such as Azure Active Directory and Office 365. In this case, there is a single RP trust to represent both rich clients that use WS-Trust (such as Lync and Outlook), as well as browser based applications (like Sharepoint Online and OWA). In that case, we have to use the claim "x-ms-endpoint-absolute-path" which contains the URL through which the token request came in to AD FS in order to derive the protocol of the request as follows:</p>
<ul>
<li>If the URL contains /adfs/ls, then the protocol is either WS-Federation or SAML Protocol. Both of these support MFA.</li>
<li>If the URL contains /adfs/oauth2, then protocol is OAuth, which also supports MFA. (I added this for the sake of completeness of determining the protocol based on the URL of the request. Office 365 does not use OAuth)</li>
</ul>
<p>Using the regex operations of the claims language, the condition above looks like this:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">[Type == "https://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value =~ "(/adfs/ls)|(/adfs/oauth2)"] </span></p>
<p>You can use this condition with any of the rules we discussed above to craft a more complex policy, using the AND operator. For example, if you want to require MFA for office 365 web applications when coming from extranet, then the rule is:</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c:[Type == "https://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", Value == "false"] &amp;&amp; </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">c1:[Type == "https://schemas.microsoft.com/2012/01/requestcontext/claims/x-ms-endpoint-absolute-path", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value =~ "(/adfs/ls)|(/adfs/oauth2)"] </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "https://schemas.microsoft.com/claims/multipleauthn"); </span></p>
<h3>Scenario 6: Deny authorization to the relying party if MFA was not performed</h3>
<p>This one is a bit different from the previous examples because the rule to accomplish this will be in the authorization rule set, instead of the MFA rule set. As I mentioned above, the claim https://schemas.microsoft.com/claims/authnmethodsreferences contains the list of authentications methods performed. It will hold the value https://schemas.microsoft.com/claims/multipleauthn if MFA was performed.</p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">NOT EXISTS([Type == "https://schemas.microsoft.com/claims/authnmethodsreferences", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value == "https://schemas.microsoft.com/claims/multipleauthn"])</span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;"> =&gt; issue(Type = "https://schemas.microsoft.com/authorization/claims/deny", </span></p>
<p><span style="color: #1f4e79; font-family: Consolas; font-size: 10pt;">Value = "DenyUsersWithClaim"); </span></p>
<h3>Conclusion</h3>
<p>As you can see, with the set of claims available and the richness of the claims language, AD FS offers a lot of flexibility when it comes to engage MFA in your browser applications. We only went through a couple of examples, but you can do a lot more!</p>
<p>Stay tuned for more details on MFA on AD FS!.</p>
<h3>Now, your turn:</h3>
<p>How would you author the policy for an application with the following constraints:</p>
<ol>
<li>Allow single factor authentication (as in, no MFA) from workplace joined devices</li>
<li>Skip MFA when using Smart Card if the device is NOT joined to the workplace</li>
<li>Do MFA in any other case</li>
</ol>
<p><em>Hint: you might need the <a href="https://technet.microsoft.com/en-us/library/dd807118.aspx">claim language's</a> add issuance statement</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Hello!</title>
      <link>https://docs.microsoft.com/archive/blogs/ramical/hello</link>
      <pubDate>Thu, 30 Jan 2014 22:09:42 GMT</pubDate>
      <dc:creator><![CDATA[Ramiro Calderon - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/ramical/2014/01/30/hello/</guid>
      <description><![CDATA[Hi there. I am Ramiro Calderon, and I am part of the Active Directory team here in Redmond, WA....]]></description>
      <content:encoded><![CDATA[<p>Hi there. I am Ramiro Calderon, and I am part of the Active Directory team here in Redmond, WA. 
</p><p>What can you expect from this blog? Well, mostly technical stuff about the products we deliver in our team:
</p><ul><li>Deep dives 
</li><li>Scenario discussions 
</li><li>Troubleshooting 
</li><li>Tips and tricks, mostly with .NET code and Windows PowerShell
</li></ul><p>I hope you find my posts useful. 
</p><p>Cheers!</p>]]></content:encoded>
    </item>
  </channel>
</rss>