LINE Loginを利用したWebアプリを ASP.NET Core + OpenID Connectで実装する


■ 目次


チュートリアル

今回は、LINE Loginを利用したWebアプリケーションをASP.NET Core +OpenID Connectで実装する方法について解説したいと思います。

はじめに

ここでは、Visual Studio 2017を使って「「ASP.NET Core Webアプリケーション」テンプレートからLINE Loginに対応したWebアプリを実装する手順」について解説します。

なお、ASP.NET CoreでOpenID Connectを使用する手順は、こちらの記事を参考にさせていただきました。

blog.shibayan.jp

また、この記事の解説に使用したサンプルアプリをGitHubに上げています。 とりあえず動かしてみたいという方はこちらをCloneして試してみてください。

github.com

※ このサンプルアプリを試す際は、以下の手順でLINE LoginのChannel情報等を設定してください。

  • appSettings.Base.json ファイルをappSettings.jsonにリネームする
  • リネームしたファイル内の"LineSettings"以下を、ご自身のLINE Loginアカウント情報に書き換える
    • LINE Login アカウントのChannel ID
    • LINE Login アカウントのChannel Secret
    • 連携するBotアカウント のAccess Token

LINE の設定

LINE Loginアカウントを作成する

まずは、以下の公式ドキュメントに従ってLINE Loginアカウントを作成します。

LINEログインを利用するには

連携するBotアカウントの用意

ログイン時に友達追加するBotのアカウントも作成しておきます。プッシュメッセージを使用するのでDeveloper Trialプランで作成します。 (もちろん、すでに作成済みのBotアカウントで試しても良いです)

Messaging APIを利用するには

なお、今回はログイン時に友達追加できることと、Webアプリからプッシュメッセージが送信できることが確認できれば良いのでWebhookの設定は不要です。

LINE Login アカウントの設定

LINE Loginアカウントを作成したら「Channel基本設定」を確認します。

  • Channel ID とChannel Secretは後で必要となるので控えておきます
  • 「このチャネルにリンクされたボット」欄で、作成済みのBotの一覧から先ほど用意したBotを選択します
  • 「アプリタイプ」にはWEBを選択します

f:id:pierre3:20180331072144p:plain:w400

左側のメニューで「アプリ設定」を選択し、ログイン後のリダイレクト先を設定します。

f:id:pierre3:20180331065551p:plain:w600

  • リダイレクト先のパスは「/signin-oidc」とします。
  • ローカルデバッグでの確認であれば、Visual Studio でのデバッグ実行時のURLでOKです。(デバッグ時のURLはPropertiesフォルダ内にある「launchSettings.json」で確認できます)

Webアプリケーションの作成

ASP.NET Coreのプロジェクトを作成する

メニュー[ファイル]-[新規作成]-[プロジェクト...]から「ASP.NET Core Webアプリケーション」を選択

f:id:pierre3:20180329215712p:plain:w600

テンプレートのタイプは「Webアプリケーション(モデル ビュー コントローラ)」を選択。「認証なし」のままで作成します。

f:id:pierre3:20180329220425p:plain:w600

NuGetパッケージを追加する

「ソリューションのNuGetパッケージの管理」画面または、パッケージマネージャコンソールで Microsoft.AspNetCore.Authentication.OpenIdConnect をインストールします。

PM> Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect

AppSettings.json に LINE Login のChannel情報を設定する

予め、appsettings.json に LINE Login のアカウント登録時に取得した ChannelIdとChannel Secretの値を追加しておきます。

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "LineSettings": {
    "LoginClientId": "156xxxxxxxxxx",
    "LoginClientSecret": "934a4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  }
}

StartupクラスでOpenID Connectを使用するように設定する

あとは、Stertup.cs で以下の記述を追加するだけでOKです。 (詳細は冒頭でリンクしたしばやん先生の記事をご確認ください)

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        //OpenID Connectを使用するための記述 ここから==>
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(o =>
        {
            //ClientIdとClientSecretをConfiguration(appsettings.json)から取得。階層のあるデータはコロン(:)で区切って指定する
            o.ClientId = Configuration["LineSettings:LoginClientId"];
            o.ClientSecret = Configuration["LineSettings:LoginClientSecret"];
            o.ResponseType = OpenIdConnectResponseType.Code;
            o.UseTokenLifetime = true;
            o.SaveTokens = true;
            
            o.Configuration = new OpenIdConnectConfiguration
            {
                Issuer = "https://access.line.me",
                AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize",
                TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"
            };

            o.TokenValidationParameters = new TokenValidationParameters
            {
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(o.ClientSecret)),
                NameClaimType = "name",
                ValidAudience = o.ClientId
            };
        });
        //<==ここまで
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();
        
        //Authentication Middleware を使う
        app.UseAuthentication();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

これで、Controller の[Authorize]属性を付けたメソッドにアクセスすると、LINEアカウントのログイン画面にリダイレクトされるようになります。

・Controllers/HomeController.c

//<HomeController.cs>

public class HomeController:Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }
}

f:id:pierre3:20180329234308p:plain:w300

ログイン時にBotアカウントを友達追加できるようにする

ユーザーがLINEアカウントの認可画面で関連するBotを友達追加するか否かを選択できるようにするには、認可URLに、クエリパラメータbot_promptを追加します。

先ほどのコードのAddOpenIdConnect() に渡すラムダ式内、AuthorizationEndpoint に設定するURLを以下の様に修正します。

o.Configuration = new OpenIdConnectConfiguration
{
    Issuer = "https://access.line.me",
    AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize?bot_prompt=normal",
    TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"
};

bot_prompt の値は、normal、aggressiveの2種類があり、aggressiveを設定した場合は、認可画面を閉じた後にボット追加用の確認画面が表示されるようになります。

f:id:pierre3:20180330223612p:plain:w300

  • aggressive の場合(認可画面からログイン後、確認用のダイアログが表示される)

f:id:pierre3:20180330224231p:plain:w300

なお、「ブロック解除」と表示されているのは、動作確認用に一度友達追加したBOTを「ブロック」しているためです。初めて友達追加する場合は「友達追加」と表示されます。

取得したユーザー情報を確認する

デバッカで確認してみると、取得したユーザーの情報はUser.Identity.Claimesに格納されているのが分かります。

f:id:pierre3:20180331095929p:plain

ClaimsプロパティはKey-Valueのコレクションとなっていて、値はUser.FindFirst()メソッドにKeyを指定して取得することができます。
定義済みのClaimは"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"の様にURLで指定する必要がありますが、 System.Security.Claims.ClaimTypes に定数として定義されているのでこれを使用します。

//View(Razor)でもControllerでも同じ様に取得できます
var name =User.FindFirst("picture").Value;  //ユーザー名はUser.Identity.Nameでも取得可
var picture = User.FindFirst("picture").Value;  //画像のURLを取得
var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value;  //LINEアカウントに紐づくユーザーID

それでは、ログインしたユーザーの情報が確認できるように、トップページ(Home/Index)のViewを書き換えてみます。

<-- Index.cshtml -->

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <div class="panel panel-default center-block">
        <div class="panel panel-heading">
            <div class="row">
                <div class="col-md-10 col-sm-10 col-xs-8">
                    <p><img src="@User.FindFirst("picture").Value" width="32" height="32" alt="User Image"> @User.Identity.Name</p>
                </div>
                <div class="col-md-2 col-sm-2 col-xs-4">
                    <a class="btn btn-default" role="button" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a>
                </div>
            </div>
        </div>
    </div>
</div>

ついでに「Logout」ボタンでログアウトできるように、AccountControllerを追加してLogout()メソッドを実装します。

//<AccountController.cs>

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;

namespace LineLoginSampleApp.Controllers
{
    public class AccountController : Controller
    {
        public IActionResult Logout()
        {
            return SignOut(new AuthenticationProperties { RedirectUri = "/" }, CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

ここで、一度デバッグ実行してみましょう。 LINEのログイン画面からログイン後、以下の様にユーザー名と画像が表示されればOKです。
「Logout」ボタンも確認してみましょう。ボタン押下後、再びLINEのログイン画面にリダイレクトされることが確認できると思います。

f:id:pierre3:20180331093703p:plain:w400

Botからプッシュメッセージを送ってみる

次は、LINEログイン時にBotを友達追加したユーザーに対してBotからプッシュメッセージを送ってみましょう。

Messaging APIのライブラリを追加

LINE Messaging APISDKをNuGetからインストールします。

PM > Install-Package Line.Messaging
appSettings.jsonBotのChannel アクセストークンを追加する

LINE Developersコンソールを開き、今回用意したBotを選択します。
Channel基本設定で「アクセストークン(ロングターム)」を確認し、appSettings.jsonに追加します。

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "LineSettings": {
    "LoginClientId": "156xxxxxxxxxx",
    "LoginClientSecret": "934a4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "MessagingAccessToken": "zd1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}
メッセージを送ってみる

画面から送信するメッセージの内容を設定して送れるようにViewとControllerを書き換えます。

・Home/Index.cshtml に、メッセージの入力ボックスと送信ボタンを追加します。

<-- Index.cshtml -->

@{
    ViewData["Title"] = "Home Page";
}
@using System.Security.Claims;

<div class="jumbotron">
    <div class="panel panel-default center-block">
        <div class="panel panel-heading">
            <div class="row">
                <div class="col-md-10 col-sm-10 col-xs-8">
                    <p>
                        <img src="@User.FindFirst("picture")?.Value" width="32" height="32" alt="User Image" />
                        @User.Identity.Name
                    </p>
                </div>
                <div class="col-md-2 col-sm-2 col-xs-4">
                    <a class="btn btn-default" role="button" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a>
                </div>
            </div>
        </div>
        <div class="panel panel-body">
            <h2>プッシュメッセージ送信テスト</h2>
            <form role="form" asp-area="" asp-controller="Home" asp-action="PushMessage" method="post">
                <input type="text" name="pushMessage" class="form-control" placeholder="プッシュするメッセージを入力してください">
                <button type="submit" class="btn btn-default">送信</button>
            </form>
        </div>
    </div>
</div>

・Views/Home/ の配下にメッセージ送信後の遷移先となるView(PushMessage.cshtml)を作成しておきます。

<-- PushMessage.cshtml -->

@{
    ViewData["Title"] = "PushMessage";
}

<h2>送信しました</h2>
<a class="btn btn-default" role="button" asp-area="" asp-controller="Home" asp-action="Index">戻る</a>

・HomeControllerにPushMessageメソッドを追加してそこに送信処理を記述します。

//<HomeController.cs>

public class HomeController : Controller
{
    private LineSettings lineSettings;
    public HomeController(IOptions<LineSettings> options)
    {
        lineSettings = options.Value;
    }

    [Authorize]
    public IActionResult Index()
    {
        return View();
    }
    
    [HttpPost]
    [Authorize]
    public async Task<IActionResult> PushMessage(string pushMessage)
    {
        var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value;
         using (var line = new Line.Messaging.LineMessagingClient(lineSettings.MessagingAccessToken))
        {
            await line.PushMessageAsync(userId, pushMessage);
        }
        return View();
    }
}

なお、appSettings.jsonに設定したアクセストークンを、HomeControllerのコンストラクタの引数LineSettingsオブジェクトから取得していますが、これを行うためにはStartupクラスでアプリケーション設定をDI(Dependency Injection)するよう設定しておく必要があります。

・ModelsディレクトリにLineSettingsクラスを追加します。

//<LineSettings.cs>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace LineLoginSampleApp.Models
{
    public class LineSettings
    {
        public string LoginClientId { get; set; }
        public string LoginClientSecret { get; set; }
        public string MessagingAccessToken { get; set; }
    }
}

・StartupクラスのConfigurationServices()メソッドに、アプリケーション設定をDIするコードを追加します。

//<Stertup.cs>

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    
    //ConfigurationからLineSettingsセクションを抽出してDependency Injectionする
    services.Configure<LineSettings>(Configuration.GetSection("LineSettings"));
    
   //...
}

これで準備完了です。実行してみましょう。

・メッセージを入力して「送信」

f:id:pierre3:20180331161942p:plain:w500

・LINEアプリを確認し、BOTから入力したメッセージが送られてきていれば成功です!

f:id:pierre3:20180331162032p:plain:w500

次は

次は、ユーザーのメールアドレスを取得する方法を見てみようと思いますが、長くなりすぎてしまったので次回にします。

pierre3.hatenablog.com