2016-08-22 207 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