{"id":3350,"date":"2015-08-26T01:55:07","date_gmt":"2015-08-26T08:55:07","guid":{"rendered":"http:\/\/www.cloudidentity.com\/blog\/?p=3350"},"modified":"2015-08-26T01:59:29","modified_gmt":"2015-08-26T08:59:29","slug":"augmenting-the-set-of-incoming-claims-with-the-openid-connect-and-oauth2-middleware-in-katana-3-x","status":"publish","type":"post","link":"https:\/\/www.cloudidentity.com\/blog\/2015\/08\/26\/augmenting-the-set-of-incoming-claims-with-the-openid-connect-and-oauth2-middleware-in-katana-3-x\/","title":{"rendered":"Augmenting the set of incoming claims with the OpenID Connect and OAuth2 middleware in Katana 3.x"},"content":{"rendered":"<p><a href=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/08\/image18.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px; border: 0px;\" title=\"image\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/08\/image_thumb18.png\" alt=\"image\" width=\"499\" height=\"499\" border=\"0\" \/><\/a><\/p>\n<p>Here there\u2019s another (very) frequently asked question. I have the eerie sensation that I have already blogged about it, but a quick search did not yield any result post-WIF\u2026. so here you go.<br \/>\nSay that I have a web app or a web API secured with Azure AD (or any other provider, really). Say that in my app I maintain attributes about my user, and I would find it handy to have such attributes exposed in form of claims, alongside the ones I receive from the trusted authority at authentication (nee token validation) time. How do I make it happen with Katana 3.x, OWIN\u2019s implementation in ASP.NET4.6?<\/p>\n<h2>OpenId Connect<\/h2>\n<p>Easy. Let\u2019s start with OpenID Connect (OIDC for brevity). The OIDC middleware graciously offer notifications at key stages of the validation pipeline. The last of those, <span style=\"font-family: Consolas;\">SecurityTokenValidated<\/span>, offers you the chance to modify the <span style=\"font-family: Consolas;\">ClaimsIdentity<\/span> obtained from the incoming token. Here there\u2019s an example, where \u201cRetrieveHairLenght\u201d is a hypothetical function that queries by local DB for the desired attribute.<\/p>\n<pre class=\"csharpcode\">SecurityTokenValidated = (context) =&gt;\r\n{\r\n    <span class=\"kwrd\">string<\/span> userID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;\r\n    Claim userHair =<\/pre>\n<pre class=\"csharpcode\">      <span class=\"kwrd\">new<\/span> Claim(<span class=\"str\"><a href=\"http:\/\/mycustomclaims\/hairlenght\">http:\/\/mycustomclaims\/hairlenght<\/a><\/span>,<\/pre>\n<pre class=\"csharpcode\">                RetrieveHairLenght(userID),<\/pre>\n<pre class=\"csharpcode\">                ClaimValueTypes.Double, <span class=\"str\">\"LOCAL AUTHORITY\"<\/span>);\r\n    context.AuthenticationTicket.Identity.AddClaim(userHair);\r\n    <span class=\"kwrd\">return<\/span> Task.FromResult(0);\r\n},\r\n<\/pre>\n<p>Once you have added that to your Notifications property of the options you initialize the OIDC middleware with, you\u2019ll be able to read that claim from anywhere your app &#8211; just like any other \u201cofficial\u201d claim:<\/p>\n<pre class=\"csharpcode\">var userHair = ClaimsPrincipal.Current.FindFirst(<span class=\"str\"><a href=\"http:\/\/mycustomclaims\/hairlenght\">http:\/\/mycustomclaims\/hairlenght<\/a><\/span>);<\/pre>\n<p>&nbsp;<\/p>\n<p>Preeeety neat. Note that this happens at token reception time, right before establishing the session. That means that whatever I\/O you performed for retrieving your extra attributes will be done only once, which is good; it also means that the resulting custom claims will end up in your session cookie\u2026 and if you add too much stuff, the effects might not be good: performance hits, cookie clipping if you exceed the browser limits, and so on. Keep all those considerations in mind as you plan your augmentation strategy.<\/p>\n<h3>Web API<\/h3>\n<p>Now, say that you want to do the same for a web API.<br \/>\nIf you are on ASP.NET 5, good news! You do exactly like the above (module the <span style=\"font-family: Consolas;\">ClaimsPrincipal.Current<\/span> part, topic for another post).<\/p>\n<p>If you are on ASP.NET4.6 and Katana, that is a bit trickier. The web API middleware in Katana3.x does not have a very rich notifications pipeline. However you still have a mechanism for injecting your custom claims, it\u2019s just a bit different. Given that this is a tad more exotic than just filling up a notification<\/p>\n<pre class=\"csharpcode\">app.UseWindowsAzureActiveDirectoryBearerAuthentication(\r\n    <span class=\"kwrd\">new<\/span> WindowsAzureActiveDirectoryBearerAuthenticationOptions\r\n    {\r\n        Audience = ConfigurationManager.AppSettings[<span class=\"str\">\"ida:Audience\"<\/span>],\r\n        Tenant = ConfigurationManager.AppSettings[<span class=\"str\">\"ida:Tenant\"<\/span>],\r\n        Provider = <span class=\"kwrd\">new<\/span> OAuthBearerAuthenticationProvider()\r\n        {\r\n            OnValidateIdentity = async context =&gt;\r\n            {\r\n                context.Ticket.Identity.AddClaim(\r\n                   <span class=\"kwrd\">new<\/span> Claim(http:<span class=\"rem\">\/\/mycustomclaims\/hairlenght, <\/span>\r\n                                   RetrieveHairLenght(userID),                \r\n                                   ClaimValueTypes.Double, \r\n                                   <span class=\"str\">\"LOCAL AUTHORITY\"<\/span>);));\r\n            }\r\n        }\r\n    });<\/pre>\n<p><span style=\"font-family: Consolas;\">OnValidateIdentity<\/span> gives you a last chance of modifying the <span style=\"font-family: Consolas;\">ClaimsIdentity<\/span> before it gets passed to the app, in analogy to what you have seen for OIDC.<\/p>\n<p>Note that in this case there is no cookie to remember the authority-issued claims and your custom attributes \u2013 the nature of the web API is that you\u2019ll get the token at every. single. call.<br \/>\nYou\u2019ll probably be well served by some inmemory caching strategy, so that the call to RetrieveHairLenght does not have to query slow persistent storage all the time.<\/p>\n<p>Short and sweet, especially because it\u2019s 2:00am here <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" style=\"border-style: none;\" src=\"https:\/\/www.cloudidentity.com\/blog\/wp-content\/uploads\/2015\/08\/wlEmoticon-smile2.png\" alt=\"Smile\" \/> have fun with your custom claims!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here there\u2019s another (very) frequently asked question. I have the eerie sensation that I have already blogged about it, but a quick search did not yield any result post-WIF\u2026. so here you go. Say that I have a web app or a web API secured with Azure AD (or any other provider, really)&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":3348,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3350","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/comments?post=3350"}],"version-history":[{"count":2,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3350\/revisions"}],"predecessor-version":[{"id":3352,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/posts\/3350\/revisions\/3352"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media\/3348"}],"wp:attachment":[{"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/media?parent=3350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/categories?post=3350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cloudidentity.com\/blog\/wp-json\/wp\/v2\/tags?post=3350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}