2

I am currently following this tutorial to implement Jwt Refresh Tokens. Currently I'm trying to add a header called Token-Expired : "true" when I get a specific exception when responding to an API request.

In the tutorial, this section shows how to do it in the Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    //...

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "bearer";
        options.DefaultChallengeScheme = "bearer";
    }).AddJwtBearer("bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
        };
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                return Task.CompletedTask;
            }
        };
    });
}

The problem is I am using ASP.NET Web Api 2 and not .net core 2.1. How can I add this code to mine? One way that I think might work is that I can add it in my TokenValidation class but I don't know how to do so:

public class TokenValidationHandler : DelegatingHandler
{
    private static bool RetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpStatusCode statusCode;
        string token;
        //determine whether a jwt exists or not
        if (!RetrieveToken(request, out token))
        {
            statusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            const string sec = HostConfig.SecurityKey;
            var now = DateTime.UtcNow;
            var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));


            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = HostConfig.Audience,
                ValidIssuer = HostConfig.Issuer,
                //Set false to ignore expiration date
                ValidateLifetime = false,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,
                IssuerSigningKey = securityKey
            };
            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException e)
        {
            statusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            statusCode = HttpStatusCode.InternalServerError;
        }
        return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }
}
6
  • I thought it was pretty much the same? Commented Sep 28, 2018 at 5:07
  • Yes it is almost same Commented Sep 28, 2018 at 5:08
  • Yeah but for now I just need to know how to add my header content globally Commented Sep 28, 2018 at 5:09
  • It's already in global isn't it? Commented Sep 28, 2018 at 5:13
  • no. i want to add it like the first code block Commented Sep 28, 2018 at 5:15

2 Answers 2

1

Please add one more SecurityTokenExpiredException catch block to catch the token expiration error and add response header inside the catch block like below.

public class TokenValidationHandler : DelegatingHandler
{
    private static bool RetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage();
        string token;
        //determine whether a jwt exists or not
        if (!RetrieveToken(request, out token))
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            const string sec = HostConfig.SecurityKey;
            var now = DateTime.UtcNow;
            var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));


            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = HostConfig.Audience,
                ValidIssuer = HostConfig.Issuer,
                //Set false to ignore expiration date
                ValidateLifetime = false,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,
                IssuerSigningKey = securityKey
            };
            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenExpiredException e)
        {
            var expireResponse = base.SendAsync(request, cancellationToken).Result;
            response.Headers.Add("Token-Expired", "true");
            response.StatusCode = HttpStatusCode.Unauthorized;

        }
        catch (SecurityTokenValidationException e)
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            response.StatusCode = HttpStatusCode.InternalServerError;
        }
        return Task<HttpResponseMessage>.Factory.StartNew(() => response);
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }


}
Sign up to request clarification or add additional context in comments.

Comments

0

You also need Microsoft.Owin.Host.SystemWeb package to install first.Then Create One Class With Name Startup.cs

this will help you..

public class Startup
{
   public void Configuration(IAppBuilder app)
   {
      //
   }
   public void ConfigureServices(IServiceCollection services)
   {
    services.AddMvc();
    //...

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "bearer";
        options.DefaultChallengeScheme = "bearer";
    }).AddJwtBearer("bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
        };
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                return Task.CompletedTask;
            }
        };
    });
}
}

In addition to this, if your startup class is somehow not in your default name space, add a web config line to the <appSettings> area like: <add key="owin:AppStartup" value="[NameSpace].Startup" />

To use ConfigureServices method you need to have Build-in dependency injection is only available in ASP.NET Core. You will need to use third party IoC container such as -

Autofac for Web API

or

Ninject

For that get below library.

Microsoft.Extensions.DependencyInjection

3 Comments

Where did you get Iservicecollection from?
I think your solution is for ASP.NET Core 1.0 and above.
You will need to use Dependency injection . See the edited answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.