しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

OpenID Connect に対応した LINE Login を ASP.NET Core 2.0 からお手軽に使ってみる

LINE のイベントで LINE Login が OpenID Connect に対応したという発表があったようです。

昔に比べると、簡単に LINE Login の設定が出来るようになっているので、今後は色々な部分での採用が広がりそうな気もします。OpenID Connect 対応は特に重要なアップデートだと感じますね。

Integrating LINE Login with your web app

既に IdM の凄い人は試しているようでしたし、最近は ASP.NET Core 2.0 で OpenID Connect について調べてたので、ついでに試してみることにしました。

LINE Login の設定をする

LINE Developers から LINE Login を追加すると Channel Id と Channel Secret が貰えます。

f:id:shiba-yan:20171002132626p:plain

テスト用に適当な名前で追加しました。使い道は当然ながら Web にしておきます。

ASP.NET Core 2.0 のドキュメントにある OpenID Connect の使い方は、大体が Azure AD ベースで考えられていて、微妙に理解しにくいです。1.x から 2.0 へのマイグレーションのドキュメントがまだましです。

要するに AddOpenIdConnect を呼び出して設定すれば、後は Middleware が良い感じに認証回り含め処理してくれるという、とても素敵な機能です。

LINE 側には Callback URL を登録する必要がありますが、コールバック自体は Middleware が行ってくれるので /signin-oidc という固定のパスを設定しておきます。

f:id:shiba-yan:20171002124725p:plain

これで LINE 側の設定は完了なので、ASP.NET Core アプリケーション側に移っていきます。

ASP.NET Core 2.0 に組み込む

Azure AD の場合は OpenID Connect の Discovery に対応しているので、最小限の設定だけで使えるようになりますが、LINE Login は非対応なので Discovery 相当を自分で設定してあげる必要があります。

具体的な設定例を Startup.cs のコードとして挙げます。サンプルなので直接設定値を指定してますが、ちゃんと Configuration から取るようにした方が良いですね。

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

    // OpenID Connect は Cookie とセットで使う必要がある
    services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(options =>
            {
                options.ClientId = "CHANNEL_ID";
                options.ClientSecret = "CHANNEL_SECRET";
                options.ResponseType = OpenIdConnectResponseType.Code;
                options.UseTokenLifetime = true;
                options.SaveTokens = true;

                // Discovery 相当の設定を追加してあげる
                options.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"
                };

                // LINE Login の署名は HS256 なので IssuerSigningKey を SymmetricSecurityKey で設定する
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(options.ClientSecret)),
                    NameClaimType = "name",
                    ValidAudience = options.ClientId
                };
            });
}

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

    app.UseStaticFiles();

    // Authentication Middleware を使う場合には必須
    app.UseAuthentication();

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

OpenIdConnectConfiguration を使って authorize と token のエンドポイントを指定します。

そして重要なのが TokenValidationParameters で、これの設定を正しく行わないと id_token の検証で落ちたり、ClaimsIdentity に正しく Name の値が渡ってこなくなったりします。

OpenID Connect の設定もこれでおしまいです。あとは以下のようにログインとログアウトのアクションを書けば、LINE Login を使うことが出来ます。

public class AccountController : Controller
{
    public IActionResult Login()
    {
        return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
    }

    public IActionResult Logout()
    {
        return SignOut(new AuthenticationProperties { RedirectUri = "/" }, CookieAuthenticationDefaults.AuthenticationScheme);
    }
}

ログイン時には OpenID Connect の認証を行いつつ、ログアウト時には Cookie 認証の破棄を行っています。これは LINE Login が OpenID の End Session Endpoint に対応していないので、こういう対応にしています。

後はビューを少し弄ってログインしていることが分かりやすい表示にしてあげました。

@if (User.Identity.IsAuthenticated)
{
    <li class="dropdown">
        <img src="@User.FindFirst("picture").Value" style="height: 50px;"/>
        <a href="#" data-toggle="dropdown" style="display: inline;">@User.Identity.Name</a>
        <ul class="dropdown-menu">
            <li><a asp-action="Logout" asp-controller="Account">Logout</a></li>
        </ul>
    </li>
}
else
{
    <li><a asp-action="Login" asp-controller="Account">Login</a></li>
}

ログイン前はログインリンクを表示し、ログイン中はプロフィールアイコンと名前、そしてログアウトのリンクを表示します。プロフィール画像は picture という名前で Claims に入ってます。

これで ASP.NET Core 2.0 への組み込みも完了したので、実際にログインを試してみます。

LINE Login の動作を確認する

アプリケーションを実行してログインリンクをクリックすると、LINE Login の画面に飛びます。

既にログイン済みなのでユーザー名が表示されていますが、未ログインの場合はメールアドレスとパスワードの入力となります。

f:id:shiba-yan:20171002135643p:plain

先に進むと権限周りのよくある確認画面が表示されます。

デフォルトではプロフィール情報のみが許可されるようになってますが、もうちょっと権限有りそうです。

f:id:shiba-yan:20171002135653p:plain

同意すれば、ASP.NET Core アプリケーション側に戻ってきて、ログイン処理が行われます。

ちゃんとユーザー名とプロフィール画像が表示されていることが確認できます。

f:id:shiba-yan:20171002135757p:plain

一応クレームも確認しておきます。ユーザー ID は NameIdentifier に入ってきていることが確認できますね。

f:id:shiba-yan:20171002141622p:plain

LINE Login の組み込みはたったこれだけです。ASP.NET Core の OpenID Connect を使うことで、設定だけ追加すれば最低限の環境は整うようになりました。素晴らしいですね。

アクセストークンを取得する

OpenID Connect に関する処理は全て Middleware がやってくれるので、アクセストークンなどは意識する必要はありませんが、API を叩きたい時に必要となるので取得することが出来るようになってます。

public async Task<IActionResult> Index()
{
    var accessToken = await HttpContext.GetTokenAsync("access_token");

    return View();
}

これで LINE Login を行うと、アクセストークンが無事に取得できることが分かります。

f:id:shiba-yan:20171002140525p:plain

後はこのトークンを使って API の実行などを行えばよいですね。ASP.NET Core MVC でかなり簡単に OpenID Connect が使えるようになって嬉しいです。