I have a web app and web API both written in ASP .Net Core 1.1 MVC web app using Auth0 authentication server. After logging in (vis the Auth0 login form), when I try retrieve the access token in my controller before calling a web API, the token is incorrect. Here is how I retrieve the token:
So this is the code
public async Task<IActionResult> Index()
{
string accessToken = User.Claims.FirstOrDefault(c => c.Type == "access_token")?.Value;
if (accessToken == null || accessToken == "")
return View("Error");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage responseMessage = await client.GetAsync(inspectionsUrl);
if (responseMessage.IsSuccessStatusCode)
{
var responseData = responseMessage.Content.ReadAsStringAsync().Result;
List<Inspection> inspections = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Inspection>>(responseData);
return View(inspections);
}
return View("Error");
}
The “access_token” is fLnLKtdjsP4QYwm7 which is too short.
However, I also see there is an “id_token” which looks more correct, but still wrong. You can view both in the screenshot below:
http://mysql1.propworx.co.za/propworxsetup/screenshot.png
Now, if I copy and paste the id_token into https://jwt.io/ it tell me the algorithm is HS256. But this is strange as I have both my web app and web API set to RS256 on the Auth0 settings page.
I’ve been adding things to my Startup.cs file to try and figure out what is going on, so it’s become quite long, but here it is in case…
public class Startup
{
public IConfigurationRoot Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
builder.AddUserSecrets<Startup>();
builder.AddEnvironmentVariables();
Configuration = builder.Build();
HostingEnvironment = env;
}
public void ConfigureServices(IServiceCollection services)
{
// Add authentication services
services.AddAuthentication(
options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
services.AddMvc();
services.AddOptions();
services.Configure<Auth0Settings>(Configuration.GetSection("Auth0"));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<Auth0Settings> auth0Settings)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
//Set up JWT Bearer authentication first
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Audience = auth0Settings.Value.ApiIdentifier,
Authority = $"https://{auth0Settings.Value.Domain}"
});
// Add the cookie middleware
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = ctx =>
{
// if it is an ajax / api request, don't redirect to login page.
if (!(IsAjaxRequest(ctx.Request) || IsApiRequest(ctx.Request)))
{
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
}
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
return ctx.Response.WriteAsync("Unauthorized");
}
}
});
// Get the client secret used for signing the tokens
var keyAsBytes = Encoding.UTF8.GetBytes(auth0Settings.Value.ClientSecret);
// if using non-base64 encoded key, just use:
//var keyAsBase64 = auth0Settings.Value.ClientSecret.Replace('_', '/').Replace('-', '+');
//var keyAsBytes = Convert.FromBase64String(keyAsBase64);
var issuerSigningKey = new SymmetricSecurityKey(keyAsBytes);
// Add the OIDC middleware
var options = new OpenIdConnectOptions("Auth0")
{
// Set the authority to your Auth0 domain
Authority = $"https://{auth0Settings.Value.Domain}",
// Configure the Auth0 Client ID and Client Secret
ClientId = auth0Settings.Value.ClientId,
ClientSecret = auth0Settings.Value.ClientSecret,
// Do not automatically authenticate and challenge
AutomaticAuthenticate = false,
AutomaticChallenge = false,
// Set response type to code
ResponseType = "code",
// Set the callback path, so Auth0 will call back to http://localhost:5000/signin-auth0
// Also ensure that you have added the URL as an Allowed Callback URL in your Auth0 dashboard
CallbackPath = new PathString("/signin-auth0"),
// Configure the Claims Issuer to be Auth0
ClaimsIssuer = "Auth0",
// The UserInfo endpoint does not really return any extra claims which were not returned in the original auth response, so
// we can save ourselves from making an extra request
GetClaimsFromUserInfoEndpoint = false,
// Saves tokens to the AuthenticationProperties
SaveTokens = true,
Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
// Get the ClaimsIdentity
var identity = context.Principal.Identity as ClaimsIdentity;
if (identity != null)
{
// Add the Name ClaimType. This is required if we want User.Identity.Name to actually return something!
if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Name) &&
identity.HasClaim(c => c.Type == "name"))
identity.AddClaim(new Claim(ClaimTypes.Name, identity.FindFirst("name").Value));
// Check if token names are stored in Properties
if (context.Properties.Items.ContainsKey(".TokenNames"))
{
// Token names a semicolon separated
string] tokenNames = context.Properties.Items".TokenNames"].Split(';');
// Add each token value as Claim
foreach (var tokenName in tokenNames)
{
// Tokens are stored in a Dictionary with the Key ".Token.<token name>"
string tokenValue = context.Properties.Items$".Token.{tokenName}"];
identity.AddClaim(new Claim(tokenName, tokenValue));
}
}
}
return Task.CompletedTask;
},
// handle the logout redirection
OnRedirectToIdentityProviderForSignOut = (context) =>
{
var logoutUri = $"https://{auth0Settings.Value.Domain}/v2/logout?client_id={auth0Settings.Value.ClientId}";
var postLogoutUri = context.Properties.RedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
{
if (postLogoutUri.StartsWith("/"))
{
// transform to absolute
var request = context.Request;
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
}
logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
}
context.Response.Redirect(logoutUri);
context.HandleResponse();
return Task.CompletedTask;
}
},
// manually setup the signature validation key
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKey = issuerSigningKey
}
};
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("name");
options.Scope.Add("email");
options.Scope.Add("picture");
options.Scope.Add("country");
options.Scope.Add("roles");
options.Scope.Add("read:inspections");
app.UseOpenIdConnectAuthentication(options);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
private static bool IsAjaxRequest(HttpRequest request)
{
var query = request.Query;
if ((query != null) && (query"X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IHeaderDictionary headers = request.Headers;
return ((headers != null) && (headers"X-Requested-With"] == "XMLHttpRequest"));
}
private static bool IsApiRequest(HttpRequest request)
{
return request.Path.StartsWithSegments(new PathString("/api"));
}
}