Search code examples
c#botframeworkmicrosoft-teams

Microsoft Teams bot - link unfurling auth flow


Can't find good example of auth flow for link unfurling. I managed to run oauth flow using this example. But after user provided login and password and bot hits OnTeamsAppBasedLinkQueryAsync second time GetUserTokenAsync still returns null. So I don't follow where should I get then token from when auth flow is finished. Should I persist it somehow? Will Teams send me the token on every request or how it should work?

So in my case following code always returns null:

var tokenResponse = await (turnContext.Adapter as IUserTokenProvider)
    .GetUserTokenAsync(turnContext, _connectionName, default(string),
        cancellationToken: cancellationToken);

Solution

  • It seems the 'state' field is not present on AppBasedLinkQuery. When the auth flow completes, OnTeamsAppBasedLinkQueryAsync will be called again and the turnContext.Activity.Value will contain the url and the 'state' (or magic code). We will get this field added to AppBasedLinkQuery (created an issue here: microsoft/botbuilder-dotnet#3429 ).

    A workaround is to retrieve the state/magiccode directly from the Activity.Value Something like:

     protected async override Task<MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext<IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
            {
                var magicCode = string.Empty;
                var state = (turnContext.Activity.Value as Newtonsoft.Json.Linq.JObject).Value<string>("state");
                if (!string.IsNullOrEmpty(state))
                {
                    int parsed = 0;
                    if (int.TryParse(state, out parsed))
                    {
                        magicCode = parsed.ToString();
                    }
                }
    
                var tokenResponse = await(turnContext.Adapter as IUserTokenProvider).GetUserTokenAsync(turnContext, _connectionName, magicCode, cancellationToken: cancellationToken);
                if (tokenResponse == null || string.IsNullOrEmpty(tokenResponse.Token))
                {
                    // There is no token, so the user has not signed in yet.
    
                    // Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions
                    var signInLink = await(turnContext.Adapter as IUserTokenProvider).GetOauthSignInLinkAsync(turnContext, _connectionName, cancellationToken);
    
                    return new MessagingExtensionResponse
                    {
                        ComposeExtension = new MessagingExtensionResult
                        {
                            Type = "auth",
                            SuggestedActions = new MessagingExtensionSuggestedAction
                            {
                                Actions = new List<CardAction>
                                    {
                                        new CardAction
                                        {
                                            Type = ActionTypes.OpenUrl,
                                            Value = signInLink,
                                            Title = "Bot Service OAuth",
                                        },
                                    },
                            },
                        },
                    };
                }
    
                var heroCard = new ThumbnailCard
                {
                    Title = "Thumbnail Card",
                    Text = query.Url,
                    Images = new List<CardImage> { new CardImage("https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png") },
                };
    
                var attachments = new MessagingExtensionAttachment(HeroCard.ContentType, null, heroCard);
                var result = new MessagingExtensionResult("list", "result", new[] { attachments });
    
                return new MessagingExtensionResponse(result);
            }