public async Task <IActionResult> GetRepositoriesAsync()
    {
        var userId   = User.FindFirstValue(ClaimConstants.ObjectId);
        var tenantId = User.FindFirstValue(ClaimConstants.TenantId);

        if (userId == default)
        {
            return(new BadRequestObjectResult(new { Error = "Object (User) id in token null or undefined" }));
        }
        if (tenantId == default)
        {
            return(new BadRequestObjectResult(new { Error = "Tenant id in token null or undefined" }));
        }

        var tokenResult = await _tokenProvider.GetAccessTokenAsync(tenantId : tenantId, userId : userId);

        if (tokenResult is NeedsConsentResult needsConsentResult)
        {
            return(new ObjectResult(new
            {
                authorize_url = needsConsentResult.AuthorizeUri,
            })
            {
                StatusCode = (int)HttpStatusCode.PreconditionFailed,
            });
        }
        else if (tokenResult is AccessTokenResult accessTokenResult)
        {
            var repositories = await _githubServiceClient.GetRepositoriesAsync(accessTokenResult.AccessToken);

            _logger.LogInformation("Repo count: {count}", repositories.Count);
            return(new JsonResult(repositories.ToList()));
        }

        _logger.LogWarning("Unknown access token return type: [{type}]", tokenResult.GetType());
        return(new StatusCodeResult((int)HttpStatusCode.InternalServerError));
    }
    public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object?options = null, CancellationToken cancellationToken = default)
    {
        dc = dc ?? throw new ArgumentNullException(nameof(dc));

        if (options != null)
        {
            throw new ArgumentException($"{nameof(options)} cannot be defined for {nameof(AccountLinkingPrompt)}");
        }

        // Initialize state
        var state = dc.ActiveDialog.State;

        state[ExpirationKey] = DateTimeOffset.UtcNow + _options.Timeout;
        var userId   = dc.Context.Activity.From.AadObjectId;
        var tenantId = dc.Context.Activity.Conversation.TenantId;

        // Attempt to get the users token
        var tokenResult = await _oauthTokenProvider.GetAccessTokenAsync(tenantId : tenantId, userId : userId);

        if (tokenResult is NeedsConsentResult needsConsentResult)
        {
            var(codeChallenge, codeVerifier) = Pkce.GeneratePkceCodes();
            var queryParams = HttpUtility.ParseQueryString(needsConsentResult.AuthorizeUri.Query);
            queryParams.Add("state", codeChallenge); // For bot we'll just use the codeChallenge as the 'state'
            queryParams.Add("code_challenge", codeChallenge);
            var loginConsentUri = new UriBuilder(needsConsentResult.AuthorizeUri)
            {
                Query = queryParams.ToString()
            };
            // we can keep the code verifier out of the
            state[CodeVerifierKey] = codeVerifier;
            var activity = MessageFactory.Attachment(new Attachment
            {
                ContentType = SigninCard.ContentType,
                Content     = new SigninCard
                {
                    Text    = "Please sign in",
                    Buttons = new[]
                    {
                        new CardAction
                        {
                            Title = "Sign in",
                            Type  = ActionTypes.Signin,
                            Value = loginConsentUri.ToString()
                        },
                    },
                },
            });
            var response = await dc.Context.SendActivityAsync(activity, cancellationToken : cancellationToken).ConfigureAwait(false);

            state[CardActivityKey] = response.Id;
            return(EndOfTurn);
        }
        else if (tokenResult is AccessTokenResult accessTokenResult)
        {
            return(await dc.EndDialogAsync(accessTokenResult, cancellationToken).ConfigureAwait(false));
        }

        // Prompt user to login
        _logger.LogWarning("Unknown token result, ending turn");
        return(EndOfTurn);
    }
    protected override async Task <MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(
        ITurnContext <IInvokeActivity> turnContext,
        MessagingExtensionQuery query,
        CancellationToken cancellationToken)
    {
        var userId   = turnContext.Activity.From.AadObjectId;
        var tenantId = turnContext.Activity.Conversation.TenantId;

        if (!string.IsNullOrEmpty(query.State))
        {
            var authResponseObject = JsonSerializer.Deserialize <AuthResponse>(query.State);
            if (authResponseObject == default)
            {
                _logger.LogWarning("Invalid state object provided: {state}", query.State);
                throw new Exception("Invalid state format");
            }
            _logger.LogInformation("Params:\nState: {state}\nCode: {code}", authResponseObject.State, authResponseObject.AccountLinkingState);
            var codeVerifier = _dataProtector.Unprotect(authResponseObject.State);
            await _oAuthTokenProvider.ClaimTokenAsync(
                accountLinkingToken : authResponseObject.AccountLinkingState, // these are inverted because
                codeVerifier : codeVerifier,
                tenantId : tenantId,
                userId : userId);
        }

        // Attempt to retrieve the github token
        var tokenResult = await _oAuthTokenProvider.GetAccessTokenAsync(tenantId : tenantId, userId : userId);

        if (tokenResult is NeedsConsentResult needsConsentResult)
        {
            _logger.LogInformation("Messaging Extension query with no GitHub token, sending login prompt");
            var(codeChallenge, codeVerifier) = Pkce.GeneratePkceCodes();
            var queryParams = HttpUtility.ParseQueryString(needsConsentResult.AuthorizeUri.Query);
            queryParams.Add("state", _dataProtector.Protect(codeVerifier));
            queryParams.Add("code_challenge", codeChallenge);
            var loginConsentUri = new UriBuilder(needsConsentResult.AuthorizeUri)
            {
                Query = queryParams.ToString()
            };
            return(new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type = "auth",
                    SuggestedActions = new MessagingExtensionSuggestedAction
                    {
                        Actions = new List <CardAction>
                        {
                            new CardAction
                            {
                                Type = ActionTypes.OpenUrl,
                                Title = "Please login to GitHub",
                                Value = loginConsentUri.ToString()
                            },
                        },
                    },
                },
            });
        }
        else if (tokenResult is AccessTokenResult accessTokenResult)
        {
            var repos = await _gitHubServiceClient.GetRepositoriesAsync(accessTokenResult.AccessToken);

            return(new MessagingExtensionResponse
            {
                ComposeExtension = new MessagingExtensionResult
                {
                    Type = "result",
                    AttachmentLayout = "list",
                    Attachments = repos.Select(r =>
                                               new MessagingExtensionAttachment
                    {
                        ContentType = HeroCard.ContentType,
                        Content = new HeroCard {
                            Title = $"{r.Name} ({r.Stars})"
                        },
                        Preview = new HeroCard {
                            Title = $"{r.Name} ({r.Stars})"
                        }.ToAttachment(),
                    }).ToList(),
                },
            });
        }
        // There was an error
        return(new MessagingExtensionResponse
        {
        });
    }