/// <summary> /// Retrieve a signin link for a user based on the Connection Name. This is then used for the user to click and authenticate, generating a token returned back to the Token store. /// </summary> /// <param name="userId">The user id value.</param> /// <param name="credentialProvider">The credential provider value.</param> /// <param name="connectionName">The connection name value.</param> /// <param name="finalRedirect">The final redirect value.</param> /// <returns>Sign in link string value.</returns> public async Task <string> GetSignInLinkAsync(string userId, ICredentialProvider credentialProvider, string connectionName, string finalRedirect) { // The BotFramework Adapter, Bot ApplicationID and Bot Secret is required to access the Token APIs // These must match the Bot making use of the Linked Accounts feature. var adapter = new BotFrameworkAdapter(credentialProvider); var botAppId = ((ConfigurationCredentialProvider)credentialProvider).AppId; var botAppPassword = ((ConfigurationCredentialProvider)credentialProvider).Password; string link = null; using (var context = new TurnContext(adapter, new Microsoft.Bot.Schema.Activity { })) { var connectorClient = new ConnectorClient(new Uri(TokenServiceUrl), botAppId, botAppPassword); context.TurnState.Add <IConnectorClient>(connectorClient); // Retrieve a signin link for a given Connection Name and UserId link = await adapter.GetOauthSignInLinkAsync(context, connectionName, userId, finalRedirect); // Add on code_challenge (SessionId) into the redirect var sessionId = SessionController.Sessions.FirstOrDefault(s => s.Key == userId).Value; if (!string.IsNullOrEmpty(sessionId)) { link += HttpUtility.UrlEncode($"&code_challenge={sessionId}"); } } return(link); }