Scenario:
I have an angular5 client application, which uses hello.js to authenticate users using their office 365 credentials.
Client Code:
hello.init({
msft: {
id: configuration.AppID,
oauth: {
version: 2,
auth: 'https://login.microsoftonline.com/' + configuration.TenantID + '/oauth2/v2.0/authorize'
},
scope_delim: ' ',
form: false
},
},
{ redirect_uri: configuration.redirecturl }
);
}
login() {
hello('msft').login({ scope: 'User.Read People.Read', display: 'popup' })
.then((authData: any) => { // console.log(authData);
this.zone.run(() => {
// get profile
}
A successful response is (Manipulated for security reasons)
{
"msft":{
"access_token":"REMOVED TOKEN HERE",
"token_type":"Bearer",
"expires_in":3599,
"scope":"basic,User.Read,People.Read",
"state":"",
"session_state":"3b82898a-2b3f-445363f-89ae-d9696gg64ad3",
"client_id":"672330148-2bb43-3080-9eee-1f46311f789c",
"network":"msft",
"display":"popup",
"redirect_uri":"http://localhost:5653/",
"expires":15245366.218
}
}
The decoded access_token has these few keys:
Header:
1. nonce (requires some special processing, I couldn't find any documentation regarding special processing)
2. typ: JWT
PayLoad:
"aud": "https://graph.microsoft.com",
Once the access_token is received, I am sending the access_token in authorization header of every call to my backend API. The goal is to validate the token and only send a successful response if the access_token is validated and authorized. If unsuccessful, 401 Unauthorized is the response.
API Code to validate access_token, ASP .NET CORE 2, Following (https://auth0.com/blog/securing-asp-dot-net-core-2-applications-with-jwts/)
namespace JWT
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddMvc();
}
}
}
// other methods
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
In appsettings.json I have:
{ "Jwt": {
"Key": "verySecretKey", **(I got the key from https://login.microsoftonline.com/common/discovery/keys with the kid value in access_token header)**
"Issuer": "https://sts.windows.net/49bcf059-afa8-4bf9-8470-fad0c9cce27d/", } }
Finally, the error I receive is : "WWW-Authenticate →Bearer error="invalid_token", error_description="The signature key was not found""
I have been stuck here since past few days, any help will be life savior.
Key Points:
I tried to validate the access_token in jwt.io (https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx) but I was not able to.
The aud here is https://graph.microsoft.com, I am not sure if I need to and why do I need to change aud to my client id. how do I do that?
Is there something wrong in the code or do i need to tweak the way I am requesting header tokens.
Please let me know if you need more information.
Microsoft Graph API access tokens are signed differently from other access tokens from what I can see. You do not need to validate tokens that are meant for another API, it is their job.
I don't know about HelloJS, but you should be able to get an Id token after authentication with
response_type=id_token token. Then you need to attach that to the requests. It should have your client id as the audience.The only thing that stands out to me is that you are doing a lot of unnecessary configuration. Basically the configuration should be:
The handler will automatically fetch the public signing keys on startup. It's not really a good idea to hard-code signing keys in your app since your app will break when AAD finishes signing key rollover.