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).

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

Any ideas as to why my header is not sticking to my response in the javascript?
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-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragmaReference : 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 .
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With