iT邦幫忙

第 11 屆 iThome 鐵人賽

1

今天要介紹的是 Account Link,此功能可以將網站帳號和 LINE Bot 整合,讓用戶可以使用網站帳號操作 LINE Bot 功能,例如購物、查訂單等等。


和 LINE Login 的差異

兩者對帳號的應用是相反的。

  • Account Link: 在 LINE Bot 上使用網站帳號。
  • LINE Login: 在網站上使用 LINE 帳號。

Account Link 運作方式

1. 當用戶按下綁定帳號,呼叫下列網址取得 linkToken。

POST https://api.line.me/v2/bot/user/{userId}/linkToken  

https://ithelp.ithome.com.tw/upload/images/20200205/20106865SHrAwwBjFD.jpg

2. 回傳網站的登入連結並帶上 linkToken。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865EG3o1pZGRv.jpg

3. 用戶登入成功後,產生隨機碼,並將用戶重新轉向下列網址。

GET https://access.line.me/dialog/bot/accountLink?
    linkToken={linkToken}&nonce={nonce}

https://ithelp.ithome.com.tw/upload/images/20200205/20106865oJDoZgrIK2.jpg

4. LINE 透過 Webhook 回傳 LINE userId、nonce 和綁定結果。

接下來我們就能透過 nonce 將網站帳號和 LINE userId 關聯在一起。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865SlNiQBAXbV.jpg

5. 完成 Account Link 後,LINE Bot 就可以使用 userId 存取網站資料。

詳細說明可以參考 #account-link-sequence


程式部分

1. 尚未綁定帳號,跳出綁定按鈕。

var flexMessage = new FlexMessage("AltText")
{
    Contents = new BubbleContainer
    {
        Body = new BoxComponent
        {
            Layout = BoxLayout.Vertical,
            Contents = new List<IFlexComponent>
            {
                new TextComponent
                {
                    Text = "尚未綁定帳號",
                    Weight = Weight.Bold,
                    Size = ComponentSize.Md,
                    Align = Align.Center
                }
            }
        },
        Footer = new BoxComponent
        {
            Layout = BoxLayout.Vertical,
            Contents = new List<IFlexComponent>
            {
                new ButtonComponent
                {
                    Style = ButtonStyle.Primary,
                    Action = new PostbackTemplateAction("綁定帳號", "Link")
                }
            }
        },
        Styles = new BubbleStyles
        {
            Footer = new BlockStyle
            {
                Separator = true
            }
        }
    }
};

await _messagingClient.ReplyMessageAsync(ev.ReplyToken,
    new List<ISendMessage> { flexMessage });

2. 呼叫 API 取得 linkToken 並回傳網站登入連結。

protected override async Task OnPostbackAsync(PostbackEvent ev)
{
    if (ev.Postback.Data == "Link")
    {
        //取得連接令牌
        var token = await _messagingClient
            .IssueLinkTokenAsync(ev.Source.UserId);

        //回覆登入連結並帶上 linkToken
        await _messagingClient.ReplyMessageAsync(ev.ReplyToken,
            $"https://ibottestapi.azurewebsites.net/user/login?linkToken={token}");
    }
}

3. 登入畫面。

  • Controller
[HttpGet("login")]
public IActionResult Login()
{
    return View();
}
  • View
@model ViewModels.Login
@{
    Layout = null;
}

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
</head>
<body>
    <form method="post">
        <div style="margin: 0px auto; text-align: center">
            <div style="padding: 10px">
                <span>帳號:</span>
                <input type="text" asp-for="Account" />
            </div>
            <div style="padding: 10px">
                <span>密碼:</span>
                <input type="password" asp-for="Password" />
            </div>
            <div style="padding: 10px">
                <input type="submit" value="登入" />
            </div>
        </div>
    </form>
</body>
</html>

4. 登入成功後,產生隨機碼,並回傳完成綁定連結。

  • Controller
[HttpPost("login")]
public async Task<IActionResult> Login([FromForm]Login model, 
    [FromQuery] string linkToken)
{
    //登入成功
    if (model.Account == "abc" && model.Password == "123")
    {
        //產生隨機碼
        var nonce = GenerateNonce();

        //儲存隨機碼
        var user = await _db.Users.Where(it => it.Account == "abc")
            .FirstOrDefaultAsync();
        user.Nonce = nonce;
        user.UserId = "";
        await _db.SaveChangesAsync();

        //返回連結給用戶
        return View("Link",
            $"https://access.line.me/dialog/bot/accountLink?linkToken={linkToken}&nonce={nonce}");
    }
    return View(model);
}

//產生隨機碼
private static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
private string GenerateNonce()
{
    var bytes = new Byte[16];
    rng.GetBytes(bytes);
    return Convert.ToBase64String(bytes)
        .Replace("=", "");
}
  • ViewModel
public class Login
{
    public string Account { get; set; }
    public string Password { get; set; }
}
  • View
@model string
@{
    Layout = null;
}

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Link</title>
</head>
<body>
    <div style="margin: 0px auto; text-align: center">
        <a href="@Model">點擊連結完成帳號綁定</a>
    </div>
</body>
</html>

5. LINE 透過 Webhook 回傳 LINE userId、nonce 和綁定結果。

protected override async Task OnAccountLinkAsync(AccountLinkEvent ev)
{
    //綁定成功
    if (ev.Link.Result == LinkResult.OK)
    {
        //儲存 LINE ID
        var user = await _db.Users.Where(it => it.Nonce == ev.Link.Nonce)
            .FirstOrDefaultAsync();
        user.UserId = ev.Source.UserId;
        await _db.SaveChangesAsync();

        //回覆訊息
        await _messagingClient.ReplyMessageAsync(ev.ReplyToken,
            "帳號綁定完成!!");
    }
}

結果

1. 尚未綁定帳號,跳出綁定按鈕。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865Q0ggya0uUz.jpg

2. 回傳網站登入連結並帶上 linkToken。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865PF5ejhfsts.jpg

3. 登入系統。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865oBoyYkGvMb.jpg

4. 點擊連結完成綁定。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865wI8sJ0mNje.jpg

5. 成功後出現這個畫面。

https://ithelp.ithome.com.tw/upload/images/20200205/20106865EKOu8CkBrM.jpg

6. 回到 LINE Bot 顯示綁定結果。

https://ithelp.ithome.com.tw/upload/images/20200205/201068655kB1FTFPEn.jpg


取消綁定

LINE 規定使用 Account Link 必需提供取消鏈接的功能。

程式碼

if (ev.Postback.Data == "UnLink")
{
    //取消綁定
    var user = await _db.Users.Where(it => it.UserId == ev.Source.UserId)
        .FirstOrDefaultAsync();
    user.Nonce = "";
    user.UserId = "";
    await _db.SaveChangesAsync();
}

結果

https://ithelp.ithome.com.tw/upload/images/20200205/20106865mEWqCseoYm.jpg

  • 取消前

https://ithelp.ithome.com.tw/upload/images/20200205/20106865wJOtZ5Gy8R.jpg

  • 取消後

https://ithelp.ithome.com.tw/upload/images/20200205/20106865Q4QFKsgSiB.jpg


結語

下一篇要介紹 LINE Things,今天就到這裡,感謝大家觀看。 (´・ω・`)


參考文章

如何讓 Chatbot 透過 Account Link 來串接使用者體驗


上一篇
[Day22] 如何製作 LINE Bot 行動支付機器人 - LINE Pay
下一篇
[Day24] LINE Bot 也可以 IOT - LINE Things
系列文
Line Bot 心得分享 LineMessagingApi + LUIS + BotFramework27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言