この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは、中村です。
LIFFアプリおよびサーバーでユーザー情報を使用するにてLINE公式アナウンスされておりますが、LINEミニアプリ(LIFFアプリ)においてはフロントで取得したIDトークンやアクセストークンを元にAPIを操作することが想定されます。今回はAPI GatewayのLambdaオーソライザーを使ってAPIへのアクセス制御を実現します。
LINEミニアプリ用にLambdaオーソライザーでアクセス制御する
今回は下記のようにAPI GatewayとLambdaを使ってサーバレスAPIにします。
API検証にあたりIDトークンを取得するLINEミニアプリ(LIFFアプリ)が必要です。LIFFに登録するためには、HTTPS
のエンドポイントを用意してください。またIDトークンを利用するためScopeでopenid
を選択します。LIFF SDKの読み込み・下記のコードの実行することでLIFFアプリ内でIDトークンを取得することができます。
取得するためのコード
liff.init({
liffId: 'xxxxxxxxxx-xxxxxxx'
}).then(() => {
if (!liff.isLoggedIn()) {
liff.login(); // External browser support
}
const idToken = liff.getIDToken();
// console.log() or display id token.
}).catch((err) => {
alert(err.code, err.message);
})
Lambda
構成図を見ていただくと2つのLambda関数がありますが、今回は認証の部分の話をしたいのでLambdaAuth
のコードについて説明します。LambdaBackend
は、LambdaのHelloWorld関数を利用します。Lambdaのメイン処理でAuthorizationヘッダーのIDトークンを取得しIDトークンを検証するAPIを実行します。その結果に応じてポリシーAllow
・Deny
を分岐します。
Lambda環境変数 - CLIENT_IDは、LINEログインのチャネルIDを入力してください。
const fetch = require('node-fetch');
exports.handler = async (event) => {
console.log('Event: ' + JSON.stringify(event, null, 2));
/**
* Get id token from `Bearer xxxxxxxxxxxxxxxxxx`
*/
const [, idToken] = event.headers['Authorization'].split(' ');
/**
* Verify id token using social API v2.1 and create policy
*/
return await fetch('https://api.line.me/oauth2/v2.1/verify', {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `id_token=${idToken}&client_id=${process.env.CLIENT_ID}`
})
.then((res) => res.json())
.then((json) => {
console.log('Social API Response: ' + JSON.stringify(json, null, 2));
if (json.error) {
return generatePolicy('user', 'Deny', event.methodArn);
} else {
return generatePolicy('user', 'Allow', event.methodArn, json.sub);
}
});
};
const generatePolicy = (principalId, effect, resource, userId) => {
let authReponse = {};
authReponse.principalId = principalId;
if (effect && resource) {
let policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
let statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement.push(statementOne);
authReponse.policyDocument = policyDocument;
}
if (userId) authReponse.context = {
userId: userId
};
console.log('Policy: ' + JSON.stringify(authReponse, null, 2));
return authReponse;
};
Lambda2つを作成したら、API Gatewayの設定に移ります。
API Gateway
API Gatewayにアクセスして、APIの作成をクリックします。REST API
を構築しましょう。下記の設定を入力しAPIの作成
をクリックします。
項目名 | 値 |
---|---|
プロトコルを選択する | REST |
新しいAPIの作成 | 新しいAPI |
API名 | 任意 |
API作成が成功したらサイドバーのオーソライザー
にアクセスし、新しいオーソライザーの作成
をクリックします。下記の設定でオーソライザーを作成してください。
項目名 | 値 |
---|---|
名前 | 任意 |
タイプ | Lambda |
Lambda関数 | LambdaAuth 用の関数 |
Lambdaイベントペイロード | リクエスト |
IDソース | ヘッダー・Authorization |
認可のキャッシュ | 無効 |
作成が完了すると、登録内容としては以下のようになると思います。テストをクリックして実際に正常に動作するか確認しましょう。
AuthorizationヘッダーがBearer ${id_token}
でリクエストされる想定ですので、下記のように指定してください。
検証成功・失敗は下記の画像を参考にしてください。成功の場合はLambdaBackend
にリクエストがされます。
検証失敗されると、LambdaBackend
へはアクセスされず403が返されます。
ここまで確認ができたらあとはLambdaBackend
をメソッドに登録します。サイドバーからリソースへアクセスしてください。今回はルートにPOST
でリクエストするメソッドを作成します。アクション
プルダウンからメソッドの作成
をクリックします。リソースパスの下にメソッドのプルダウンが表示されますので、POST
を選択しチェックアイコンをクリックします。
作成が完了すると、統合ポイント(バックエンド)の登録に入ります。下記の設定を入力して保存
をクリックします。保存しようとするとAPI GatewayからLambdaを実行する権限を付与する旨のメッセージが表示されるのでOKで権限を付与します。
項目名 | 値 |
---|---|
統合タイプ | Lambda関数 |
Lambdaプロキシ統合の使用 | チェックする |
Lambdaリージョン | LambdaBackend のリージョン |
Lambda関数 | LambdaBackend を選択 |
ここまで完了すると、下記の画面になりますのでオーソライザーの設定をします。メソッドリクエスト
をクリックしてください。
認可
の編集アイコンをタップして先ほど作成したオーソライザーを選択し保存します。
さて最後にAPIをデプロイして完成です。アクション
プルダウンからAPIのデプロイ
をクリックします。デプロイするステージ名を入力してデプロイ
をクリックします。デプロイが完了すると、ステージへ遷移しAPIのエンドポイントが確認できます。こちらをメモしておいてください。
テスト
デプロイが完了したので、Postmanを使ってAPIにアクセスしてみます。最初に用意したLIFFアプリからIDトークンを取得して実行してみます。
IDトークンが検証できた場合はバックエンドまでリクエストされ200が返され、失敗時は403が返されました。IDトークンによりAPIアクセス制御ができることが確認できました。
まとめ
今回は、LINEミニアプリ(LIFFアプリ)で利用するAPIの認証をAPI GatewayのLambdaオーソライザーを使って実現しました。現時点でHTTP APIのJWTオーソライザーではまだES256アルゴリズムが未対応のため
利用できませんでしたが、対応されればこのソースを書くこともなくなるでしょう。LINEミニアプリ(LIFFアプリ)はサーバレスで!