4
votes

Following the info from Azure Bot Service Authentication I tried to verify the JWT token using the public keys exposed via OpenId:

but the key from the directline.botframework.com conversation JWT token is in neither of them, see the error below:

"IDX10501: Signature validation failed. Unable to match key: kid: '...."

        ConfigurationManager<OpenIdConnectConfiguration> configurationManager =
            new ConfigurationManager<OpenIdConnectConfiguration>(openIdMetadataAddress, new OpenIdConnectConfigurationRetriever());
        OpenIdConnectConfiguration openIdConnectConfiguration = await configurationManager.GetConfigurationAsync(CancellationToken.None);
        TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
        {
            ValidIssuer = authorizationDomain,
            ValidateAudience = false,
            IssuerSigningKeys = openIdConnectConfiguration.SigningKeys
        };
        try
        {
            JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
            jwtSecurityTokenHandler.ValidateToken(jwt, tokenValidationParameters, out _);
            return true;
        }
        catch (SecurityTokenException)
        {
            return false;
        }

JWT token example (generated when you start a directline conversation in bot framework):

ew0KICAiYWxnIjogIlJTMjU2IiwNCiAgImtpZCI6ICJBT08tZXhGd2puR3lDTEJhOTgwVkxOME1tUTgiLA0KICAieDV0IjogIkFPTy1leEZ3am5HeUNMQmE5ODBWTE4wTW1ROCIsDQogICJ0eXAiOiAiSldUIg0KfQ.ew0KICAiYm90IjogImRldi1tYXJpdXNpbXBvLW5lcnRlc3Rib3QwbmVnNC1ib3QiLA0KICAic2l0ZSI6ICJ0RVRMM2ZES3ZGdyIsDQogICJjb252IjogIkZPeXRUdThrTzVRNFVOZmxpS3pSMlgtaCIsDQogICJuYmYiOiAxNTc1MzcxNDYzLA0KICAiZXhwIjogMTU3NTM3NTA2MywNCiAgImlzcyI6ICJodHRwczovL2RpcmVjdGxpbmUuYm90ZnJhbWV3b3JrLmNvbS8iLA0KICAiYXVkIjogImh0dHBzOi8vZGlyZWN0bGluZS5ib3RmcmFtZXdvcmsuY29tLyINCn0.IMKMdlart3nEg6iegVvz5MQ86cp36nLXK1mIT0a7xiOmRLMMlvUjqHA9d2EJUovYAML4RGAapP7BWYgU9CnYtL9dXrJwj_JNacJDov18zUTzbyfzcL8goFJG_PJRjJZbN7ZZZdp1lIis9DbrL56HQBgiBuW4BGhNhgmBauh8SFOIvWfhOYmWoxyfI7Uzkd_5LTVdeL7Lyqi5Ulxzf8UsuDI372US6dA0LZ0BZMCU-M6S9bYFCSBwrvjD5uZOYJ8drCuXnuOl1rxRP_kfMVi-kodWZ84-puo5JYt5QhpptP6vuBYO5-6fW359zJ1csUk-xWFlOH88dh09lpJDbcXgXg

enter image description here

using (var client = new DirectLineClient(secretKey))
{
    var conversation = await client.Conversations.StartConversationAsync();
    var token = conversation.Token;
}
1
Hey Danut, were you able to verify the token. I've a similar case where the token generated from directline.botframework.com/v3/directline/tokens/generate needs to be validated. I pass it through some middleware for extra modifications. I need to validate it but could not find the open-id config urlVijay
Microsoft does not plan to make the keys public. I used "directline.botframework.com/v3/directline/tokens/refresh" to validate the token, via http clientDanut Radoaica
Thank you @Danut RadoaicaVijay

1 Answers

3
votes

UPD: I'm not sure what the key from the directline.botframework.com conversation JWT token exactly is. If you can provide expired token for me, it should be possible to find out how to validate it.


Metadata endpoint:

https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration

Your code works fine.

Please check out the steps of the test I've done below:

  1. Create a Web App Bot via Azure Portal.

    Full description here: https://docs.microsoft.com/en-us/azure/bot-service/abs-quickstart?view=azure-bot-service-4.0

  2. Obtain a token.

    Take MICROSOFT-APP-ID and MICROSOFT-APP-PASSWORD from the Configuration of your Web App Bot.

    POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token
    Host: login.microsoftonline.com
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT-APP-PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default
    
  3. Come up with values to validate the token.

    3.1. Metadata endpoint

    Constructed from token endpoint.

    https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration

    3.2. Issuer

    Decoded the token at jwt.io and took actual issuer from there.

    https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/

    3.3. Audience

    Same way as for issuer.

    https://api.botframework.com

  4. Validate the token and get ClaimsPrincipal object decoded from the token.

    static async Task Main(string[] args)
    {
        var jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJodHRwczovL2FwaS5ib3RmcmFtZXdvcmsuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiLyIsImlhdCI6MTU3NTkyMDQwMSwibmJmIjoxNTc1OTIwNDAxLCJleHAiOjE1NzU5MjQzMDEsImFpbyI6IjQyVmdZRGhjMDMwNGFrdENBcXZMYTM2aFJTTExBUT09IiwiYXBwaWQiOiI0MmY5NGM0MS0wYmMwLTRiN2MtODc2MC1jOGI1NTRhYjE2NDIiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIvIiwidGlkIjoiZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiIiwidXRpIjoiMWpvWi1TUng5a1MwdUxucVYyOE5BQSIsInZlciI6IjEuMCJ9.WWxIinArkAJgVyAUMu6UJvCy9OJ-B2KGxpT-t9wdRF9qlpw00GvXXuL0HCpUEIWC0efA3ETF3bBBJVYjcXoKsC6Up2UWzkAgA2O_TZhPkG5Tkm5MT7f_mIdoEVWoddawjv3ec_EUfSq1B_UrQu-05AHMe0n46kN94yUWbsIAv9z6Q_HSuKO6_kSSyGwbnsAbsT2nWqYyE05BstvZUccQrSvR4UdbugKDEDxAixhVvOrFJiLng3pKeSljXUxWte7ETw59X9EuA4WJPURzW-kWPJ8tGIP2Wz6RVDU-D1eCp-DB3o4PxT-t8UTBMjwUJBFqQo-w1GtQasJwcnUKKkBhgA";
        var claimsPrincipal = await Authenticate(jwt);
    }
    
    
    public static async Task<ClaimsPrincipal> Authenticate(string jwt)
    {
        var openIdMetadataAddress = "https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration";
        var issuer = "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/";
        var audience = "https://api.botframework.com";
    
        var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            openIdMetadataAddress,
            new OpenIdConnectConfigurationRetriever());
        var openIdConnectConfiguration = await configurationManager.GetConfigurationAsync();
        var tokenValidationParameters = new TokenValidationParameters
        {
            // Updated validation parameters
            ValidIssuer = issuer,
            ValidAudience = audience,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKeys = openIdConnectConfiguration.SigningKeys
        };
    
        try
        {
            var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
            var claimsPrincipal = jwtSecurityTokenHandler.ValidateToken(jwt, tokenValidationParameters, out _);
            return claimsPrincipal;
        }
        catch (SecurityTokenException e)
        {
            return null;
        }
    }