Zulfiqar's weblog

Middleware, security & random .Net

Azure AD OAuth 2.0 Authorization Grant

Posted by zamd on May 17, 2013

Yesterday I talked about a bug which prevented me to complete the authorization grant flow with Azure AD. It turn out the bug is only exposed when using Azure Management Portal for Relying party registration. In this post, I’ll use Graph Explorer to do the registration which works fine.

My scenario is to create a simple MVC application which would do the user authentication against the Azure AD.

Once the user is signed in, the web app then acquires an “access” & “refresh token” for the Graph API (I’ll work with other resources in future) using the 3-leg authorization grant flow.

I started by creating an empty MVC 4.0 application and added a home controller with a simple view displaying the identity & claims of the authenticated user.

image

Running the app gave me the url which I would use to register my app with Azure AD using Graph Explorer.  Registration instruction are available in this blog post under the ‘Setting up permissions’ section. My registration settings looks like this

image

Now back to VS and using the “Identity & Access”, I have externalized the authentication of my app to windows azure AD.

image

The tooling does all the magic and generates required WIF configuration.

<system.identityModel>
<identityConfiguration>
<audienceUris>
<addvalue=http://localhost:45906/ />
</audienceUris>
<issuerNameRegistrytype=System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089>
<trustedIssuers>
<addthumbprint=3464C5BDD2BE7F2B6112E2F08E9C0024E33D9FE0name=https://sts.windows.net/{tenantid}/ />
</trustedIssuers>
</issuerNameRegistry>
<certificateValidationcertificateValidationMode=None/>
</identityConfiguration>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration>
<cookieHandlerrequireSsl=false />
<wsFederationpassiveRedirectEnabled=trueissuer=https://login.windows.net/common/wsfedrealm=http://localhost:45906/reply=http://localhost:45906/requireHttps=false />
</federationConfiguration>
</system.identityModel.services>

Now when I run my application, I’m redirected to azure AD for authentication and after signing in, I’m back in my app with following claims.

image

The Authorize action simply redirects to the authorization endpoint requesting an ‘authorization code’.

Acquire authorization code
publicActionResult Authorize()
{
var @params = newNameValueCollection
{
{“response_type”, “code”},
{“client_id”, “87638e3d-6b56-46b6-946f-8b3b9fa6f04e”},
{“resource”, https://graph.windows.net&#8221;},
{“redirect_uri”, Url.Action(“Authorized”, null, null, “http”)}
};
var query = HttpUtility.ParseQueryString(“”);
query.Add(@params);    return Redirect(Constants.AzureADAuthorizationEndpoint + “?” + query);
}

 

I’m passing Authorized action as the redirect_uri to Azure AD. That’s where AD would send me the ‘authorization code’.

Clicking the ‘Authorize’ link takes me to Azure Ad & after authentication AD redirects my browser back to Authorized action with an authorization code.

http://localhost:45906/Home/Authorized?code=AAAAAAAAcJ3MFRmxlJAB5CwTBrXmI7t8zdfTrUmfPCbGHQCxbWXOxLieUD4r_fah-rnOwQkxS6B_KOSdLhjsf6n5sQghMwu9ynqFD2-qHBfRkxE3DnS8htlHaDqCdG-Wa5MvbRXZeCKUFr-k37n5Mn4T2KCdKx-nkyNheqZJmwmDFWwix_Gi_QJohhUMk-SDyrnV4Jy-tT_gfiKwKRzVi31JmsJV_b8u-5p398GsGfxBlfxuWuNYuUiUOUDdfLQCYbK0urn0HChsndnPuTZJxtJtNH7WdWnUdha108kVctvHW4u8IihV9P10OsM1gT_D67f00SjVIAA

From the Authorized controller action I fire a post request which gives me access and refresh tokens back. Please note, the redirect_uri MUST be same for both authorization code and token request.

Exchange code with tokens
publicasyncTask<ActionResult> Authorized(string code)
{
if (string.IsNullOrEmpty(code))
RedirectToAction(“Index”);    var data = newDictionary<string, string>
{
{“grant_type”, “authorization_code”},
{“client_id”, “87638e3d-6b56-46b6-946f-8b3b9fa6f04e”},
{“redirect_uri”, Url.Action(“Authorized”, null, null, “http”)},
{“client_secret”, “V/7FTVm****************UQi6MkbwhmBqKxz0=”},
{“code”, code}
};

var client = newHttpClient(newWebRequestHandler());
var response = await client.PostAsync(Constants.AzureADTokenEndpoint, newFormUrlEncodedContent(data));
response.EnsureSuccessStatusCode();

var msg = await response.Content.ReadAsStringAsync();
var tokenResponse = JsonConvert.DeserializeObject<TokenResponse>(msg);
return View(tokenResponse);
}

image

The access & refresh tokens are scoped to Graph API in this case. I can now attach this “access token” to my requests to graph API to read/write the directory data. There are few samples on this topic already so I’m not going to cover that in this post.

Source: HomeController.cs

Posted in Azure AD, OAuth 2.0 | Leave a Comment »

Azure AD OAuth 2.0 Client_Credentials Flow

Posted by zamd on May 16, 2013

I was playing with the Authorization code grant type recently added to Azure Active directory however there is bug in the preview implementation which prevents exchange an ‘authorization code’ with an access token.

I can get the authorization code for graph api by using following url in the browser.

https://login.windows.net/69383356-56dd-4e78-a18e-a4ff5450c995/oauth2/authorize?response_type=code&client_id=9f030b74-1ec1-4b6b-8911-f4e6e465ff9d&resource=https://graph.windows.net&api-version=1.0

AAD authenticates me and redirects with an authorization code below.

http://localhost/?code=AAAAAAAAfgMZDe1KgG-yqkz7_upts_gtuQ5RgzoM71bkz3PKJo312flYTAp8PrpaUnl3nMaRF_asmQnEZpMjQSflFRJoFl0nVO5HyGnyRG2haLQAM7kz0wrW3I_D2s5FResTcc2g3hnStrVLWFoAovnw6gjh7Imir3Zistm7ZuJgFkbzMJp8Q1Nm1ZCoqW8W9lDDZtdizd9uI6ALo04OW9jp4bjW9KXshy2x6Nz41N35RzXD2DGiAJP5PqtCB1q54wmRsMauPtKrBxU7Nd3T3cbUV5XJxdNojZWaz-ER-tWYySVNMwLZ0gcRzIp-FMWy3Y7oGsHEpAWrrpmnV3Nx6K2Wb_BIXW-EI6HAQX57HDCFVmhBR8cgAA

At this stage, I should be able to exchange this ‘code’ with an ‘access token’ & a refresh token by issuing following POST request via fiddler.

‘https://login.windows.net/69383356-56dd-4e78-a18e-a4ff5450c995/oauth2/token

User-Agent: Fiddler

Content-Type: application/x-www-form-urlencoded

Host: login.windows.net

Content-Length: 546

grant_type=authorization_code&client_id=9f030b74-1ec1-4b6b-8911-f4e6e465ff9d&redirect_uri=http://localhost/&code=AAAAAAAAU1SjeRieJ2c2QVOayghJbBw-kihXTp7BrG10v73WsqEzFVgsgm3SfYv64_mT9WXFU02vvoVY8J7AxuAfcbJ9fysCW3TI3zixbrwPoz2rZruWJVMT5J7XSI34nxRA13kXIvwf8Sbv7EALsJw-2V56jCFz1sXNwQkkw7UBWX72qvlCP6CuhX0Rqw6LI_4Uqh5dk9D6MLNk02ttTyKnrMkFqV0eN96DhuYZXYD_dF331of8_AULAvkGNGwQzCcqCpws57X8l2rmczkDjvXTAQD2yC6Pb_Hq7eBNNxvFHKTR0D9gquwmPsXVY_jDae2nOa7fI4JdNM92Nto0UFLVoVnIuhxGhhIQzxLz9qb_b-I6fnUgAA&client_secret=V62j8vqo4pRHpCSOK/+jSa1ww=+V62j8vqo4pRHpCSOK/+jSa1ww=

However doing this results in a ‘ACS50000: There was an error issuing a token. ACS70001: Error validating credentials. ACS50012: Invalid client secret is provided’ error. I’ll do a follow up post when this bug is fixed.

My second choice was to use the simple client_credentials (also known as two-leg) flow.

This time I used fiddler to craft a POST request to directly acquire a token from AAD OAuth 2.0 endpoint.

https://login.windows.net/69383356-56dd-4e78-a18e-a4ff5450c995/oauth2/token?api-version=1.0

User-Agent: Fiddler
Content-Type: application/x-www-form-urlencoded
Host: login.windows.net
Content-Length: 178

grant_type=client_credentials&client_id=9f030b74-1ec1-33s3-8911-f4e6e465ff9d&client_secret=V62j8vqo4pRHpCSOK/+jSa1ww=+V62j8vqo4pRHpCSOK/+jSa1ww=&resource=http%3A%2F%2Flocalhost%2F

image

I got 200 OK with JWT token as the payload. I can now attach this token to my REST services where I can process it using the WIF JWTTokenHandler extension as shown below:

  1. static void Main(string[] args)
  2. {
  3.     var handler = new JWTSecurityTokenHandler();
  4.     var token = (JWTSecurityToken)
  5.     handler.ReadToken(
  6.         "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1THdqcHdBSk9NOW4tQSJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0LyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzY5MzgzMzU2LTU2ZGQtNGU3OC1hMThlLWE0ZmY1NDUwYzk5NS8iLCJuYmYiOjEzNjg3Mzk2NTYsImV4cCI6MTM2ODc4Mjg1Niwic3ViIjoiY2MyMjQ1NTQtZDhlZC00YTY0LThmZjUtOTJiZjNiYjEzZDkyIiwiYXBwaWQiOiI5ZjAzMGI3NC0xZWMxLTRiNmItODkxMS1mNGU2ZTQ2NWZmOWQiLCJvaWQiOiJjYzIyNDU1NC1kOGVkLTRhNjQtOGZmNS05MmJmM2JiMTNkOTIiLCJ0aWQiOiI2OTM4MzM1Ni01NmRkLTRlNzgtYTE4ZS1hNGZmNTQ1MGM5OTUiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC82OTM4MzM1Ni01NmRkLTRlNzgtYTE4ZS1hNGZmNTQ1MGM5OTUvIn0.BLtqbzU5pEyn5c6ubQxu2UPzoCd_I9Rokycq4LqThWGkdAy9vL3vqptAHXlKOTK-VFPkarfJ1Jui-GaiGZE_BKLFW0x_cxv4bTx_fAktTsDK51iv9wD8jYuftrUWaaqoonD29SQxRmic_r38LBqJwQIJRO4IfMUeMLmgYQ7B1DQs24D9oSx36pyc7CzX3sZH-nfbNPF4z8wUHrX0zzf7KwWCu5RhK6wmXKbiNKaMIw3VzTq6KsEbqFBV-3IuGFSGadrUfpJG0KZrEc3ZhNJ_gEWuBwhwTKtaVrWQ3_1wyxTtdKG1dPuVZmFxKCIfOJkqsvTFZKD4bECv5DJfvyhzlQ");
  7.     var validationParams = new TokenValidationParameters
  8.                                {
  9.                                    AudienceUriMode = AudienceUriMode.Never,
  10.                                    SigningTokenResolver = new HardcodedCertResolver(),
  11.                                    ValidateIssuer = false
  12.                                };
  13.     var ci = handler.ValidateToken(token,validationParams);
  14.  
  15.     ci.Claims.ToList().ForEach(c =>
  16.                                Console.WriteLine("{0} = {1}", c.Type, c.Value));
  17.  
  18.     Console.WriteLine(token.ValidTo);
  19.     Console.ReadLine();
  20. }

 

Hope that helps.

Posted in Azure AD, OAuth 2.0 | Leave a Comment »

CRM Online Entity Creation

Posted by zamd on April 30, 2013

Recently I started exploring the world of Dynamics and specifically Dynamics CRM. Technically the platform looks fairly simple with a reasonably clean web services API (mostly SOAP) & SAML based message security (remember legacy WS-Trust :) ) using Live ID as the identity provider.

The helper code from the sdk (\sdk\samplecode\cs\helpercode) hides all the complexity but the under the hood following flow happens to interact with Dynamics Online Web Services.

image

Following is small console application I used to create a new lead into CRM Online.

Code Snippet
    var connection =
        CrmConnection.Parse("Url=https://psfd365.crm4.dynamics.com; User ID=zamd@psfd365.onmicrosoft.com; Password=*password*;");
    var organization = new OrganizationService(connection);

    var who = organization.Execute(new WhoAmIRequest()) as WhoAmIResponse;
    Console.WriteLine("{0}@{1}", who.UserId, who.OrganizationId);
    var user =
    organization.Retrieve("systemuser", who.UserId,
                          new ColumnSet(new[] {"firstname", "lastname"})) as SystemUser;

    Console.WriteLine("creating lead…");

    var newLead = new Lead
                       {
                           Subject = "Interested in dyanamics crm…",
                           FirstName = user.FirstName,
                           LastName = user.LastName,
                           MobilePhone = "004412123231212"
                       };

    var leadId = organization.Create(newLead);
    Console.WriteLine("New lead {0} created.",leadId);
}

that’s it – you can see the new lead created below.

image

Posted in Dynamics CRM | Leave a Comment »

WF Security Pack Update

Posted by zamd on March 13, 2013

Quite a few folks have asked me about updating WF Security Pack to .NET 4.5 as WIF is now integrated into .NET 4.5.

Today I manage to spare sometime to upgrade the WFSP to .NET 4.5/WIF 4.5.  I have also pushed the updated source code to github which you can pull down from https://github.com/zamd/wfsp/

Please note github version of the codebase is different from codeplex, which was refactored by a WF team member. The github version of the source code came straight from laptop. I intend to create a Nuget package and potentially a Visual Studio Extension as well. Stay tuned…

Posted in WF4.5, WFSP | Leave a Comment »

Federating Office 365 (Azure Active Directory) with a Custom STS

Posted by zamd on February 8, 2013

Let’s start of with a clarification: As of today, federating Office 365 (Azure AD) with a Custom STS is NOT supported by Microsoft.  Today the only supported STSs are AD FS 2.0, Shibboleth 2, Optimal IDM Federation Services and PingFederate 6.10.

With that cleared, Office 365 STS supports both WS-Federation & SAML protocols for user authentication which means technically any compatible STS can be used as the Identity Provider STS for Office 365 services or other Relying Parties with a trust relationship with Azure Active Directory.

Azure AD supports In-cloud & Federated Identities.

With In-Cloud identities all user information, including the passwords, are stored in the online directory.

With Federating identities, only basic information is stored in online directory (as shadow accounts) and user identities are mastered in on-premise directories. Passwords are never copied to online directory and Azure AD relies on federation for user sign in.

A key prerequisite for Office 365 SSO is to create federated identities (shadow accounts) in Azure AD and there are different options/tools to do this.

  1. DirSync is the recommended tool but it only supports Active Directory as the identity source. DirSync & AD FS 2.0 are the primary tools to enable federation between an on-premises AD and Azure AD.
  2. Graph API is a new RESTful API to manage online directory and looks very promising for creating cloud-only identities. Graph API today doesn’t support creating Federated identities.
  3. MSOL PowerShell cmdlets: These cmdlets use the SOAP based Provisioning Service and are functionally quite rich. They support most of the operations including the creation of federated identities. I have used these cmdlet for my scenairo. Few commerical tools also wrap these cmdlets to perform various Office 365 provisioning operations.
  4. Forfront Identity Manager (FIM) is another potential option which can create Federated accounts from source directories other than AD but I haven’t explored that in detail.

Now once you have the federated identities provisioned (or synced from your on-premises user identity store) in Azure AD, the next step is to establish a trust relationship between Azure AD and your custom STS. This is assuming you have already done the domain verification etc.

I have used Set-MsolDomainAuthentication cmdlet for this.

Set-MsolDomainAuthentication –DomainName bccoss.com –federationBrandName bccoss.com -Authentication Federated  -PassiveLogOnUri $url -SigningCertificate $certData -IssuerUri $uri -ActiveLogOnUri $ecpUrl -LogOffUri $logoutUrl -PreferredAuthenticationProtocol WSFed

image 

At this stage, if I browse to the Microsoft Online Services portal (http://portal.microsoftonline.com/) and choose to login using my federated domain (@bccoss.com) – I got redirected to my custom STS.

In this case, I’m using Thinktecture STS but that doesn’t work out of box with Office 365 / Azure AD so I have to modify the STS to make it compatible with Azure AD. I’ll explain the Office 365 compatibility requirements of an STS in a future post.

I’ll also try to contribute my Thinktecture modification to code back to git at some point.

image image

image image

image image

Posted in Azure AD, Office 365, SSO | 4 Comments »

Custom STS for Sitefinity 5.x

Posted by zamd on February 6, 2013

Sitefinity 5.x introduced claims based security & Single-Sign-On features based on a simple HTTP redirect based token issuance protocol which I’m going to call ‘Sitefinity sign-in protocol’ in my posts. Version 5.x has also standardized on using Simple Web Token (SWT) as the default token format for user authentication and SSO needs.

Sitefinity 5.x comes with a built-in local STS which authenticates users using the standard membership authentication and issue SWT tokens in accordance with Sitefinity sign-in protocol. Sitefinity doesn’t have a hard dependency on this built-in STS rather it relies on it’s sign-in protocol and SWT token format which means we can introduce a custom STS in the mix and Sitefinity would happily work with our Custom STS which obviously has to adhere to Sitfinity sign-in protocol and token format.

This STS based design in Sitefinity 5.x could enable many SSO scenarios, some of which I’m going to explore in future posts. Following are examples of few possibilities:

  • I can create a Custom STS and then have multiple applications (RPs :) ) including Sitefinity 5.x trust this single STS, which would enable the users to single sign-on across all those applications.
  • I can create a multi-protocol STS which can enable user SSO across workloads/products. For example, SSO between Sitefinity & Office 365 or another portals, speaking the SAML protocol.

For now, I’ll show you how to use a custom STS with Sitefinity for user authentication. I have already developed and deployed a Sitefinity compatible STS @ http://sts.pilesoft.com while Sitefinity is running @ http://pilesoft.com.

Step 1: Register custom STS with Sitefinity so that it can trust the token issued by custom STS.

Open the .\App_Data\Sitefinity\Configuration\SecurityConfig.config file and locate the <securityTokenIssuers> element and following line to <securityTokenIssuers> element.

<add key="CD29559E6EDC312272976AC43F7E921C5766D7063DAF6D177F3EEDEB1802FABE" encoding="Hexadecimal" membershipProvider="Default" realm="http://sts.pilesoft.com"/>

Your config should now look like following:

  1. <securityTokenIssuers>
  2.   <add key="CD29559E6EDC312272976AC43F7E921C5766D7063DAF6D177F3EEDEB1802FABE" encoding="Hexadecimal" membershipProvider="Default" realm="http://sts.pilesoft.com"/>
  3.       <add key="6C4B865442D166796756C8DA1765584F7DD5EC0DE81B1CF29AC5FCE85AE5331D" encoding="Hexadecimal" membershipProvider="Default" realm="http://localhost" />
  4.   </securityTokenIssuers>

 

In most cases, you need to configure a custom Membership provider as well, which I’m going to talk in a future post.

Step 2: Open the main web.config file and locate the <federatedAuthentication> under the <microsoft.identityModel> section. This is WIF configuration and we need to change the <wsFederation> element to point to our custom STS.

Locate the <wsFederation> element & change the issuer attribute to point to our Custom STS as shown below:

<microsoft.identityModel>
  <service>
    <claimsAuthenticationManager type="Telerik.Sitefinity.Security.Claims.SFClaimsAuthenticationManager, Telerik.Sitefinity" />
    <securityTokenHandlers>
      <add type="Telerik.Sitefinity.Security.Claims.SWT.SWTSecurityTokenHandler, Telerik.Sitefinity" />
    </securityTokenHandlers>
    <audienceUris mode="Never"></audienceUris>
    <federatedAuthentication>
      
    ==>  <wsFederation passiveRedirectEnabled="true"
                       issuer="http://sts.pilesoft.com/issue/sitefinity" realm="http://localhost" requireHttps="false" />
      
      <cookieHandler requireSsl="false" />
    </federatedAuthentication>
    <issuerNameRegistry type="Telerik.Sitefinity.Security.Claims.CustomIssuerNameRegistry, Telerik.Sitefinity">
      <trustedIssuers></trustedIssuers>
    </issuerNameRegistry>
    <issuerTokenResolver type="Telerik.Sitefinity.Security.Claims.SWT.WrapIssuerTokenResolver, Telerik.Sitefinity" />
  </service>
</microsoft.identityModel>

 

Now if I browse to Sitefinity – I get:

image

When I click on ‘Login to the backend link’, I’m redirected to my Custom STS. The address bar shows the sitefinity sign-in protocol in action.

image

When I sign-in at the STS, it issues a SWT token & redirects me back to the Sitefinity app.

As this STS is trusted by Sitefinity, it happily accepts the incoming SWT token and logs me in.

image

I’ll publish the Custom STS code after removing the IP related bits. Ping me if you desperately needs it :)

Posted in Federation/STS, Sitefinity, SSO | Leave a Comment »

Enabling ‘Import Service Contract’ menu option

Posted by zamd on January 2, 2013

WF 4.5 introduced contact first development using which you can generate messaging activities from your existing WCF contracts. Out of box, this feature is only enabled for ‘WCF Workflow Service Application’ project type and is exposed using the ‘Import Service Contract’ context menu.

image image

This is quite useful feature and is certainly required in other project types as well. For example, a workflow hosted using WorkflowServiceHost in a windows service or a console application. You can easily enable the context menu option for other project types by including an additional GUID in the <ProjectTypeGuids> element in csproj file.

  <ProjectTypeGuids>{32f31d43-81cc-4c15-9de6-3fc5453562b6};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

  • Unload the project in VS and open the csproj file using the xml editor.
  • Locate the <ProjectTypeGuids> element and insert this {349c5851-65df-11da-9384-00065b846f21} as the content of element along with other GUIDs.
  • Make sure to put a semicolon at the end of your newly inserted GUID.
  • Reload the project in VS and you should now see the ‘Import Service Contract’ menu option.

Posted in WF4.5 | Tagged: , , | Leave a Comment »

Service Bus Property Promotion Nuget Package

Posted by zamd on July 18, 2012

I have just published a Nuget package which adds property promotion features to Service Bus WCF programing model.

image

Once you added the package to your project you can use the PromotedProperty attribute to mark your properties as promoted. The package supports promotion from both complex & primitive arguments. In addition to PromotedPropertyAttribute you also need to stick PropertyPromotionBehavior on each method of your service contract.

Following service contract captures the sample usage.

  1. public class Order
  2. {
  3.     public double Amount { get; set; }
  4.     [PromotedProperty]
  5.     public string ShipCity { get; set; }
  6. }
  7.  
  8. [ServiceContract]
  9. public interface IOrderService
  10. {
  11.     [OperationContract(Name = "SubmitFlat", IsOneWay = true)]
  12.     [PropertyPromotionBehavior]
  13.     void Submit(double amount, [PromotedProperty] string shipCity);
  14.  
  15.     [OperationContract(IsOneWay = true)]
  16.     [PropertyPromotionBehavior]
  17.     void Submit(Order order);
  18. }

Enjoy…

Posted in Uncategorized | Leave a Comment »

Service Bus Server Install Experience

Posted by zamd on July 17, 2012

Today I installed Service Bus Server Beta release and the overall install experience was fairly smooth until I reached the New-SBFarm step of the ‘Getting started’ tutorial. The cmdlet just seems to hang for few minutes and failed ultimately – I tried on another machine & got same results. After lot of head–scratching I narrowed down the issue to SQL connectivity. Turns out New-SBFarm create 3 different databases, Farm management DB, Gateway DB & the message container database. The first two DBs are created by the cmdlet itself & it uses the Connection String passed into the cmdlet and just replaces the DB name. The message container DB creation is handled by another cmdlet ‘New-SBMessageContainer’ which uses the FQDN of the database server.

When server is identified using FQDN, SQL client code treats the connection as ‘Remote’ and because I was using a named instance – it tries to resolve the name using SQL Browser service which was by default disabled :(

Hence the cmdlet hanged until connection request timeout – Enabling remote connections on Sql express & starting the SQL Browser service has fixed the issue.

Posted in Uncategorized | Leave a Comment »

Claim-based-security for ASP.NET Web APIs using DotNetOpenAuth

Posted by zamd on May 4, 2012

Source Code

Recently I worked with a customer assisting them in implementing their Web APIs using the new ASP.NET Web API framework. Their API would be public so obviously security came up as the key concern to address. Claims-Based-Security is widely used in SOAP/WS-* world and we have rich APIs available in .NET Framework in the form of WCF, WIF & ADFS 2.0. Even though we now have this cool library to develop Web APIs, the claims-based-security story for REST/HTTP is still catching up. OAuth 2.0 is almost ready, OpenID Connect is catching up quickly however it would still take sometime before we have WIF equivalent libraries for implementing claims-based-security in REST/HTTP world. DotNetOpenAuth seems to be the most prominent open-source library claiming to support OAuth 2.0 so I decided to give it a go to implement the ‘Resource Owner Password Credentials’ authorization grant. Following diagram shows the solution structure for my target scenario.

clip_image002

1. OAuth 2.0 issuer is an ASP.NET MVC application responsible for issuing token based on OAuth 2.0 ‘Password Credentials’ grant type.

2. Web API Host exposes secured Web APIs which can only be accessed by presenting a valid token issued by the trusted issuer

3. Sample thick client which consumes the Web API

I have used the DotNetOpenAuth.Ultimate NuGet package which is just a single assembly implementing quite a few security protocols. From OAuth 2.0 perspective, AuthorizationServer is the main class responsible for processing the token issuance request, producing and returning a token for valid & authenticated request. The token issuance action of my OAuthIssuerController looks like this:

OAuth 2.0 Issuer

public class OAuthIssuerController : Controller {
    public ActionResult Index()
    {
        var configuration = new IssuerConfiguration {
            EncryptionCertificate = new X509Certificate2(Server.MapPath("~/Certs/localhost.cer")),
            SigningCertificate = new X509Certificate2(Server.MapPath("~/Certs/localhost.pfx"), "a")
        };

        var authorizationServer = new AuthorizationServer(new OAuth2Issuer(configuration));
        var response = authorizationServer.HandleTokenRequest(Request).AsActionResult();

        return response;
    }
}

AuthorizationServer handles all the protocol details and delegate the real token issuance logic to a custom token issuer handler (OAuth2Issuer in following snippet)

Protocol independent issuer
  1. public class OAuth2Issuer : IAuthorizationServer
  2. {
  3.     private readonly IssuerConfiguration _configuration;
  4.     public OAuth2Issuer(IssuerConfiguration configuration)
  5.     {
  6.         if (configuration == null) throw new ArgumentNullException(“configuration”);
  7.         _configuration = configuration;
  8.     }
  9.     public RSACryptoServiceProvider AccessTokenSigningKey
  10.     {
  11.         get
  12.         {
  13.             return (RSACryptoServiceProvider)_configuration.SigningCertificate.PrivateKey;
  14.         }
  15.     }
  16.     public DotNetOpenAuth.Messaging.Bindings.ICryptoKeyStore CryptoKeyStore
  17.     {
  18.         get { throw new NotImplementedException(); }
  19.     }
  20.     public TimeSpan GetAccessTokenLifetime(DotNetOpenAuth.OAuth2.Messages.IAccessTokenRequest accessTokenRequestMessage)
  21.     {
  22.         return _configuration.TokenLifetime;
  23.     }
  24.     public IClientDescription GetClient(string clientIdentifier)
  25.     {
  26.         const string secretPassword = “test1243″;
  27.         return new ClientDescription(secretPassword, new Uri(http://localhost/&#8221;), ClientType.Confidential);
  28.     }
  29.     public RSACryptoServiceProvider GetResourceServerEncryptionKey(DotNetOpenAuth.OAuth2.Messages.IAccessTokenRequest accessTokenRequestMessage)
  30.     {
  31.         return (RSACryptoServiceProvider)_configuration.EncryptionCertificate.PublicKey.Key;
  32.     }
  33.     public bool IsAuthorizationValid(DotNetOpenAuth.OAuth2.ChannelElements.IAuthorizationDescription authorization)
  34.     {
  35.         //claims added to the token
  36.         authorization.Scope.Add(“adminstrator”);
  37.         authorization.Scope.Add(“poweruser”);
  38.         return true;
  39.     }
  40.     public bool IsResourceOwnerCredentialValid(string userName, string password)
  41.     {
  42.         return true;
  43.     }
  44.     public DotNetOpenAuth.Messaging.Bindings.INonceStore VerificationCodeNonceStore
  45.     {
  46.         get
  47.         {
  48.             throw new NotImplementedException();
  49.         }
  50.     }
  51. }

Now with my issuer setup, I can acquire access tokens by POSTing following request to the token issuer endpoint

Client

POST /Issuer HTTP/1.1

Content-Type: application/x-www-form-urlencoded; charset=utf-8

scope=http%3A%2F%2Flocalhost%2F&grant_type=client_credentials&client_id=zamd&client_secret=test1243

 

In response, I get 200 OK with following payload

 

HTTP/1.1 200 OK

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

Pragma: no-cache

Content-Type: application/json; charset=utf-8

Server: Microsoft-IIS/7.5

Content-Length: 685

{“access_token”:”gAAAAC5KksmbH0FyG5snks_xOcROnIcPldpgksi5b8Egk7DmrRhbswiEYCX7RLdb2l0siW8ZWyqTqxOFxBCjthjTfAHrE8owe3hPxur7Wmn2LZciTYfTlKQZW6ujlhEv6N4V1HL4Md5hdtwy51_7RMzGG6MvvNbEU8_3GauIgaF7JcbQJAEAAIAAAABR4tbwLFF57frAdPyZsIeA6ljo_Y01u-2p5KTfJ2xa6ZhtEpzmC46Omcvps9MbFWgyz6536_77jx9nE3sePTSeyB5zyLznkGDKhjfWwx3KjbYnxCVCV-n2pqKtry0l8nkMj4MrjqoTXpvd_P0c_VGfVXCsVt7BYOO68QbD-m7Yz9rHIZn-CQ4po0FqS2elDVe9qwu_uATbAmOXlkWsbnFwa6_ZDHcSr2M-WZxHTVFin7vEWO7FxIQStabu_r4_0Mo_xaFlBKp2hl9Podq8ltx7KvhqFS0Xu8oIJGp1t5lQKoaJSRTgU8N8iEyQfCeU5hvynZVeoVPaXfMA-gyYfMGspLybaw7XaBOuFJ20-BZW0sAFGm_0sqNq7CLm7LibWNw”,”token_type”:”bearer”,”expires_in”:”300″,”scope”:”http:\/\/localhost\/ adminstrator poweruser”}

image

DotNetOpenAuth also has a WebServerClient class which can be used to acquire tokens and I have used in my test application instead of crafting raw HTTP requests. Following code snippet generates the same above request/response

Get Access Token
  1. private static IAuthorizationState GetAccessToken()
  2. {
  3.     var authorizationServer = new AuthorizationServerDescription
  4.     {
  5.         TokenEndpoint = new Uri(http://localhost:1960/Issuer&#8221;),
  6.         ProtocolVersion = ProtocolVersion.V20
  7.     };
  8.     var client = new WebServerClient(authorizationServer, http://localhost/&#8221;);
  9.     client.ClientIdentifier = “zamd”;
  10.     client.ClientSecret = “test1243″;
  11.     var state = client.GetClientAccessToken(new[] { http://localhost/&#8221; });
  12.     return state;
  13. }

Ok Now the 2nd part is to use this access token for authentication & authorization when consuming ASP.NET Web APIs.

Web API Client
  1. static void Main(string[] args)
  2. {
  3.     var state = GetAccessToken();
  4.     Console.WriteLine(“Expires = {0}”, state.AccessTokenExpirationUtc);
  5.     Console.WriteLine(“Token = {0}”, state.AccessToken);
  6.     var httpClient = new OAuthHttpClient(state.AccessToken)
  7.     {
  8.         BaseAddress = new Uri(http://localhost:2150/api/values&#8221;)
  9.     };
  10.     Console.WriteLine(“Calling web api…”);
  11.     Console.WriteLine();
  12.     var response = httpClient.GetAsync(“”).Result;
  13.     if (response.StatusCode==HttpStatusCode.OK)
  14.         Console.WriteLine(response.Content.ReadAsStringAsync().Result);
  15.     else
  16.         Console.WriteLine(response);
  17.     Console.ReadLine();
  18. }

On line 8, I’m creating an instance of a customized HttpClient passing in the access token. The httpClient would use this access token for all subsequent HTTP requests

OAuth enabled HttpClient
  1. public class OAuthHttpClient : HttpClient
  2. {
  3.     public OAuthHttpClient(string accessToken)
  4.         : base(new OAuthTokenHandler(accessToken))
  5.     {
  6.     }
  7.     class OAuthTokenHandler : MessageProcessingHandler
  8.     {
  9.         string _accessToken;
  10.         public OAuthTokenHandler(string accessToken)
  11.             : base(new HttpClientHandler())
  12.         {
  13.             _accessToken = accessToken;
  14.         }
  15.         protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
  16.         {
  17.             request.Headers.Authorization = new AuthenticationHeaderValue(“Bearer”, _accessToken);
  18.             return request;
  19.         }
  20.         protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, System.Threading.CancellationToken cancellationToken)
  21.         {
  22.             return response;
  23.         }
  24.     }
  25. }

Relying Party (ASP.NET Web APIs)

Finally on the RP side, I have used standard MessageHandler extensibility to extract and validate the ‘access token’. The OAuth2 message handler also extracts the claims from the access token and create a ClaimsPrincipal which is passed on the Web API implementation for authorization decisions.

OAuth2 Message Handler
  1. public class OAuth2Handler : DelegatingHandler
  2. {
  3.     private readonly ResourceServerConfiguration _configuration;
  4.     public OAuth2Handler(ResourceServerConfiguration configuration)
  5.     {
  6.         if (configuration == null) throw new ArgumentNullException(“configuration”);
  7.         _configuration = configuration;
  8.     }
  9.     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  10.     {
  11.         HttpContextBase httpContext;
  12.         string userName;
  13.         HashSet<string> scope;
  14.         if (!request.TryGetHttpContext(out httpContext))
  15.             throw new InvalidOperationException(“HttpContext must not be null.”);
  16.         var resourceServer = new ResourceServer(new StandardAccessTokenAnalyzer(
  17.                                                     (RSACryptoServiceProvider)_configuration.IssuerSigningCertificate.PublicKey.Key,
  18.                                                     (RSACryptoServiceProvider)_configuration.EncryptionVerificationCertificate.PrivateKey));
  19.         var error = resourceServer.VerifyAccess(httpContext.Request, out userName, out scope);
  20.         if (error != null)
  21.             return Task<HttpResponseMessage>.Factory.StartNew(error.ToHttpResponseMessage);
  22.         var identity = new ClaimsIdentity(scope.Select(s => new Claim(s, s)));
  23.         if (!string.IsNullOrEmpty(userName))
  24.             identity.Claims.Add(new Claim(ClaimTypes.Name, userName));
  25.         httpContext.User = ClaimsPrincipal.CreateFromIdentity(identity);
  26.         Thread.CurrentPrincipal = httpContext.User;
  27.         return base.SendAsync(request, cancellationToken);
  28.     }
  29. }

Inside my Web API, I access the claims information using the standard IClaimsIdentity abstraction.

Accessing claims information
  1. public IEnumerable<string> Get()
  2. {
  3.     if (User.Identity.IsAuthenticated && User.Identity is IClaimsIdentity)
  4.         return ((IClaimsIdentity) User.Identity).Claims.Select(c => c.Value);
  5.     return new string[] { “value1″, “value2″ };
  6. }

Fiddler Testing

Once I got the “access token”, I can test few scenarios in fiddler by attaching and tweaking the token when calling my web api.

401 without an “access token”

image

200 OK with a Valid token

image

401 with Expired token

image

401 with Tempered token

image

Source code attached. Please feel free to download and use.

Posted in ASP.NET Web APIs | Tagged: | 14 Comments »

 
Follow

Get every new post delivered to your Inbox.