Skip to content

Commit

Permalink
4.19.1 Patch (#6578)
Browse files Browse the repository at this point in the history
* Fix cast issue in SendHandoffActivity action. (#6575)

* [#6563] Expired JWT token exception not being handled (#6572)

* Add UnauthorizedAccessException when the SecurityTokenExpiredException is thrown

* Add CloudAdapter test

Co-authored-by: sw-joelmut <joel.mut@southworks.com>

Co-authored-by: Cecilia Avila <44245136+ceciliaavila@users.noreply.github.com>
Co-authored-by: erquirogasw <64815358+erquirogasw@users.noreply.github.com>
Co-authored-by: sw-joelmut <joel.mut@southworks.com>
  • Loading branch information
4 people authored Jan 17, 2023
1 parent 74a8612 commit dcaa545
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public SendHandoffActivity([CallerFilePath] string callerPath = "", [CallerLineN
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
var handoffContext = HandoffContext?.GetValue(dc.State);
var context = (JObject)handoffContext;
var context = JObject.FromObject(handoffContext);
var contextResult = ((ValueExpression)context).EvaluateExpression(dc.State);
var transcript = Transcript?.GetValue(dc.State);
var eventActivity = EventFactory.CreateHandoffInitiation(dc.Context, contextResult, transcript);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ private async Task<ClaimsPrincipal> ValidateTokenAsync(string jwtToken, string c

return principal;
}
catch (SecurityTokenExpiredException err)
{
Trace.TraceError(err.Message);
throw new UnauthorizedAccessException($"The token has expired");
}
catch (SecurityTokenSignatureKeyNotFoundException)
{
var keys = string.Join(", ", (config?.SigningKeys ?? Enumerable.Empty<SecurityKey>()).Select(t => t.KeyId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,26 @@ public async Task JwtTokenExtractor_WithValidCert_ShouldNotAllowCertSigningKey()
}
}

private static Task<ClaimsIdentity> BuildExtractorAndValidateToken(X509Certificate2 cert, TokenValidationParameters validationParameters = null)
[Fact]
[Trait("TestCategory", "WindowsOnly")]
public async Task JwtTokenExtractor_WithExpiredToken_ShouldThrowUnauthorizedAccessException()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var now = DateTimeOffset.UtcNow;
var cn = "test.cert.botframework.com";

// Create valid self-signed certificate
var cert = CreateSelfSignedCertificate(cn, from: now.AddDays(-10), to: now.AddDays(9));

// It will fail since the generated token is expired.
await Assert.ThrowsAnyAsync<UnauthorizedAccessException>(() => BuildExtractorAndValidateToken(cert, expires: now.Date.AddDays(-5), notBefore: now.Date.AddDays(-10)));

DeleteKeyContainer(cn);
}
}

private static Task<ClaimsIdentity> BuildExtractorAndValidateToken(X509Certificate2 cert, TokenValidationParameters validationParameters = null, DateTime? expires = null, DateTime? notBefore = null)
{
// Custom validation parameters that allow us to test the extractor logic
var tokenValidationParams = validationParameters ?? CreateTokenValidationParameters(cert);
Expand All @@ -103,17 +122,18 @@ private static Task<ClaimsIdentity> BuildExtractorAndValidateToken(X509Certifica
"https://login.botframework.com/v1/.well-known/openidconfiguration",
AuthenticationConstants.AllowedSigningAlgorithms);

var token = CreateTokenForCertificate(cert);
var token = CreateTokenForCertificate(cert, expires, notBefore);

return tokenExtractor.GetIdentityAsync($"Bearer {token}", "test");
}

private static string CreateTokenForCertificate(X509Certificate2 cert)
private static string CreateTokenForCertificate(X509Certificate2 cert, DateTime? expires = null, DateTime? notBefore = null)
{
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Expires = DateTime.UtcNow.AddMinutes(5),
Expires = expires ?? DateTime.UtcNow.AddMinutes(5),
NotBefore = notBefore,
SigningCredentials = new SigningCredentials(new X509SecurityKey(cert), SecurityAlgorithms.RsaSha256Signature)
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,36 @@ public async Task CloudAdapterCreateConversation()
Assert.Equal(expectedChannelId, actualChannelId);
}

[Fact]
public async Task ExpiredTokenShouldThrowUnauthorizedAccessException()
{
// Arrange
var headerDictionaryMock = new Mock<IHeaderDictionary>();

// Expired token with removed AppID
var token = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJaUXBKM1VwYmpBWVhZR2FYRUpsOGxWMFRPSSIsImtpZCI6IjJaUXBKM1VwYmpBWVhZR2FYRUpsOGxWMFRPSSJ9.eyJhdWQiOiJodHRwczovL2FwaS5ib3RmcmFtZXdvcmsuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiLyIsImlhdCI6MTY3MDM1MDQxNSwibmJmIjoxNjcwMzUwNDE1LCJleHAiOjE2NzA0MzcxMTUsImFpbyI6IkUyWmdZTkJONEpWZmxlOTJUc2wxYjhtOHBjOWpBQT09IiwiYXBwaWQiOiI5ZGRmM2QwZS02ZDRlLTQ2MWEtYjM4Yi0zMTYzZWQ3Yjg1NmIiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIvIiwicmgiOiIwLkFXNEFJSlRVMXB2ejkwMmgzTldhazFoeDIwSXpMWTBwejFsSmxYY09EcS05RnJ4dUFBQS4iLCJ0aWQiOiJkNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIiLCJ1dGkiOiJIWDlncld2bU1rMlhESTRkS3BHSEFBIiwidmVyIjoiMS4wIn0.PBLuja5sCcDfFjweoy-VucvbfHEyEcs1GyqXjekzBqgvK-mSc1UrEfqr5834qY6dLNsXVIMJzMFuH6WyPbnAfIfRcabdiVSOAl8N8e9Tex6vHfPi4h4P2F96VkXU80EtZX4QMjsJMDJ5eXbJlIDEAxXoJbAdHqgy-lHcVBx8XK7toJ_W7vSsFhis3C4CPCHI1cf1WuHVwfFXBiNwsOzj9cnRUKpea6UELV89q4C0L6aeSNdWYXehZmgq-wlo2wIaGgQ7rOXx4MlIrc83LBzMMc6TWvBJecK6O8pJWLe6BTwOltBI8Tmo2hWnY1OnsbOhbSSlfwLaZqKI7QpA50_2GQ";

headerDictionaryMock.Setup(h => h[It.Is<string>(v => v == "Authorization")]).Returns<string>((_) => token);

var httpRequestMock = new Mock<HttpRequest>();
httpRequestMock.Setup(r => r.Method).Returns(HttpMethods.Post);
httpRequestMock.Setup(r => r.Body).Returns(CreateInvokeActivityStream());
httpRequestMock.Setup(r => r.Headers).Returns(headerDictionaryMock.Object);

var response = new MemoryStream();
var httpResponseMock = new Mock<HttpResponse>().SetupAllProperties();
httpResponseMock.Setup(r => r.Body).Returns(response);

var bot = new InvokeResponseBot();

// Act
var adapter = new CloudAdapter();
await adapter.ProcessAsync(httpRequestMock.Object, httpResponseMock.Object, bot);

// Assert
Assert.Equal((int)HttpStatusCode.Unauthorized, httpResponseMock.Object.StatusCode);
}

private static Stream CreateMessageActivityStream(string userId, string channelId, string conversationId, string recipient, string relatesToActivityId)
{
return CreateStream(new Activity
Expand Down Expand Up @@ -978,11 +1008,11 @@ private class ConnectorFactoryBot : IBot
public IIdentity Identity { get; private set; }

public IConnectorClient ConnectorClient { get; private set; }

public UserTokenClient UserTokenClient { get; private set; }

public BotCallbackHandler BotCallbackHandler { get; private set; }

public string OAuthScope { get; private set; }

public AuthenticationHeaderValue Authorization { get; private set; }
Expand Down Expand Up @@ -1036,9 +1066,9 @@ private class TestContentStream : IContentStream
public Guid Id { get; set; }

public string ContentType { get; set; }

public int? Length { get; set; }

public Stream Stream { get; set; }
}

Expand Down

0 comments on commit dcaa545

Please sign in to comment.