Secure ASP.NET WebAPI 2 using Azure Active Directory AD with ADAL JS

 

What we will use
  • OAuth 2.0 middleware
  • ASP.NET WebAPI 2.2
  • Authentication Project Template: Organization Account
  • Azure Active Directory
  • SPA
  • Azure AD Authentication Library (ADAL) for javascript
  •  
Create a WebAPI Project with Organizational Accounts
  • Cloud – Single Organization
  • Domain/AD Tenant – yourorganization.onmicrosoft.com
  • Access Level – Single Sign On (i.e. lets the directory issue tokens for your application)
    • Other access level include “Read/ReadWrite Directory data” using the REST Graph API
Setup:

At Startup.Auth.cs

// ida:Tenant – yourorganization.onmicrosoft.com
// ida:Audience –
https://yourorganization.onmicrosoft.com/MyWebAPIProjectName
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings[“ida:Tenant”],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings[“ida:Audience”]
},
});

Publish the WebAPI app.

Register the SPA App with Azure AD
  • Go to Active Directory –> Application
  • Add Application (Web Client)
  • Add Sign-On Url
  • Add App ID Url
  • Add Redirect URI
  • Enable OAuth2 Implicit Grant – refer to sample app readme.
  • Configure the App.js tenant, clientId with web.config ‘s ida:Tenant & ida:Audience respecitively.

p.s. As of Dec. 20 2014. Looks like the sample is still not ready for hosting the client app in Azure. i.e. the client app will only work when running localhost.

Reference: https://github.com/AzureADSamples/SinglePageApp-DotNet

Secure ASP.NET WebAPI 2 using Azure Active Directory AD with ADAL .NET

image

What we will use

  • OAuth 2.0 middleware
  • ASP.NET WebAPI 2.2
  • Authentication Project Template: Organization Account
  • Azure Active Directory
  • Native Application e.g. Windows Form App
  • Azure AD Authentication Library (ADAL) for .NET 2.x

The ‘Authority’ / ‘Identity Provider’ – Azure AD

Resources often offload most of the authentication functions to an external services provider, commonly known as an authority or an identity provider.

With those functions out of the way, the only authentication task left is to verify that authentication succeeded at the authority. This typically involves examining a security token, a data fragment issued by an authority to a caller upon successful authentication.

Security tokens are usually crafted according to a specific format, digitally signed by a key that will unequivocally identify the issuing authority, and contain some data that uniquely ties the token to the target resource. When the resource receives a request, it looks for an accompanying token. If it finds one that meets the required validation attributes, the caller is authenticated.

Create a WebAPI Project with Organizational Accounts

  • Cloud – Single Organization
  • Domain/AD Tenant – yourorganization.onmicrosoft.com
  • Access Level – Single Sign On (i.e. lets the directory issue tokens for your application)
    • Other access level include “Read/ReadWrite Directory data” using the REST Graph API
Setup:

In Startup.Auth.cs

// ida:Tenant – yourorganization.onmicrosoft.com
// ida:Audience –
https://yourorganization.onmicrosoft.com/MyWebAPIProjectName
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings[“ida:Tenant”],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings[“ida:Audience”]
},
});

Publish the WebAPI App.

The app.Use* naming convention adds a middleware implementation to the OWIN pipeline. The added middleware inspects the incoming request to see if the HTTP header Authorization contains a security token. If it finds a token, it validates the issuing authority, the integrity, expiration date.

If the token looks good, the middleware projects its content in a principal. If it isn’t, sends back an error code.

If there’s no token, the middleware simply lets the call go through without creating a principal (i.e. anonymous). [Authorize] decides whether the request should be served or access denied.

The Audience value is the identifier by which the Web API is known to Windows Azure AD. Any tokens carrying a different Audience are meant for another resource and should be rejected.

The middleware uses that Tenant property value to read all the other properties (such as which key should be used to verify the token’s signatures) that determine the validity of a token.

Register the Native Client App with Azure AD

  • Go to Active Directory –> Application
  • Add Application (Web Client / Native Client)
  • Add Redirect URI

More AD configuration for the client app

  • Click the Configure tab
  • Copy the Client ID
  • Add/Select  the target WebAPI server application and then hit save

Create Native Client App

  • Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory

// Get token
AuthenticationContext ac = new AuthenticationContext(
// Azure -> AD -> Domain -> 
https://login.windows.net/ + AD tenant/domain name
https://login.windows.net/yourorganization.onmicrosoft.com“);
AuthenticationResult ar =
ac.AcquireToken(
// WebAPIApp web.config ‘s ida:Audience
https://yourorganization.onmicrosoft.com/MyWebAPI”,
// Azure -> AD -> Application -> clientapp’s CLIENT ID
“8a39841b-7dcc-4b92-b546-a0799bae312c”,
// Azure -> AD -> Application -> clientapp’s REDIRECT URIS
new Uri(“
https://mynativeclient”));

// Call Web API
string authHeader = ar.CreateAuthorizationHeader();
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(
HttpMethod.Get, “
https://mywebapi.azurewebsites.net/Api/Values”);
request.Headers.TryAddWithoutValidation(“Authorization”, authHeader);
HttpResponseMessage response = await client.SendAsync(request);
string responseString = await response.Content.ReadAsStringAsync();
MessageBox.Show(responseString);

When I provide the credentials of any valid user from my directory tenant, I get a token back. The subsequent code presents the token to the Web API in the request headers. The security middleware validates it. Because all validation parameters are a match, it sends back HTTP status code 200 with the results.

If click the button again. You’ll get a token back right away, without being prompted. That’s because ADAL has a built-in token cache that keeps track of the tokens. It even takes care of silently refreshing expired tokens whenever possible.

Reference: http://msdn.microsoft.com/en-us/magazine/dn463788.aspx

https://vincenthomedev.wordpress.com/2014/11/30/azure-active-directory-for-mvc-webapi/

Self-Host ASP.NET WebAPI 2 using OWIN

Setup

1. Install-Package Microsoft.AspNet.WebApi.OwinSelfHost

2. Create a new class Startup.cs to configure the WebAPI

public class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        // Configure Web API for self-host.
        HttpConfiguration config = new HttpConfiguration();
        config.Routes.MapHttpRoute(
            name: “DefaultApi”,
            routeTemplate: “api/{controller}/{id}”,
            defaults: new { id = RouteParameter.Optional }
        );

        appBuilder.UseWebApi(config);
    }
}

 

3. Add a Controller derived from ApiController valuescontroller.cs

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { “value1”, “value2” };
    }

    // GET api/values/5
    public string Get(int id)
    {
        return “value”;
    }

    // POST api/values
    public void Post([FromBody]string value)
    {     }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {     }

    // DELETE api/values/5
    public void Delete(int id)
    {     }
}

 

4. Start the Owin Host (Console App or a Windows Service)

static void Main()
{
    string baseAddress = “
http://localhost:9000/”; 
    Microsoft.Owin.Hosting.WebApp.Start<Startup>(url: baseAddress);
    Console.ReadLine(); // Prevent Exit
}

5. Calling the WebAPI

Request

GET http://localhost:9000/api/values HTTP/1.1
Host: localhost:9000
Accept: application/json

Response

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json; charset=utf-8
Server: Microsoft-HTTPAPI/2.0

[“value1″,”value2”]

Reference: http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-host-web-api

Calling WebAPI using HttpClient

 

What is HttpClient?

  • A WebAPI REST friendly client.
  • An HttpClient instance is the place to configure extensions, set default headers, cancel outstanding requests and more.
  • You can issue as many requests as you like through a single HttpClient instance.
  • HttpClients are not tied to particular HTTP server or host; you can submit any HTTP request using the same HttpClient instance. i.e. no CORS restriction.
  • You can derive from HttpClient to create specialized clients for particular sites or patterns
  • HttpClient uses the new Task-oriented pattern for handling asynchronous requests making it dramatically easier to manage and coordinate multiple outstanding requests.

Setup

Install-Package Microsoft.AspNet.WebApi.Client

HTTP GET
using (var client = new HttpClient())
{
    client.BaseAddress = new Uri(“
http://localhost:9000/”);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json“));

    HttpResponseMessage response = await client.GetAsync(“api/products/1”);
    if (response.IsSuccessStatusCode)
    {
        Product product = await response.Content.ReadAsAsync>Product>();
        Console.WriteLine(“{0}\t${1}\t{2}”, product.Name, product.Price, product.Category);
    }
}

If the status code in the response is a success code, the response body contains the JSON representation of a product. Call ReadAsAsync to deserialize the JSON payload to a Product instance. The ReadAsync method is asynchronous because the response body can be arbitrarily large.

A note about error handling: HttpClient does not throw an exception when the HTTP response contains an error code. Instead, the IsSuccessStatusCode property is false if the status is an error code.

If you prefer to treat HTTP error codes as exceptions, call the EnsureSuccessStatusCode method. This method throws an exception if the response status is not a success code:

try
{
    HttpResponseMessage response = await client.GetAsync(“api/products/1”);
    resp.EnsureSuccessStatusCode();    // Throw if not a success code.
    // …
}
catch (HttpRequestException e)
{
}

HTTP POST
Uri gizmoUrl;
var gizmo = new Product() { Name = “Gizmo”, Price = 100, Category = “Widget” };
response = await client.PostAsJsonAsync(“api/products”, gizmo);
if (response.IsSuccessStatusCode)
{
    // Get the URI of the created resource.
    gizmoUrl = response.Headers.Location;
}

HTTP PUT
gizmo.Price = 80;   // Update price
response = await client.PutAsJsonAsync(gizmoUrl, gizmo);

HTTP DELETE
response = await client.DeleteAsync(gizmoUrl);

Reference: http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client

http://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx

Enabling CORS (Cross-Origin Requests) in your ASP.NET Web API 2 service

 

image
What is “Same Origin”?

Two URLs have the same origin if they have identical schemes, domain, subdomain, and ports. (RFC 6454)

Enable CORS in your Web API Service

1. Install-Package Microsoft.AspNet.WebApi.Cors

2. In WebApiConfig.cs ‘s WebApiConfig.Register method, add:

 config.EnableCors();

3. Add the [EnableCors] attribute to the Controller/Action:

[EnableCors(origins: “http://mywebclient.azurewebsites.net”, headers: “*”, methods: “*”)]
* Do not include a forward slash at the end of the origins URL.

How CORS Works

The CORS specification introduces several new HTTP headers that enable cross-origin requests. If a browser supports CORS, it sets the Origin header automatically for cross-origin request; you don’t need to do anything special in your JavaScript code. And the browser will inspect Access-Control-Allow-Origin header in the response to determine whether to return the response content to the caller or not.

Here is an example of a cross-origin request. The “Origin” header gives the domain of the site that is making the request.

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

Response

If the server allows the request, it sets the Access-Control-Allow-Origin header. The value of this header 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

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

Secure ASP.NET WebAPI 2 using ASP.NET Identity membership DB with OAuth 2

Code Download.

What we will use:
  • OAuth 2.0 middleware
  • ASP.NET WebAPI 2.2
  • ASP.NET Identity 2.1 membership
  • Authentication Project Template: Individual Account
  • SPA using knockoutjs (optional)

OAuth 2.0 terminology:

  • Resource: Some piece of data that can be protected. e.g. Controller/Controller’s Action method.
  • Resource server: The server that hosts the resource.
  • Resource owner: The entity that can grant permission to access a resource. Typically the user.
  • Access token: A token that grants access to a resource.
  • Bearer token: A special type of access token which a client doesn’t need a cryptographic key to use it. For that reason, it should only be used over a HTTPS, and with short expiration time.
  • Authorization server: A server that gives out Bearer/Access tokens.
  • Client: The app that wants access to the resource. e.g. a web browser, native app.
  • OAuth 2.0 Access Token Retrieval – Grant types:
  • Authorization Code Grant
  • Implicit Grant
  • Resource Owner Password Credentials Grant – we will be using this!
  • Client Credentials Grant
  • JWT Tokens

note: A Resource server and Authorization server can be part of the same ASP.NET application.

Using Resource Owner Password Credentials Grant:

image

Architecture

image

Let’s get the Bearer/Access token from Authorization Server

Setup:

In StartupAuth.cs,

PublicClientId = “self”;
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString(“/Token”),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString(“/api/Account/ExternalLogin”),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // Note: Remove the following line before you deploy to production:
    AllowInsecureHttp = true
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

The TokenEndpointPath property is the URL path to the authorization server endpoint. That’s the URL that app uses to get the bearer tokens. e.g. “/Token”

The Provider property specifies ApplicationOAuthProvider which provide access to the ASP.NET Identity membership database via the UserManager

Flow:

image

  1. To get an access token, the app sends a request to ~/Token.
  2. The OAuth middleware calls GrantResourceOwnerCredentials on the ApplicationOAuthProvider (which implements OAuthAuthorizationServerProvider).
  3. The provider calls the ApplicationUserManager which derives from ASP.NET Identity’s UserManager to validate the credentials with the ASP.NET Identity membership db and create a claims identity.
  4. If that succeeds, the provider creates an authentication ticket, which is used to generate the Bearer token.

Sample Request/Response

Javascript

var loginData = {
    grant_type: ‘password’,
    username: self.loginEmail(),
    password: self.loginPassword()
};

$.ajax({
    type: ‘POST’,
    url: ‘/Token’,
    data: loginData
}).done(function (data) {
    self.user(data.userName);
    // Cache the access token in session storage.
    sessionStorage.setItem(tokenKey, data.access_token);
}).fail(showError);

Notice that we store the token in session storage, to use later when sending requests to the API. Unlike some forms of authentication (such as cookie-based authentication), the browser will not automatically include the access token in subsequent requests. The application must do so explicitly. That’s a good thing, because it limits CSRF vulnerabilities
HTTP Request

POST https://localhost:44305/Token HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer:
https://localhost:44305/
Content-Length: 68

grant_type=password&username=alice%40example.com&password=Password1!

HTTP Response

HTTP/1.1 200 OK
Content-Length: 669
Content-Type: application/json;charset=UTF-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:22:36 GMT

{
  access_token“:”imSXTs2OqSrGWzsFQhIXziFCO3rF…”,
  token_type“:”bearer”,
  “expires_in”:1209599,
  “userName”:”alice@example.com”,
  “.issued”:”Wed, 01 Oct 2014 01:22:33 GMT”,
  “.expires”:”Wed, 15 Oct 2014 01:22:33 GMT”
}

Log Out

Because the browser does not cache the credentials or the access token, logging out is simply a matter of “forgetting” the token, by removing it from session storage:

self.logout = function () {
    sessionStorage.removeItem(tokenKey);
}

Let’s get the protected Resource from WebAPI Resource Server

Setup:

In the WebApiConfig.cs Register method, the following code sets up Bearer Token authentication for the Web API pipeline:

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

The SuppressDefaultHostAuthentication method tells Web API to ignore any authentication(e.g. MVC cookies authentication) that happens before the request reaches the Web API pipeline, either by IIS or by OWIN middleware. That way, we can restrict Web API to authenticate ONLY using bearer tokens.

The HostAuthenticationFilter class enables authentication using bearer tokens.

Flow:

image

  1. The HostAuthenticationFilter calls the OAuth middleware to validate the token.
  2. The middleware converts the token into a claims identity.
  3. At this point, the request is authenticated but not authorized.
  4. The AuthorizationFilter [Authorize] examines the claims identity.
  5. The controller returns the protected resource. Otherwise, the client receives a 401 (Unauthorized) error.
Sample Request/Response
Javascript

// If we already have a bearer token, set the Authorization header.
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
    headers.Authorization = ‘Bearer ‘ + token;
}

$.ajax({
    type: ‘GET’,
    url: ‘api/values/1’,
   
headers: headers
}).done(function (data) {
    self.result(data);
}).fail(showError);

HTTP Request

GET https://localhost:44305/api/values/1 HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Authorization: Bearer imSXTs2OqSrGWzsFQhIXziFCO3rF…
X-Requested-With: XMLHttpRequest

HTTP Response
A failed one when Bearer token is missing or incorrect:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
WWW-Authenticate: Bearer
Date: Tue, 30 Sep 2014 21:54:43 GMT
Content-Length: 61

{“Message”:”Authorization has been denied for this request.”}

A success one:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:41:29 GMT
Content-Length: 45

“Hello from Action Method, alice@example.com.”

Reference: http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api

ASP.NET Web API 2–Token Authentication for SPA

image