Contents

LINE登入-手動建立登入 OAuth 2

LINE登入-手動建立登入 OAuth 2

Line 手動建立OAuth 和 Facebook及Google並沒有不同,都是標準的OAuth流程,有差異的地方在於Line Profile 並沒有提供Email,相反的在取出AccessToken時,也會同時取得IdToken,就看Email是否是必要項目。這邊分兩部分,上半部是申請應用程式服務,下半部是手動建立OAuth程式碼,如果建立應用程式服務的畫面與你現在的不同,不要太意外,可能改版了。

Note

要特別留意的是,若決定使用第三方驗證的Id作為會員Primary key,則服務不可隨意中止,因為不同的Provider取得的UserId是不一樣的。

例如:同時申請兩種不同的服務,LINE Login、Line Bot,兩者皆可取得UserId,但同一使用者在這兩者服務中是不同UserId的。

建立應用程式服務

  1. Line Developer API 登入建立應用程式

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-01-46.png
  2. 選擇Create a LINE Login channel

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-02-06.png
  3. 填入Channel name和Channel Description,有提醒你哪些欄位不能為空

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-03-06.png
  4. 完成後你應該就可以看到Channel ID 和 Channel secret,這兩欄我們在交換權杖的時候會用到

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-03-43.png /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-04-02.png
  5. 下面有一個Email address permission,這個是選填,勾選後,使用者在第一次登入時,會出現是否同意讓應用程式讀取Email,如果你的應用程式不需要Email就不用管他

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-04-54.png /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-44-44.png
  6. 接下來在LINE Login的地方,要填寫回傳網址,填寫完畢後,整個建立過程就結束了

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-08-25.png

實作

  1. 取得叫用「登入」對話方塊與設定重新導向網址

    這邊參數請參考https://developers.line.biz/zh-hant/docs/line-login/integrate-line-login/#making-an-authorization-request

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    public IActionResult Index()
    {
        State = Guid.NewGuid();
        ViewData["LineAuth"] = $"https://access.line.me/oauth2/v2.1/authorize?" +
            $"client_id={_appId}" +
            $"&response_type=code" +
            $"&redirect_uri={RedirectUrl}" +
            $"&scope={HttpUtility.UrlEncode("profile openid email")}" +
            $"&state={State}";
        return View();
    }
    
  2. 前台簡單建立一個連結按鈕

    1
    
    <a href="@ViewData["LineAuth"]" class="btn btn-primary" title="Log in your account">Line</a>
    
  3. 點了會出現登入介面

  4. 使用者輸入完後,會導回我們剛剛設定的回傳網址

    /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-24_14-45-10.png
  5. 傳送使用代碼交換存取權杖,完整程式碼放在後面

    留意一下官網告知的傳送格式,是使用 application/x-www-form-urlencoded

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    // 使用代碼交換存取權杖 與Facebook 和 Google不同,是使用 application/x-www-form-urlencoded
    var url = "https://api.line.me/oauth2/v2.1/token";
    var postData = new Dictionary<string, string>()
    {
        {"client_id",_appId},
        {"client_secret",_appSecret},
        {"code",code},
        {"grant_type","authorization_code"},
        {"redirect_uri",RedirectUrl}
    };
    
    var contentPost = new FormUrlEncodedContent(postData);
    var client = _clientFactory.CreateClient();
    var response = await client.PostAsync(url, contentPost);
    
  6. Line API 可以同時取得ClientId TokenAccess Token,所以這邊有兩種選擇

    1. 使用JWT解析Id Token, Nuget > System.IdentityModel.Tokens.Jwt

      /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-17_14-52-46.png
    2. 使用 profile API,但很遺憾,這方法不能取得Email

      /static/LINE_手動建立登入_OAuth2_6f1edfe5cffb4d2da39edcc1d920ad0c/2020-09-17_15-23-50.png

完整程式碼

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public class LoginController : Controller
{
    // 這邊要改你申請的應用程式編號及密鑰
	private readonly string _appId = "{應用程式編號}";
    private readonly string _appSecret = "{應用程式密鑰}";

    private string RedirectUrl => "https://" + HttpContext.Request.Host.ToString() + "/Login/SignInLine";
    private readonly IHttpClientFactory _clientFactory;

    public LoginController(IHttpClientFactory clientFactory)
    {
        _clientFactory = clientFactory;
    }

    [TempData]
    public Guid State { get; set; }

    public IActionResult Index()
    {
        State = Guid.NewGuid();
        ViewData["LineAuth"] = $"https://access.line.me/oauth2/v2.1/authorize?" +
            $"client_id={_appId}" +
            $"&response_type=code" +
            $"&redirect_uri={RedirectUrl}" +
            $"&scope={HttpUtility.UrlEncode("profile openid email")}" +
            $"&state={State}";
        return View();
    }

    public async Task<IActionResult> SignInLine(string code, Guid state, string error, string error_description)
    {
        // 有錯誤訊息(未授權等)、State遺失、State不相同、沒有code
        if (!string.IsNullOrEmpty(error) || state == null || State != state || string.IsNullOrEmpty(code))
            return RedirectToAction(nameof(Index));

        // 使用代碼交換存取權杖 與Facebook 和 Google不同,是使用 application/x-www-form-urlencoded
        var url = "https://api.line.me/oauth2/v2.1/token";
        var postData = new Dictionary<string, string>()
        {
            {"client_id",_appId},
            {"client_secret",_appSecret},
            {"code",code},
            {"grant_type","authorization_code"},
            {"redirect_uri",RedirectUrl}
        };

        var contentPost = new FormUrlEncodedContent(postData);

        var client = _clientFactory.CreateClient();
        var response = await client.PostAsync(url, contentPost);

        string responseContent;
        if (response.IsSuccessStatusCode)
            responseContent = await response.Content.ReadAsStringAsync();
        else
            return RedirectToAction(nameof(Index));

        var lineLoginResource = JsonConvert.DeserializeObject<LINELoginResource>(responseContent);

        // 因為Line API 可以同時取得ClientId Token 和 Access Token,所以這邊有兩種選擇
        // 1. 使用JWT解析Id Token, Nuget > System.IdentityModel.Tokens.Jwt
        // var userInfo = new JwtSecurityToken(lineLoginResource.IDToken).Payload;

        // 2. https://developers.line.biz/en/reference/social-api/#profile
        url = $"https://api.line.me/v2/profile";
        client.DefaultRequestHeaders.Add("authorization", $"Bearer {lineLoginResource.AccessToken}");
        response = await client.GetAsync(url);
        if (response.IsSuccessStatusCode)
        {
            responseContent = await response.Content.ReadAsStringAsync();
            var user = JsonConvert.DeserializeObject<LINEUser>(responseContent);
        }

        return View();
    }

    public class LINELoginResource
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("token_type")]
        public string TokenType { get; set; }

        [JsonProperty("expires_in")]
        public string ExpiresIn { get; set; }

        [JsonProperty("scope")]
        public string Scope { get; set; }

        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }

        // 這邊跟一般的TokenResponse不同,多了使用者的Id Token
        [JsonProperty("id_token")]
        public string IDToken { get; set; }
    }

    public class LINEUser
    {
        [JsonProperty("userId")]
        public string Id { get; set; }

        [JsonProperty("displayName")]
        public string Name { get; set; }

        [JsonProperty("pictureUrl")]
        public string PictureUrl { get; set; }

        [JsonProperty("statusMessage")]
        public string StatusMessage { get; set; }
    }
}

參考

Line Developer

https://developers.line.biz/zh-hant/

整合 LINE Login 與 web app

https://developers.line.biz/zh-hant/docs/line-login/integrate-line-login/#設定-channel

LINE Profile API

https://developers.line.biz/zh-hant/reference/social-api/#get-user-profile

結論

Line是繼Google和Facebook登入後才整理的,一開始覺得Facebook和Google,有提供SDK很方便,貼上就可以動了,但其實到這邊,我覺得手動建立OAuth感覺更簡單,流程都相同(複製貼上就三篇…),就只有些微的地方是不同,而且自己可控的範圍提高了,完全可以把這些東西抽象化,只要第三方驗證是採用OAuth方式,作法都是一樣的,也不用太在乎官網如何改版,讓人找不到文件說明…考慮到Line沒有提供SDK,以及需要Email的情境下,若同時要有Facebook、Google、Line登入,這作法成為我最後的選擇。