RSS

Category Archives: Web API ASP.NET

Extending your ASP.NET Web API responses with useful metadata

By: 

If you ever worked with any API, which, in this day of age, you must have, you surely noticed that in most situations the API response isn’t just the result (requested data), but also a set of helpful metadata, like “total Results”, “timestamp”, “status” and so on.

In Web API, by default, you just serialize your models (or DTO) and such information are not present. Let’s build something which will solve this problem and help you decorate your response with hepful information. This would make it very easy for the client to implement paging, auto-loading scenarios, caching (if you return last modified information) and a lot more.

 

What are we going to build

First let’s go through a plan of what we are going to build.

Assume you have some sample repository. In the recent samples, I have always been using a dummy repository from this post, so let’s use it again.

Now, by default, the response will look like this (for a single item):

 

If we request for IQueryable, we obivously get an Array.

Now, what we’d like to have is something like this:

 

So much nicer isn’t it?

Designing the service

So how are we going to accomplish this? Pretty simple:
1. We will implement a DelegatingHandler which will capture all HttpResponseMessages, extract the response object and wrap into our custom generic Metadata. Then it will be flushed to the client.
2. Additionally, we’ll have a CustomQueryableAttribute, which we will use on IQueryableActions, to keep the information about the size (count) of the IQueryable. This way we will be able to provide information about what is the total size of the collection and thus support OData filtering. This way the client can request i.e. $top=3 results, but still have information about the total size of the IQueryable.

Models

As mentioned, we will use a repository from here – it’s really simple and perfect for testing. Let’s have a look at the Metadata instead.

 

 

It is a generic class, which will provide information such as:
– total results (since the data may be filtered)
– returned results (since the data may be filtered)
– results object (which is equal to the default WebAPI response)
– timestamp of the response
– status – to indicate a successful or unsuccessful response

I will leave the constructor empty for now, it will be more clear to what’s happening there, once we go through the handler and filter.

The Data annotations are there for compatibility with DataContractSerializer. Content negotation is supported, but in principle, this functionality is better suited for JSON.NET, but more on that later.

MetadataHandler

MetadataHandler will inherit from a DelegatingHandler, which means it will have access to theHttpResponseMessage before it is flushed to the client and before applying MediaFormatting.

 

 

As mentioned, we need to modify the response, and to do that we need to overrideTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) method and add a ContinueWith to it.

In there, we extract the object from the response and pass it to a private method for altering.

Now, here is a note. If you serialize to JSON, using JSON.NET you can do just that, it can handle that just fine. On the other hand, if you are willing to use DataContractSerializer, it will not be able to serialize object types properly. You would need known types, so you’d need either reflection or typecasting:

 

Anyway, going back to our example, the ProcessObject method:

 

In this case, we instantiate a new Metadata and set it as the content of the response. As you see, I arbitrairly set the formatter to JSON.NET. The commented out code preservers content negotiation, but if you use that you’d need to type cast the objects as mentioned before.

We also used a simple helper method to check if the response is even worth processing:

 

Let’s now revisit the constructor we omitted earlier.

 

 

The constructor takes the HttpResponseMessage and builds up useful information based on it – i.e. setting the “sucess” or “error”, and also calculating the number of returned results.

Note, if single item is requested (via regular Get(int id) type of action, not OData) it will show 1 total result, because that’s how many matches there were. Total results is greater than returned results only if you filter data with OData.

Adding support for OData and IQueryable

All of this wouldn’t be very useful if we didn’t add our key functionality, which is “total results”. To do that, we need to introduce a new attribute filter, CustomQueryableAttribute, inheriting fromQueryableAttribute.

We’d then decorate all IQueryable actions with it.

 

We override the method IQueryable ApplyResultLimit(HttpActionExecutedContext actionExecutedContext, IQueryable query), which gives us access to IQueryable prior to applying the filtering. What it means, is that we can easily save aside the total number of results of theIQueryable and then use it later in our Metadata object.

We use a bit of a hack here, since I save that in a custom header field “originalSize”. In the MessageHandler later on, we will read that value from the headers and remove it so that they don’t get sent to the client – so it only saves the purpose of transporting a variable from the ActionFilter to the MessageHandler. If you want to pass data between two ActionFilters you could useControllerContext.RouteData.Values, but for our scenario I couldn’t find a better way.

Now we need to update the MessageHandler, to make it aware of the “originalSize”:

 

So we read the header value and get rid of the redundant header key.

Registering handler, decorating methods

The final thing to do is to register the handler, so in App_Start we add:

 

For testing, I get rid of the XmlFormatter as well.

We also add our custom queryable filter to all Action’s returning IQueryable:

 

Running the application

Everything is ready, so let’s roll.

First let’s get all Urls:

Let’s test OData:

Now let’s get a single Url:

Now, just to show that this doesn’t really on any strong types, let’s add a new repository with Blogobjects.
 

Summary & source code

Hopefully someone will find the functionality described here useful. It can be obviously extended further, and optimized (perhaps someone can figure out a better way to handleDataContractSerializer).

 

Copy from: http://www.strathweb.com/2012/06/extending-your-asp-net-web-api-responses-with-useful-metadata/

Source: https://github.com/filipw/Metadata.WebApi

 
Leave a comment

Posted by on July 17, 2014 in C#, Web API ASP.NET

 

OWIN OAuth 2.0 Authorization Server

By Rick AndersonHongye Sun and Praburaj Thiagarajan|March 20, 2014

This tutorial will guide you on how to implement an OAuth 2.0 Authorization Server using OWIN OAuth middleware. This is an advanced tutorial that only outlines the steps to create an OWIN OAuth 2.0 Authorization Server. This is not a step by step tutorial. Download the sample code.

Note: This outline should not be intended to be used for creating a secure production app. This tutorial is intended to provide only an outline on how to implement an OAuth 2.0 Authorization Server using OWIN OAuth middleware.

Software versions

Shown in the tutorial Also works with
Windows 8.1 Windows 8, Windows 7
Visual Studio 2013 Visual Studio 2013 Express for Desktop.

Visual Studio 2012 with the latest update should work, but the tutorial has not been tested with it, and some menu selections and dialog boxes are different.

.NET 4.5

Questions and Comments

If you have questions that are not directly related to the tutorial, you can post them athttp://katanaproject.codeplex.com/discussions. For questions and comments regarding the tutorial itself, see the comments section at the bottom of the page.

The  OAuth 2.0 framework enables a third-party app to obtain limited access to an HTTP service. Instead of using the resource owner’s credentials to access a protected resource, the client obtains an access token (which is a string denoting a specific scope, lifetime, and other access attributes). Access tokens are issued to third-party clients by an authorization server with the approval of the resource owner.

This tutorial will cover:

  • How to create an authorization server to support 4 Authorization Grants and refresh tokens.
  • Authorization code grant:
    • Implicit Grant
    • Resource Owner Password Credentials Grant
    • Client Credentials Grant
  • Creating a resource server which is protected by an access token.
  • Creating OAuth 2.0 clients.

Prerequisites

Create an Authorization Server

In this tutorial, we will roughly sketch out how to use OWIN and ASP.NET MVC to create an authorization server. We hope to soon provide a download for the completed sample, as this tutorial does not include each step. First, create an empty web app named AuthorizationServer and install the following packages:

  • Microsoft.AspNet.Mvc
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.OAuth
  • Microsoft.Owin.Security.Cookies
  • Microsoft.Owin.Security.Google (Or any other social login package such as Microsoft.Owin.Security.Facebook)

Add an OWIN Startup class under the project root folder named Startup.

usingMicrosoft.Owin;usingOwin;[assembly:OwinStartup(typeof(AuthorizationServer.Startup))]namespaceAuthorizationServer{publicpartialclassStartup{publicvoidConfiguration(IAppBuilder app){ConfigureAuth(app);}}}

Create an App_Start folder. Select the App_Start folder and use Shift+Alt+A to add the downloaded version of theAuthorizationServer\App_Start\Startup.Auth.cs file.

publicvoidConfigureAuth(IAppBuilder app){// Enable the Application Sign In Cookie.
     app.UseCookieAuthentication(newCookieAuthenticationOptions{AuthenticationType="Application",AuthenticationMode=AuthenticationMode.Passive,LoginPath=newPathString(Paths.LoginPath),LogoutPath=newPathString(Paths.LogoutPath),});// Enable the External Sign In Cookie.
     app.SetDefaultSignInAsAuthenticationType("External");
     app.UseCookieAuthentication(newCookieAuthenticationOptions{AuthenticationType="External",AuthenticationMode=AuthenticationMode.Passive,CookieName=CookieAuthenticationDefaults.CookiePrefix+"External",ExpireTimeSpan=TimeSpan.FromMinutes(5),});// Enable Google authentication.
     app.UseGoogleAuthentication();// Setup Authorization Server
     app.UseOAuthAuthorizationServer(newOAuthAuthorizationServerOptions{AuthorizeEndpointPath=newPathString(Paths.AuthorizePath),TokenEndpointPath=newPathString(Paths.TokenPath),ApplicationCanDisplayErrors=true,#if DEBUGAllowInsecureHttp=true,#endif// Authorization server provider which controls the lifecycle of Authorization ServerProvider=newOAuthAuthorizationServerProvider{OnValidateClientRedirectUri=ValidateClientRedirectUri,OnValidateClientAuthentication=ValidateClientAuthentication,OnGrantResourceOwnerCredentials=GrantResourceOwnerCredentials,OnGrantClientCredentials=GrantClientCredetails},// Authorization code provider which creates and receives the authorization code.AuthorizationCodeProvider=newAuthenticationTokenProvider{OnCreate=CreateAuthenticationCode,OnReceive=ReceiveAuthenticationCode,},// Refresh token provider which creates and receives refresh token.RefreshTokenProvider=newAuthenticationTokenProvider{OnCreate=CreateRefreshToken,OnReceive=ReceiveRefreshToken,}});}

The code above enables application/external sign in cookies and Google authentication, which are used by authorization server itself to manage accounts.

The UseOAuthAuthorizationServer extension method is to setup the authorization server. The setup options are:

  • AuthorizeEndpointPath: The request path where client applications will redirect the user-agent in order to obtain the users consent to issue a token or code. It must begin with a leading slash, for example,  “/Authorize“.
  • TokenEndpointPath: The request path client applications directly communicate to obtain the access token. It must begin with a leading slash, like “/Token”. If the client is issued a client_secret, it must be provided to this endpoint.
  • ApplicationCanDisplayErrors: Set to true if the web application wants to generate a custom error page for the client validation errors on /Authorize endpoint. This is only needed for cases where the browser is not redirected back to the client application, for example, when the client_id or redirect_uri are incorrect. The /Authorize endpoint should expect to see the “oauth.Error”, “oauth.ErrorDescription”, and “oauth.ErrorUri” properties are added to the OWIN environment.
    Note: If not true, the authorization server will return a default error page with the error details.
  • AllowInsecureHttp: True to allow authorize and token requests to arrive on HTTP URI addresses, and to allow incoming redirect_uri authorize request parameters to have HTTP URI addresses.
    Security Note: This is for development only.
  • Provider: The object provided by the application to process events raised by the Authorization Server middleware. The application may implement the interface fully, or it may create an instance ofOAuthAuthorizationServerProvider and assign delegates necessary for the OAuth flows this server supports.
  • AuthorizationCodeProvider: Produces a single-use authorization code to return to the client application. For the OAuth server to be secure the application MUST provide an instance forAuthorizationCodeProvider where the token produced by the OnCreate/OnCreateAsync event is considered valid for only one call to OnReceive/OnReceiveAsync.
  • RefreshTokenProvider: Produces a refresh token which may be used to produce a new access token when needed. If not provided the authorization server will not return refresh tokens from the /Token endpoint.

Account Management

 OAuth doesn’t care where or how you manage your user account information. It’s ASP.NET Identity which is responsible for it. In this tutorial, we will simplify the account management code and just make sure that user can login using OWIN cookie middleware. Here is the simplified sample code for the AccountController:

publicclassAccountController:Controller{publicActionResultLogin(){var authentication =HttpContext.GetOwinContext().Authentication;if(Request.HttpMethod=="POST"){var isPersistent =!string.IsNullOrEmpty(Request.Form.Get("isPersistent"));if(!string.IsNullOrEmpty(Request.Form.Get("submit.Signin"))){
                authentication.SignIn(newAuthenticationProperties{IsPersistent= isPersistent },newClaimsIdentity(new[]{newClaim(ClaimsIdentity.DefaultNameClaimType,Request.Form["username"])},"Application"));}}returnView();}publicActionResultLogout(){returnView();}publicActionResultExternal(){var authentication =HttpContext.GetOwinContext().Authentication;if(Request.HttpMethod=="POST"){foreach(var key inRequest.Form.AllKeys){if(key.StartsWith("submit.External.")&&!string.IsNullOrEmpty(Request.Form.Get(key))){var authType = key.Substring("submit.External.".Length);
                    authentication.Challenge(authType);returnnewHttpUnauthorizedResult();}}}var identity = authentication.AuthenticateAsync("External").Result.Identity;if(identity !=null){
            authentication.SignOut("External");
            authentication.SignIn(newAuthenticationProperties{IsPersistent=true},newClaimsIdentity(identity.Claims,"Application", identity.NameClaimType, identity.RoleClaimType));returnRedirect(Request.QueryString["ReturnUrl"]);}returnView();}}
privateTaskValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context){if(context.ClientId==Clients.Client1.Id){
        context.Validated(Clients.Client1.RedirectUrl);}elseif(context.ClientId==Clients.Client2.Id){
        context.Validated(Clients.Client2.RedirectUrl);}returnTask.FromResult(0);}privateTaskValidateClientAuthentication(OAuthValidateClientAuthenticationContext context){string clientId;string clientSecret;if(context.TryGetBasicCredentials(out clientId,out clientSecret)||
        context.TryGetFormCredentials(out clientId,out clientSecret)){if(clientId ==Clients.Client1.Id&& clientSecret ==Clients.Client1.Secret){
            context.Validated();}elseif(clientId ==Clients.Client2.Id&& clientSecret ==Clients.Client2.Secret){
            context.Validated();}}returnTask.FromResult(0);}

ValidateClientRedirectUri is used to validate the client with its registered redirect URL.ValidateClientAuthentication checks the basic scheme header and form body to get the client’s credentials.

The login page is shown below:

Review the IETF’s OAuth 2 Authorization Code Grant section now.  Provider (in the table below) isOAuthAuthorizationServerOptions.Provider, which is of type OAuthAuthorizationServerProvider, which contains all OAuth server events.

Flow steps from Authorization Code Grant section Sample download performs these steps with:
(A) The client initiates the flow by directing the resource owner’s user-agent to the authorization endpoint. The client includes its client identifier, requested scope, local state, and a redirection URI to which the authorization server will send the user-agent back once access is granted (or denied). Provider.MatchEndpoint
Provider.ValidateClientRedirectUri
Provider.ValidateAuthorizeRequest
Provider.AuthorizeEndpoint
(B) The authorization server authenticates the resource owner (via the user-agent) and establishes whether the resource owner grants or denies the client’s access request. <If user grants access>
Provider.MatchEndpoint
Provider.ValidateClientRedirectUri
Provider.ValidateAuthorizeRequest
Provider.AuthorizeEndpoint
AuthorizationCodeProvider.CreateAsync
(C) Assuming the resource owner grants access, the authorization server redirects the user-agent back to the client using the redirection URI provided earlier (in the request or during client registration). …
  (D) The client requests an access token from the authorization server’s token endpoint by including the authorization code received in the previous step. When making the request, the client authenticates with the authorization server. The client includes the redirection URI used to obtain the authorization code for verification. Provider.MatchEndpoint
Provider.ValidateClientAuthentication
AuthorizationCodeProvider.ReceiveAsync
Provider.ValidateTokenRequest
Provider.GrantAuthorizationCode
Provider.TokenEndpoint
AccessTokenProvider.CreateAsync
RefreshTokenProvider.CreateAsync

A sample implementation for AuthorizationCodeProvider.CreateAsync and ReceiveAsync to control the creation and validation of authorization code is shown below.

privatereadonlyConcurrentDictionary<string,string> _authenticationCodes =newConcurrentDictionary<string,string>(StringComparer.Ordinal);privatevoidCreateAuthenticationCode(AuthenticationTokenCreateContext context){
     context.SetToken(Guid.NewGuid().ToString("n")+Guid.NewGuid().ToString("n"));
     _authenticationCodes[context.Token]= context.SerializeTicket();}privatevoidReceiveAuthenticationCode(AuthenticationTokenReceiveContext context){string value;if(_authenticationCodes.TryRemove(context.Token,out value)){
         context.DeserializeTicket(value);}}

The code above uses an in-memory concurrent dictionary to store the code and identity ticket and restore the identity after receiving the code. In a real application, it would be replaced by a persistent data store.

The authorization endpoint is for the resource owner to grant access to the client. Usually, it needs a user interface to allow the user to click a button and confirm the grant. OWIN OAuth middleware allows application code to handle the authorization endpoint. In our sample app, we use an MVC controller called OAuthController to handle it. Here is the sample implementation:

publicclassOAuthController:Controller{publicActionResultAuthorize(){if(Response.StatusCode!=200){returnView("AuthorizeError");}var authentication =HttpContext.GetOwinContext().Authentication;var ticket = authentication.AuthenticateAsync("Application").Result;var identity = ticket !=null? ticket.Identity:null;if(identity ==null){authentication.Challenge("Application");returnnewHttpUnauthorizedResult();}var scopes =(Request.QueryString.Get("scope")??"").Split(' ');if(Request.HttpMethod=="POST"){if(!string.IsNullOrEmpty(Request.Form.Get("submit.Grant"))){
                 identity =newClaimsIdentity(identity.Claims,"Bearer", identity.NameClaimType, identity.RoleClaimType);foreach(var scope in scopes){
                     identity.AddClaim(newClaim("urn:oauth:scope", scope));}
                 authentication.SignIn(identity);}if(!string.IsNullOrEmpty(Request.Form.Get("submit.Login"))){
                 authentication.SignOut("Application");
                 authentication.Challenge("Application");returnnewHttpUnauthorizedResult();}}returnView();}}

The Authorize action will first check if the user has logged in to the authorization server. If not, the authentication middleware challenges the caller to authenticate using the “Application” cookie and redirects to the login page. (See highlighted code above.) If user has logged in, it will render the Authorize view, as shown below:

If the Grant button is selected, the Authorize action will create a new “Bearer” identity and sign in with it. It will trigger the authorization server to generate a bearer token and send it back to the client with JSON payload.

Implicit Grant

Refer to the IETF’s OAuth 2 Implicit Grant section now.

The Implicit Grant flow shown in Figure 4 is the flow and mapping which the OWIN OAuth middleware follows.

Flow steps from Implicit Grant section Sample download performs these steps with:
(A) The client initiates the flow by directing the resource owner’s user-agent to the authorization endpoint. The client includes its client identifier, requested scope, local state, and a redirection URI to which the authorization server will send the user-agent back once access is granted (or denied). Provider.MatchEndpoint
Provider.ValidateClientRedirectUri
Provider.ValidateAuthorizeRequest
Provider.AuthorizeEndpoint
(B) The authorization server authenticates the resource owner (via the user-agent) and establishes whether the resource owner grants or denies the client’s access request. <If user grants access>
Provider.MatchEndpoint
Provider.ValidateClientRedirectUri
Provider.ValidateAuthorizeRequest
Provider.AuthorizeEndpoint
AuthorizationCodeProvider.CreateAsync
(C) Assuming the resource owner grants access, the authorization server redirects the user-agent back to the client using the redirection URI provided earlier (in the request or during client registration). …
  (D) The client requests an access token from the authorization server’s token endpoint by including the authorization code received in the previous step. When making the request, the client authenticates with the authorization server. The client includes the redirection URI used to obtain the authorization code for verification.

Since we already implemented the authorization endpoint (OAuthController.Authorize action) for authorization code grant, it automatically enables implicit flow as well.

Note: Provider.ValidateClientRedirectUri is used to validate the client ID with its redirection URL, which protects the implicit grant flow from sending the access token to malicious clients (Man-in-the-middle attack).

Resource Owner Password Credentials Grant

Refer to the IETF’s OAuth 2 Resource Owner Password Credentials Grant section now.

The Resource Owner Password Credentials Grant flow shown in Figure 5 is the flow and mapping which the OWIN OAuth middleware follows.

Flow steps from Resource Owner Password Credentials Grant section Sample download performs these steps with:
(A) The resource owner provides the client with its username and password.
(B) The client requests an access token from the authorization server’s token endpoint by including the credentials received from the resource owner. When making the request, the client authenticates with the authorization server. Provider.MatchEndpoint
Provider.ValidateClientAuthentication Provider.ValidateTokenRequest
Provider.GrantResourceOwnerCredentials Provider.TokenEndpoint AccessToken
Provider.CreateAsync
RefreshTokenProvider.CreateAsync
(C) The authorization server authenticates the client and validates the resource owner credentials, and if valid, issues an access token.

Here is the sample implementation for Provider.GrantResourceOwnerCredentials:

privateTaskGrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context){var identity =newClaimsIdentity(newGenericIdentity(
        context.UserName,OAuthDefaults.AuthenticationType), 
        context.Scope.Select(x =>newClaim("urn:oauth:scope", x)));

     context.Validated(identity);returnTask.FromResult(0);}
The code above is intended to explain this section of the tutorial and should not be used in secure or production apps. It does not check the resource owners credentials. It assumes every credential is valid and creates a new identity for it. The new identity will be used to generate the access token and refresh token. Please replace the code with your own secure account management code.

Client Credentials Grant

Refer to the IETF’s OAuth 2 Client Credentials Grant section now.

The Client Credentials Grant flow shown in Figure 6 is the flow and mapping which the OWIN OAuth middleware follows.

Flow steps from Client Credentials Grant  section Sample download performs these steps with:
(A) The client authenticates with the authorization server and requests an access token from the token endpoint. Provider.MatchEndpoint
Provider.ValidateClientAuthentication Provider.ValidateTokenRequest
Provider.GrantClientCredentials
Provider.TokenEndpoint
AccessTokenProvider.CreateAsync
RefreshTokenProvider.CreateAsync
 (B) The authorization server authenticates the client, and if valid, issues an access token.

Here is the sample implementation for Provider.GrantClientCredentials:

privateTaskGrantClientCredetails(OAuthGrantClientCredentialsContext context){var identity =newClaimsIdentity(newGenericIdentity(
        context.ClientId,OAuthDefaults.AuthenticationType), 
        context.Scope.Select(x =>newClaim("urn:oauth:scope", x)));

     context.Validated(identity);returnTask.FromResult(0);}
The code above is intended to explain this section of the tutorial and should not be used in secure or production apps.  Please replace the code with your own secure client management code.

Refresh Token

Refer to the IETF’s OAuth 2 Refresh Token section now.

The Refresh Token  flow shown in Figure 2 is the flow and mapping which the OWIN OAuth middleware follows.

Flow steps from Client Credentials Grant  section Sample download performs these steps with:
(G) The client requests a new access token by authenticating with the authorization server and presenting the refresh token. The client authentication requirements are based on the client type and on the authorization server policies. Provider.MatchEndpoint
Provider.ValidateClientAuthentication RefreshTokenProvider.ReceiveAsync
Provider.ValidateTokenRequest
Provider.GrantRefreshToken
Provider.TokenEndpoint
AccessTokenProvider.CreateAsync
RefreshTokenProvider.CreateAsync
(H) The authorization server authenticates the client and validates the refresh token, and if valid, issues a new access token (and, optionally, a new refresh token).

Here is the sample implementation for Provider.GrantRefreshToken:

publicvoidConfigureAuth(IAppBuilder app){// Code removed for clarity     // Refresh token provider which creates and receives refresh token.RefreshTokenProvider=newAuthenticationTokenProvider{OnCreate=CreateRefreshToken,OnReceive=ReceiveRefreshToken,}});}
privatevoidCreateRefreshToken(AuthenticationTokenCreateContext context){
     context.SetToken(context.SerializeTicket());}privatevoidReceiveRefreshToken(AuthenticationTokenReceiveContext context){
     context.DeserializeTicket(context.Token);}

Create a Resource Server which is protected by Access Token

 Create an empty web app project and install following packages in the project:

  • Microsoft.AspNet.WebApi.Owin
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.OAuth

Create a startup class and configure authentication and Web API. See AuthorizationServer\ResourceServer\Startup.csin the sample download.

[assembly:OwinStartup(typeof(ResourceServer.Startup))]namespaceResourceServer{publicpartialclassStartup{publicvoidConfiguration(IAppBuilder app){ConfigureAuth(app);ConfigureWebApi(app);}}}

See AuthorizationServer\ResourceServer\App_Start\Startup.Auth.cs in the sample download.

usingMicrosoft.Owin.Cors;usingMicrosoft.Owin.Security.OAuth;usingOwin;namespaceResourceServer{publicpartialclassStartup{publicvoidConfigureAuth(IAppBuilder app){
            app.UseCors(CorsOptions.AllowAll);

            app.UseOAuthBearerAuthentication(newOAuthBearerAuthenticationOptions{});}}}

See AuthorizationServer\ResourceServer\App_Start\Startup.WebApi.cs in the sample download.

usingMicrosoft.Owin.Security.OAuth;usingOwin;usingSystem.Web.Http;namespaceResourceServer{publicpartialclassStartup{publicvoidConfigureWebApi(IAppBuilder app){var config =newHttpConfiguration();// Web API configuration and services// Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(newHostAuthenticationFilter(OAuthDefaults.AuthenticationType));// Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name:"DefaultApi",
                routeTemplate:"api/{controller}/{id}",
                defaults:new{ id =RouteParameter.Optional});

            app.UseWebApi(config);}}}
  • UseCors method allows CORS for all domains.
  • UseOAuthBearerAuthentication method enables OAuth bearer token authentication middleware which will receive and validate bearer token from authorization header in the request.
  • Config.SuppressDefaultHostAuthenticaiton suppresses default host authenticated principal from the app, therefore all requests will be anonymous after this call.
  • HostAuthenticationFilter enables authentication just for the specified authentication type. In this case, it’s bearer authentication type.

In order to demonstrate the authenticated identity, we create an ApiController to output current user’s claims.

namespaceResourceServer.Controllers{[Authorize]publicclassMeController:ApiController{// GET api/<controller>publicIEnumerable<object>Get(){var identity =User.IdentityasClaimsIdentity;return identity.Claims.Select(c =>new{Type= c.Type,Value= c.Value});}}}

If the authorization server and  the resource server are not on the same computer, the OAuth middleware will use the different machine keys to encrypt and decrypt bearer access token. In order to share the same private key between both projects, we add the same machinekey setting in both web.config files.

<configuration><appSettings><!-- Keys removed for clarity. --></appSettings><system.web><compilationdebug="true"targetFramework="4.5"/><httpRuntimetargetFramework="4.5"/><machineKeydecryptionKey="Enter decryption Key here"validation="SHA1"validationKey="Enter validation Key here"/></system.web><runtime><!-- Keys removed for clarity. --></runtime></configuration>

Create OAuth 2.0 Clients

We use the DotNetOpenAuth.OAuth2.Client  NuGet package to simplify the client code.

Authorization Code Grant Client

This client is an MVC application. It will trigger an authorization code grant flow to get the access token from backend. It has a single page as shown below:

  • The Authorize button will redirect browser to the authorization server to notify the resource owner to grant access to this client.
  • The Refresh button will get a new access token and refresh token using the current refresh token.
  • The Access Protected Resource API button will call the resource server to get current user’s claims data and show them on the page.

Here is the sample code of the HomeController of the client.

usingConstants;usingDotNetOpenAuth.OAuth2;usingSystem;usingSystem.Net.Http;usingSystem.Web.Mvc;namespaceAuthorizationCodeGrant.Controllers{publicclassHomeController:Controller{privateWebServerClient _webServerClient;publicActionResultIndex(){ViewBag.AccessToken=Request.Form["AccessToken"]??"";ViewBag.RefreshToken=Request.Form["RefreshToken"]??"";ViewBag.Action="";ViewBag.ApiResponse="";InitializeWebServerClient();var accessToken =Request.Form["AccessToken"];if(string.IsNullOrEmpty(accessToken)){var authorizationState = _webServerClient.ProcessUserAuthorization(Request);if(authorizationState !=null){ViewBag.AccessToken= authorizationState.AccessToken;ViewBag.RefreshToken= authorizationState.RefreshToken;ViewBag.Action=Request.Path;}}if(!string.IsNullOrEmpty(Request.Form.Get("submit.Authorize"))){var userAuthorization = _webServerClient.PrepareRequestUserAuthorization(new[]{"bio","notes"});
            userAuthorization.Send(HttpContext);Response.End();}elseif(!string.IsNullOrEmpty(Request.Form.Get("submit.Refresh"))){var state =newAuthorizationState{AccessToken=Request.Form["AccessToken"],RefreshToken=Request.Form["RefreshToken"]};if(_webServerClient.RefreshAuthorization(state)){ViewBag.AccessToken= state.AccessToken;ViewBag.RefreshToken= state.RefreshToken;}}elseif(!string.IsNullOrEmpty(Request.Form.Get("submit.CallApi"))){var resourceServerUri =newUri(Paths.ResourceServerBaseAddress);var client =newHttpClient(_webServerClient.CreateAuthorizingHandler(accessToken));var body = client.GetStringAsync(newUri(resourceServerUri,Paths.MePath)).Result;ViewBag.ApiResponse= body;}returnView();}privatevoidInitializeWebServerClient(){var authorizationServerUri =newUri(Paths.AuthorizationServerBaseAddress);var authorizationServer =newAuthorizationServerDescription{AuthorizationEndpoint=newUri(authorizationServerUri,Paths.AuthorizePath),TokenEndpoint=newUri(authorizationServerUri,Paths.TokenPath)};
         _webServerClient =newWebServerClient(authorizationServer,Clients.Client1.Id,Clients.Client1.Secret);}}}

DotNetOpenAuth requires SSL by default. Since our demo is using HTTP, you need to add following setting in the config file:

<configuration><!-- Markup removed for clarity. --><dotNetOpenAuth><messagingrelaxSslRequirements="true"/></dotNetOpenAuth></configuration>
Security Note: Never disable SSL in a production app. Your login credentials are now being sent in clear-text across the wire. The code above is just for local sample debugging and exploration.

Implicit Grant Client

This client is using JavaScript to:

  1. Open a new window and redirect to the authorize endpoint of the Authorization Server.
  2. Get the access token from URL fragments when it redirects back.

The following image shows this process:

The client should have two pages: one for home page and the other for callback.

Here is the sample JavaScript code found in the Index.cshtml file:

<scripttype="text/javascript">(function($){var authorizeUri ='@(Paths.AuthorizationServerBaseAddress + Paths.AuthorizePath)';var tokenUri ='@(Paths.AuthorizationServerBaseAddress + Paths.TokenPath)';var apiUri ='@(Paths.ResourceServerBaseAddress + Paths.MePath)';var returnUri ='@Paths.ImplicitGrantCallBackPath';

        $('#Authorize').click(function(){var nonce ='my-nonce';var uri = addQueryString(authorizeUri,{'client_id':'7890ab','redirect_uri': returnUri,'state': nonce,'scope':'bio notes','response_type':'token',});

            window.oauth ={};
            window.oauth.signin =function(data){if(data.state !== nonce){return;}

                $('#AccessToken').val(data.access_token);}

            window.open(uri,'Authorize','width=640,height=480');});

        $('#CallApi').click(function(){
            $.ajax(apiUri,{
                beforeSend:function(xhr){
                    xhr.setRequestHeader('Authorization','Bearer '+ $('#AccessToken').val());},
                dataType:'text',
                cache:false,
                success:function(data){
                    console.log(data);
                    $('#output').text(data);}});});function addQueryString(uri, parameters){var delimiter =(uri.indexOf('?')==-1)?'?':'&';for(var parameterName in parameters){var parameterValue = parameters[parameterName];
                uri += delimiter + encodeURIComponent(parameterName)+'='+ encodeURIComponent(parameterValue);
                delimiter ='&';}return uri;}})(jQuery);</script>

Here is the callback handling code in SignIn.cshtml file:

<scripttype="text/javascript">(function($){function getFragment(){if(window.location.hash.indexOf("#")===0){return parseQueryString(window.location.hash.substr(1));}else{return{};}}function parseQueryString(queryString){var data ={},
                pairs, pair, separatorIndex, escapedKey, escapedValue, key, value;if(queryString ===null){return data;}

            pairs = queryString.split("&");for(var i =0; i < pairs.length; i++){
                pair = pairs[i];
                separatorIndex = pair.indexOf("=");if(separatorIndex ===-1){
                    escapedKey = pair;
                    escapedValue =null;}else{
                    escapedKey = pair.substr(0, separatorIndex);
                    escapedValue = pair.substr(separatorIndex +1);}

                key = decodeURIComponent(escapedKey);
                value = decodeURIComponent(escapedValue);

                data[key]= value;}return data;}var fragments = getFragment();if(window.opener && window.opener.oauth && window.opener.oauth.signin){
            window.opener.oauth.signin(fragments);}
        window.close();})(jQuery);</script>
A best practice is to move the JavaScript to an external file and not embed it with the Razor markup. To keep this sample simple, they have been combined.

Resource Owner Password Credentials Grant Client

We uses a console app to demo this client. Here is the code:

classProgram{privatestaticWebServerClient _webServerClient;privatestaticstring _accessToken;staticvoidMain(string[] args){InitializeWebServerClient();Console.WriteLine("Requesting Token...");RequestToken();Console.WriteLine("Access Token: {0}", _accessToken);Console.WriteLine("Access Protected Resource");AccessProtectedResource();}privatestaticvoidInitializeWebServerClient(){var authorizationServerUri =newUri(Paths.AuthorizationServerBaseAddress);var authorizationServer =newAuthorizationServerDescription{AuthorizationEndpoint=newUri(authorizationServerUri,Paths.AuthorizePath),TokenEndpoint=newUri(authorizationServerUri,Paths.TokenPath)};
        _webServerClient =newWebServerClient(authorizationServer,Clients.Client1.Id,Clients.Client1.Secret);}privatestaticvoidRequestToken(){var state = _webServerClient.GetClientAccessToken(new[]{"bio","notes"});
        _accessToken = state.AccessToken;}privatestaticvoidAccessProtectedResource(){var resourceServerUri =newUri(Paths.ResourceServerBaseAddress);var client =newHttpClient(_webServerClient.CreateAuthorizingHandler(_accessToken));var body = client.GetStringAsync(newUri(resourceServerUri,Paths.MePath)).Result;Console.WriteLine(body);}}

Client Credentials Grant Client

Similar to the Resource Owner Password Credentials Grant, here is console app code:

classProgram{privatestaticWebServerClient _webServerClient;privatestaticstring _accessToken;staticvoidMain(string[] args){InitializeWebServerClient();Console.WriteLine("Requesting Token...");RequestToken();Console.WriteLine("Access Token: {0}", _accessToken);Console.WriteLine("Access Protected Resource");AccessProtectedResource();}privatestaticvoidInitializeWebServerClient(){var authorizationServerUri =newUri(Paths.AuthorizationServerBaseAddress);var authorizationServer =newAuthorizationServerDescription{AuthorizationEndpoint=newUri(authorizationServerUri,Paths.AuthorizePath),TokenEndpoint=newUri(authorizationServerUri,Paths.TokenPath)};
        _webServerClient =newWebServerClient(authorizationServer,Clients.Client1.Id,Clients.Client1.Secret);}privatestaticvoidRequestToken(){var state = _webServerClient.ExchangeUserCredentialForToken("test","test",new[]{"bio","notes"});
        _accessToken = state.AccessToken;}privatestaticvoidAccessProtectedResource(){var resourceServerUri =newUri(Paths.ResourceServerBaseAddress);var client =newHttpClient(_webServerClient.CreateAuthorizingHandler(_accessToken));var body = client.GetStringAsync(newUri(resourceServerUri,Paths.MePath)).Result;Console.WriteLine(body);}}
 
Leave a comment

Posted by on April 24, 2014 in C#, OAuth, Web API ASP.NET

 

Get Error 404 for using UriFormatExtension in WebAPI

I was able to achieve this by doing the following: replace "*." with "*" in system.webServer.handlers in web.config, i.e. remove the period.

<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />



http://stackoverflow.com/questions/11494200/can-periods-be-used-in-asp-net-web-api-routes
 
Leave a comment

Posted by on December 5, 2013 in Web API ASP.NET

 

Enabling Cross-Origin Requests in ASP.NET Web API

By: Mike Wasson – Mike Wasson is a programmer-writer at Microsoft.

Introduction

Browser security prevents a web page from making AJAX requests to another domain. This restriction is called thesame-origin policy. However, sometimes you might want to let other sites call your web API.

Cross Origin Resource Sharing (CORS) is a W3C standard that allows a server to relax the same-origin policy. Using CORS, a server can explicitly allow some cross-origin requests while rejecting others.

This tutorial demonstrates the new CORS support in ASP.NET Web API. We’ll start by creating two ASP.NET projects – one called “WebService”, which hosts a Web API controller, and the other called “WebClient”, which calls WebService. Because the two applications are hosted at different domains, an AJAX request from WebClient to WebService is a cross-origin request.

Prerequisites

Visual Studio 2013 Preview or Visual Studio Express 2013 Preview for Web

Create the Web API Project

Open Visual Studio. Click New Project in the Start page or in the File menu.

In the New Project dialog, click Web in the left pane and ASP.NET Web Application in the middle pane. Name the project “WebService”.

In the New ASP.NET Project dialog, select the Empty project template. Under “Add folders and core references for”, select the Web API checkbox. Click Create Project.

Add a Web API Controller

In Solution Explorer, right-click the Controllers folder. Select Add, then select Scaffold.

In the Add Scaffold dialog, select “Web API 2 Controller – Empty.” (You might need to scroll down to see this option.)

In the Add Controller dialog, name the controller “TestController”. Click Add.

Replace the boilerplate code with the following:

using System.Net.Http;
using System.Web.Http;

namespace WebService.Controllers
{
    public class TestController : ApiController
    {
        public HttpResponseMessage Get()
        {
            return new HttpResponseMessage()
            {
                Content = new StringContent("GET: Test message")
            };
        }

        public HttpResponseMessage Post()
        {
            return new HttpResponseMessage()
            {
                Content = new StringContent("POST: Test message")
            };
        }

        public HttpResponseMessage Put()
        {
            return new HttpResponseMessage()
            {
                Content = new StringContent("PUT: Test message")
            };
        }
    }
}

Now deploy this application to a server or VM. (For the screenshots in this tutorial, I deployed to Windows Azure Web Sites.)

To verify that the web API is working, navigate to http://hostname/api/test/, where hostname is the domain where you deployed the application. You should see the response text, “GET: Test Message”.

Create the Client Application

In Solution Explorer, right-click the solution. Select Add and then select New Project.

In the Add New Project dialog, select ASP.NET Web Application, as before. Name the project “WebClient”. ClickOK.

In the New ASP.NET Project dialog, select the MVC project template. Optionally, click Change Authenticationand select the No Authentication option. Click Create Project.

In Solution Explorer, expand the WebClient project and open the file Views/Home/Index.cshtml. Replace the code in this file with the following:

<div>
    <select id="method">
        <option value="get">GET</option>
        <option value="post">POST</option>
        <option value="put">PUT</option>
    </select>
    <input type="button" value="Try it" onclick="sendRequest()" />
    <span id='value1'>(Result)</span>
</div>

@section scripts {
<script>
    var serviceUrl = 'http://myservice.azurewebsites.net/api/test'; // Replace with your URI.

    function sendRequest() {
        var method = $('#method').val();

        $.ajax({
            type: method,
            url: serviceUrl
        }).done(function (data) {
            $('#value1').text(data);
        }).error(function (jqXHR, textStatus, errorThrown) {
            $('#value1').text(jqXHR.responseText || textStatus);
        });
    }
</script>
}

For the serviceUrl variable, use the URI of the WebService application.

Now publish the WebClient project to another domain.

When the “Try It” button is clicked, the page submits an AJAX request to the WebService application, using the HTTP method listed in the dropdown box (either GET, POST, or PUT). This will let us examine various types of CORS request. Right now, the WebService application does not support cross-origin requests, so if you click the button, you will get an error.

If you watch the HTTP traffic in Fiddler or in the F12 developer tools, you will see that the browser does send the GET request. But in the application, the AJAX call returns an error. It’s important to understand that same-origin policy does not prevent the browser from sendingthe request. Instead, it prevents a cross-domain application from seeing the response.

Enable CORS in Web API

To enable CORS in Web API, use the Microsoft.AspNet.WebApi.Cors package, which is available on NuGet.

In Visual Studio, from the Tools menu, select Library Package Manager, then select Package Manager Console. In the Package Manager Console window, type the following command:

Install-Package Microsoft.AspNet.WebApi.Cors -pre -project WebService

This installs the CORS package, along with any dependencies, into the WebService project.

In Solution Explorer, expand the WebService project. Open the file App_Start/WebApiConfig.cs. Add the following code to the WebApiConfig.Register method.

using System.Web.Http;
namespace WebService
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // New code
            config.EnableCors();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Next, add the [EnableCors] attribute to the TestController class, as follows”

using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;

namespace WebService.Controllers
{
    [EnableCors(origins: "http://myclient.azurewebsites.net", headers: "*", methods: "*")]
    public class TestController : ApiController
    {
        // Controller methods not shown...
    }
}

For the origins parameter, use the URI where you deployed the WebClient application. This allows cross-domain requests from WebClient. I’ll describe the parameters for [EnableCors] in more detail later in this topic.

Deploy the updated WebService application. Now the AJAX request from WebClient should succeed:

The POST and PUT methods are also allowed:

Scope Rules for [EnableCors]

You can enable CORS per action, per controller, or globally for all Web API controllers in your application.

Per Action

To enable CORS for a single action, set the [EnableCors] attribute on the action method. The following example enables CORS for the GetItem method only.

public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }

    [EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
    public HttpResponseMessage GetItem(int id) { ... }

    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage PutItem(int id) { ... }
}

Per Controller

If you set [EnableCors] on the controller class, it applies to all the actions on the controller. Then to disable CORS for an action, set the [DisableCors] attribute on the action. The following example enables CORS for all of the controller methods except PutItem.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }
    public HttpResponseMessage GetItem(int id) { ... }
    public HttpResponseMessage Post() { ... }

    [DisableCors]
    public HttpResponseMessage PutItem(int id) { ... }
}

Globally

To enable CORS for all Web API controllers in your application, pass an EnableCorsAttribute instance to theEnableCors method:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("www.example.com", "*", "*");
        config.EnableCors(cors);
        // ...
    }
}

If you apply the attribute at more than one scope, the order of precedence is:

  1. Action
  2. Controller
  3. Global

How It Works

This section describes how a CORS request is actually performed, at the level of the HTTP messages. It’s important to understand how CORS works, so that you can configure the [EnableCors] attribute correctly, and to troubleshoot if things don’t work as you expect.

The CORS specification introduces several new HTTP headers that enable cross-origin requests. If a browser supports CORS, it sets these headers automatically for cross-origin requests; you don’t need to do anything special in your JavaScript code.

Here is an example of a cross-origin request:

GET http://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: http://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: http://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

The browser includes an “Origin” header that gives the domain of the site that is making the request. If the server allows the request, it sets the Access-Control-Allow-Origin header. The value of Access-Control-Allow-Origin either matches the Origin header, or is the wildcard value “*”, meaning that any origin is allowed.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Date: Wed, 05 Jun 2013 06:27:30 GMT
Content-Length: 17

GET: Test message

If the response does not include the Access-Control-Allow-Origin header, the AJAX request fails. Specifically, the browser disallows the request. Even if the server returns a successful response, the browser does not make the response available to the client application.

Preflight Requests

For some CORS requests, the browser sends an additional request, called a “preflight request”, before it sends the actual request for the resource.

The browser can skip the preflight request if the following conditions are true:

  • The request method is GET, HEAD, or POST, and
  • The application does not set any request headers other than Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID, and
  • The Content-Type header (if set) is one of the following:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

The rule about request headers applies only to headers that the application sets, by calling setRequestHeader on the XMLHttpRequest object. (The CORS specification calls these “author request headers”.) The rule does not apply to headers set by the browser, such as User-Agent, Host, or Content-Length.

Here is an example of a preflight request:

OPTIONS http://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: http://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

The pre-flight request uses the HTTP OPTIONS method. It includes two special headers:

  • Access-Control-Request-Method: Specifies the HTTP method for the actual request.
  • Access-Control-Request-Headers: A list of request headers that the application has set for the actual request.

Here is an example response, assuming that the server allows the request:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 05 Jun 2013 06:33:22 GMT

The response includes an Access-Control-Allow-Methods header that lists the allowed methods, and optionally an Access-Control-Allow-Headers header, which lists the allowed headers. If the preflight request succeeds, the browser sends the actual request, as described earlier.

Specify the Allowed Origins

The origins parameter of the [EnableCors] attribute specifies which domains are allowed to access the resource. The value is a comma-separated list of the allowed domains.

[EnableCors(origins: "http://www.contoso.com,http://www.example.com", 
    headers: "*", methods: "*")]

You can also use the wildcard value “*” to allow requests from any domain.

Consider carefully before allowing requests from any origin. It means that literally any web page can make AJAX calls to your web API.

// Allow CORS for all origins. (Caution!)
[EnableCors(origins: "*", headers: "*", methods: "*")]

Specify the Allowed HTTP Methods

The methods parameter of the [EnableCors] attribute specifies which HTTP methods are allowed to access the resource. To allow all methods, use the wildcard value “*”. The following example allows only GET and POST requests.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "get,post")]
public class TestController : ApiController
{
    public HttpResponseMessage Get() { ... }
    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage Put() { ... }    
}

Specify the Allowed Headers

Request Headers

Earlier I described how a preflight request might include an Access-Control-Request-Headers header, listing the HTTP headers set by the application (the so-called “author request headers”). The headers parameter of the[EnableCors] attribute specifies which author request headers are allowed. To allow any headers, set headers to “*”. To whitelist specific headers, set headers to a comma-separated list of the allowed headers:

[EnableCors(origins: "http://example.com", 
    headers: "accept,content-type,origin,x-my-header", methods: "*")]

However, browsers are not entirely consistent in how they set Access-Control-Request-Headers. For example, Chrome currently includes “origin”; while FireFox does not include standard headers such as “Accept”, even when the application sets them in script.

If you set headers to anything other than “*”, you should include at least “accept”, “content-type”, and “origin”, plus any custom headers that you want to support.

Response Headers

By default, the browser does not expose all of the response headers to the application. The response headers that are available by default are:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

The CORS spec calls these simple response headers. To make other headers available to the application, set theexposedHeaders parameter of [EnableCors].

In the following example, the controller’s Get method sets a custom header named ‘X-Custom-Header’. By default, the browser will not expose this header in a cross-origin request. To make the header available, we include ‘X-Custom-Header’ in exposedHeaders.

[EnableCors(origins: "*", headers: "*", methods: "*", exposedHeaders: "X-Custom-Header")]
public class TestController : ApiController
{
    public HttpResponseMessage Get()
    {
        var resp = new HttpResponseMessage()
        {
            Content = new StringContent("GET: Test message")
        };
        resp.Headers.Add("X-Custom-Header", "hello");
        return resp;
    }
}

Here is an example response from the server:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Expires: -1
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-Custom-Header: hello
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Custom-Header
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 07 Jun 2013 18:55:39 GMT
Content-Length: 17

GET: Test message

Credentials

Credentials require special handling in a CORS request. By default, the browser does not send any credentials with a cross-origin request. (Credentials include cookies as well as HTTP authentication.) To send credentials with a cross-origin request, set the withCredentials property on the XMLHttpRequest object to true.

Using XMLHttpRequest directly:

var xhr = new XMLHttpRequest();
xhr.open('get', 'http://www.example.com/api/test');
xhr.withCredentials = true;

In jQuery:

$.ajax({
    type: 'get',
    url: 'http://www.example.com/api/test',
    xhrFields: {
        withCredentials: true
    }

In addition, the server must allow the credentials. To allow cross-origin credentials in Web API, set theSupportsCredentials property to true on the [EnableCors] attribute:

[EnableCors(origins: "http://myclient.azurewebsites.net", headers: "*", 
    methods: "*", SupportsCredentials = true)]

If this property is true, the HTTP response will include an Access-Control-Allow-Credentials header. This header tells the browser that the server allows credentials for a cross-origin request.

If the browser sends credentials, but the response does not include a valid Access-Control-Allow-Credentials header, the browser will not expose the response to the application, and the AJAX request fails.

Be very careful about setting SupportsCredentials to true, because it means a website at another domain can send a logged-in user’s credentials to your Web API on the user’s behalf, without the user necessarily being aware. Also, note that according to the CORS spec, setting origins to “*” is not valid if SupportsCredentials is true.

Custom CORS Policy Providers

The [EnableCors] attribute implements the ICorsPolicyProvider interface. You can provide your own implementation by creating a class that derives from Attribute and implements ICorsProlicyProvider.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class MyCorsPolicyAttribute : Attribute, ICorsPolicyProvider 
{
    private CorsPolicy _policy;

    public MyCorsPolicyAttribute()
    {
        // Create a CORS policy.
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true
        };

        // Add allowed origins.
        _policy.Origins.Add("http://myclient.azurewebsites.net");
        _policy.Origins.Add("http://www.contoso.com");
    }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request)
    {
        return Task.FromResult(_policy);
    }
}

Now you can apply the attribute any place that you would put [EnableCors].

[MyCorsPolicy]
public class TestController : ApiController
{
    .. // 

For example, a custom CORS policy provider could read the settings from a configuration file.

As an alternative to using attributes, you can register an ICorsPolicyProviderFactory object that createsICorsPolicyProvider objects.

public class CorsPolicyFactory : ICorsPolicyProviderFactory
{
    ICorsPolicyProvider _provider = new MyCorsPolicyProvider();

    public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
    {
        return _provider;
    }
} 

To set the ICorsPolicyProviderFactory, call the SetCorsPolicyProviderFactory extension method at startup, as follows:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
        config.EnableCors();

        // ...
    }
}

Summary

With CORS, you can allow cross-origin requests in a way that is safer and more flexible than earlier techniques such as JSONP. The Microsoft.AspNet.WebApi.Cors library lets you support CORS in your Web API application, simply by decorating your controller with the [EnableCors] attribute. For even more control, you can implement a custom CORS policy provider.

 

Copy from: http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

 
Leave a comment

Posted by on November 25, 2013 in C#, Web API ASP.NET