microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v2.0.8

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Core.UnitTests/Hosting/JwtExtensionsTests.cs

195lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Security.Claims;
5using Microsoft.AspNetCore.Authentication;
6using Microsoft.Extensions.DependencyInjection;
7using Microsoft.IdentityModel.JsonWebTokens;
8using Microsoft.IdentityModel.Tokens;
9using Microsoft.Teams.Core.Hosting;
10
11namespace Microsoft.Teams.Core.UnitTests.Hosting;
12
13public class JwtExtensionsTests
14{
15 private const string Tenant = "00000000-0000-0000-0000-000000000001";
16 private const string ClientId = "11111111-1111-1111-1111-111111111111";
17
18 private static SecurityToken FakeJsonWebToken(string tenantId)
19 {
20 // Minimal JWT-shaped string with a tid claim for token-claim extraction.
21 JsonWebTokenHandler handler = new();
22 SecurityTokenDescriptor descriptor = new()
23 {
24 Issuer = "unused-by-test",
25 Claims = new Dictionary<string, object> { ["tid"] = tenantId },
26 };
27 return new JsonWebToken(handler.CreateToken(descriptor));
28 }
29
30 [Fact]
31 public void ValidateTeamsIssuer_AcceptsBotFrameworkIssuer()
32 {
33 SecurityToken token = FakeJsonWebToken(Tenant);
34
35 string result = JwtExtensions.ValidateTeamsIssuer(
36 "https://api.botframework.com", token, Tenant, "https://login.microsoftonline.com/", "https://api.botframework.com");
37
38 Assert.Equal("https://api.botframework.com", result);
39 }
40
41 [Theory]
42 [InlineData("https://api.botframework.us")]
43 [InlineData("https://api.botframework.azure.cn")]
44 public void ValidateTeamsIssuer_AcceptsSovereignBotFrameworkIssuer_WhenConfigured(string sovereignBotIssuer)
45 {
46 SecurityToken token = FakeJsonWebToken(Tenant);
47
48 string result = JwtExtensions.ValidateTeamsIssuer(
49 sovereignBotIssuer, token, Tenant, "https://login.microsoftonline.us/", sovereignBotIssuer);
50
51 Assert.Equal(sovereignBotIssuer, result);
52 }
53
54 [Fact]
55 public void ValidateTeamsIssuer_RejectsPublicBotIssuer_WhenSovereignBotIssuerConfigured()
56 {
57 SecurityToken token = FakeJsonWebToken(Tenant);
58
59 Assert.Throws<SecurityTokenInvalidIssuerException>(() =>
60 JwtExtensions.ValidateTeamsIssuer(
61 "https://api.botframework.com", token, Tenant,
62 "https://login.microsoftonline.us/", "https://api.botframework.us"));
63 }
64
65 [Fact]
66 public void ValidateTeamsIssuer_AcceptsPublicEntraV2Issuer()
67 {
68 SecurityToken token = FakeJsonWebToken(Tenant);
69 string issuer = $"https://login.microsoftonline.com/{Tenant}/v2.0";
70
71 string result = JwtExtensions.ValidateTeamsIssuer(
72 issuer, token, Tenant, "https://login.microsoftonline.com/", "https://api.botframework.com");
73
74 Assert.Equal(issuer, result);
75 }
76
77 [Fact]
78 public void ValidateTeamsIssuer_AcceptsSovereignEntraIssuer_WhenInstanceConfigured()
79 {
80 SecurityToken token = FakeJsonWebToken(Tenant);
81 string sovereignInstance = "https://login.microsoftonline.us/";
82 string issuer = $"{sovereignInstance}{Tenant}/v2.0";
83
84 string result = JwtExtensions.ValidateTeamsIssuer(
85 issuer, token, Tenant, sovereignInstance, "https://api.botframework.com");
86
87 Assert.Equal(issuer, result);
88 }
89
90 [Fact]
91 public void ValidateTeamsIssuer_RejectsPublicEntraIssuer_WhenSovereignInstanceConfigured()
92 {
93 SecurityToken token = FakeJsonWebToken(Tenant);
94 string publicIssuer = $"https://login.microsoftonline.com/{Tenant}/v2.0";
95
96 SecurityTokenInvalidIssuerException ex = Assert.Throws<SecurityTokenInvalidIssuerException>(() =>
97 JwtExtensions.ValidateTeamsIssuer(
98 publicIssuer, token, Tenant, "https://login.microsoftonline.us/", "https://api.botframework.com"));
99
100 Assert.Contains(publicIssuer, ex.Message, StringComparison.Ordinal);
101 Assert.Contains(Tenant, ex.Message, StringComparison.Ordinal);
102 }
103
104 [Fact]
105 public void ValidateTeamsIssuer_AcceptsStsWindowsNetV1Issuer()
106 {
107 SecurityToken token = FakeJsonWebToken(Tenant);
108 string issuer = $"https://sts.windows.net/{Tenant}/";
109
110 string result = JwtExtensions.ValidateTeamsIssuer(
111 issuer, token, Tenant, "https://login.microsoftonline.com/", "https://api.botframework.com");
112
113 Assert.Equal(issuer, result);
114 }
115
116 [Fact]
117 public void ResolveSigningAuthority_RoutesPublicBotIssuer_ToConfiguredBotOidcUrl()
118 {
119 string authority = JwtExtensions.ResolveSigningAuthority(
120 iss: "https://api.botframework.com",
121 tid: Tenant,
122 botTokenIssuer: "https://api.botframework.com",
123 botOidcUrl: "https://login.botframework.com/v1/.well-known/openid-configuration",
124 entraInstance: "https://login.microsoftonline.com/");
125
126 Assert.Equal("https://login.botframework.com/v1/.well-known/openid-configuration", authority);
127 }
128
129 [Theory]
130 [InlineData("https://api.botframework.us", "https://login.botframework.azure.us/v1/.well-known/openid-configuration")]
131 [InlineData("https://api.botframework.azure.cn", "https://login.botframework.azure.cn/v1/.well-known/openid-configuration")]
132 public void ResolveSigningAuthority_RoutesSovereignBotIssuer_ToConfiguredBotOidcUrl(string sovereignBotIssuer, string sovereignBotOidcUrl)
133 {
134 string authority = JwtExtensions.ResolveSigningAuthority(
135 iss: sovereignBotIssuer,
136 tid: Tenant,
137 botTokenIssuer: sovereignBotIssuer,
138 botOidcUrl: sovereignBotOidcUrl,
139 entraInstance: "https://login.microsoftonline.us/");
140
141 Assert.Equal(sovereignBotOidcUrl, authority);
142 }
143
144 [Fact]
145 public void ResolveSigningAuthority_RoutesEntraIssuer_ToInstanceDerivedAuthority()
146 {
147 string authority = JwtExtensions.ResolveSigningAuthority(
148 iss: $"https://login.microsoftonline.com/{Tenant}/v2.0",
149 tid: Tenant,
150 botTokenIssuer: "https://api.botframework.com",
151 botOidcUrl: "https://login.botframework.com/v1/.well-known/openid-configuration",
152 entraInstance: "https://login.microsoftonline.com/");
153
154 Assert.Equal($"https://login.microsoftonline.com/{Tenant}/v2.0/.well-known/openid-configuration", authority);
155 }
156
157 [Fact]
158 public void ResolveSigningAuthority_RoutesEntraIssuer_ToSovereignInstanceWhenConfigured()
159 {
160 string authority = JwtExtensions.ResolveSigningAuthority(
161 iss: $"https://login.microsoftonline.us/{Tenant}/v2.0",
162 tid: Tenant,
163 botTokenIssuer: "https://api.botframework.us",
164 botOidcUrl: "https://login.botframework.azure.us/v1/.well-known/openid-configuration",
165 entraInstance: "https://login.microsoftonline.us/");
166
167 Assert.Equal($"https://login.microsoftonline.us/{Tenant}/v2.0/.well-known/openid-configuration", authority);
168 }
169
170 [Fact]
171 public void ResolveSigningAuthority_ReturnsEmpty_WhenIssuerNull()
172 {
173 string authority = JwtExtensions.ResolveSigningAuthority(
174 iss: null,
175 tid: Tenant,
176 botTokenIssuer: "https://api.botframework.com",
177 botOidcUrl: "https://login.botframework.com/v1/.well-known/openid-configuration",
178 entraInstance: "https://login.microsoftonline.com/");
179
180 Assert.Equal(string.Empty, authority);
181 }
182
183 [Fact]
184 public void AddBotAuthentication_ManualOverload_DoesNotThrow_WhenNoIConfigurationRegistered()
185 {
186 // Regression: AddBotAuthentication(clientId, tenantId) manual overload should remain usable
187 // even when no IConfiguration is registered (e.g. plain ServiceCollection scenarios).
188 ServiceCollection services = new();
189 services.AddLogging();
190
191 Exception? caught = Record.Exception(() => services.AddBotAuthentication(ClientId, Tenant));
192
193 Assert.Null(caught);
194 }
195}
196