ASP.NET CoreのアプリとAzure AD認証

AzureADはAzureのためのID認証サービスです。AzureへのログインもAzureADを使っています。
今回はAzure Web Appsで実行しているASP.NET CoreアプリをAzureAD認証する方法を紹介します。

# ActiveDirectoryとの違い
まず従来のActiveDirectoryとAzureADはどう違うの？というところが気になると思います。

ActiveDirectoryもAzureADも認証するためのシステムなのですが、ActiveDirectoryは組織アカウントを管理するためのもの、対してAzureADはあくまでAzure上でIDを管理するためのものです。
例えばAzureADを使ってグループポリシーを管理するといったことはできません。代わりにAzureADではデバイス単位の認証管理ができるなどよりクラウドに最適化されています。

# Azure Web AppsとAzureADの連携
今回は自分が作ったSlackと連携できるbotアプリ「OverReaction」で説明します。
[Slackで特定のキーワードに反応して自動的にReactionをつけるBotを作った](http://qiita.com/kuluna/items/bda141eafc968d4810f7)

連携は非常に簡単です。Web Appsの設定の中にAzureADがあるので選ぶだけです。
AzureADでの認証には実は2タイプ選ぶことができます。

## アプリ全体の認証
こちらはアプリ全体に認証をかけるもので、メリットは何といっても__アプリそのものに変更はいらない__ということです！

<img width="1002" src="https://qiita-image-store.s3.amazonaws.com/0/44992/17512aea-92f0-5234-15e2-4ffb6e664b83.png">

1. WebAppsのメニューから「認証/承認」を選び、App Service認証をオンにします。
1. 要求が認証されない場合に実行するアクションにAzureADを選びます。
1. 上の保存を押します。

この設定にするとWebAppにアクセスした時にAzureADによる認証画面に自動的にリダイレクトされます。
<img width="779" src="https://qiita-image-store.s3.amazonaws.com/0/44992/bfd0abae-6151-cc85-a057-06a3431fd3ac.png">
自分のAzureADにログインできるアカウントでログインすると、自分のアプリにリダイレクトがされるという仕組みです。
アプリの中にログイン機能を実装する必要がなく、単純にアプリを特定の人だけに使わせたいという場合はこれだけでOKです。

## アプリの一部分の認証
アプリ全体に保護をかけたい場合は上でOkですが、ある部分は誰でも見れるようにしたい場合はアプリとAzureADを連携させる必要があります。
OverReactionではSlackからのwebhookを受け取りますが、全体に保護がかかっている状態ではSlackもアクセスできない状態です。

### Azure側の設定
まずは先ほどの設定画面を開きます。
要求が認証されない場合に実行するアクションを今回は「匿名要求を許可する」にします。
<img width="1007" src="https://qiita-image-store.s3.amazonaws.com/0/44992/79777bc9-5b5c-0fe3-1be6-8898739dd2a9.png">
この設定にするとAzureADの認証機能は使えるが、WebApps側でリダイレクトはしないので結果誰でも閲覧できる状態になります。アプリがAzureADと連携するためにはAzureADでアプリIDを発行し、それをアプリ側に持たせることでできます。

認証プロバイダーでAzure ADを選択します。
新しいAzureポータルはここからAzure ADアプリIDを発行できるようになってるので楽ですね。
<img width="1007" src="https://qiita-image-store.s3.amazonaws.com/0/44992/6a112035-f5b0-cf00-ed4e-e0a81b85561b.png">

> ここまでの設定では自分ひとりしかログインできないので、AzureADにメンバーを追加することでそのメンバーもログインできるようになります。

### アプリ側の設定
VisualStudioで一から新しくプロジェクトを作成するときにAzureADとの連携をすると自動的にアプリIDを設定してくれます。
<img width="584" src="https://qiita-image-store.s3.amazonaws.com/0/44992/df6168de-1c81-2bcc-0ec7-5f0df53342db.png">

基本的にはこれで作成されたアプリをAzureにアップロードするだけで完了です。トップページにはふつうにアクセスできますが、ログインを押すと先ほどのAzureAD認証ページに遷移するはずです。

---
ここからは既存のASP.NET Core MVCでAzure ADと連携するようコードを書いていってみます。

まずは`project.json`にAzureADと認証するためのライブラリを追加します。

```project.json
"dependencies": {
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
    "Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0"
}
```

次に`appsettings.json`にAzureADのアプリIDを追加します。

```appsettings.json
{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "Authentication": {
    "AzureAd": {
      "AADInstance": "https://login.microsoftonline.com/",
      "CallbackPath": "/signin-oidc",
      "ClientId": "xxx",
      "Domain": "yyy.onmicrosoft.com",
      "TenantId": "zzz"
    }
  }
}
```

ClientId、Domain、TenantIdはAzureポータルから確認できますが、一度VisualStudioでAzureADと連携した新規プロジェクトを作成し、そこからそれぞれの情報を抜き取ったほうが確実だと思います。

ここから追加した情報をプログラムで読み込みます。

```startup.cs
public void ConfigureServices(IServiceCollection services)
{
    .
    .
    .
    services.AddAuthentication(SharedOptions =>
    {
        SharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    .
    .
    .
    app.UseCookieAuthentication();
    app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
    {
        ClientId = Configuration["Authentication:AzureAd:ClientId"],
        Authority = Configuration["Authentication:AzureAd:AADInstance"] + Configuration["Authentication:AzureAd:TenantId"],
        CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"]
    });

    // コイツは常に一番最後！
    app.UseMvc();
}
```

最後に認証をかけたい部分のコントローラに[Authorize]アノテーションを追加します。

```HomeController.cs
[Authorize]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return File("/index.html", "text/html");
    }
}
```

これでアクセスしたときにログインに飛ばされるようになります。
逆に認証なしでもアクセスできるようにするには[AllowAnonymous]をつけます。

```WebhookController.cs
[Route("api/[controller]"), AllowAnonymous]
public class WebhookController : Controller
{
.
.
.
```

SlackからのWebhookを受けるコントローラにこれをつけます。

これでAzureにアプリをデプロイするとindex.htmlにアクセスするときはAzureAD認証が、Webhookには誰からでもアクセスできるようなアプリになりました。

# さいごに
ログインの仕組みをアプリに実装せずAzureADに任せることで楽に認証システムを導入することができます。
一からAzureADと連携するのは少し難しく感じたかもしれませんが、VisualStudioの新規プロジェクトで初めからAzureADとの認証を有効にすると上の設定がすべて書かれた状態ですので、そちらを使うことをおすすめします。