Swagger定義ファイルにSecurityDefinitionsを定義する

前回、SwaggerでRESTful環境をスマートに構築しました。

[SwaggerでRESTful環境を構築する](https://qiita.com/poruruba/items/72d8c85f8923bc78b918)

使いこなしていく上で、手間取ったことの一つに、SecurityDefinitionsの定義があります。
SwaggerにおけるSecurityDefinitionsとは、HTTPヘッダのAuthorizationを使って、ユーザ・パスワードによるベーシック認証やOpenID Connectのトークン認証するための定義です。

swagger-nodeを使ったRESTful環境でもちゃんと対応しているんです！
せっかくなので、使ってみます。

# Swagger SecurityDefitions定義

Swagger 2.0では、以下の3つの方式が定義可能です。

* Basic認証
* API Key 
* OAuth2 認証フロー

[参考情報]
https://swagger.io/docs/specification/2-0/authentication/basic-authentication/

このうち、OAuth2 認証フローは、細かな設定ができるようなのですが、設定が面倒だったり、Swagger 3.0でまた仕様がかわるので、使うのをやめて、API Keyで代用します。トークンはどうなっちゃうの？と心配されるかもしれませんが、以降で、それっぽくトークン検証していますので、お楽しみに。

それでは早速、定義方法を示します。

共通の定義をしてから、各APIにどの定義を使うかという順番です。

まず定義から。

```swagger.yaml
securityDefinitions:
  basicAuth:
    type: basic
  tokenAuth:
    type: "apiKey"
    name: "Authorization"
    in: "header"
```

見ての通りですね。
名前は何でもよいのですが、それぞれ「basicAuth」と「tokenAuth」という定義の名前を付けてみました。

それらを実際に使うAPIの定義では、以下のように指定します。security: のところです。
これだけです。

```swagger.yaml
paths:
  /test_auth:
    post:
      x-swagger-router-controller: test
      operationId: test_post
      security:
        - basicAuth: []  もしくは – tokenAuth: []
      tags:
      - test
      description: Post message to test
      parameters:
        - in: body
          name: body
          required: true
          schema:
            $ref: '#/definitions/SimpleRequest'
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/SimpleResponse'
        default:
          description: Error
          schema:
            $ref: "#/definitions/ErrorResponse"
```

それではさっそくSwagger-EditorでSwagger定義ファイルを見てみましょう。
ページの先頭あたりに、Authorize というボタンが増えています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/9b40fa3d-5f1b-ae6d-1a6a-e6b1ad224a10.png)

押してみると、

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/ffeb5e2f-c53a-750c-8f8c-b401fc1898ef.png)

上記のように、ベーシック認証では、ユーザ名とパスワードが入れられるようになりました。
トークン認証においても、Authorizationヘッダに値を指定できるようになりました。
これらは、Swagger定義ファイルのSecurityDefinitionsの定義部分に該当します。

次は、API定義の右端の所に、錠のマークがついているのがわかりますでしょうか？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/261826/f0bf542c-5b31-5d67-9fba-dbf78b113253.png)

「Try it out」で実行してみると、basicAuthなのかtokenAuthなのかに従って、Authorizationヘッダに値を勝手につけてくれます。
しかも、ベーシック認証の場合には、ユーザ名・パスワードが以下のようにエンコードしてくれますので、至れり尽くせりです。

> Authorization: Basic base64Encode("ユーザ名:パスワード”)

ここまでやってくれるSwagger-Editorは素晴らしいとしか言いようがないですね。


# Swagger実行環境の整備

実は、上記のSwagger定義ファイルにSecurityDefinitionsを定義した状態でSwagger実行環境を立ち上げると、実行に失敗してしまいます。定義が足りないという理由です。

そこで、以下の定義を追加することで、Swaggerを実行できる状態になります。
config.swaggerSecurityHandlersの部分です。

```app.js

・・・・

var config = {
  appRoot: __dirname // required config
};

config.swaggerSecurityHandlers = {
  basicAuth : function (req, authOrSecDef, scopesOrApiKey, cb) {
	// todo
	// cb() for success
	// cb(error) for error
  },
  tokenAuth : function (req, authOrSecDef, scopesOrApiKey, cb) {
	// todo
	// cb() for success
	// cb(error) for error
  }
};

SwaggerExpress.create(config, function(err, swaggerExpress) {

・・・

```

basicAuthとtokenAuthという名称は、SecurityDefinitionsで定義したあの名前です。
これでエラーにはならなくなるのですが、ついでに便利なロジックを埋め込んどいてあげましょう。
Authorizationヘッダに入力された内容を解析して、コントローラに渡してあげる、というものです。そうすることで、コントローラ側で、ユーザ名とパスワードが合っているか、など判断することができるようになります。

最終的にはこの通りです。

```app.js

var jwt_decode = require('jwt-decode');

config.swaggerSecurityHandlers = {
  basicAuth : function (req, authOrSecDef, scopesOrApiKey, cb) {
    try{
      var basic = req.headers.authorization.trim();
      if(basic.toLowerCase().startsWith('basic '))
        basic = basic.slice(6).trim();
      
      var buf = new Buffer(basic, 'base64');
      var ascii = buf.toString('ascii');

      req.requestContext = {
        authorizer : {
          basic : ascii.split(':')
        }
      };

      cb();
    }catch(error){
      cb(error);
    }
  },
  tokenAuth : function (req, authOrSecDef, scopesOrApiKey, cb) {
    try{
      var decoded = jwt_decode(scopesOrApiKey);

      req.requestContext = {
        authorizer : {
          claims : decoded
        }
      };

      cb();
    }catch(error){
      cb(error);
    }
  }
};
```

まずは、ベーシック認証(basicAuth)から。
AuthorizationヘッダにはBase64エンコードされてきますので、それをデコードします。
デコードした結果を、req.requestContext.authorizer.basicに、ユーザ名とパスワードの配列として、コントローラに渡します。

次は、トークン認証(tokenAuth)。
トークンは、JWTでエンコードされています。それを解析するのに便利なモジュール「jwt-decode」を使わせていただきました。
これを使ってデコードした結果をreq.requestContext.authorizer.claimsに、subやらaudやらissやら、トークンの内容をそのまま設定してコントローラに渡しています。
以下も忘れずに実行しておいてください。

> npm install jwt-decode --save

本来であれば、JWTの公開鍵を使って検証するのですが、今回は単純にデコードだけにしました。ですので、上記のコードでは、トークンの生成元が正しいかどうかは確認していないので、そのまま運用には使わないで下さい。正しくは、ここら辺を参考にしてください。

[参考情報]
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html

以上で、Swagger-Editor上から、ユーザパスワードやトークンを指定して、API呼び出しができるようになりました。
swagger-nodeは本当に素晴らしいですね。

#ご参考

最終系はこちらです。
[SwaggerでLambdaのデバッグ環境を作る(1)](https://qiita.com/poruruba/items/e3a46787c79517a815f9)

以上