diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/AuthenticationManagerTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/AuthenticationManagerTests.cs index 7d13c7910a..954491ef77 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/AuthenticationManagerTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/AuthenticationManagerTests.cs @@ -23,7 +23,7 @@ public async void Test_SignIn_DefaultHandler() var turnState = await TurnStateConfig.GetTurnStateWithConversationStateAsync(turnContext); // act - var response = await authManager.SignUserIn(turnContext, turnState); + var response = await authManager.SignUserInAsync(turnContext, turnState); // assert Assert.Equal(SignInStatus.Complete, response.Status); @@ -48,7 +48,7 @@ public async void Test_SignIn_SpecificHandler() var turnState = await TurnStateConfig.GetTurnStateWithConversationStateAsync(turnContext); // act - var response = await authManager.SignUserIn(turnContext, turnState, "sharepoint"); + var response = await authManager.SignUserInAsync(turnContext, turnState, "sharepoint"); // assert Assert.Equal(SignInStatus.Complete, response.Status); @@ -72,7 +72,7 @@ public async void Test_SignIn_Pending() var turnState = await TurnStateConfig.GetTurnStateWithConversationStateAsync(turnContext); // act - var response = await authManager.SignUserIn(turnContext, turnState); + var response = await authManager.SignUserInAsync(turnContext, turnState); // assert Assert.Equal(SignInStatus.Pending, response.Status); @@ -99,7 +99,7 @@ public async void Test_SignOut_DefaultHandler() }; // act - await authManager.SignOutUser(turnContext, turnState); + await authManager.SignOutUserAsync(turnContext, turnState); // assert Assert.False(turnState.Temp.AuthTokens.ContainsKey("graph")); @@ -126,7 +126,7 @@ public async void Test_SignOut_SpecificHandler() }; // act - await authManager.SignOutUser(turnContext, turnState, "sharepoint"); + await authManager.SignOutUserAsync(turnContext, turnState, "sharepoint"); // assert Assert.False(turnState.Temp.AuthTokens.ContainsKey("sharepoint")); @@ -147,7 +147,7 @@ public async void Test_IsValidActivity_DefaultHandler() var turnContext = MockTurnContext(); // act - var validActivity = await authManager.IsValidActivity(turnContext); + var validActivity = await authManager.IsValidActivityAsync(turnContext); // assert Assert.True(validActivity); @@ -167,7 +167,7 @@ public async void Test_IsValidActivity_SpecificHandler() var turnContext = MockTurnContext(); // act - var validActivity = await authManager.IsValidActivity(turnContext, "sharepoint"); + var validActivity = await authManager.IsValidActivityAsync(turnContext, "sharepoint"); // assert Assert.True(validActivity); diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/BotAuthenticationBaseTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/BotAuthenticationBaseTests.cs index 72c4f1ba03..e660ca7ae1 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/BotAuthenticationBaseTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/Bot/BotAuthenticationBaseTests.cs @@ -21,16 +21,16 @@ public MockedBotAuthentication(Application app, string name, List ContinueDialog(ITurnContext context, TState state, string dialogStateProperty) + public override Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { if (_throwExceptionWhenContinue) { - throw new Exception("mocked error"); + throw new TeamsAIAuthException("mocked error"); } return Task.FromResult(_continueDialogResult); } - public override Task RunDialog(ITurnContext context, TState state, string dialogStateProperty) + public override Task RunDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { var result = _runDialogResult.FirstOrDefault(); if (result == null) @@ -113,7 +113,7 @@ public async void Test_Authenticate_Complete() { // arrange var app = new Application(new ApplicationOptions()); - var botAuth = new MockedBotAuthentication(app, "test", runDialogResult: new List() { new DialogTurnResult(DialogTurnStatus.Complete, new TokenResponse(token: "test token")) }); + var botAuth = new MockedBotAuthentication(app, "test", runDialogResult: new List() { new(DialogTurnStatus.Complete, new TokenResponse(token: "test token")) }); var context = MockTurnContext(); var state = await TurnStateConfig.GetTurnStateWithConversationStateAsync(context); @@ -130,7 +130,7 @@ public async void Test_Authenticate_CompleteWithoutToken() { // arrange var app = new Application(new ApplicationOptions()); - var botAuth = new MockedBotAuthentication(app, "test", runDialogResult: new List() { new DialogTurnResult(DialogTurnStatus.Complete) }); + var botAuth = new MockedBotAuthentication(app, "test", runDialogResult: new List() { new(DialogTurnStatus.Complete) }); var context = MockTurnContext(); var state = await TurnStateConfig.GetTurnStateWithConversationStateAsync(context); @@ -161,7 +161,7 @@ public async void Test_HandleSignInActivity_Complete() messageText = context.Activity.Text; return Task.CompletedTask; }); - botAuth.OnUserSignInFailure((context, state, exception) => { throw new Exception("sign in failure handler should not be called"); }); + botAuth.OnUserSignInFailure((context, state, exception) => { throw new TeamsAIAuthException("sign in failure handler should not be called"); }); // act await botAuth.HandleSignInActivity(context, state, new CancellationToken()); @@ -180,7 +180,7 @@ public async void Test_HandleSignInActivity_CompleteWithoutToken() var context = MockTurnContext(); var state = await TurnStateConfig.GetTurnStateWithConversationStateAsync(context); TeamsAIAuthException? authException = null; - botAuth.OnUserSignInSuccess((context, state) => { throw new Exception("sign in success handler should not be called"); }); + botAuth.OnUserSignInSuccess((context, state) => { throw new TeamsAIAuthException("sign in success handler should not be called"); }); botAuth.OnUserSignInFailure((context, state, exception) => { authException = exception; return Task.CompletedTask; }); // act @@ -200,7 +200,7 @@ public async void Test_HandleSignInActivity_ThrowException() var context = MockTurnContext(); var state = await TurnStateConfig.GetTurnStateWithConversationStateAsync(context); TeamsAIAuthException? authException = null; - botAuth.OnUserSignInSuccess((context, state) => { throw new Exception("sign in success handler should not be called"); }); + botAuth.OnUserSignInSuccess((context, state) => { throw new TeamsAIAuthException("sign in success handler should not be called"); }); botAuth.OnUserSignInFailure((context, state, exception) => { authException = exception; return Task.CompletedTask; }); // act diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBaseTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBaseTests.cs index c77b28da78..aa3fb5815a 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBaseTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBaseTests.cs @@ -3,6 +3,7 @@ using Microsoft.Bot.Schema; using Microsoft.Bot.Schema.Teams; using Microsoft.Teams.AI.Tests.TestUtils; +using Microsoft.Teams.AI.Exceptions; namespace Microsoft.Teams.AI.Tests.Application.Authentication.MessageExtensions { @@ -26,7 +27,7 @@ public override Task HandleUserSignIn(ITurnContext context, strin { if (_signInResponse == null) { - throw new Exception("HandlerUserSignIn failed"); + throw new TeamsAIAuthException("HandlerUserSignIn failed"); } return Task.FromResult(_signInResponse); } @@ -35,7 +36,7 @@ public override Task HandleSsoTokenExchange(ITurnContext context) { if (_tokenExchangeResponse == null) { - throw new Exception("HandleSsoTokenExchange failed"); + throw new TeamsAIAuthException("HandleSsoTokenExchange failed"); } return Task.FromResult(_tokenExchangeResponse); } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MockedAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MockedAuthentication.cs index 6ad4865096..f8ba9d8568 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MockedAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/Authentication/MockedAuthentication.cs @@ -23,7 +23,7 @@ public void Initialize(Application app, string name, IStorage? storage = return; } - public Task IsValidActivity(ITurnContext context) + public Task IsValidActivityAsync(ITurnContext context) { return Task.FromResult(_validActivity); } @@ -38,7 +38,7 @@ public IAuthentication OnUserSignInSuccess(Func SignInUser(ITurnContext context, TState state) + public Task SignInUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { var result = new SignInResponse(_mockedStatus); if (_mockedStatus == SignInStatus.Complete) @@ -48,7 +48,7 @@ public Task SignInUser(ITurnContext context, TState state) return Task.FromResult(result); } - public Task SignOutUser(ITurnContext context, TState state) + public Task SignOutUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { return Task.CompletedTask; } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Application.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Application.cs index af9e5ecde1..dd57ecf42f 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Application.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Application.cs @@ -846,9 +846,9 @@ private async Task _OnTurnAsync(ITurnContext turnContext, CancellationToken canc if (Authentication != null && _startSignIn != null && await _startSignIn(turnContext, cancellationToken)) { // Should skip activity that does not support sign-in - if (await Authentication.IsValidActivity(turnContext)) + if (await Authentication.IsValidActivityAsync(turnContext)) { - SignInResponse response = await Authentication.SignUserIn(turnContext, turnState); + SignInResponse response = await Authentication.SignUserInAsync(turnContext, turnState); if (response.Status == SignInStatus.Pending) { // Requires user action, save state and stop processing current activity diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/AdaptiveCardsAuthenticationBase.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/AdaptiveCardsAuthenticationBase.cs index c562a9c211..1613505d45 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/AdaptiveCardsAuthenticationBase.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/AdaptiveCardsAuthenticationBase.cs @@ -6,7 +6,7 @@ namespace Microsoft.Teams.AI /// /// Base class for adaptive card authentication that handles common logic /// - public abstract class AdaptiveCardsAuthenticationBase + internal abstract class AdaptiveCardsAuthenticationBase { /// /// Authenticate current user diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/OAuthAdaptiveCardsAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/OAuthAdaptiveCardsAuthentication.cs index 0c3fcfcff4..e323d746a6 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/OAuthAdaptiveCardsAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/OAuthAdaptiveCardsAuthentication.cs @@ -3,7 +3,7 @@ /// /// Handles authentication for Adaptive Cards in Teams using OAuth Connection. /// - public class OAuthAdaptiveCardsAuthentication : AdaptiveCardsAuthenticationBase + internal class OAuthAdaptiveCardsAuthentication : AdaptiveCardsAuthenticationBase { } } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/TeamsSsoAdaptiveCardsAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/TeamsSsoAdaptiveCardsAuthentication.cs deleted file mode 100644 index 44c5c401e9..0000000000 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AdaptiveCards/TeamsSsoAdaptiveCardsAuthentication.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Microsoft.Teams.AI -{ - /// - /// Handles authentication for Adaptive Cards in Teams based on Teams SSO. - /// - public class TeamsSsoAdaptiveCardsAuthentication : AdaptiveCardsAuthenticationBase - { - } -} diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AuthenticationManager.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AuthenticationManager.cs index 328a329092..7e9094c67b 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AuthenticationManager.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/AuthenticationManager.cs @@ -38,8 +38,9 @@ public AuthenticationManager(AuthenticationOptions options) /// The turn context /// The turn state /// Optional. The name of the authentication handler to use. If not specified, the default handler name is used. + /// The cancellation token /// The sign in response - public async Task SignUserIn(ITurnContext context, TState state, string? handlerName = null) + public async Task SignUserInAsync(ITurnContext context, TState state, string? handlerName = null, CancellationToken cancellationToken = default) { if (handlerName == null) { @@ -47,7 +48,7 @@ public async Task SignUserIn(ITurnContext context, TState state, } IAuthentication auth = Get(handlerName); - SignInResponse response = await auth.SignInUser(context, state); + SignInResponse response = await auth.SignInUserAsync(context, state, cancellationToken); if (response.Status == SignInStatus.Complete) { AuthUtilities.SetTokenInState(state, handlerName, response.Token!); @@ -61,7 +62,8 @@ public async Task SignUserIn(ITurnContext context, TState state, /// The turn context /// The turn state /// Optional. The name of the authentication handler to use. If not specified, the default handler name is used. - public async Task SignOutUser(ITurnContext context, TState state, string? handlerName = null) + /// The cancellation token + public async Task SignOutUserAsync(ITurnContext context, TState state, string? handlerName = null, CancellationToken cancellationToken = default) { if (handlerName == null) { @@ -69,7 +71,7 @@ public async Task SignOutUser(ITurnContext context, TState state, string? handle } IAuthentication auth = Get(handlerName); - await auth.SignOutUser(context, state); + await auth.SignOutUserAsync(context, state, cancellationToken); AuthUtilities.DeleteTokenFromState(state, handlerName); } @@ -79,7 +81,7 @@ public async Task SignOutUser(ITurnContext context, TState state, string? handle /// Current turn context. /// Optional. The name of the authentication handler to use. If not specified, the default handler name is used. /// True if current activity supports authentication. Otherwise, false. - public async Task IsValidActivity(ITurnContext context, string? handlerName = null) + public async Task IsValidActivityAsync(ITurnContext context, string? handlerName = null) { if (handlerName == null) { @@ -87,7 +89,7 @@ public async Task IsValidActivity(ITurnContext context, string? handlerNam } IAuthentication auth = Get(handlerName); - return await auth.IsValidActivity(context); + return await auth.IsValidActivityAsync(context); } /// diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/BotAuthenticationBase.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/BotAuthenticationBase.cs index 118dbad76a..48c1bc5230 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/BotAuthenticationBase.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/BotAuthenticationBase.cs @@ -9,7 +9,7 @@ namespace Microsoft.Teams.AI /// /// Base class for bot authentication that handles common logic /// - public abstract class BotAuthenticationBase + internal abstract class BotAuthenticationBase where TState : TurnState, new() { /// @@ -47,12 +47,12 @@ public BotAuthenticationBase(Application app, string name, IStorage? sto app.AddRoute(this.VerifyStateRouteSelector, async (context, state, cancellationToken) => { await this.HandleSignInActivity(context, state, cancellationToken); - }); + }, true); app.AddRoute(this.TokenExchangeRouteSelector, async (context, state, cancellationToken) => { await this.HandleSignInActivity(context, state, cancellationToken); - }); + }, true); } /// @@ -60,8 +60,9 @@ public BotAuthenticationBase(Application app, string name, IStorage? sto /// /// The turn context /// The turn state + /// The cancellation token /// The sign in response - public async Task AuthenticateAsync(ITurnContext context, TState state) + public async Task AuthenticateAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { // Get property names to use string userAuthStatePropertyName = GetUserAuthStatePropertyName(context); @@ -76,7 +77,7 @@ public async Task AuthenticateAsync(ITurnContext context, TState }); } - DialogTurnResult result = await RunDialog(context, state, userDialogStatePropertyName); + DialogTurnResult result = await RunDialog(context, state, userDialogStatePropertyName, cancellationToken); if (result.Status == DialogTurnStatus.Complete) { // Delete user auth state @@ -87,7 +88,7 @@ public async Task AuthenticateAsync(ITurnContext context, TState // Completed dialog without a token. // This could mean the user declined the consent prompt in the previous turn. // Retry authentication flow again. - return await AuthenticateAsync(context, state); + return await AuthenticateAsync(context, state, cancellationToken); } else { @@ -125,7 +126,7 @@ public async Task HandleSignInActivity(ITurnContext context, TState state, Cance try { string userDialogStatePropertyName = GetUserDialogStatePropertyName(context); - DialogTurnResult result = await ContinueDialog(context, state, userDialogStatePropertyName); + DialogTurnResult result = await ContinueDialog(context, state, userDialogStatePropertyName, cancellationToken); if (result.Status == DialogTurnStatus.Complete) { @@ -259,8 +260,9 @@ private void DeleteAuthFlowState(ITurnContext context, TState state) /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public abstract Task RunDialog(ITurnContext context, TState state, string dialogStateProperty); + public abstract Task RunDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default); /// /// Continue the authentication dialog. @@ -268,7 +270,8 @@ private void DeleteAuthFlowState(ITurnContext context, TState state) /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public abstract Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty); + public abstract Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default); } } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/OAuthBotAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/OAuthBotAuthentication.cs index eea83d8d7b..0705910890 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/OAuthBotAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/OAuthBotAuthentication.cs @@ -26,8 +26,9 @@ public OAuthBotAuthentication(Application app, string name, IStorage? st /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public override Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty) + public override Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } @@ -38,8 +39,9 @@ public override Task ContinueDialog(ITurnContext context, TSta /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public override Task RunDialog(ITurnContext context, TState state, string dialogStateProperty) + public override Task RunDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoBotAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoBotAuthentication.cs index 039a0e39f4..797039c511 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoBotAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoBotAuthentication.cs @@ -43,8 +43,9 @@ public TeamsSsoBotAuthentication(Application app, string name, TeamsSsoS /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public override async Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty) + public override async Task ContinueDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { DialogContext dialogContext = await CreateSsoDialogContext(context, state, dialogStateProperty); return await dialogContext.ContinueDialogAsync(); @@ -56,8 +57,9 @@ public override async Task ContinueDialog(ITurnContext context /// The turn context /// The turn state /// The property name for storing dialog state. + /// The cancellation token /// Dialog turn result that contains token if sign in success - public override async Task RunDialog(ITurnContext context, TState state, string dialogStateProperty) + public override async Task RunDialog(ITurnContext context, TState state, string dialogStateProperty, CancellationToken cancellationToken = default) { DialogContext dialogContext = await CreateSsoDialogContext(context, state, dialogStateProperty); DialogTurnResult result = await dialogContext.ContinueDialogAsync(); @@ -76,8 +78,8 @@ public override async Task RunDialog(ITurnContext context, TSt /// True if the activity should be handled by current authentication hanlder. Otherwise, false. protected override async Task TokenExchangeRouteSelector(ITurnContext context, CancellationToken cancellationToken) { - JObject value = JObject.FromObject(context.Activity.Value); - JToken? id = value["id"]; + JObject? value = context.Activity.Value as JObject; + JToken? id = value?["id"]; string idStr = id?.ToString() ?? ""; return await base.TokenExchangeRouteSelector(context, cancellationToken) && this._tokenExchangeIdRegex.IsMatch(idStr); diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoPrompt.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoPrompt.cs index 8b9de21fad..9ae67e5049 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoPrompt.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/Bot/TeamsSsoPrompt.cs @@ -105,7 +105,12 @@ private async Task> RecognizeTokenAsync(Di { try { - AuthenticationResult exchangedToken = await _settings.MSAL.AcquireTokenOnBehalfOf(_settings.Scopes, new UserAssertion(ssoToken)).ExecuteAsync(); + string homeAccountId = $"{context.Activity.From.AadObjectId}.{context.Activity.Conversation.TenantId}"; + AuthenticationResult exchangedToken = await ((ILongRunningWebApi)_settings.MSAL).InitiateLongRunningProcessInWebApi( + _settings.Scopes, + ssoToken, + ref homeAccountId + ).ExecuteAsync(); tokenResponse = new TokenResponse { @@ -215,7 +220,7 @@ private bool IsTokenExchangeRequestInvoke(ITurnContext context) return (context.Activity.Type == ActivityTypes.Invoke) && (context.Activity.Name == SignInConstants.TokenExchangeOperationName); } - private static async Task SendInvokeResponseAsync(ITurnContext turnContext, HttpStatusCode statusCode, object body, CancellationToken cancellationToken) + private static async Task SendInvokeResponseAsync(ITurnContext turnContext, HttpStatusCode statusCode, object? body, CancellationToken cancellationToken) { await turnContext.SendActivityAsync( new Activity diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/IAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/IAuthentication.cs index ebcf9786b5..4242d3b5a4 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/IAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/IAuthentication.cs @@ -56,22 +56,24 @@ public interface IAuthentication /// /// Current turn context. /// Application state. + /// The cancellation token /// The authentication token if user is signed in. - Task SignInUser(ITurnContext context, TState state); + Task SignInUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default); /// /// Signs out a user. /// /// Current turn context. /// Application state. - Task SignOutUser(ITurnContext context, TState state); + /// The cancellation token + Task SignOutUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default); /// /// Check whether current activity supports authentication. /// /// Current turn context. /// True if current activity supports authentication. Otherwise, false. - Task IsValidActivity(ITurnContext context); + Task IsValidActivityAsync(ITurnContext context); /// /// Initialize the authentication class diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBase.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBase.cs index 58cf8c7de9..86e6787a4b 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBase.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/MessageExtensionsAuthenticationBase.cs @@ -8,14 +8,15 @@ namespace Microsoft.Teams.AI /// /// Base class for message extension authentication that handles common logic /// - public abstract class MessageExtensionsAuthenticationBase + internal abstract class MessageExtensionsAuthenticationBase { /// /// Authenticate current user /// /// The turn context + /// The cancellation token /// The sign in response - public async Task AuthenticateAsync(ITurnContext context) + public async Task AuthenticateAsync(ITurnContext context, CancellationToken cancellationToken = default) { JObject value = JObject.FromObject(context.Activity.Value); JToken? tokenExchangeRequest = value["authentication"]; @@ -106,7 +107,7 @@ public async Task AuthenticateAsync(ITurnContext context) }, }; - await context.SendActivityAsync(ActivityUtilities.CreateInvokeResponseActivity(resposne)); + await context.SendActivityAsync(ActivityUtilities.CreateInvokeResponseActivity(resposne), cancellationToken); return new SignInResponse(SignInStatus.Pending); } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/TeamsSsoMessageExtensionsAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/TeamsSsoMessageExtensionsAuthentication.cs index 44b745ee6c..7562fae11b 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/TeamsSsoMessageExtensionsAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/MessageExtensions/TeamsSsoMessageExtensionsAuthentication.cs @@ -57,10 +57,13 @@ public override async Task HandleSsoTokenExchange(ITurnContext co { try { - AuthenticationResult result = await _settings.MSAL.AcquireTokenOnBehalfOf( - _settings.Scopes, - new UserAssertion(token.ToString()) - ).ExecuteAsync(); + string homeAccountId = $"{context.Activity.From.AadObjectId}.{context.Activity.Conversation.TenantId}"; + AuthenticationResult result = await ((ILongRunningWebApi)_settings.MSAL).InitiateLongRunningProcessInWebApi( + _settings.Scopes, + token.ToString(), + ref homeAccountId + ).ExecuteAsync(); + return new TokenResponse() { Token = result.AccessToken, diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/OAuthAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/OAuthAuthentication.cs index 77d459a78d..85dd3e28cf 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/OAuthAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/OAuthAuthentication.cs @@ -26,7 +26,7 @@ public void Initialize(Application app, string name, IStorage? storage = /// /// The turn context /// True if valid. Otherwise, false. - public Task IsValidActivity(ITurnContext context) + public Task IsValidActivityAsync(ITurnContext context) { throw new NotImplementedException(); } @@ -56,8 +56,9 @@ public IAuthentication OnUserSignInSuccess(Func /// The turn context /// The turn state + /// The cancellation token /// The sign in response - public Task SignInUser(ITurnContext context, TState state) + public Task SignInUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } @@ -67,7 +68,8 @@ public Task SignInUser(ITurnContext context, TState state) /// /// The turn context /// The turn state - public Task SignOutUser(ITurnContext context, TState state) + /// The cancellation token + public Task SignOutUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TeamsSsoAuthentication.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TeamsSsoAuthentication.cs index ca7459bad1..a40bf74b70 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TeamsSsoAuthentication.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TeamsSsoAuthentication.cs @@ -1,5 +1,6 @@ using Microsoft.Bot.Builder; using Microsoft.Identity.Client; +using Microsoft.Identity.Client.Extensibility; using Microsoft.Teams.AI.Exceptions; using Microsoft.Teams.AI.State; @@ -11,9 +12,8 @@ namespace Microsoft.Teams.AI public class TeamsSsoAuthentication : IAuthentication where TState : TurnState, new() { - private TeamsSsoBotAuthentication _botAuth; - private TeamsSsoMessageExtensionsAuthentication _messageExtensionsAuth; - private TeamsSsoAdaptiveCardsAuthentication _adaptiveCardsAuth; + private TeamsSsoBotAuthentication? _botAuth; + private TeamsSsoMessageExtensionsAuthentication? _messageExtensionsAuth; private TeamsSsoSettings _settings; /// @@ -35,7 +35,6 @@ public void Initialize(Application app, string name, IStorage? storage = { _botAuth = new TeamsSsoBotAuthentication(app, name, _settings, storage); _messageExtensionsAuth = new TeamsSsoMessageExtensionsAuthentication(_settings); - _adaptiveCardsAuth = new TeamsSsoAdaptiveCardsAuthentication(); } /// @@ -43,11 +42,11 @@ public void Initialize(Application app, string name, IStorage? storage = /// /// The turn context /// True if valid. Otherwise, false. - public Task IsValidActivity(ITurnContext context) + public Task IsValidActivityAsync(ITurnContext context) { - return Task.FromResult(_botAuth.IsValidActivity(context) - || _messageExtensionsAuth.IsValidActivity(context) - || _adaptiveCardsAuth.IsValidActivity(context)); + return Task.FromResult( + (_botAuth != null && _botAuth.IsValidActivity(context)) + || (_messageExtensionsAuth != null && _messageExtensionsAuth.IsValidActivity(context))); } /// @@ -55,8 +54,9 @@ public Task IsValidActivity(ITurnContext context) /// /// The turn context /// The turn state + /// The cancellation token /// The sign in response - public async Task SignInUser(ITurnContext context, TState state) + public async Task SignInUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { string token = await TryGetUserToken(context); if (!string.IsNullOrEmpty(token)) @@ -67,21 +67,16 @@ public async Task SignInUser(ITurnContext context, TState state) }; } - if (_botAuth.IsValidActivity(context)) + if ((_botAuth != null && _botAuth.IsValidActivity(context))) { return await _botAuth.AuthenticateAsync(context, state); } - if (_messageExtensionsAuth.IsValidActivity(context)) + if ((_messageExtensionsAuth != null && _messageExtensionsAuth.IsValidActivity(context))) { return await _messageExtensionsAuth.AuthenticateAsync(context); } - if (_adaptiveCardsAuth.IsValidActivity(context)) - { - return await _adaptiveCardsAuth.AuthenticateAsync(context); - } - throw new TeamsAIException("Incoming activity is not a valid activity to initiate authentication flow."); } @@ -90,13 +85,15 @@ public async Task SignInUser(ITurnContext context, TState state) /// /// The turn context /// The turn state - public async Task SignOutUser(ITurnContext context, TState state) + /// The cancellation token + public async Task SignOutUserAsync(ITurnContext context, TState state, CancellationToken cancellationToken = default) { string homeAccountId = $"{context.Activity.From.AadObjectId}.{context.Activity.Conversation.TenantId}"; - IAccount account = await _settings.MSAL.GetAccountAsync(homeAccountId); - if (account != null) + + ILongRunningWebApi? oboCca = _settings.MSAL as ILongRunningWebApi; + if (oboCca != null) { - await _settings.MSAL.RemoveAsync(account); + await oboCca.StopLongRunningProcessInWebApiAsync(homeAccountId, cancellationToken); } } @@ -107,7 +104,10 @@ public async Task SignOutUser(ITurnContext context, TState state) /// The class itself for chaining purpose public IAuthentication OnUserSignInSuccess(Func handler) { - _botAuth.OnUserSignInSuccess(handler); + if (_botAuth != null) + { + _botAuth.OnUserSignInSuccess(handler); + } return this; } @@ -118,19 +118,29 @@ public IAuthentication OnUserSignInSuccess(FuncThe class itself for chaining purpose public IAuthentication OnUserSignInFailure(Func handler) { - _botAuth.OnUserSignInFailure(handler); + if (_botAuth != null) + { + _botAuth.OnUserSignInFailure(handler); + } return this; } private async Task TryGetUserToken(ITurnContext context) { string homeAccountId = $"{context.Activity.From.AadObjectId}.{context.Activity.Conversation.TenantId}"; - IAccount account = await _settings.MSAL.GetAccountAsync(homeAccountId); - if (account != null) + try { - AuthenticationResult result = await _settings.MSAL.AcquireTokenSilent(_settings.Scopes, account).ExecuteAsync(); + AuthenticationResult result = await ((ILongRunningWebApi)_settings.MSAL).AcquireTokenInLongRunningProcess( + _settings.Scopes, + homeAccountId + ).ExecuteAsync(); return result.AccessToken; } + catch (MsalClientException) + { + // Cannot acquire token from cache + } + return ""; // Return empty indication no token found in cache } } diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TurnStateProperty.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TurnStateProperty.cs index 3b67fc7a75..6fdd4ed500 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TurnStateProperty.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/Authentication/TurnStateProperty.cs @@ -31,22 +31,25 @@ public Task DeleteAsync(ITurnContext turnContext, CancellationToken cancellation return Task.CompletedTask; } - public Task GetAsync(ITurnContext turnContext, Func defaultValueFactory = null, CancellationToken cancellationToken = default) + public Task GetAsync(ITurnContext turnContext, Func? defaultValueFactory = null, CancellationToken cancellationToken = default) { if (_state.Value != null) { - if (!_state.Value.ContainsKey(_propertyName)) - { - _state.Value[_propertyName] = defaultValueFactory(); - } - if (_state.Value.TryGetValue(_propertyName, out TState result)) { return Task.FromResult(result); } else { + if (defaultValueFactory == null) + { + throw new ArgumentNullException(nameof(defaultValueFactory)); + } TState defaultValue = defaultValueFactory(); + if (defaultValue == null) + { + throw new ArgumentNullException(nameof(defaultValue)); + } _state.Value[_propertyName] = defaultValue; return Task.FromResult(defaultValue); }