2016-08-22 36 views
5

使用Microsoft Bot Framework V3我开始使用登录卡。Bot框架 - 登录卡,如何获得身份验证结果

我做了一个简单的剪裁和示例代码页粘贴到我的代码,并假设它的工作原理(编译): https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html

什么预期类似OAuth的过程,从而被重定向到一个问题,请它是自己的东西,并返回包括所有信息的身份验证结果。

我意识到的是,它只是打开一个新的网页,我提供的链接,这一切......

成立elsewere没有其他代码...

到目前为止,似乎没用,因为我可以根据这种行为简单地提供正常消息的链接,也没有与bot的通信。

我错过了什么吗?

回答

5

不,你没有错过任何东西。登录卡只是向用户显示需要验证的可视方式。每个频道将以不同方式显示登录卡;取决于渠道的实施。

要实施OAuth过程,我建议您看看AuthBot

AuthBot是一个.Net库,用于通过Microsoft Bot Framework构建的 机器人上的Azure Active Directory身份验证。

即使您不使用AAD,该库仍然有助于了解如何实现OAuth进程。此外,在某些情况下,AuthBot也使用登录卡来要求用户进行身份验证(请参阅this code)。

还有其他样本也可以帮助您了解如何建立OAuth的过程:

+0

做u有任何替代AuthBot节点 – aWebDeveloper

+0

的看看到https://github.com/CatalystCode /节点authbot –

3

选项1)使用自定义身份验证Windows Active Directory的

我做了一个自定义验证技术e使用Kerberos LDAP协议并使用PrincipalContext类来查询Windows AD。

首先,在根对话框中保存ConversationReference中聊天的上下文并使用Base64编码对其进行编码。

using System; 
using System.Threading.Tasks; 
using Microsoft.Bot.Builder.Dialogs; 
using Microsoft.Bot.Connector; 
using Microsoft.Bot.Builder.ConnectorEx; 
using System.Threading; 

namespace ADAuthBot.Dialogs 
{ 
    [Serializable] 
    public class RootDialog : IDialog<object> 
    { 
     public async Task StartAsync(IDialogContext context) 
     { 
      await context.PostAsync("Welcome to Auth Bot!"); 
      context.Wait(MessageReceivedAsync); 
     } 

     private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result) 
     { 
      var message = await result as Activity; 
      ConversationReference conversationReference = message.ToConversationReference(); 

      string username = string.Empty; 

      context.PrivateConversationData.SetValue<string>("usertext", message.Text); 
      if (!context.PrivateConversationData.TryGetValue<string>("Username", out username)) 
      { 
       string encodedCookie = UrlToken.Encode(conversationReference); 
       await AuthDialog.createPromptForLogin(context, encodedCookie); 
      } 

      else 
      { 
       context.Call(this, ResumeAfter); 
      } 


     } 

     private async Task ResumeAfter(IDialogContext context, IAwaitable<object> result) 
     { 
      var item = await result; 
      context.Wait(MessageReceivedAsync); 
     } 
    } 
} 

接下来,我们就来在我们创建一个登录卡并给需要的身份验证点击按钮来打开的URL页面的验证对话框。

using System; 
using System.Threading.Tasks; 
using Microsoft.Bot.Builder.Dialogs; 
using Microsoft.Bot.Connector; 
using Microsoft.Bot.Builder.ConnectorEx; 
using System.Threading; 
using System.Collections.Generic; 
using System.Configuration; 

namespace ADAuthBot.Dialogs 
{ 
    [Serializable] 
    public class AuthDialog: IDialog<object> 
    { 
     static string authenticationUrl = string.Empty; //Authentication URL is the MVC View URL, which will have the username and password window. 
     static string callbackurl = string.Empty; 
     static AuthDialog() 
     { 
      authenticationUrl = ConfigurationManager.AppSettings["AuthenticationUrl"]; 
      callbackurl = ConfigurationManager.AppSettings["AuthCallbackUrl"]; 
     } 
     public async Task StartAsync(IDialogContext context) 
     { 
      context.Wait(MessageReceivedAsync); 
     } 

     private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result) 
     { 
     } 

     public static async Task createPromptForLogin(IDialogContext context, string encodedCookie) 
     { 
      IMessageActivity response = context.MakeMessage(); 
      response.Attachments = new List<Attachment>(); 

      SigninCard signincard = new SigninCard() 
      { 
       Text = "Click here to sign in", 
       Buttons = new List<CardAction>() { 
         new CardAction() 
         { 
          Title = "Authentication Required", 
          Type = ActionTypes.OpenUrl, 
          Value = $"{authenticationUrl}?{encodedCookie}" 
         } 
        } 
      }; 

      response.Attachments.Add(signincard.ToAttachment()); 
      await context.PostAsync(response); 
     } 
    } 
} 

接下来我做它输入您的用户名和密码,并将其发送到ADAuthController查询它针对的Windows Active Directory中的MVC视图。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 

namespace ADAuthService.Controllers 
{ 
    public class LoginADController : Controller 
    { 
     // GET: LoginAD 
     [Route("Login")] 
     public ActionResult LoginUsingAD() 
     { 
      return View(); 
     } 

    } 
} 

接下来,我创建了用了jQuery AJAX调用由Base64编码的编码它使用JavaScript的BTOA()函数来发送用户名和密码的简单Razor视图。

<script src="~/scripts/jquery-3.2.1.min.js"></script> 
<script src="~/scripts/bootstrap.min.js"></script> 
<link href="~/Content/bootstrap.min.css" rel="stylesheet" /> 

<script> 
     $(function() { 
      $("#txtUserName").html(""); 
      $("#txtPassword").html(""); 


      function make_base64_auth(username, password) { 
       var tok = username + ' ' + password; 
       var hash = btoa(tok); 
       return hash; 
      } 

      $("#btnSubmit").click(function() { 
       var userName = $("#txtUserName").val(); 
       var passWord = $("#txtPassword").val(); 

       var conversationReference = $(location).attr('search'); 
       console.log(conversationReference); 

       var dataToBeSent = { 
        "ConversationReference": conversationReference, 
        "HashedUserCredentials": make_base64_auth(userName, passWord) 
       }; 

       $.ajax({ 
        url: "http://localhost:1070/api/Login", 
        method: "POST", 
        dataType: "json", 
        data: dataToBeSent, 
        contentType: "application/json", 
        crossDomain: true, 
        success: function (data) { 
         debugger; 
         console.log(data); 
         if(!$.isEmptyObject(data)) 
          alert(data); 
        }, 
        error: function (jqXHR, textStatus, errorThrown) { 
         debugger; 
         if (!$.isEmptyObject(jqXHR)) 
         alert("Something happened wrong because: " + jqXHR.responseText); 
        } 
       }); 

      }); 
     }); 
</script> 
<div class="panel-info"> 
    <div class="panel panel-heading"> 
     Enter your credentials 
    </div> 
    <div class="panel panel-body"> 
     <div class="form-group"> 
      <label for="username">Username: </label> <input id="txtUserName" type="text" placeholder="Enter username" required class="form-control" /> 
      <label for="password">Password: </label> <input id="txtPassword" type="password" placeholder="Enter password" required class="form-control" /> 
      <button id="btnSubmit" class="btn btn-info">Submit</button> 
      <button id="btnReset" class="btn btn-danger" type="reset">Reset</button> 
     </div> 
    </div> 
</div> 

我做了一个模型类来存储用户是否被识别。

namespace ADAuthService.Models 
{ 
    public class AuthenticatedUser 
    { 
     public string AuthenticatedUserName { get; set; } = string.Empty; 
     public bool IsAuthenticated { get; set; } = false; 
    } 
} 

和模型类从MVC视图中获取详细信息。

namespace ADAuthService.Models 
{ 
    public class UserDetailsHashed 
    { 
     public string HashedUserCredentials { get; set; } = string.Empty; 
     public string ConversationReference { get; set; } = string.Empty; 
    } 
} 

现在主要内容是编写一个方法,通过以用户名,密码和域作为输入来查询Windows Active Directory。通过身份验证后,我使用服务URL通过使用Autofac IoC容器解析范围来将经过身份验证的用户名发送到bot框架。

using ADAuthService.Models; 
using Autofac; 
using Microsoft.Bot.Builder.Dialogs; 
using Microsoft.Bot.Builder.Dialogs.Internals; 
using Microsoft.Bot.Connector; 
using System; 
using System.Collections.Generic; 
using System.DirectoryServices.AccountManagement; 
using System.Linq; 
using System.Net; 
using System.Net.Http; 
using System.Security.Authentication; 
using System.Text; 
using System.Threading.Tasks; 
using System.Web.Http; 
using System.Web.Http.Cors; 

namespace ADAuthService.Controllers 
{ 
    public class ADAuthController : ApiController 
    { 
     [NonAction] 
     private void extractUserDetailsFromHash(UserDetailsHashed userDetails, out string username, out string password, out string conversationReference) 
     { 
      try 
      { 
       string[] userCredentials = userDetails.HashedUserCredentials.Split(' '); 
       byte[] userCredentialsBinary = Convert.FromBase64String(userCredentials.Last()); 
       string decodedString = Encoding.UTF8.GetString(userCredentialsBinary); 
       string[] decodedStringArray = decodedString.Split(' '); 
       username = decodedStringArray[0]; 
       password = decodedStringArray[1]; 
       string[] userConversationReference = userDetails.ConversationReference.Split('?'); 
       conversationReference = userConversationReference[1]; 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 
     } 

     [NonAction] 
     private Task<AuthenticatedUser> ValidateUserAgainstAD(string username, string password) 
     { 
      AuthenticatedUser user = new AuthenticatedUser(); 
      return Task.Run<AuthenticatedUser>(() => { 
       string ADDisplayName = string.Empty; 
       try 
       { 
        using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName)) 
        { 
         bool isValidCredentials = ctx.ValidateCredentials(username, password, ContextOptions.Negotiate); 

         // Additional check to search user in directory. 
         if (isValidCredentials) 
         { 
          UserPrincipal prUsr = new UserPrincipal(ctx); 
          prUsr.SamAccountName = username; 
          PrincipalSearcher srchUser = new PrincipalSearcher(prUsr); 
          UserPrincipal foundUsr = srchUser.FindOne() as UserPrincipal; 

          if (foundUsr != null) 
          { 
           user.AuthenticatedUserName = foundUsr.DisplayName; 
           user.IsAuthenticated = isValidCredentials; 
          } 
         } 
         else 
          throw new AuthenticationException($"Couldn't query no such credentials in Microsoft Active Directory such as Username: {username} and Password: {password}. Try entering a valid username and password combination."); 
        } 
       } 
       catch (Exception ex) 
       { 
        throw ex; 
       } 
       return user; 
      });  
     } 

     [NonAction] 
     public async Task ReplyToBot(string userName, string encodedConversationReference) 
     { 
      Activity reply = null; 
      ConversationReference decodedConversationReference = UrlToken.Decode<ConversationReference>(encodedConversationReference); 
      bool writeSuccessful = false; 
      IMessageActivity msgToBeSent = decodedConversationReference.GetPostToUserMessage(); 

      using (ILifetimeScope scope = DialogModule.BeginLifetimeScope(Conversation.Container, msgToBeSent)) 
      { 
       try 
       { 
        IConnectorClient client = scope.Resolve<IConnectorClient>(); 
        IStateClient sc = scope.Resolve<IStateClient>(); 

        BotData userData = sc.BotState.GetPrivateConversationData(msgToBeSent.ChannelId, msgToBeSent.From.Id, msgToBeSent.Id); 
        userData.SetProperty("Username", userName); 
        sc.BotState.SetPrivateConversationData(msgToBeSent.ChannelId, msgToBeSent.Conversation.Id, msgToBeSent.Id, userData); 
        writeSuccessful = true; 
       } 
       catch (Exception ex) 
       { 
        writeSuccessful = false; 
        throw ex; 
       } 

       if (!writeSuccessful) 
       { 
        msgToBeSent.Text = string.Empty; 
        await Conversation.ResumeAsync(decodedConversationReference, msgToBeSent); 
       } 
       if (writeSuccessful) 
       { 
        reply = msgToBeSent as Activity; 
        var connector = new ConnectorClient(new Uri(msgToBeSent.ServiceUrl)); 
        reply.Text = $"Welcome {userName}!"; 
        connector.Conversations.SendToConversation(reply); 
       } 
      } 
     } 

     [HttpPost] 
     [EnableCors("*", "*", "*")] 
     [Route("api/Login")] 
     public async Task<HttpResponseMessage> Login(UserDetailsHashed userDetails) 
     { 
      try 
      { 
       string username = string.Empty; 
       string password = string.Empty; 
       string conversationReference = string.Empty; 
       AuthenticatedUser userToBeAuthenticated = new AuthenticatedUser(); 

       extractUserDetailsFromHash(userDetails, out username, out password, out conversationReference); 
       userToBeAuthenticated = await ValidateUserAgainstAD(username, password); 


       if (userToBeAuthenticated.IsAuthenticated) 
       { 
        await ReplyToBot(userName: userToBeAuthenticated.AuthenticatedUserName, encodedConversationReference: conversationReference); 
        return new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent($"Thanks, {userToBeAuthenticated.AuthenticatedUserName} you're now logged in!") }; 
       } 
       else 
       { 
        return new HttpResponseMessage { StatusCode = HttpStatusCode.Forbidden, Content = new StringContent($"Couldn't query no such credentials in Microsoft Active Directory such as Username: {username} and Password: {password}. Try entering a valid username and password combination.") }; 
       } 
      } 
      catch(Exception ex) 
      { 
       throw new HttpResponseException(new HttpResponseMessage() { StatusCode = HttpStatusCode.Forbidden, Content = new StringContent($"Couldn't query no such credentials in Microsoft Active Directory. Try entering a valid username and password combination.") }); 
      } 
     }   
    } 
} 

选项2)使用下面的链接描述的模式:

MSDN Magic number Pattern