opened
mcinnes01
username_0
Hi,

In order to authenticate a user of my bot with our main product, I create LoginCard with a button that has a link to register page for their particular tenant. The url contains a query string containing the ConversationReference and a bot ID and then our main product (which doesn't contain any botframework packages) calls an endpoint on my bot.

The payload contains the bot ID, and the Conversation Reference, along with a refresh token. This used to allow me to resume my conversation and then call our oAuth endpoints on our main product to get an access token from the refresh token. I then use the access token to authenticate any requests to our main product.

Previously this was handled by a standard controller and then I did the following:

```
		[HttpPost]
		public async Task<HttpResponseMessage> RegisterConversation([FromBody] RegistrationRequest registration)
		{
			if (registration == null || registration.SharedKey != _interactkey)
			{
				return Request.CreateResponse(HttpStatusCode.BadRequest);
			}

			// Construct an activity from the resumption cookie
			// Currently a bug in the botbuilder so using a work around for now this is what it should be eventually
			//var conversationReference = Microsoft.Bot.Builder.ConnectorEx.ConversationReferenceHelpers.GZipDeserialize(registration.State);
			var conversationReference = ConversationReferenceHelpers.GZipDeserializer(registration.State);
			var activity = conversationReference.GetPostToBotMessage();

			using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
			{
				try
				{
					var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
					var key = Address.FromActivity(activity);
					var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);

					// Retrieve the site info from the user data
					var siteInfo = userData.GetProperty<SiteInfo>(StateType.SiteInfo.GetDescription());

					// If there is no site info, the user needs to setup an intranet
					if (siteInfo == null)
					{
						try
						{
							var dialog = scope.Resolve<IDialog<object>>();
							await Conversation.SendAsync(activity, () => dialog);
							return Request.CreateResponse(HttpStatusCode.NotFound);
						}
						catch (Exception conversationException)
						{
							return Request.CreateErrorResponse(HttpStatusCode.BadRequest, conversationException);
						}
					}

					// Add the token to the site info
					siteInfo.RefreshToken = registration.RefreshToken;
					userData.SetProperty(StateType.SiteInfo.GetDescription(), siteInfo);

					await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
					await botDataStore.FlushAsync(key, CancellationToken.None);

					// Reply to the conversation
					await Conversation.ResumeAsync(conversationReference, activity);
					return Request.CreateResponse(HttpStatusCode.OK);
				}
				catch (Exception ex)
				{
					return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.Message);
				}
			}
		}
```

I am now using v4 SDK with netcore so I've built my own message handler, following the structure of the proactive message handle. I've notice during this process there are some subtle difference between master and the 4.0.1 preview branch, but I've kept my code inline with the preview for now:

```
    public class InteractBotAuthenticationHandler : BotMessageHandlerBase
	{
		public InteractBotAuthenticationHandler(BotFrameworkAdapter botFrameworkAdapter) : base(botFrameworkAdapter)
		{
		}

		protected override async Task<InvokeResponse> ProcessMessageRequestAsync(HttpRequest request, BotFrameworkAdapter botFrameworkAdapter, Func<ITurnContext, Task> botCallbackHandler)
		{
			const string BotAppIdHttpHeaderName = "MS-BotFramework-BotAppId";
			const string BotIdQueryStringParameterName = "BotAppId";

			if (!request.Headers.TryGetValue(BotAppIdHttpHeaderName, out var botAppIdHeaders))
			{
				if (!request.Query.TryGetValue(BotIdQueryStringParameterName, out botAppIdHeaders))
				{
					throw new InvalidOperationException($"Expected a Bot App ID in a header named \"{BotAppIdHttpHeaderName}\" or in a querystring parameter named \"{BotIdQueryStringParameterName}\".");
				}
			}

			var botAppId = botAppIdHeaders.First();
			ConversationReference conversationReference;

			using (var bodyReader = new JsonTextReader(new StreamReader(request.Body, Encoding.UTF8)))
			{
				var registrationResponse = BotMessageHandlerBase.BotMessageSerializer.Deserialize<RegistrationRequest>(bodyReader);
				if (registrationResponse == null)
					return null;

				conversationReference = ConversationReferenceHelpers.GZipDeserializer(registrationResponse.State);

				using (var context = new TurnContext(botFrameworkAdapter, conversationReference.GetPostToBotMessage()))
				{
					var userState = context.GetUserState<BotUserState>().User;
					userState.RefreshToken = registrationResponse.RefreshToken;
					
				}
			}

			await botFrameworkAdapter.ContinueConversation(botAppId, conversationReference, botCallbackHandler);

			return null;
		}
	}
```

And here is my application builder extension:

```
	public static class ApplicationBuilderExtensions
	{
		private const string AuthenticatorPath = "/api/machine";

		public static IApplicationBuilder UseInteractBotAuthenticator(this IApplicationBuilder applicationBuilder)
		{
			if (applicationBuilder == null)
			{
				throw new ArgumentNullException(nameof(applicationBuilder));
			}

			var options = applicationBuilder.ApplicationServices.GetRequiredService<IOptions<BotFrameworkOptions>>().Value;
			var botFrameworkAdapter = new BotFrameworkAdapter(options.CredentialProvider, options.ConnectorClientRetryPolicy);

			foreach (var middleware in options.Middleware)
			{
				botFrameworkAdapter.Use(middleware);
			}

			applicationBuilder.Map(AuthenticatorPath, botActivitiesAppBuilder
				=> botActivitiesAppBuilder.Run(new InteractBotAuthenticationHandler(botFrameworkAdapter).HandleAsync));

			return applicationBuilder;
		}
	}
```
I've then registered this in the configure section of my bot's startup, but when I try to hit the endpoint it doesn't seem to invoke my new handler and doesn't hit any endpoints.

Is there something I am missing or is there a workaround for getting the proactive handler to take a payload?

Many thanks

Andy
Proactive message with payload
issue
created
mcinnes01
username_0
Just wondered if there is any update, I still haven't managed to successfully continue a conversation? 

I wonder does this perhaps have anything to do with no using an MicrosoftAppId or Password whilst debugging, and perhaps an empty string is not handled or the bot ID isn't being defaulted to "default-bot" for continuing a conversation?
comment
created
JonathanFingold
username_1
@username_0 Have you tried using a custom, bot-wide state? The [v4 proactive messages](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/16.proactive-messages) sample uses one, and seems very close to what you're describing. There is a corresponding [how-to](https://docs.microsoft.com/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0) article that goes into a little more depth.
comment
created
gabog
username_2
Closing as part of R10 triage.
comment
closed
gabog
username_2
issue