microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fix/msal-agentic-cache

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

193lines · modecode

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