コード例 #1
0
        /// <summary>
        /// Uses a magic code in message text to create a TokenResponse.
        /// </summary>
        /// <param name="userTokenClient">The UserTokenClient to use.</param>
        /// <param name="connectionName">The Connection Name to use.</param>
        /// <param name="activity">A message activity.</param>
        /// <param name="cancellationToken">A CancellationToken.</param>
        /// <returns>A TokenResponse.</returns>
        public static async Task <TokenResponse> CreateTokenResponseFromMessageAsync(UserTokenClient userTokenClient, string connectionName, Activity activity, CancellationToken cancellationToken)
        {
            // Attempt to recognize a magic code in the message text.
            var magicCode = RecognizeMagicCode(activity);

            // If we have a magic code then call the user token service.
            var userId    = activity.From.Id;
            var channelId = activity.ChannelId;

            return(magicCode != null ? await userTokenClient.GetUserTokenAsync(userId, connectionName, channelId, magicCode, cancellationToken).ConfigureAwait(false) : null);
        }
コード例 #2
0
        private static async Task <TokenResponse> OnContinueVerifyStateAsync(UserTokenClient userTokenClient, string connectionName, ITurnContext turnContext, CancellationToken cancellationToken)
        {
            // Getting the token follows a different flow in Teams. At the signin completion, Teams
            // will send the bot an "invoke" activity that contains a "magic" code. This code MUST
            // then be used to try fetching the token from Botframework service within some time
            // period. We try here. If it succeeds, we return 200 with an empty body. If it fails
            // with a retriable error, we return 500. Teams will re-send another invoke in this case.
            // If it fails with a non-retriable error, we return 404. Teams will not (still work in
            // progress) retry in that case.
            var userId        = turnContext.Activity.From.Id;
            var channelId     = turnContext.Activity.ChannelId;
            var magicCode     = (turnContext.Activity.Value as JObject)?.GetValue("state", StringComparison.Ordinal)?.ToString();
            var tokenResponse = await userTokenClient.GetUserTokenAsync(userId, connectionName, channelId, magicCode, cancellationToken).ConfigureAwait(false);

            await turnContext.SendActivityAsync(CreateInvokeResponseActivity(tokenResponse != null ? HttpStatusCode.OK : HttpStatusCode.NotFound), cancellationToken).ConfigureAwait(false);

            return(tokenResponse);
        }
コード例 #3
0
        private TurnContext CreateTurnContext(Activity activity, ClaimsIdentity claimsIdentity, string oauthScope, IConnectorClient connectorClient, UserTokenClient userTokenClient, BotCallbackHandler callback, ConnectorFactory connectorFactory)
        {
            var turnContext = new TurnContext(this, activity);

            turnContext.TurnState.Add <IIdentity>(BotIdentityKey, claimsIdentity);
            turnContext.TurnState.Add(connectorClient);
            turnContext.TurnState.Add(userTokenClient);
            turnContext.TurnState.Add(callback);
            turnContext.TurnState.Add(connectorFactory);
            turnContext.TurnState.Set(OAuthScopeKey, oauthScope); // in non-skills scenarios the oauth scope value here will be null, so use Set

            return(turnContext);
        }
コード例 #4
0
        private static async Task <DialogTurnResult> OnContinueWithMessageActivityAsync(DialogContext dc, UserTokenClient userTokenClient, string connectionName, PromptValidator <TokenResponse> validator, bool endOnInvalidMessage, CancellationToken cancellationToken)
        {
            var tokenResponse = await OAuthHelper.CreateTokenResponseFromMessageAsync(userTokenClient, connectionName, dc.Context.Activity, cancellationToken).ConfigureAwait(false);

            // Call any custom validation we might have.
            if (await CheckValidatorAsync(dc, validator, tokenResponse, cancellationToken).ConfigureAwait(false))
            {
                return(await dc.EndDialogAsync(tokenResponse, cancellationToken).ConfigureAwait(false));
            }
            else
            {
                // If EndOnInvalidMessage is set, complete the prompt with no result.
                if (endOnInvalidMessage)
                {
                    return(await dc.EndDialogAsync(null, cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    // If this was a message Activity we can use the retry prompt
                    var promptOptions = dc.ActiveDialog.State[OAuthHelper.PersistedOptions].CastTo <PromptOptions>();
                    if (!dc.Context.Responded && promptOptions?.RetryPrompt != null)
                    {
                        await dc.Context.SendActivityAsync(promptOptions.RetryPrompt, cancellationToken).ConfigureAwait(false);
                    }

                    return(EndOfTurn);
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Helper function used in BeginDialog.
        /// </summary>
        /// <param name="userTokenClient">The userTokenClient.</param>
        /// <param name="settings">The oauth settings.</param>
        /// <param name="turnContext">The turncontext.</param>
        /// <param name="prompt">A message activity for the prompt.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>A Task.</returns>
        public static async Task SendOAuthCardAsync(UserTokenClient userTokenClient, OAuthPromptSettings settings, ITurnContext turnContext, IMessageActivity prompt, CancellationToken cancellationToken)
        {
            // Ensure prompt initialized
            prompt ??= Activity.CreateMessageActivity();

            if (prompt.Attachments == null)
            {
                prompt.Attachments = new List <Attachment>();
            }

            // Append appropriate card if missing
            if (!ChannelSupportsOAuthCard(turnContext.Activity.ChannelId))
            {
                if (!prompt.Attachments.Any(a => a.Content is SigninCard))
                {
                    var signInResource = await userTokenClient.GetSignInResourceAsync(settings.ConnectionName, turnContext.Activity, null, cancellationToken).ConfigureAwait(false);

                    prompt.Attachments.Add(new Attachment
                    {
                        ContentType = SigninCard.ContentType,
                        Content     = new SigninCard
                        {
                            Text    = settings.Text,
                            Buttons = new[]
                            {
                                new CardAction
                                {
                                    Title = settings.Title,
                                    Value = signInResource.SignInLink,
                                    Type  = ActionTypes.Signin,
                                },
                            },
                        },
                    });
                }
            }
            else if (!prompt.Attachments.Any(a => a.Content is OAuthCard))
            {
                var cardActionType = ActionTypes.Signin;
                var signInResource = await userTokenClient.GetSignInResourceAsync(settings.ConnectionName, turnContext.Activity, null, cancellationToken).ConfigureAwait(false);

                var value = signInResource.SignInLink;

                // use the SignInLink when
                //   in speech channel or
                //   bot is a skill or
                // TODO: the OauthPrompt code also checked for || settings.OAuthAppCredentials != null
                if (turnContext.Activity.IsFromStreamingConnection() ||
                    IsSkill(turnContext.TurnState.Get <ClaimsIdentity>(BotAdapter.BotIdentityKey)))
                {
                    if (turnContext.Activity.ChannelId == Channels.Emulator)
                    {
                        cardActionType = ActionTypes.OpenUrl;
                    }
                }
                else if (!ChannelRequiresSignInLink(turnContext.Activity.ChannelId))
                {
                    value = null;
                }

                prompt.Attachments.Add(new Attachment
                {
                    ContentType = OAuthCard.ContentType,
                    Content     = new OAuthCard
                    {
                        Text           = settings.Text,
                        ConnectionName = settings.ConnectionName,
                        Buttons        = new[]
                        {
                            new CardAction
                            {
                                Title = settings.Title,
                                Text  = settings.Text,
                                Type  = cardActionType,
                                Value = value
                            },
                        },
                        TokenExchangeResource = signInResource.TokenExchangeResource,
                    },
                });
            }

            // Add the login timeout specified in OAuthPromptSettings to TurnState so it can be referenced if polling is needed
            if (!turnContext.TurnState.ContainsKey(TurnStateConstants.OAuthLoginTimeoutKey) && settings.Timeout.HasValue)
            {
                turnContext.TurnState.Add <object>(TurnStateConstants.OAuthLoginTimeoutKey, TimeSpan.FromMilliseconds(settings.Timeout.Value));
            }

            // Set input hint
            if (string.IsNullOrEmpty(prompt.InputHint))
            {
                prompt.InputHint = InputHints.AcceptingInput;
            }

            await turnContext.SendActivityAsync(prompt, cancellationToken).ConfigureAwait(false);
        }
コード例 #6
0
        private static async Task <TokenResponse> OnContinueTokenExchangeAsync(UserTokenClient userTokenClient, string connectionName, ITurnContext turnContext, CancellationToken cancellationToken)
        {
            var tokenExchangeInvokeRequest = ((JObject)turnContext.Activity.Value)?.ToObject <TokenExchangeInvokeRequest>();

            HttpStatusCode httpStatusCode;
            TokenExchangeInvokeResponse tokenExchangeInvokeResponse;
            TokenResponse tokenResponse;

            if (tokenExchangeInvokeRequest == null)
            {
                httpStatusCode = HttpStatusCode.BadRequest;
                tokenExchangeInvokeResponse = new TokenExchangeInvokeResponse
                {
                    ConnectionName = connectionName,
                    FailureDetail  = "The bot received an InvokeActivity that is missing a TokenExchangeInvokeRequest value. This is required to be sent with the InvokeActivity.",
                };
                tokenResponse = null;
            }
            else if (tokenExchangeInvokeRequest.ConnectionName != connectionName)
            {
                httpStatusCode = HttpStatusCode.BadRequest;
                tokenExchangeInvokeResponse = new TokenExchangeInvokeResponse
                {
                    Id             = tokenExchangeInvokeRequest.Id,
                    ConnectionName = connectionName,
                    FailureDetail  = $"The bot received an InvokeActivity with a TokenExchangeInvokeRequest containing a ConnectionName {tokenExchangeInvokeRequest.ConnectionName} that does not match the ConnectionName {connectionName} expected by the bot's active OAuthPrompt. Ensure these names match when sending the InvokeActivityInvalid ConnectionName in the TokenExchangeInvokeRequest",
                };
                tokenResponse = null;
            }
            else
            {
                // inbound invoke activity appears valid so we will call the user token service

                var userId                = turnContext.Activity.From.Id;
                var channelId             = turnContext.Activity.ChannelId;
                var tokenExchangeResponse = await userTokenClient.ExchangeTokenAsync(userId, connectionName, channelId, new TokenExchangeRequest { Token = tokenExchangeInvokeRequest.Token }, cancellationToken).ConfigureAwait(false);

                if (tokenExchangeResponse == null || string.IsNullOrEmpty(tokenExchangeResponse.Token))
                {
                    httpStatusCode = HttpStatusCode.PreconditionFailed;
                    tokenExchangeInvokeResponse = new TokenExchangeInvokeResponse
                    {
                        Id             = tokenExchangeInvokeRequest.Id,
                        ConnectionName = connectionName,
                        FailureDetail  = "The bot is unable to exchange token. Proceed with regular login.",
                    };
                    tokenResponse = null;
                }
                else
                {
                    httpStatusCode = HttpStatusCode.OK;
                    tokenExchangeInvokeResponse = new TokenExchangeInvokeResponse
                    {
                        Id             = tokenExchangeInvokeRequest.Id,
                        ConnectionName = connectionName,
                    };
                    tokenResponse = new TokenResponse
                    {
                        ChannelId      = tokenExchangeResponse.ChannelId,
                        ConnectionName = tokenExchangeResponse.ConnectionName,
                        Token          = tokenExchangeResponse.Token,
                    };
                }
            }

            await turnContext.SendActivityAsync(CreateInvokeResponseActivity(httpStatusCode, tokenExchangeInvokeResponse)).ConfigureAwait(false);

            return(tokenResponse);
        }
コード例 #7
0
        private static async Task <DialogTurnResult> OnContinueInvokeActivityAsync(DialogContext dc, UserTokenClient userTokenClient, string connectionName, CancellationToken cancellationToken)
        {
            var turnContext = dc.Context;

            switch (turnContext.Activity.Name)
            {
            case SignInConstants.VerifyStateOperationName:
            {
                var tokenResponse = await OnContinueVerifyStateAsync(userTokenClient, connectionName, turnContext, cancellationToken).ConfigureAwait(false);

                return(await dc.EndDialogAsync(tokenResponse, cancellationToken).ConfigureAwait(false));
            }

            case SignInConstants.TokenExchangeOperationName:
            {
                var tokenResponse = await OnContinueTokenExchangeAsync(userTokenClient, connectionName, turnContext, cancellationToken).ConfigureAwait(false);

                return(await dc.EndDialogAsync(tokenResponse, cancellationToken).ConfigureAwait(false));
            }

            default:
                throw new Exception($"unexpected Invoke Activity name {turnContext.Activity.Name}");
            }
        }
コード例 #8
0
        /// <summary>
        /// Method to process Event and Invoke activities that are part of the Bot Framework Protocol oauth support.
        /// </summary>
        /// <param name="dc">The DialogContext.</param>
        /// <param name="userTokenClient">The user token client.</param>
        /// <param name="connectionName">The ConnectionName.</param>
        /// <param name="cancellationToken">CancellationToken.</param>
        /// <returns>A Task of DialogTurnResult.</returns>
        public static Task <DialogTurnResult> OnContinueWithNonMessageActivityAsync(DialogContext dc, UserTokenClient userTokenClient, string connectionName, CancellationToken cancellationToken)
        {
            switch (dc.Context.Activity.Type)
            {
            case ActivityTypes.Event:
                return(OnContinueEventActivityAsync(dc, cancellationToken));

            case ActivityTypes.Invoke:
                return(OnContinueInvokeActivityAsync(dc, userTokenClient, connectionName, cancellationToken));

            default:
                // unexpected Activity type
                return(Task.FromResult(Dialog.EndOfTurn));
            }
        }