Search code examples
angularwcfjwtazure-active-directoryopenid-connect

Get Error "IDX10511: Signature validation failed" when trying to validate an Azure AD token


I have an Angular 10 application that authenticates with Azure AD using oidc. It successfully gets the token and then makes a REST call to a WCF service (.net framework 4.7) sending the token it received from Azure. The validation code in the WCF Service fails with the following error: IDX10511: Signature validation failed. Keys tried: 'System.Text.StringBuilder'. kid: 'System.String'. Exceptions caught: 'System.Text.StringBuilder'. token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.

If I authenticate using Auth0, the same WCF code is able to validate the jwt from Auth0 with no errors.

Below is the UserManagerSettings for my oidc client:

          this.stsSettings = {
            authority: "https://login.microsoftonline.com",
            client_id: myclientId,
            redirect_uri: `${myclientRoot}signin-callback`,
            scope: "openid profile email",
            response_type: "code",
            loadUserInfo: false, // setting loadUserInfo to true causes error because CORS blocks the call to the user info endpoint.
            post_logout_redirect_uri: `${myclientRoot}signout-callback`,
            silent_redirect_uri: `${myclientRoot}assets/silent-callback.html`,
            metadata: {
              issuer: `${mystsAuthority}/${mytenantId}/v2.0`,
              authorization_endpoint: `${mystsAuthority}/${mytenantId}/oauth2/v2.0/authorize`,
              token_endpoint: `${mystsAuthority}/${mytenantId}/oauth2/v2.0/token`,
              "jwks_uri": `https://login.microsoftonline.com/${mytenantId}/discovery/v2.0/keys`,
            }

My C# code in the WCF Service to validate the token

                JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

                JwtSecurityToken jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;

                if (jwtToken == null)
                {
                    return null;
                }


                IConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{Properties.Settings.Default.ValidIssuer.TrimEnd('/')}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());

                OpenIdConnectConfiguration openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);

                var validationParameters = new TokenValidationParameters()
                {
                    LifetimeValidator = LifetimeValidator,
                    ValidAudiences = new[] { Properties.Settings.Default.CommaDelimitedValidAudiences },
                    AudienceValidator = AudienceValidator,
                    ValidIssuer = Properties.Settings.Default.ValidIssuer,
                    ValidateLifetime = true,
                    RequireExpirationTime = true,
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = false,
                    IssuerSigningKeys = openIdConfig.SigningKeys,
                    RequireSignedTokens = false
                };

                SecurityToken securityToken;
                var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);

                return principal;
            }

            catch (Exception ex)
            {
                return null;
            }

The exception occurs at the code tokenHandler.ValidateToken(token, validationParameters, out securityToken);

I tried various online suggestions, adding a scope in Azure, the workaround for "a 0 byte prefix.", etc and they didn't work.

I've been on this too long and am running out of ideas.

Please help.

Thanks,


Solution

  • I'm posting this in case it will help someone else.

    I resolved this issue mostly through adding some extra configuration in Azure AD. I went to "Expose an API" and added a new scope (keeping all defaults and putting "myapi" in the “Scope name”, “Admin consent display name”, and “Admin consent description” fields. I then clicked on “Add a client application”, entered the Client ID of my application (can be found at Overview), ticked the Authorized scopes checkbox and then clicked “Add application”. Once this was done the scopes have an entry something like "api://09098082309823009ljo24/myapi" which I copied. I then added this to my scope in the UserManagerSettings for my oidc client. This value became the audience in my token which you may need to validate your token.