Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix:[#6877] Bot is not accepting v2 tokens from Bot Framework Emulator - Single Tenant Bots #6879

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;

namespace Microsoft.Bot.Connector.Authentication
Expand Down Expand Up @@ -77,9 +78,13 @@ public static bool IsTokenFromEmulator(string authHeader)
{
//If the issuer doesn't exist, this is added using the Emulator token issuer structure.
//This allows use of the SingleTenant authentication through Emulator.
var newIssuer = AuthenticationConstants.ValidTokenIssuerUrlTemplateV1.Replace("{0}", tenantId);
ToBotFromEmulatorTokenValidationParameters.ValidIssuers =
ToBotFromEmulatorTokenValidationParameters.ValidIssuers.Concat(new string[] { newIssuer });
var validTokenIssuers = new List<string>();
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));

ToBotFromEmulatorTokenValidationParameters.ValidIssuers = validTokenIssuers;
}

// Is the token issues by a source we consider to be the emulator?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.IdentityModel.Tokens;
using Xunit;

namespace Microsoft.Bot.Connector.Tests.Authentication
{
public class EmulatorValidationTests
{
[Fact]
public void IsTokenFromEmulator_ShouldReturnFalseOnTokenBadFormat()
{
//Token with bad format is not processed
var authHeader = string.Empty;
Assert.False(EmulatorValidation.IsTokenFromEmulator(authHeader));

//If the token doesn't contain an issuer value it returns false
authHeader = GenerateMockBearerToken(null);
Assert.False(EmulatorValidation.IsTokenFromEmulator(authHeader));
}

[Fact]
public void IsTokenFromEmulator_ShouldReturnFalseOnInvalidTokenIssuer()
{
var authHeader = GenerateMockBearerToken("https://mockIssuer.com");
Assert.False(EmulatorValidation.IsTokenFromEmulator(authHeader));
}

[Fact]
public void IsTokenFromEmulator_ShouldReturnTrueOnValidTokenIssuer()
{
//Validate issuer with V1 Token
var authHeader = GenerateMockBearerToken(AuthenticationConstants.ValidTokenIssuerUrlTemplateV1);
Assert.True(EmulatorValidation.IsTokenFromEmulator(authHeader));

//Validate issuer with V2 Token
authHeader = GenerateMockBearerToken(AuthenticationConstants.ValidTokenIssuerUrlTemplateV2);
Assert.True(EmulatorValidation.IsTokenFromEmulator(authHeader));

//Validate Government issuer with V1 Token
authHeader = GenerateMockBearerToken(AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1);
Assert.True(EmulatorValidation.IsTokenFromEmulator(authHeader));

//Validate Government issuer with V2 Token
authHeader = GenerateMockBearerToken(AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2);
Assert.True(EmulatorValidation.IsTokenFromEmulator(authHeader));
}

private string GenerateMockBearerToken(string issuer)
{
var key = Encoding.UTF8.GetBytes("ThisIsASuperMockSecretKey123456789");
var signingKey = new SymmetricSecurityKey(key);
var tenantId = Guid.NewGuid().ToString();

var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Issuer = issuer?.Replace("{0}", tenantId),
Audience = "https://api.example.com",
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "John Doe"),
new Claim(ClaimTypes.Role, "Admin"),
new Claim("tid", tenantId)
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256Signature)
};

// Create and return the JWT
var token = tokenHandler.CreateToken(tokenDescriptor);
return $"Bearer {tokenHandler.WriteToken(token)}";
}
}
}
Loading