Yash Verma
Yash Verma

Reputation: 91

Validating ADAL JWT token in C# REST service

I have a web application which uses the ADAL library for authentication through Azure Active Directory.

This web application makes a call to a C# REST service by passing the ADAL token string as a parameter. In my REST service, I want to validate this token. If the token is valid only then the service will perform the operation.

I searched a lot but could not find a way to validate the JWT token in my rest service. Can you guys please help me on this?

Upvotes: 8

Views: 4166

Answers (2)

wlscaudill
wlscaudill

Reputation: 525

If you're using an OAuth spec JWT then the key to validate the ticket can be retrieved via 'http://auth.myApplication.com/.well-known/jwks.json' and the built in JwtSecurityTokenHandler can be used.

OWIN Example:

public static class AppBuilderExtensions
{
    /// <summary>
    /// Use JWT Authentication.
    /// </summary>
    /// <param name="app">The <see cref="IAppBuilder"/> to use.</param>
    /// <param name="audiences">The expected audiences.  Will be validated in the token.</param>
    /// <returns>The <see cref="IAppBuilder"/> instance.</returns>
    public static IAppBuilder UseJwtTokenAuthentication(
        this IAppBuilder app,
        params string[] audiences)
    {
        var validationParameters = new TokenValidationParameters
                                   {
                                       ValidateLifetime = true,
                                       ValidateAudience = true,
                                       ValidateIssuer = true,
                                       ValidateActor = true,
                                       ValidateIssuerSigningKey = true,
                                       ValidAudiences = audiences,
                                       ValidIssuer = Constants.Issuer,
                                       IssuerSigningKeyResolver = Constants.GetSigningKey,
                                   };

        var tokenHandler = new JwtSecurityTokenHandler();
        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = audiences,
                TokenHandler = tokenHandler,
                TokenValidationParameters = validationParameters,
            });

        return app;
    }
}

public static class Constants
{
    /// <summary>
    /// The authentication issuer.
    /// </summary>
    public const string Issuer = "https://auth.myApplication.com/"; // custom domain for Auth0

    private static readonly OpenIdConnectKeyResolver KeyResolver;

    static Constants() { KeyResolver = new OpenIdConnectKeyResolver(Issuer, TimeSpan.FromHours(1), TimeSpan.FromSeconds(10)); }

    /// <summary>
    /// Gets the <see cref="IssuerSigningKeyResolver" /> delegate to provide to <see cref="JwtSecurityTokenHandler" />.
    /// </summary>
    public static IssuerSigningKeyResolver GetSigningKey => KeyResolver.GetSigningKey;
}

Direct call Example:

        void ValidateToken(string authenticationToken)
        {
            var issuer = "https://auth.myApplication.com/"; // custom domain for Auth0
            var audiences = new[]
                            {
                                "https://secure.myApplication.com/",
                            };

            using (var signingKeyResolver = new OpenIdConnectKeyResolver(issuer, TimeSpan.Zero, TimeSpan.FromSeconds(10)))
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var validationParameters = new TokenValidationParameters
                                           {
                                               ValidateLifetime = true,
                                               ValidateAudience = true,
                                               ValidateIssuer = true,
                                               ValidateActor = true,
                                               ValidateIssuerSigningKey = true,
                                               ValidAudiences = audiences,
                                               ValidIssuer = issuer,
                                               IssuerSigningKeyResolver = signingKeyResolver.GetSigningKey,
                                           };

                var principal = tokenHandler.ValidateToken(authenticationToken, validationParameters, out var securityToken);

                if (principal == null || securityToken == null)
                {
                    throw new UnauthorizedAccessException();
                }
            }
        }

This requires a signing key resolver, the example is using the second option as that's what I needed:

It is baked into Microsoft.IdentityModel.Tokens (see example here: https://github.com/auth0/auth0-aspnet-owin/blob/master/src/Auth0.Owin.OpenIdConnectSigningKeyResolver/OpenIdConnectSigningKeyResolver.cs).

It is a little more work if using System.IdentityModel.Tokens.Jwt (see example here: https://github.com/NaosProject/Naos.Auth/blob/main/Naos.Auth.Recipes.Jwt/OpenIdConnectKeyResolver.cs) I wrote a piece of code to accomplish this which can be copied directly (MIT license) or installed as a mix-in using package 'Naos.Auth.Recipes.Jwt'.

Upvotes: 0

Philippe Signoret
Philippe Signoret

Reputation: 14326

You have two options:

1. Use OWIN middleware

Use middleware that will handle token validation for you. A common case will be the OWIN middleware, which does all the magic for you. Usually, this is the best approach, as it allows you to focus your code on the business logic for your API, not on low-level token validation. For a sample REST API that uses OWIN, check out these two samples:

2. Manual JWT validation

You can use the JSON Web Token Handler for ASP.NET to do manual JWT token validation. (Ok, so it's not entirely manual, but it is manually invoked.) There's also a sample for this:

  • https://github.com/Azure-Samples/active-directory-dotnet-webapi-manual-jwt-validation (the actual JWT validation happens in Global.asax.cs and looks something like this:

    JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
    
    TokenValidationParameters validationParameters = new TokenValidationParameters
    {
        ValidAudience = audience,
        ValidIssuer = issuer,
        IssuerSigningTokens = signingTokens,
        CertificateValidator = X509CertificateValidator.None
    };
    
    try
    {
        // Validate token.
        SecurityToken validatedToken = new JwtSecurityToken();
        ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);
    
        // Do other validation things, like making claims available to controller...
    }
    catch (SecurityTokenValidationException)
    {
        // Token validation failed
        HttpResponseMessage response = BuildResponseErrorMessage(HttpStatusCode.Unauthorized);
        return response;
    }
    

Upvotes: 8

Related Questions