Auth0ラボ - その1 : Web Sign-In

# はじめに
この記事はAuth0のハンズオンラボで[Auth0 Identity Labs](https://auth0.com/docs/identity-labs)を元に作成しています。Node.js + Express.jsで作成されたSample ApplicationでExpressミドルウェアを利用して認証をリダイレクトし、リダイレクト先の認可サーバとしてAuth0を使います。Auth0の無料アカウントの取得とテナントの作成が完了していることが前提となっています。Auth0の無料アカウント取得がまだの方は[こちら](https://qiita.com/hisashiyamaguchi/items/2d0bcae052077f6ae4a4#auth0%E7%84%A1%E6%96%99%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E5%8F%96%E5%BE%97%E3%83%86%E3%83%8A%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)の記事を参照の上ご準備をお願いします。

# 検証環境
- **OS** : `macOS Catalina 10.15.2`
- **node** : `10.15.3`
- **npm** : `6.13.2`
- **Git** : `2.23.0`

# ラボ
## Part1
Part1ではSample ApplicationとAuth0を連携してAuth0に認証プロセスをオフロードします。[Git Repo](https://github.com/auth0/identity-102-exercises/)をローカルにクローンします。

```console
$ git clone https://github.com/auth0/identity-102-exercises.git
$ cd identity-102-exercises/lab-01/begin
```

Node.jsの環境変数定義ファイルを作成します。

```console
$ pwd
~/identity-102-exercises/lab-01/begin
$ cp cp .env-sample .env
```

cookie-sessionとexpress-openid-connectパッケージをインストールします。

- **cookie-session** : ユーザのログインセッションを保管する
- **express-openid-connect** : Opne ID Connect, JSON Web Tokenを実装するExpressミドルウェア

```console
$ pwd
~/identity-102-exercises/lab-01/begin
$ npm install cookie-session express-openid-connect
```

server.jsを編集してcookie-sessionとexpress-openid-connectを読み込みます。以下、修正後のコードです。

```jsx:server.js
require('dotenv').config();
const express = require('express');
const http = require('http');
const morgan = require('morgan');

// 下2行を追加
const session = require('cookie-session');
const { auth } = require('express-openid-connect');

const appUrl = process.env.BASE_URL || `http://localhost:${process.env.PORT}`;

const app = express();

app.set('view engine', 'ejs');

app.use(morgan('combined'));

app.use(express.urlencoded({ extended: false }));

app.get('/', (req, res) => {
  res.render('home',  { user: req.openid && req.openid.user });
});

app.get('/expenses', (req, res) => {
  res.render('expenses', {
    expenses: [
      {
        date: new Date(),
        description: 'Coffee for a Coding Dojo session.',
        value: 42,
      }
    ]
  });
});

http.createServer(app).listen(process.env.PORT, () => {
  console.log(`listening on ${appUrl}`);
});
```

server.jsを修正してExpressミドルウェアで認証を取り扱うようにします。以下、修正後のコードです。
**<font color="DarkOrange">Expressミドルウェアは/login(OIDCのリクエストを認可サーバに投げる), /callback(認可サーバからのレスポンスを受け取る), /logout(Applicationのセッションを終了する)の3つのルートを自動的に定義します。</font>**

```jsx:server.js
require('dotenv').config();
const express = require('express');
const http = require('http');
const morgan = require('morgan');
const session = require('cookie-session');
const { auth } = require('express-openid-connect');

const appUrl = process.env.BASE_URL || `http://localhost:${process.env.PORT}`;

const app = express();

app.set('view engine', 'ejs');

app.use(morgan('combined'));

app.use(express.urlencoded({ extended: false }));

// 下7行を追加
app.use(session({
  name: 'identity102-l01-e01',
  secret: process.env.COOKIE_SECRET
 }));

 app.use(auth({
  auth0Logout: true
 }));

app.get('/', (req, res) => {
  res.render('home',  { user: req.openid && req.openid.user });
});

app.get('/expenses', (req, res) => {
  res.render('expenses', {
    expenses: [
      {
        date: new Date(),
        description: 'Coffee for a Coding Dojo session.',
        value: 42,
      }
    ]
  });
});

http.createServer(app).listen(process.env.PORT, () => {
  console.log(`listening on ${appUrl}`);
});
```

Expressミドルウェアは認可サーバ(Auth0)のURLとApplicationのIDが必要になるため、Auth0のDashboardからApplicationを登録します。左ペインの"Applications"をクリックして右上の"CREATE APPLICATION"を押します。

<img src="https://i.imgur.com/ZtGP5PL.png" width=70%>

"Name"に任意の名前を入力、"Choose an application type"で"Regular Web Applications"を選択して”CREATE”を押します。

<img src="https://i.imgur.com/VlzjtAX.png" width=70%>

"Allowed Callback URLs"に"`http://localhost:3000/callback`", "Allowed Logout URLs"に"`http://localhost:3000`"を入力して画面下の"SAVE CHANGES"を押します。

<img src="https://i.imgur.com/EsPKME1.png" width=70%>

.envを編集してExpressミドルウェアに渡す環境変数を設定します。以下、修正後のファイルです。

"COOKIE_SECRET"は任意のランダムで生成した文字列を指定します。ターミナルからopenssl rand -base64 32を実行するとランダム文字列を出力できます。
**<font color="DarkOrange">"ISSUER_BASE_URL"にはAuth0のテナントドメイン名、CLIENT_IDにはAuth0に登録したApplicationのClient_IDを設定します。Auth0 Dashboardの"Applications"->"作成したApplication"->"Settings"から確認できます。</font>**

```jsx
ISSUER_BASE_URL=https://xxxx.auth0.com
CLIENT_ID=xxxx
COOKIE_SECRET=xxxx
PORT=3000
```

必要なパッケージをインストールしてApplicationを起動します。

```console
$ pwd
~/identity-102-exercises/lab-01/begin
$ npm install
$ npm start
```
Chromeで`http://localhost:3000`にアクセスします。Auth0のログインウィジェットが表示されてサインアップできれば成功です。

<img src="https://i.imgur.com/LIUc3kS.png" width=70%>

<img src="https://i.imgur.com/kDQd8Ri.png" width=70%>

## Part2
Part2では認証プロセスで発生しているトラフィックをトレースして詳細を確認してみます。サインアップしたアカウントでログアウトしてChromeのデベロッパーツールを開き"Network"タブをクリックします。"authorize?client_idxxxx"を選択するとAuth0にGETリクエストが送信されていることが確認できます。
**<font color="DarkOrange">ExpressミドルウェアがAuth0の/authorize End PointにOpen ID Connectの認証リクエストを送信しています。</font>**

<img src="https://i.imgur.com/DVSckJ6.png" width=70%>

```text:GETリクエストの詳細
https://kiriko.auth0.com/authorize
?client_id=rJbxtul1gfExXU9K5610LgBR4SpF2d4R
&scope=openid%20profile%20email
&response_type=id_token
&nonce=71890cc63567e17b
&state=85d5152581b310e3389b
&redirect_uri=http://localhost:3000/callback
&response_mode=form_post
```

ログインして"callback"を選択します。
**<font color="DarkOrange">Auth0がApplicationにID Token付きのPOSTレスポンスを返しています。</font>**

<img src="https://i.imgur.com/zAdOcaK.png" width=70%>

ID TokenをコピーしてChromeで`https://jwt.io`にアクセスしてEncoded欄にコピーしたID Tokenをペーストします。

```text
id_token: eyJ0eXAiOixxxx
```

<img src="https://i.imgur.com/XZg2Jzp.png" width=70%>

ID Tokenの詳細を見てみます。

```json
{
  "nickname": "mokomokogaugau",
  "name": "mokomokogaugau@gmail.com",
  "picture": "https://s.gravatar.com/avatar/72fe59dcd73bba7543fc36d2c3c8131d?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fmo.png",
  "updated_at": "2019-12-20T02:20:42.537Z",
  "email": "mokomokogaugau@gmail.com",
  "email_verified": false,
  "iss": "https://kiriko.auth0.com/", -> 発行元. Auth0です。
  "sub": "auth0|5dfc2f44d7ca3516bee542d4", -> 発行された人。mokomokogaugau@gmail.comさんです。
  "aud": "rJbxtul1gfExXU9K5610LgBR4SpF2d4R", -> 発行されたApplication. Auth0に登録したApplicationのClient IDです。
  "iat": 1576808442,
  "exp": 1576844442,
  "nonce": "b3dbc93dd431a750"
}
```

Auth0 Dashboardの左ペイン"Users & Roles"->"Users"でmokomokogaugau@gmail.comさんのIDを確認してみます。ID Tokenの"sub"と一致していますね。

<img src="https://i.imgur.com/mq2CerO.png" width=70%>


# おわりに
最後までお付き合い頂きありがとうございます。ID Tokenは認可サーバから一度発行されたら有効期限内は再利用できるため、Applicationは何度も認可サーバに問い合わせする必要はなくなります。また、複数のApplicationで認証情報を共有できるためSSOも簡単に実装できます。Auth0やExpressミドルウェアのようなOpen ID Connectに準拠したテクノロジを利用することで少ない工数でセキュアでスケーラブルなWeb Applicationを構築できますね。