Edited at

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

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

SwaggerでRESTful環境を構築する

使いこなしていく上で、手間取ったことの一つに、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

押してみると、

image.png

上記のように、ベーシック認証では、ユーザ名とパスワードが入れられるようになりました。

トークン認証においても、Authorizationヘッダに値を指定できるようになりました。

これらは、Swagger定義ファイルのSecurityDefinitionsの定義部分に該当します。

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

image.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)

以上