UWP - 整合 LINE Login

在 < UWP - 整合 Google Sign-In 與 Twitter Sign-In API> 介紹整合 Google 與 Twitter,這篇補充把 LINE login 整合進來。

整合 LINE Login 前需要先申請 LINE developer 帳號,并且建立 Channel 來取得需要的 client_id 與 client_secret。 詳細的申請可以參考 https://business.line.me/zh-hant/ 的説明,主要步驟:

  • 用原本綁定在 LINE 裏面的 email 登入,它會要用戶建立一個開發者帳號
  • 建立一個公司名稱,填寫必要資訊 (公司名稱可以自定義)
  • 建立一個 Channel,填寫必要資訊跟圖像,要記得選擇 WEB 類型的應用,因爲 LINE App 在 Windows 上還沒有支援互相認證的功能

完成之後可看到如下圖的結果:

這樣就可以得到 client_id 與 client_secret,接下來透過下面的程式範例就可以簡單拿到 access token 做事情了。 [範例説明] 1. 沿用之前的專案 13-AppWithOAuth 加入 LINE Login。 2. 建立一個 class 來轉換 json 結果與存放 LINE access token:


public class LINEAccessToken
{
    public string mid { get; set; }

    public string access_token { get; set; }

    public string token_type { get; set; }

    public int expires_in { get; set; }

    public string refresh_token { get; set; }

    public object scope { get; set; }
}

3. 建立一個搭配 WebAuthenticationBroker 向 LINE 做 OAuth 的類別:


public class LINELoginAPI
{
    // 先向 LINE Developer 申請帳號並建立 Channel 就可以拿到 client_id 與 client_secret
    private const string ClientId = "";

    private const string ClientSecret = "";

    // callback 可以設定自己需要的内容,預設可以使用下列的值
    private const string CallbackUri = "https://localhost";

    public static async Task RequestLoginAsync()
    {
        // state 可以自定義,用於識別是來自該 app 所請求
        string state = "Windows_LINELoginAPI";
        string encodingCallback = Uri.EscapeDataString(CallbackUri);
        string oauthUrl = $"https://access.line.me/dialog/oauth/weblogin?response_type=code&client_id={ClientId}&redirect_uri={encodingCallback}&state={state}";
        string result = string.Empty;

        try
        {
            WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(oauthUrl), new Uri(CallbackUri));

            if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
            {
                var response = WebAuthenticationResult.ResponseData.ToString();

                // if your callback value is not URL, don't use it. please use split string methods.
                Uri redirectUri = new Uri(response);
                string query = redirectUri.Query.Substring(1);
                Dictionary keyValuePair = Utility.StringToDictionary(query);
 
                // 根據結果拆解需要的内容
                if (keyValuePair.ContainsKey("error"))
                {
                    // fail: http://sample.com/{Callback URL}?error=access_denied&state=[state]&errorCode=417&errorMessage=DISALLOWED
                    result = keyValuePair["errorMessage"];
                }
                else
                {
                    // success: http://sample.com/callback?code=b5fd32eacc791df&state=123abc
                    string code = keyValuePair["code"];
                    // 請求取得 access token
                    string accessToken = await RedirectGetAccessTokenAsync(code);
                    result = accessToken;
                }
                return result;
            }
            else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
            {
                return "HTTP Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseErrorDetail.ToString();
            }
            else
            {
                return "Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseStatus.ToString();
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

    private static async Task RedirectGetAccessTokenAsync(string code)
    {
        string encodingCallback = Uri.EscapeDataString(CallbackUri);
        string url = "https://api.line.me/v1/oauth/accessToken";
        string postParameter = $"grant_type=authorization_code&client_id={ClientId}&client_secret={ClientSecret}&code={code}&redirect_uri={encodingCallback}";

        HttpStringContent httpContent = new HttpStringContent(postParameter, Windows.Storage.Streams.UnicodeEncoding.Utf8);
        httpContent.Headers.ContentType = HttpMediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");

        using (HttpClient httpClient = new HttpClient())
        {
            var httpResponseMessage = await httpClient.PostAsync(new Uri(url), httpContent);
            return await httpResponseMessage.Content.ReadAsStringAsync();
        }
    }
}

上面有定義 callback 是 http://localhost 這要 Channel 裏面設定 (詳細説明),參考下圖:

上面的步驟就可以讓 UWP app 與 LINE Login 的整合了。 4. 取得用戶的個人資訊:


public static async Task GetProfile(string accessToken)
{
    using (HttpClient client = new HttpClient())
    {
        // https://api.line.me/v2/profile 設定 Authentication header
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        string jsonResult = await client.GetStringAsync(new Uri("https://api.line.me/v2/profile"));

        DataContractJsonSerializer tJsonSerial = new DataContractJsonSerializer(typeof(ProfileData));
        MemoryStream tMS = new MemoryStream(Encoding.UTF8.GetBytes(jsonResult));
        ProfileData data = tJsonSerial.ReadObject(tMS) as ProfileData;

        return data;
    }
}

public class ProfileData
{
    public string userId { get; set; }
    public string displayName { get; set; }
    public string pictureUrl { get; set; }
}

======

簡單分享了整合 LINE login 到自己的 App 或是網站裏。 希望有幫助。

References: