microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
99620f7f6ecccb78c4ebfb54686bb2a4e56dcea8

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/samples/PABot/Dialogs/MainDialog.cs

319lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Bot.Builder;
5using Microsoft.Bot.Builder.Dialogs;
6using Microsoft.Bot.Connector.Authentication;
7using Microsoft.Bot.Schema;
8using Microsoft.Graph.Models;
9
10namespace PABot.Dialogs
11{
12 /// <summary>
13 /// Main dialog that handles the authentication and user interactions.
14 /// </summary>
15 public class MainDialog : LogoutDialog
16 {
17 protected readonly ILogger _logger;
18 private const string FirstOAuthPrompt = "FirstOAuthPrompt";
19 private const string SecondOAuthPrompt = "SecondOAuthPrompt";
20 private readonly string _firstConnectionName;
21 private readonly string _secondConnectionName;
22
23 /// <summary>
24 /// Initializes a new instance of the <see cref="MainDialog"/> class.
25 /// </summary>
26 /// <param name="configuration">The configuration.</param>
27 /// <param name="logger">The logger.</param>
28 public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
29 : base(nameof(MainDialog), configuration["ConnectionName"] ?? "graph")
30 {
31 _logger = logger;
32
33 // Load connection names from configuration
34 _firstConnectionName = configuration["ConnectionName"] ?? "graph";
35 _secondConnectionName = configuration["SecondConnectionName"] ?? "graph-2";
36
37 _logger.LogInformation("Using OAuth connections: {FirstConnection} and {SecondConnection}",
38 _firstConnectionName, _secondConnectionName);
39
40 // Add first OAuth prompt
41 AddDialog(new OAuthPrompt(
42 FirstOAuthPrompt,
43 new OAuthPromptSettings
44 {
45 ConnectionName = _firstConnectionName,
46 Text = $"Please Sign In to the first connection ({_firstConnectionName})",
47 Title = $"Sign In - {_firstConnectionName}",
48 Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
49 EndOnInvalidMessage = true
50 }));
51
52 // Add second OAuth prompt
53 AddDialog(new OAuthPrompt(
54 SecondOAuthPrompt,
55 new OAuthPromptSettings
56 {
57 ConnectionName = _secondConnectionName,
58 Text = $"Please Sign In to the second connection ({_secondConnectionName})",
59 Title = $"Sign In - {_secondConnectionName}",
60 Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
61 EndOnInvalidMessage = true
62 }));
63
64 AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
65
66 AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
67 {
68 PromptFirstConnectionAsync,
69 LoginFirstConnectionAsync,
70 PromptSecondConnectionAsync,
71 LoginSecondConnectionAsync,
72 DisplayTokenPhase1Async,
73 DisplayTokenPhase2Async,
74 }));
75
76 // The initial child Dialog to run.
77 InitialDialogId = nameof(WaterfallDialog);
78 }
79
80 /// <summary>
81 /// Prompts the user to sign in to the first connection.
82 /// </summary>
83 /// <param name="stepContext">The waterfall step context.</param>
84 /// <param name="cancellationToken">The cancellation token.</param>
85 /// <returns>A task representing the asynchronous operation.</returns>
86 private async Task<DialogTurnResult> PromptFirstConnectionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
87 {
88 _logger.LogInformation("PromptFirstConnectionAsync() called.");
89 return await stepContext.BeginDialogAsync(FirstOAuthPrompt, null, cancellationToken);
90 }
91
92 /// <summary>
93 /// Handles the first connection login step.
94 /// </summary>
95 /// <param name="stepContext">The waterfall step context.</param>
96 /// <param name="cancellationToken">The cancellation token.</param>
97 /// <returns>A task representing the asynchronous operation.</returns>
98 private async Task<DialogTurnResult> LoginFirstConnectionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
99 {
100 TokenResponse firstTokenResponse = (TokenResponse)stepContext.Result;
101 if (firstTokenResponse?.Token != null)
102 {
103 _logger.LogInformation("First connection ({ConnectionName}) authenticated successfully.", _firstConnectionName);
104 await stepContext.Context.SendActivityAsync(MessageFactory.Text($"✓ First connection ({_firstConnectionName}) authenticated successfully!"), cancellationToken);
105
106 // Store the first token in step context values
107 stepContext.Values["FirstToken"] = firstTokenResponse;
108
109 // Continue to next step
110 return await stepContext.NextAsync(null, cancellationToken);
111 }
112 else
113 {
114 _logger.LogInformation("First connection ({ConnectionName}) authentication failed.", _firstConnectionName);
115 await stepContext.Context.SendActivityAsync(MessageFactory.Text($"First connection ({_firstConnectionName}) login was not successful, please try again."), cancellationToken);
116 return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
117 }
118 }
119
120 /// <summary>
121 /// Prompts the user to sign in to the second connection.
122 /// </summary>
123 /// <param name="stepContext">The waterfall step context.</param>
124 /// <param name="cancellationToken">The cancellation token.</param>
125 /// <returns>A task representing the asynchronous operation.</returns>
126 private async Task<DialogTurnResult> PromptSecondConnectionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
127 {
128 _logger.LogInformation("PromptSecondConnectionAsync() called.");
129 return await stepContext.BeginDialogAsync(SecondOAuthPrompt, null, cancellationToken);
130 }
131
132 /// <summary>
133 /// Handles the second connection login step and displays user information.
134 /// </summary>
135 /// <param name="stepContext">The waterfall step context.</param>
136 /// <param name="cancellationToken">The cancellation token.</param>
137 /// <returns>A task representing the asynchronous operation.</returns>
138 private async Task<DialogTurnResult> LoginSecondConnectionAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
139 {
140 TokenResponse secondTokenResponse = (TokenResponse)stepContext.Result;
141 if (secondTokenResponse?.Token != null)
142 {
143 _logger.LogInformation("Second connection ({ConnectionName}) authenticated successfully.", _secondConnectionName);
144 await stepContext.Context.SendActivityAsync(MessageFactory.Text($"✓ Second connection ({_secondConnectionName}) authenticated successfully!"), cancellationToken);
145
146 // Store the second token
147 stepContext.Values["SecondToken"] = secondTokenResponse;
148
149 // Retrieve the first token
150 TokenResponse firstTokenResponse = (TokenResponse)stepContext.Values["FirstToken"];
151
152 try
153 {
154 // Use the first token to get user information
155 SimpleGraphClient client = new(firstTokenResponse.Token);
156 User me = await client.GetMeAsync();
157 string title = !string.IsNullOrEmpty(me.JobTitle) ? me.JobTitle : "Unknown";
158
159 await stepContext.Context.SendActivityAsync($"You're logged in as {me.DisplayName} ({me.UserPrincipalName}); your job title is: {title}");
160
161 string photo = await client.GetPhotoAsync();
162
163 if (!string.IsNullOrEmpty(photo))
164 {
165 CardImage cardImage = new(photo);
166 ThumbnailCard card = new(images: new List<CardImage> { cardImage });
167 IMessageActivity reply = MessageFactory.Attachment(card.ToAttachment());
168
169 await stepContext.Context.SendActivityAsync(reply, cancellationToken);
170 }
171 else
172 {
173 await stepContext.Context.SendActivityAsync(MessageFactory.Text("Sorry! User doesn't have a profile picture to display."), cancellationToken);
174 }
175
176 return await stepContext.PromptAsync(
177 nameof(ConfirmPrompt),
178 new PromptOptions { Prompt = MessageFactory.Text("Would you like to view your tokens?") },
179 cancellationToken);
180 }
181 catch (Exception ex)
182 {
183 _logger.LogError(ex, "Error occurred while processing your request.");
184 }
185 }
186 else
187 {
188 _logger.LogInformation("Second connection ({ConnectionName}) authentication failed.", _secondConnectionName);
189 }
190
191 await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Second connection ({_secondConnectionName}) login was not successful, please try again."), cancellationToken);
192 return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
193 }
194
195 /// <summary>
196 /// Displays the tokens if the user confirms.
197 /// </summary>
198 /// <param name="stepContext">The waterfall step context.</param>
199 /// <param name="cancellationToken">The cancellation token.</param>
200 /// <returns>A task representing the asynchronous operation.</returns>
201 private async Task<DialogTurnResult> DisplayTokenPhase1Async(WaterfallStepContext stepContext, CancellationToken cancellationToken)
202 {
203 _logger.LogInformation("DisplayTokenPhase1Async() method called.");
204
205 await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken);
206
207 bool result = (bool)stepContext.Result;
208 if (result)
209 {
210 // Pass both tokens to the next step
211 return await stepContext.NextAsync(null, cancellationToken);
212 }
213
214 return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
215 }
216
217 /// <summary>
218 /// Displays both tokens to the user.
219 /// </summary>
220 /// <param name="stepContext">The waterfall step context.</param>
221 /// <param name="cancellationToken">The cancellation token.</param>
222 /// <returns>A task representing the asynchronous operation.</returns>
223 private async Task<DialogTurnResult> DisplayTokenPhase2Async(WaterfallStepContext stepContext, CancellationToken cancellationToken)
224 {
225 _logger.LogInformation("DisplayTokenPhase2Async() method called.");
226
227 // Retrieve both tokens from step context
228 TokenResponse firstTokenResponse = (TokenResponse)stepContext.Values["FirstToken"];
229 TokenResponse secondTokenResponse = (TokenResponse)stepContext.Values["SecondToken"];
230
231 if (firstTokenResponse != null && secondTokenResponse != null)
232 {
233 string tokenMessage = $"Here are your tokens:\n\n" +
234 $"{_firstConnectionName} Connection Token:\n{firstTokenResponse.Token}\n\n" +
235 $"{_secondConnectionName} Connection Token:\n{secondTokenResponse.Token}";
236 await stepContext.Context.SendActivityAsync(MessageFactory.Text(tokenMessage), cancellationToken);
237 }
238
239 return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
240 }
241
242 /// <summary>
243 /// Override to handle logout from both OAuth connections.
244 /// </summary>
245 protected override async Task<DialogTurnResult> OnBeginDialogAsync(
246 DialogContext innerDc,
247 object options,
248 CancellationToken cancellationToken = default)
249 {
250 DialogTurnResult? result = await InterruptAsync(innerDc, cancellationToken);
251 if (result != null)
252 {
253 return result;
254 }
255
256 return await base.OnBeginDialogAsync(innerDc, options, cancellationToken);
257 }
258
259 /// <summary>
260 /// Override to handle logout from both OAuth connections.
261 /// </summary>
262 protected override async Task<DialogTurnResult> OnContinueDialogAsync(
263 DialogContext innerDc,
264 CancellationToken cancellationToken = default)
265 {
266 DialogTurnResult? result = await InterruptAsync(innerDc, cancellationToken);
267 if (result != null)
268 {
269 return result;
270 }
271
272 return await base.OnContinueDialogAsync(innerDc, cancellationToken);
273 }
274
275 /// <summary>
276 /// Handles logout command by signing out from both OAuth connections.
277 /// </summary>
278 private async Task<DialogTurnResult?> InterruptAsync(
279 DialogContext innerDc,
280 CancellationToken cancellationToken = default)
281 {
282 if (innerDc.Context.Activity.Type == ActivityTypes.Message)
283 {
284 string text = innerDc.Context.Activity.Text.ToLowerInvariant();
285
286 // Allow logout anywhere in the command
287 if (text.Contains("logout"))
288 {
289 // The UserTokenClient encapsulates the authentication processes.
290 UserTokenClient userTokenClient = innerDc.Context.TurnState.Get<UserTokenClient>();
291
292 // Sign out from both connections
293 await userTokenClient.SignOutUserAsync(
294 innerDc.Context.Activity.From.Id,
295 _firstConnectionName,
296 innerDc.Context.Activity.ChannelId,
297 cancellationToken).ConfigureAwait(false);
298
299 await userTokenClient.SignOutUserAsync(
300 innerDc.Context.Activity.From.Id,
301 _secondConnectionName,
302 innerDc.Context.Activity.ChannelId,
303 cancellationToken).ConfigureAwait(false);
304
305 _logger.LogInformation("User signed out from both connections: {FirstConnection} and {SecondConnection}",
306 _firstConnectionName, _secondConnectionName);
307
308 await innerDc.Context.SendActivityAsync(
309 MessageFactory.Text($"You have been signed out from both connections ({_firstConnectionName} and {_secondConnectionName})."),
310 cancellationToken);
311
312 return await innerDc.CancelAllDialogsAsync(cancellationToken);
313 }
314 }
315
316 return null;
317 }
318 }
319}
320