public static async Task <object> HandleOAuthCallback(HttpRequestMessage req, uint maxWriteAttempts) { try { var queryParams = req.RequestUri.ParseQueryString(); if (req.Method != HttpMethod.Post) { throw new ArgumentException("The OAuth postback handler only supports POST requests."); } var formData = await req.Content.ReadAsFormDataAsync(); string stateStr = formData["state"]; string code = formData["code"]; var resumptionCookie = UrlToken.Decode <ResumptionCookie>(stateStr); var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { AuthenticationSettings authSettings = AuthenticationSettings.GetFromAppSettings(); var client = scope.Resolve <IConnectorClient>(); AuthenticationResult authenticationResult = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, authSettings); IStateClient sc = scope.Resolve <IStateClient>(); //IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT //REMOVING THIS WILL REMOVE YOUR BOT AND YOUR USERS TO SECURITY VULNERABILITIES. //MAKE SURE YOU UNDERSTAND THE ATTACK VECTORS AND WHY THIS IS IN PLACE. int magicNumber = GenerateRandomNumber(); bool writeSuccessful = false; uint writeAttempts = 0; while (!writeSuccessful && writeAttempts++ < maxWriteAttempts) { try { BotData userData = sc.BotState.GetUserData(message.ChannelId, message.From.Id); userData.SetProperty(AuthenticationConstants.AuthResultKey, authenticationResult); userData.SetProperty(AuthenticationConstants.MagicNumberKey, magicNumber); userData.SetProperty(AuthenticationConstants.MagicNumberValidated, "false"); sc.BotState.SetUserData(message.ChannelId, message.From.Id, userData); writeSuccessful = true; } catch (HttpOperationException) { writeSuccessful = false; } } var resp = new HttpResponseMessage(HttpStatusCode.OK); if (!writeSuccessful) { message.Text = String.Empty; // fail the login process if we can't write UserData await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent("<html><body>Could not log you in at this time, please try again later</body></html>", System.Text.Encoding.UTF8, @"text/html"); } else { await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete:<br/> <h1>{magicNumber}</h1>.</body></html>", System.Text.Encoding.UTF8, @"text/html"); } return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(req.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string code, [FromUri] string state) { try { object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } // Get the resumption cookie var resumptionCookie = UrlToken.Decode <ResumptionCookie>(state); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { //TODO: Scopes definition here // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, new string[] { "User.Read" }); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } var reply = await Conversation.ResumeAsync(resumptionCookie, message); var data = await client.Bots.GetPerUserConversationDataAsync(resumptionCookie.BotId, resumptionCookie.ConversationId, resumptionCookie.UserId); reply.SetBotUserData(ContextConstants.AuthResultKey, authResult); int magicNumber = GenerateRandomNumber(); reply.SetBotUserData(ContextConstants.MagicNumberKey, magicNumber); reply.SetBotUserData(ContextConstants.MagicNumberValidated, "false"); //data.SetProperty(ContextConstants.AuthResultKey, authResult); //data.SetProperty(ContextConstants.MagicNumberKey, magicNumber); //data.SetProperty(ContextConstants.MagicNumberValidated, "false"); //await client.Bots.SetUserDataAsync(resumptionCookie.BotId, resumptionCookie.UserId, data); reply.To = message.From; reply.From = message.To; await client.Messages.SendMessageAsync(reply); var resp = new HttpResponseMessage(HttpStatusCode.OK); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete: {magicNumber}.</body></html>", System.Text.Encoding.UTF8, @"text/html"); return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException("Cannot resume!"))); } }
public async Task <HttpResponseMessage> OAuthCallback( [FromUri] string code, [FromUri] string state, CancellationToken cancellationToken) { try { var queryParams = state; object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } var resumptionCookie = UrlToken.Decode <ResumptionCookie>(queryParams); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, Models.AuthSettings.Scopes); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } IStateClient sc = scope.Resolve <IStateClient>(); //IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT //REMOVING THIS WILL REMOVE YOUR BOT AND YOUR USERS TO SECURITY VULNERABILITIES. //MAKE SURE YOU UNDERSTAND THE ATTACK VECTORS AND WHY THIS IS IN PLACE. int magicNumber = GenerateRandomNumber(); bool writeSuccessful = false; uint writeAttempts = 0; while (!writeSuccessful && writeAttempts++ < MaxWriteAttempts) { try { BotData userData = sc.BotState.GetUserData(message.ChannelId, message.From.Id); userData.SetProperty(ContextConstants.AuthResultKey, authResult); userData.SetProperty(ContextConstants.MagicNumberKey, magicNumber); userData.SetProperty(ContextConstants.MagicNumberValidated, "false"); sc.BotState.SetUserData(message.ChannelId, message.From.Id, userData); writeSuccessful = true; } catch (HttpOperationException) { writeSuccessful = false; } } var resp = new HttpResponseMessage(HttpStatusCode.OK); if (!writeSuccessful) { message.Text = String.Empty; // fail the login process if we can't write UserData await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent("<html><body>Could not log you in at this time, please try again later</body></html>", System.Text.Encoding.UTF8, @"text/html"); } else { await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete:<br/> <h1>{magicNumber}</h1>.</body></html>", System.Text.Encoding.UTF8, @"text/html"); } return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string code, [FromUri] string state, CancellationToken cancellationToken) { try { object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } // Get the resumption cookie var resumptionCookie = UrlToken.Decode <ResumptionCookie>(state); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, Models.AuthSettings.Scopes); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } IStateClient sc = scope.Resolve <IStateClient>(); //IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT //REMOVING THIS WILL REMOVE YOUR BOT AND YOUR USERS TO SECURITY VULNERABILITIES. //MAKE SURE YOU UNDERSTAND THE ATTACK VECTORS AND WHY THIS IS IN PLACE. var dataBag = scope.Resolve <IBotData>(); await dataBag.LoadAsync(cancellationToken); int magicNumber = GenerateRandomNumber(); dataBag.UserData.SetValue(ContextConstants.AuthResultKey, authResult); dataBag.UserData.SetValue(ContextConstants.MagicNumberKey, magicNumber); dataBag.UserData.SetValue(ContextConstants.MagicNumberValidated, "false"); await dataBag.FlushAsync(cancellationToken); await Conversation.ResumeAsync(resumptionCookie, message); var resp = new HttpResponseMessage(HttpStatusCode.OK); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete: {magicNumber}.</body></html>", System.Text.Encoding.UTF8, @"text/html"); return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }