claim-based-security for ASP. NET Web APIs using Dotnetopenauth

Source: Internet
Author: User
Tags oauth

Recently I worked with a customer assisting them in implementing their Web APIs using the new ASP. Web API Framework . Their API would is public so obviously security came up as the key concern to address. Claims-based-security was widely used in soap/ws-* world and we had rich APIs available in. NET Framework in the form of W CF, WIF & ADFS 2.0. Even though we now have this cool library to develop Web APIs, the claims-based-security stories for Rest/http are Still catching up. OAuth 2.0 is almost ready, OpenID Connect are catching up quickly however it would still take sometime before we have WIF E Quivalent libraries for implementing claims-based-security on Rest/http World. dotnetopenauth seems to be the M OST prominent Open-source library claiming to support OAuth 2.0 so I decided to give it a go to implement the  ' Resour Ce Owner Password Credentials ' authorization grant. Following diagram shows the solution structure for my target scenario.

1. OAuth 2.0 issuer is an ASP 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 is accessed by presenting a valid tokens issued by the trusted issu Er

3. Sample thick client which consumes the Web API

I have used the Dotnetopenauth.ultimate NuGet package which be just a single assembly implementing quite a few security PR Otocols. From OAuth 2.0 perspective, Authorizationserver are the main class responsible for processing the token issuance request, p Roducing 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 Issue rconfiguration {            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 h Andler (Oauth2issuer in following snippet)

Protocol Independent Issuer
public class oauth2issuer:iauthorizationserver{private readonly issuerconfiguration _configuration; Public Oauth2issuer (Issuerconfiguration configuration) {if (configuration = null) throw new argumentnullexcept        Ion ("Configuration");    _configuration = Configuration; } public RSACryptoServiceProvider Accesstokensigningkey {get {return (Rsacryptoservicepro Vider) _configuration.        Signingcertificate.privatekey; }} public DotNetOpenAuth.Messaging.Bindings.ICryptoKeyStore Cryptokeystore {get {throw new notimplement Edexception (); }} public TimeSpan getaccesstokenlifetime (DotNetOpenAuth.OAuth2.Messages.IAccessTokenRequest accesstokenrequestmes Sage) {return _configuration.    Tokenlifetime;        } Public iclientdescription Getclient (string clientidentifier) {const string secretpassword = "Test1243″; return new Clientdescription (Secretpassword, New Uri ("http://localhost/"), ClientType.confidential); } Public RSACryptoServiceProvider Getresourceserverencryptionkey ( DotNetOpenAuth.OAuth2.Messages.IAccessTokenRequest accesstokenrequestmessage) {return (RSACryptoServiceProvider ) _configuration.    EncryptionCertificate.PublicKey.Key;         } public bool Isauthorizationvalid (DotNetOpenAuth.OAuth2.ChannelElements.IAuthorizationDescription authorization) { Claims added to the token authorization.        Scope.add ("adminstrator"); Authorization.        Scope.add ("PowerUser");    return true;    } public bool Isresourceownercredentialvalid (string userName, string password) {return true; } public DotNetOpenAuth.Messaging.Bindings.INonceStore Verificationcodenoncestore {get {t        Hrow new NotImplementedException (); }    }}

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 following payload

http/1.1 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_ Xocronicpldpgksi5b8egk7dmrrhbswieycx7rldb2l0siw8zwyqtqxofxbcjthjtfahre8owe3hpxur7wmn2lzcityftlkqzw6ujlhev6n4v1hl4md5hdtwy 51_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 ”}

Dotnetopenauth also has a webserverclient class which can is used to acquire tokens and I had used in my test application Instead of crafting raw HTTP requests. Following code snippet generates the same above Request/response

Get Access Token
private static Iauthorizationstate Getaccesstoken () {    var authorizationserver = new Authorizationserverdescription    {        tokenendpoint = new Uri ("Http://localhost:1960/Issuer"),        protocolversion = Protocolversion.v20    } ;    var client = new Webserverclient (authorizationserver, "http://localhost/");    Client. Clientidentifier = "ZAMD";    Client. Clientsecret = "Test1243″;    var state = client. Getclientaccesstoken (new[] {"http://localhost/"});    return state;}

Ok now the 2nd part was to use this access token for authentication & authorization when consuming ASP.

Web API Client
static void Main (string[] args) {    var state = Getaccesstoken ();    Console.WriteLine ("Expires = {0}", state. ACCESSTOKENEXPIRATIONUTC);    Console.WriteLine ("Token = {0}", state.) Accesstoken);    var httpClient = new Oauthhttpclient (state. Accesstoken)    {        baseaddress = new Uri ("Http://localhost:2150/api/values")    };    Console.WriteLine ("Calling Web API ...");    Console.WriteLine ();    var response = Httpclient.getasync (""). Result;    if (response. Statuscode==httpstatuscode.ok)        Console.WriteLine (response. Content.readasstringasync (). Result);    Else        Console.WriteLine (response);    Console.ReadLine ();}

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
public class oauthhttpclient:httpclient{Public    oauthhttpclient (string accesstoken)        : Base (new Oauthtokenhandler (Accesstoken))    {    }    class Oauthtokenhandler:messageprocessinghandler    {        string _accesstoken;        Public Oauthtokenhandler (String accesstoken)            : Base (New Httpclienthandler ())        {            _accesstoken = Accesstoken;        }        protected override Httprequestmessage ProcessRequest (httprequestmessage request, System.Threading.CancellationToken CancellationToken)        {            request. Headers.authorization = new Authenticationheadervalue ("Bearer", _accesstoken);            return request;        }        protected override Httpresponsemessage ProcessResponse (httpresponsemessage response, System.Threading.CancellationToken CancellationToken)        {            return response;}}    }
Relying party (ASP. NET Web APIs)

Finally on the RP side, I has 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 are passed on The Web API implementation for authorization decisions.

OAuth2 Message Handler
public class oauth2handler:delegatinghandler{private readonly resourceserverconfiguration _configuration; Public Oauth2handler (Resourceserverconfiguration configuration) {if (configuration = null) throw new ARGUMENTN        Ullexception ("Configuration");    _configuration = Configuration; } protected override task

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

Accessing claims information
Public ienumerable<string> Get () {    if (User.Identity.IsAuthenticated && user.identity are iclaimsidentity)        return ((iclaimsidentity) user.identity). Claims.select (c = c.value);    return new string[] {"Value1″," Value2″};}
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"

$ OK with a Valid token

401 with Expired token

401 with tempered Token

Source code attached. Feel free to download and use.

Original Post by zulfiqarahmed on may4th, 2012

here:http://zamd.net/2012/05/04/claim-based-security-for-asp-net-web-apis-using-dotnetopenauth/

claim-based-security for ASP. NET Web APIs using Dotnetopenauth

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.