question

77293880 avatar image
0 Votes"
77293880 asked 77293880 commented

Access Token validating fails with JWTSecurityTokenHandler - Signature invalid

I try to validate an access token, which I get from Azure. I created the token the following way: 1. I did an Azure AD App Registration for our application. 2. Created a Search Bot and added the app registration to the bot. 3. I tested the connection in the bot successfull an got an Access Token ![102398-createaccesstoken.png][1] In our c# application we try to validate the Token with JWTSecurityTokenHandler, but die signature is invalid: Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException HResult=0x80131500 Message=IDX10511: Signature validation failed. Keys tried: 'System.Text.StringBuilder'. kid: 'System.String'. Exceptions caught: 'System.Text.StringBuilder'. token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'. Source=TokenTestApp StackTrace: at TokenTestApp.Program.ValidateTokenInternal(String token, String issuer, String validAudience, String wellKnownUrl, CancellationToken ct) in C:\Users\ttt\Source\Repos\TokenTestApp\TokenTestApp\Program.cs:line 109 at TokenTestApp.Program.Main(String[] args) in C:\Users\ttt\Source\Repos\TokenTestApp\TokenTestApp\Program.cs:line 23 The only way around this was to deactivate the signature validation by using the SignatureValidator delegate in the TokenValidationParameters class – which is obviously a bad idee. Is there a way to validate these tokens with the JWTSecurityTokenHandler? Thanks for your help! [1]: /answers/storage/attachments/102398-createaccesstoken.png

azure-bot-service
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@HaraldThnig-2517 Thanks for the question. Can you please share the document that you are trying. Please follow the below thread that can help.
https://stackoverflow.com/questions/25693798/jwtsecuritytokenhandler-and-tokenvalidationparameters

0 Votes 0 ·

anonymous user Just checking any update on this.

0 Votes 0 ·
77293880 avatar image
1 Vote"
77293880 answered EvgenyVolkov-9546 commented

Hi
I got a solution from microsoft. Following problem in my app registration:
I defined a scope from Graph API: User.Read User.ReadBasic.All Mail.Read
If a scope will be set from Graph API, the token can just be validated from Graph!
You can see that in jwt.io. If the aud is like "00000003-0000-0000-c000-000000000000" the token is from Graph.
What I had to do to solve the problem:
1. To protect our own custom API, I had to register an application to represent it on Azure AD and obtain an access_token/id_token for it. As I did it already bevor.
2. Section - Expose an API: Create a new scope: name = access_as_user
3. Section - API permissions: Add a new permission for my registered application and my scope access_as_user
4. Section - Manifest: Change entry "accessTokenAcceptedVersion" from null to 2
5. Check the new token from azure with jwt.io. If the aud is equal the registered application id the token can be successfull validated.

That's it. Hope it helps all other who are searching a solution for a problem like that.

Best regards
Harald


· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi Harald!

I just got exact the same problem (!) and did the same steps to solve it, but still have aud 00000003-0000-0000-c000-000000000000 and can't validate it.
Looks I missed something!
When you created new scope what Application ID URI value you set? Is this something can make a difference?
api://<app_ID>/access_as_user

Thank you very much,
Evgeny


0 Votes 0 ·
77293880 avatar image
0 Votes"
77293880 answered

Hi Evgeny
The new scope I created looks identically as you describe (app://<app_ID>/access_as_user). If you call your new registred app to get the token, you have to set a scope. If you set a graph scope like user.read, you will get a graph token like you get. You have to set just the token you new created (app://<app_ID>/access_as_user). Then it will work.
Cheers Harald

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

EvgenyVolkov-9546 avatar image
0 Votes"
EvgenyVolkov-9546 answered

Hi Harald,

Thank you! Just to confirm! When you're saying You have to set just the token you new created - on what step? On earlier step of authentication or later on attempt to validate the received token?

Thank you very much,
Evgeny

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

77293880 avatar image
0 Votes"
77293880 answered

Hi Evgeny,
You have to set the scope if you request the token from your registered app. You can not set a scope if you validate the token.
Cheers Harald

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

EvgenyVolkov-9546 avatar image
0 Votes"
EvgenyVolkov-9546 answered

Hi Harald,

Thanks you very much again!

It does work! So, when my javasctipt (based on masl.js) client is asking for scope ["api://app_id/access_as_user","user.read"] - the received token contains expected aud and can be validated by server - which is good and it's actually what I'm trying to get! BTW the token is different and contains less information when asking scope ["api://app_id/access_as_user","user.read"].

But now I have a different problem with authentication itself. The user logged and the token received, but flow can't continue - when the client is asking scope ["api://app_id/access_as_user","user.read"] - getting back from Azure response: Your browser is currently set to block cookies. You need to allow cookies to use this service. Cookies are small text files stored on your computer that tell us when you're signed in. To learn how to allow cookies, check the online help in your web browser. - I do use cookies in my chrome and didn't block it ever.

The order of scopes is also changing behavior
["user.read", "api://app_id/access_as_user"] - looks like ignored access_as_user scope, because the token contains again "aud": "00000003-0000-0000-c000-000000000000" and login works, but token can't be validated.

May be I still missing something? May be I need to write different validation method - now I'm using some example using Appreciate if you have any suggestion.

Validation method
IdentityModelEventSource.ShowPII = true;
string token_access_token_NOT_GOOD = "";
string myTenant = "AD-ID";
var myAudience = "Client-ID"; //https://graph.microsoft.com/.default

         var myIssuer = String.Format("https://login.microsoftonline.com/{0}/v2.0", myTenant);  
         var mySecret = "zzz";  
         var mySecurityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(mySecret));              
         var stsDiscoveryEndpoint = String.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", myTenant);  
         var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());  
         var config = await configManager.GetConfigurationAsync();  
            
         var tokenHandler = new JwtSecurityTokenHandler();  
  
         var validationParameters = new TokenValidationParameters  
         {  
             ValidAudience = myAudience,  
             ValidIssuer = myIssuer,  
             IssuerSigningKeys = config.SigningKeys,  
             ValidateLifetime = false,  
             IssuerSigningKey = mySecurityKey  ,
             RequireExpirationTime = false 
         };  

  
         var validatedToken = (SecurityToken)new JwtSecurityToken();  
  
         // Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)  
         tokenHandler.ValidateToken(token_access_token_NOT_GOOD, validationParameters, out validatedToken);  
         Console.WriteLine(validatedToken);  
         Console.ReadLine();


Thanks,
Evgeny

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

EvgenyVolkov-9546 avatar image
0 Votes"
EvgenyVolkov-9546 answered 77293880 commented

Just FYI.

Found a different way to validate the token.
private async static Task<string> Validate3()
{
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var token = "";
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
return ex.ToString();
}
}

· 3
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

It's not a good solution.

The result is always OK even after logged out the the user that received the token.

0 Votes 0 ·

It's not correct as well. Sorry for confusing you!

I still can use this approach, because once the token expired - not immediately after logout - httpClient.SendAsync(request) returns Unauthorized 401.

So, for my needs it's good enough.

Thanks!

0 Votes 0 ·

Hi Evgeny
Greate! For token validation I used the default class "JWTSecurityTokenHandler" from .Net Framework. I'm sorry but I can not help you with Java Script.
Cheers Harald

0 Votes 0 ·