2

I am trying to return an altered header if the token a user sends up is expired so that I can resend up my refresh token if it is expired.

I am using .NET Core 2.2 with "In-Process" hosting incase that matters.

Here is my ConfigureServices method from my Startup.cs.

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(System.Text.Encoding.UTF8.GetBytes(Configuration["serverSigningPassword"])),
        ValidateLifetime = true,
        ClockSkew = System.TimeSpan.Zero //the default for this setting is 5 minutes
    };
    options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return System.Threading.Tasks.Task.CompletedTask;
        }
    };
});

And then when I try to fetch on an "authorize" endpoint from javascript with the following.

async function fetchWithCredentials(url, options) {    
    options.headers['Authorization'] = 'Bearer ' + jwtToken;
    var response = await fetch(url, options);
    if (response.ok) { //all is good, return the response
        return response;
    }

    console.log(response.headers) //nothing in this array

    // it will never do this "if" statement because there are no headers
    if (response.status === 401 && response.headers.has('Token-Expired')) {
        // refresh the token
        return await fetchWithCredentials(url, options); //repeat the original request
    } else { //status is not 401 and/or there's no Token-Expired header
        return response; 
    }
}

This image is from hovering over the header. It certainly hits my breakpoint (for the context.Response.Headers.Add() and I can see the count = 1 (which is the "Token-Expired" when I examine it).

enter image description here

Finally, here is a screenshot from Postman after a failed request so the response is sending, but not being received in my JS. enter image description here

Any ideas as to why my header is not sticking to my response in the javascript?

thalacker
  • 2,389
  • 3
  • 23
  • 44

2 Answers2

5

There is a restriction to access response headers when you are using Fetch API over CORS. Due to this restriction, you can access only following standard headers:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Reference : https://stackoverflow.com/a/44816592/5751404

So one way to access your custom header in client is to add the header access-control-expose-headers to response, with the comma-separated headers:

services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
    builder.AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
          .WithExposedHeaders("Token-Expired"); ;
}));

In Configure:

app.UseCors("MyPolicy");

So that you can access the custom header from client using Fetch API over CORS .

Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • Hi @NanYu, I only want that response to be returned when the authentication fails though. Not for every request – thalacker May 29 '19 at 14:01
  • 1
    @thalacker , you are only add Token-Expired as header value in `OnAuthenticationFailed ` event , so that meets your requirement . – Nan Yu May 30 '19 at 01:26
1

Here I describe for both - 1. Token expire and get refresh token 2. Only for unauthorized request.

async function fetchWithCredentials(url, options) {    
    options.headers['Authorization'] = 'Bearer ' + jwtToken;
    var response = await fetch(url, options);//this is a function for get a response. I didn't explain it here. Hope you understand. 
    if (response.ok) {
        return response;
    }

    let flag:boolean=false; //set flag for executing one if statement at a time.

if (response.status == 401 && response.headers.has('Token-Expired')) {
        // refresh the token
        flag=true;  //set flag true.
        //write something as per your requirement.
    } 

if (response.status == 401 && flag==false) {
        **// Only for unauthorized request. You can use this for your problem.**
        //write something as per your requirement.
    } 
}

And most important thing is, You have to use below code in startup.cs.

services.AddCors(context => context.AddPolicy("CustomPolicy", builder =>
{
    builder.AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader()
          .WithExposedHeaders("Token-Expired"); ;
}));

In Configure:

app.UseCors("CustomPolicy");

and use below code as it is.

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(System.Text.Encoding.UTF8.GetBytes(Configuration["serverSigningPassword"])),
        ValidateLifetime = true,
        ClockSkew = System.TimeSpan.Zero //the default for this setting is 5 minutes
    };
    options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return System.Threading.Tasks.Task.CompletedTask;
        }
    };
});

Now, you'll get response on client side. Hope you'll find your solution. Please let me know for any doubt.