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/

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