authenticationazureasp.net-web-apiacsjwt

Microsoft Owin UseJwt


I am having a difficult time using UseJwtBearerAuthentication Method, I am using Microsoft Azure ACS to obtain a token (using a service identity). The JWT token returns fine to my test program. In the test program the token is sent to a MVC WebAPI 2. (The WAAD authentication works fine when token is obtained from Azure Active Directory)

public partial class Startup
{
    private const string Issuer = "https://bluebeam-us-east.accesscontrol.windows.net/";
    public void ConfigureAuth(IAppBuilder app)
    {
        string CertificateThumbprint = "99B25E3E31FCD24F669C260A743FBD508D21FE30";
        var audience = ConfigurationManager.AppSettings["ida:Audience"];
        app.UseErrorPage(new ErrorPageOptions()
                {
                    ShowEnvironment = true,
                    ShowCookies = false, 
         ShowSourceCode = true,
                    });



        app.UseWindowsAzureActiveDirectoryBearerAuthentication(
            new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                Audience =  audience ,
                Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
            });
        app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
        {
            AllowedAudiences = new[] { audience },
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
            {
                new X509CertificateSecurityTokenProvider(Issuer, X509CertificateHelper.FindByThumbprint(StoreName.My,StoreLocation.LocalMachine,CertificateThumbprint).First())
            },
        });
    }

The Code to get Token from ACS is as follows:

private async void GetJwtToken()
{
    try
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(IdP.Authority);
            var content = new FormUrlEncodedContent(new Dictionary<String, String>
            {
                {"grant_type","client_credentials"},
                {"client_id", IdP.UserName},
                {"client_secret", IdP.Password},
                {"scope", IdP.Resource}
            });
            var response = await client.PostAsync("v2/OAuth2-13", content);
            response.EnsureSuccessStatusCode();
            var jwtdata = await response.Content.ReadAsStringAsync();
            var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
            AccessToken = jwt.access_token;
            TokenType = jwt.token_type;
            long expire;
            if (long.TryParse(jwt.expires_in, out expire))
            {
                ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
            }
            Authorization = AccessToken;
        }
    }
    catch (HttpRequestException re)
    {
        Response = re.Message;
    }
}

The code to request a Resource (WebAPI):

private async void WebApiRequestCall()
    {
        try
        {
            ConfigureSsl();
            using (var client = new HttpClient())
            {
                client.BaseAddress = _baseAddress;
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (!String.IsNullOrWhiteSpace(Authorization))
                    client.DefaultRequestHeaders.Add("Authorization", Authorization);
                var response = await client.GetAsync(WebApiRequest);
                response.EnsureSuccessStatusCode();
                Response = await response.Content.ReadAsStringAsync();
            }
        }
        catch (HttpRequestException e)
        {
            Response = e.Message;
        }
    }

The decoded Token (using google token decoder looks as follows)

Header
{
    "x5t": "mbJePjH80k9mnCYKdD-9UI0h_jA", 
    "alg": "RS256", 
    "typ": "JWT"
}
Claims
{
    "identityprovider": "https://bluebeam-us-east.accesscontrol.windows.net/", 
    "iss": "https://bluebeam-us-east.accesscontrol.windows.net/", 
    "http://schemas.microsoft.com/identity/claims/identityprovider": "revu", 
    "exp": 1406957036, 
    "nbf": 1406956676, 
    "aud": "https://bluebeam.com/Bluebeam.Licensing.WebApi/"
}

So I have the following questions:

1) Is using JwtBearerToken the correct method to use to decode decode JWT token from ACS 2) Is there any tracing facilities in Owin that can provide whats going on in the authentication pipeline?

I am using Microsoft Own 3.0-rc1.


Solution

  • It seems that I had an error in my code where I was not setting the correct "bearer header" for OWIN when sending the client request to WebAPI.

    After Receiving the JWT Token from ACS, I needed to set the Authorization correctly

    private async void GetJwtToken()
        {
            try
            {
                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri(IdP.Authority);
                    var content = new FormUrlEncodedContent(new Dictionary<String, String>
                    {
                        {"grant_type","client_credentials"},
                        {"client_id", IdP.UserName},
                        {"client_secret", IdP.Password},
                        {"scope", IdP.Resource}
                    });
                    var response = await client.PostAsync("v2/OAuth2-13", content);
                    response.EnsureSuccessStatusCode();
                    var jwtdata = await response.Content.ReadAsStringAsync();
                    var jwt = JsonConvert.DeserializeObject<Token>(jwtdata);
                    IdP.AccessToken = jwt.access_token;
                    IdP.TokenType = jwt.token_type;
                    long expire;
                    if (long.TryParse(jwt.expires_in, out expire))
                    {
                        IdP.ExpiresOn = DateTimeOffset.UtcNow.AddSeconds(expire);
                    }
                    // Ensure that Correct Authorization Header for Owin
                    Authorization = String.Format("{0} {1}", "Bearer", IdP.AccessToken);**
                }
            }
            catch (HttpRequestException re)
            {
                Response = re.Message;
            }
        }
    

    We also need support for symmetric key on the WebAPI, based upon how ACS sends the token

    public void ConfigureAuth(IAppBuilder app)
        {
            var thumbPrint = ConfigurationManager.AppSettings["ida:Thumbprint"];
            var audience = ConfigurationManager.AppSettings["ida:Audience"];
            var trustedTokenPolicyKey = ConfigurationManager.AppSettings["ida:SymmetricKey"];
    
            app.UseErrorPage(new ErrorPageOptions()
                    {
                        ShowEnvironment = true,
                        ShowCookies = false,
                        ShowSourceCode = true,
                    });
    
            app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions()
            {
                AllowedAudiences = new[] {audience},
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new X509CertificateSecurityTokenProvider(Issuer,
                        X509CertificateHelper.FindByThumbprint(StoreName.My, StoreLocation.LocalMachine, thumbPrint)
                            .First()),
                    new SymmetricKeyIssuerSecurityTokenProvider(Issuer, trustedTokenPolicyKey),
                },
            });
            app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                {
                    Audience = audience,
                    Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
                });
        }