Strapi APIに認証機能を追加してみる
前回の記事「Strapi (Headless CMS)を使ってみたので使い方まとめ + GraphQL」でHeadless CMS「Strapi」を使って簡易APIを作ってみました。
今回は特定のユーザしかAPIを呼び出せない様に、APIに認証を付けていきたいと思います。
ちなみに「公式サイト」に詳しく説明が載っているので、英語が読める方はこちらを読んで進めることを強くお勧めします。
この認証機能を使ったVueアプリのレポジトリーはこちらです
・ レポジトリ
事前知識
Strapiでは、「ロールと権限」というプラグインを使って認証を行います。(このプラグインは、デフォルトでインストールされいるはずです。)
このプラグインでは、「JSON Web Token (JWT)」を使い認証を行います。
JSON Web Tokenとは
JSON Web Token(JWT)とは、JSONというデータ構造で情報が表現されたフォーマットです。安全にデータを運ぶために使用されます。JWTの詳しい説明は、下記の記事がわかりやすいです。
・JSON Web Token の効用
・認証におけるJWTの利用について
ということで、StrapiはJWTを使って認証を行います。
APIリクエストが送信されると、サーバが「認証ヘッダーがあるか」と「ユーザからのリクエストは、リソースにアクセス可能なものか」を確認します。JWTにユーザIDが含まれているため、ユーザがどのグループに入っているか確かめることができます。結果としてリクエストされているルートにアクセスが可能かどうかの判断ができます。
ロールの種類
Strapiのロールには大きく分けて「public」と「authenticated」の2種類があります。それぞれのロールを説明していきます。
Public
「認証ヘッダ」がリクエストに含まれていない時に、このロールが使用されます。「全ての人」がこのロールに設定したエンドポイントにアクセス可能です。「find」や「findOne」などはこのpublicロールを使うといいと思います。
Authenticated
このロールでユーザがどのルートにアクセスできるかなど定義が可能です。ユーザを作成したときにデフォルトで付与されるのがこのロールです。
ユーザを作成する
Authenticatedのロールを持つユーザを作成してみます。
公式サイトにも書いてますが、基本的には下記のコードでユーザを作成できます。簡単!
import axios from 'axios';
axios
.post('http://localhost:1337/auth/local/register', {
username: 'Strapi user',
email: 'user@strapi.io',
password: 'strapiPassword',
})
.then(response => {
// Handle success.
console.log('Well done!');
console.log('User profile', response.data.user);
console.log('User token', response.data.jwt);
})
.catch(error => {
// Handle error.
console.log('An error occurred:', error);
});
実装する
今回は私が練習用に作ったチェーン店メモアプリの「チェーン店めもったろう君」で上記の関数を実行してみます。(このアプリはVuetifyを使っているので、htmlタグはVeutify仕様になっています。)
「ユーザ登録」ボタンを押すと、usernameが「momoko1」のユーザがadminに登録されるはずです。
<v-row>
<v-col cols="12" class="mt-4 d-flex justify-center">
<v-btn
class="success"
v-on:click="createUser"
>
ユーザ登録
</v-btn>
</v-col>
</v-row>
async createUser(){
axios
.post('http://localhost:1337/auth/local/register', {
username: 'momoko1',
email: 'momoko1@strapi.com',
password: 'password',
})
.then(response => {
console.log('Well done!');
console.log('User profile', response.data.user);
console.log('User token', response.data.jwt);
})
.catch(error => {
console.log('An error occurred:', error);
});
}
「ユーザ登録」ボタンを押すと、User profileとトークンが返ってきました。
strapiから「ロールと権限」→ Authenticatedにいくと・・・できてる!
このようにしてAuthenticatedのロールを持つユーザ作成ができます。
ログインしないとCRUD操作ができない様にする
それでは、ログインをしていないとチェーン店メモったろう君でチェーン店の登録も、データの取得もできない様にしていきます。
Authenticatedに権限を付与
まずは「ロールと権限」→ Publicから権限を全て外します。その次にAuthenticatedの権限で必要なCRUD操作にチェックを入れます。これでログインしていないユーザは何も操作ができない様になります。
実装する
<v-col>
<v-form>
<v-row
justify="center"
>
<v-col
cols="10"
md="4"
>
<v-text-field
v-model="email"
label="Eメールアドレス"
></v-text-field>
</v-col>
<v-col
cols="10"
md="4"
>
<v-text-field
v-model="password"
label="パスワード"
></v-text-field>
</v-col>
<v-col
md="1"
>
<v-btn
class="success"
v-on:click="login"
>
ログイン
</v-btn>
</v-col>
</v-row>
</v-form>
</v-col>
async login() {
axios
.post('http://localhost:1337/auth/local', {
identifier: this.email,
password: this.password,
})
.then(response => {
console.log('Well done!');
console.log('User profile', response.data.user);
console.log('User token', response.data.jwt);
})
.catch(error => {
console.log('An error occurred:', error);
});
}
先ほどのユーザ登録の際に使用したEメールアドレスとパスワードを使用してログインしてみます。ちなみに、identifier
に入れるのはemailでもusernameでも良い様です。
ログインが成功すると、こんな感じでUser Tokenがレスポンスとして返ってきます。
このトークンをリクエストヘッダに含めて認証を行い、認証が通ればCRUD操作ができる、という仕組みです。tokenという変数にresponse.data.jwtを格納する様にコードを修正します。
async login() {
axios
.post('https://powerful-dusk-72165.herokuapp.com/auth/local', {
identifier: this.email,
password: this.password,
})
.then(response => {
this.token = response.data.jwt
this.getRestaurants() // ログインが成功したらレストランデータをとる関数を動かす
})
.catch(error => {
console.log('An error occurred:', error);
});
},
あとは、
// 修正後
async getRestaurants(){
try {
var result = await axios({
method: "POST",
url: this.apiURL,
headers: {
Authorization: `Bearer ${this.token}`,
},
data: {
query: `
query getRestaurants {
restaurants {
id
name
description
}
}
`
}
});
this.restaurants = result.data.data.restaurants;
} catch (error) {
console.error(error);
}
}
// 修正前
async getRestaurants(){
try {
var result = await axios({
method: "POST",
url: this.apiURL,
data: {
query: `
query getRestaurants {
restaurants {
id
name
description
}
}
`
}
});
this.restaurants = result.data.data.restaurants;
} catch (error) {
console.error(error);
}
}
Strapiでユーザ認証をしてAPIを呼び出す方法でした。結構簡単でびっくり。SNSログインもできるみたいなので、また今度Qiita記事を書きながら試してみたいと思います。
公式サイトのドキュメントがとても丁寧なのもStrapiの魅力ですね。好きになっちゃいそう・・・。