microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
3ddf9fa76ec1801a0e3ca312c6d9855879571ac1

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Apps.BotBuilder/CompatUserTokenClient.cs

174lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Bot.Schema;
5using Microsoft.Teams.Core;
6
7namespace Microsoft.Teams.Apps.BotBuilder;
8
9/// <summary>
10/// Provides a compatibility layer that adapts the Teams Bot Core <see cref="UserTokenClient"/> to the Bot Framework's
11/// <see cref="Microsoft.Bot.Connector.Authentication.UserTokenClient"/> interface.
12/// </summary>
13/// <remarks>
14/// This adapter enables legacy Bot Framework bots to use the new Teams Bot Core token management system
15/// without code changes. It converts between the two different token result formats and delegates all operations
16/// to the underlying Core UserTokenClient.
17/// </remarks>
18/// <param name="utc">The underlying Teams Bot Core UserTokenClient that performs the actual token operations.</param>
19internal sealed class CompatUserTokenClient(UserTokenClient utc) : Microsoft.Bot.Connector.Authentication.UserTokenClient
20{
21 /// <summary>
22 /// Gets the status of all tokens for a specific user across all configured OAuth connections.
23 /// </summary>
24 /// <param name="userId">The unique identifier of the user. Cannot be null or empty.</param>
25 /// <param name="channelId">The channel identifier where the user is interacting. Cannot be null or empty.</param>
26 /// <param name="includeFilter">Optional filter to limit which token statuses are returned. Pass null or empty to include all.</param>
27 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
28 /// <returns>
29 /// A task that represents the asynchronous operation. The task result contains an array of <see cref="TokenStatus"/>
30 /// objects representing the status of each configured connection for the user.
31 /// </returns>
32 public async override Task<TokenStatus[]> GetTokenStatusAsync(string userId, string channelId, string includeFilter, CancellationToken cancellationToken)
33 {
34 GetTokenStatusResult[] res = await utc.GetTokenStatusAsync(userId, channelId, includeFilter, cancellationToken).ConfigureAwait(false);
35 return res.Select(t => new TokenStatus
36 {
37 ChannelId = channelId,
38 ConnectionName = t.ConnectionName,
39 HasToken = t.HasToken,
40 ServiceProviderDisplayName = t.ServiceProviderDisplayName,
41 }).ToArray();
42 }
43
44 /// <summary>
45 /// Retrieves an OAuth token for a user from a specific connection.
46 /// </summary>
47 /// <param name="userId">The unique identifier of the user requesting the token. Cannot be null or empty.</param>
48 /// <param name="connectionName">The name of the OAuth connection configured in Azure Bot Service. Cannot be null or empty.</param>
49 /// <param name="channelId">The channel identifier where the user is interacting. Cannot be null or empty.</param>
50 /// <param name="magicCode">Optional magic code from the OAuth callback. Used to complete the OAuth flow when provided.</param>
51 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
52 /// <returns>
53 /// A task that represents the asynchronous operation. The task result contains a <see cref="TokenResponse"/> with
54 /// the OAuth token if available, or null if the user has not completed authentication for this connection.
55 /// </returns>
56 public async override Task<TokenResponse?> GetUserTokenAsync(string userId, string connectionName, string channelId, string magicCode, CancellationToken cancellationToken)
57 {
58 GetTokenResult? res = await utc.GetTokenAsync(userId, connectionName, channelId, magicCode, cancellationToken).ConfigureAwait(false);
59 if (res == null)
60 {
61 return null;
62 }
63
64 return new TokenResponse
65 {
66 ChannelId = channelId,
67 ConnectionName = res.ConnectionName,
68 Token = res.Token
69 };
70 }
71
72 /// <summary>
73 /// Retrieves the sign-in resource (URL and exchange resources) needed to initiate an OAuth flow for a user.
74 /// </summary>
75 /// <param name="connectionName">The name of the OAuth connection configured in Azure Bot Service. Cannot be null or empty.</param>
76 /// <param name="activity">The activity associated with the sign-in request. Used to extract user and channel information. Cannot be null.</param>
77 /// <param name="finalRedirect">Optional URL to redirect the user to after completing authentication.</param>
78 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
79 /// <returns>
80 /// A task that represents the asynchronous operation. The task result contains a <see cref="SignInResource"/>
81 /// with the sign-in link and optional token exchange or post resources for completing the OAuth flow.
82 /// </returns>
83 /// <exception cref="ArgumentNullException">Thrown when <paramref name="activity"/> is null.</exception>
84 public async override Task<SignInResource> GetSignInResourceAsync(string connectionName, Activity activity, string finalRedirect, CancellationToken cancellationToken)
85 {
86 ArgumentNullException.ThrowIfNull(activity);
87 GetSignInResourceResult res = await utc.GetSignInResource(activity.From.Id, connectionName, activity.ChannelId, finalRedirect, cancellationToken).ConfigureAwait(false);
88 SignInResource signInResource = new()
89 {
90 SignInLink = res!.SignInLink
91 };
92
93 if (res.TokenExchangeResource != null)
94 {
95 signInResource.TokenExchangeResource = new Microsoft.Bot.Schema.TokenExchangeResource
96 {
97 Id = res.TokenExchangeResource.Id,
98 Uri = res.TokenExchangeResource.Uri?.ToString(),
99 ProviderId = res.TokenExchangeResource.ProviderId
100 };
101 }
102
103 if (res.TokenPostResource != null)
104 {
105 signInResource.TokenPostResource = new Microsoft.Bot.Schema.TokenPostResource
106 {
107 SasUrl = res.TokenPostResource.SasUrl?.ToString()
108 };
109 }
110
111 return signInResource;
112 }
113
114 /// <summary>
115 /// Exchanges a token from one OAuth connection for a token from another connection using single sign-on (SSO).
116 /// </summary>
117 /// <param name="userId">The unique identifier of the user whose token is being exchanged. Cannot be null or empty.</param>
118 /// <param name="connectionName">The name of the target OAuth connection to exchange to. Cannot be null or empty.</param>
119 /// <param name="channelId">The channel identifier where the user is interacting. Cannot be null or empty.</param>
120 /// <param name="exchangeRequest">The token exchange request containing the source token. Cannot be null.</param>
121 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
122 /// <returns>
123 /// A task that represents the asynchronous operation. The task result contains a <see cref="TokenResponse"/>
124 /// with the exchanged token for the target connection.
125 /// </returns>
126 public async override Task<TokenResponse> ExchangeTokenAsync(string userId, string connectionName, string channelId,
127 TokenExchangeRequest exchangeRequest, CancellationToken cancellationToken)
128 {
129 GetTokenResult resp = await utc.ExchangeTokenAsync(userId, connectionName, channelId, exchangeRequest.Token,
130 cancellationToken).ConfigureAwait(false);
131 return new TokenResponse
132 {
133 ChannelId = channelId,
134 ConnectionName = resp.ConnectionName,
135 Token = resp.Token
136 };
137 }
138
139 /// <summary>
140 /// Signs out a user from a specific OAuth connection, revoking their stored token.
141 /// </summary>
142 /// <param name="userId">The unique identifier of the user to sign out. Cannot be null or empty.</param>
143 /// <param name="connectionName">The name of the OAuth connection to sign out from. Cannot be null or empty.</param>
144 /// <param name="channelId">The channel identifier where the user is interacting. Cannot be null or empty.</param>
145 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
146 /// <returns>A task that represents the asynchronous sign-out operation.</returns>
147 public async override Task SignOutUserAsync(string userId, string connectionName, string channelId, CancellationToken cancellationToken)
148 {
149 await utc.SignOutUserAsync(userId, connectionName, channelId, cancellationToken).ConfigureAwait(false);
150 }
151
152 /// <summary>
153 /// Retrieves Azure Active Directory (Azure AD) tokens for multiple resource URLs in a single request.
154 /// </summary>
155 /// <param name="userId">The unique identifier of the user requesting the tokens. Cannot be null or empty.</param>
156 /// <param name="connectionName">The name of the OAuth connection configured for Azure AD. Cannot be null or empty.</param>
157 /// <param name="resourceUrls">An array of resource URLs (e.g., "https://graph.microsoft.com") to request tokens for. Cannot be null.</param>
158 /// <param name="channelId">The channel identifier where the user is interacting. Cannot be null or empty.</param>
159 /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
160 /// <returns>
161 /// A task that represents the asynchronous operation. The task result contains a dictionary mapping each
162 /// resource URL to its corresponding <see cref="TokenResponse"/>. Returns an empty dictionary if no tokens are available.
163 /// </returns>
164 public async override Task<Dictionary<string, TokenResponse>> GetAadTokensAsync(string userId, string connectionName, string[] resourceUrls, string channelId, CancellationToken cancellationToken)
165 {
166 IDictionary<string, GetTokenResult> res = await utc.GetAadTokensAsync(userId, connectionName, channelId, resourceUrls, cancellationToken).ConfigureAwait(false);
167 return res?.ToDictionary(kvp => kvp.Key, kvp => new TokenResponse
168 {
169 ChannelId = channelId,
170 ConnectionName = kvp.Value.ConnectionName,
171 Token = kvp.Value.Token
172 }) ?? new Dictionary<string, TokenResponse>();
173 }
174}
175