【第2回】Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた【Firebase設定と認証機能実装】
IT技術
第2回~モダンなフレームワークの使い方を学びながらブログシステムを構築~
本記事は、「Python Responder + Firestore で、モダンかつサーバーレスなブログシステムを作ってみる」第2回目です。
前回は、初期設定として「Responder のセットアップ」まで解説しました。
今回は、「Firestore のセットアップ」を中心に解説していきます。
Firestore セットアップ
Firestore セットアップの手順
Firestore セットアップの大まかな流れは、以下の通りです。
- プロジェクトを作成する
- データベースを作成する
- サーバを初期化する
- 動作確認
公式ドキュメントも参照してみてください。
【Cloud Firestore を使ってみる】
https://firebase.google.com/docs/firestore/quickstart?hl=ja#python
プロジェクト作成
まずは、Firebase でプロジェクトを生成します。
「Firebase コンソール」から、「プロジェクトを作成」しましょう!
今回は、仮で「resp-fire-blog」としました。
無事にプロジェクトが生成できれば、以下の様な管理画面になるはずです。
データベース作成
そうしたら、「Cloud Firestore」にアクセスし「データベースの作成」をクリック。
「テストモードで開始」にしておきましょう。
これで、データベース作成完了です。
サーバの初期化
最初に、「Cloud Firestore」の初期化を部分実装します。
新しい秘密鍵の生成
「歯車マーク > プロジェクトを管理 > サービス アカウント」で、新しい秘密鍵の生成を行います。
json ファイルの保存を求められるので、新たに「/private」を作成し、保存します。
今回は、「keys.json」という名前にしました。
ファイルを作成
その後、以下のコードを書いたファイルを作成します。
ここでは、init_db.py とします。
1import firebase_admin
2from firebase_admin import credentials
3from firebase_admin import firestore
4
5# Use a service account
6cred = credentials.Certificate('private/keys.json')
7firebase_admin.initialize_app(cred)
8
9db = firestore.client()
このファイルを実行しても何も表示されませんが、これでサーバ準備は完了です。
注意!
ここで使った json ファイルは、絶対に外部に漏らしてはいけません。
Github にあげたりする場合は、これらのファイルは外してください。
本番サーバに上げる場合も、private ディレクトリは .htaccess でアクセスできないようにしておきましょう。
動作確認
データを追加
それでは、実際に、データを追加してみましょう。
Firestore では、Python ファイルからもデータを追加できます。
以下のコードを参照してください。
1from init_db import db
2
3doc_ref = db.collection('users').document('TTanaka')
4doc_ref.set({
5 'first': 'Taro',
6 'last': 'Tanaka',
7 'born': 1994
8})
これを実行してみると、以下のようにデータが追加できていることがわかります。
ここでは、コレクションは「モデル」、ドキュメントは「ID(主キー)」にあたります。
データの取り出し
データの取り出しは、以下のようにできます。
1from init_db import db
2
3users_ref = db.collection('users')
4docs = users_ref.stream()
5
6for doc in docs:
7 print('{} => {}'.format(doc.id, doc.to_dict()))
1TTanaka => {'first': 'Taro', 'last': 'Tanaka', 'born': 1994}
とても直感的な操作が可能ですね。
本連載では、これを使ってブログシステムを構築していきます。
管理者ページを作る
最初に「管理者」を作り、「管理者ページ」と「ログインページ」を作っていきます。
Firebase Authenticationを使う
今回は、Firebase が提供する認証システム「Firebase Authentication」を使用します。
まずは、管理画面の「Authentication > Sign-in method」より、メールを選んで有効にしてください。
次に、ユーザを追加します。
GUI でユーザ追加はしない
「Authentication > Users」からユーザを追加できますが、今回この方法は行いません。
ディスプレイネーム(表示名)も設定したいからです。
GUI 操作では、ディスプレイネームは変更できないのです。
Firebase Auth RestAPI を使う
このような処理を Python から行いたい場合は、「Firebase Auth RestAPI」を使います。
これは、指定された URL へデータを POST することで、「データの作成」「更新」「認証」まで出来る API です。
【Firebase Auth RestAPI】
https://firebase.google.com/docs/reference/rest/auth/
Userを追加する
そうしたら、auth.py を作り、サインアップ関数を作ります。
1import requests
2import json
3
4
5def _sign_up(email, password, displayName):
6 # Api key取得
7 with open('private/apikey.txt') as f:
8 api_key = f.read()
9
10 # 管理者ユーザ追加
11 url = 'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key={}'.format(api_key)
12 headers = {'Content-type': 'application/json'}
13 data = json.dumps({'email': email,
14 'password': password,
15 'returnSecureToken': True})
16
17 result = requests.post(url=url,
18 headers=headers,
19 data=data,
20 )
21
22 if 'error' in result.json():
23 print('error: {}'.format(result.json()['error']['errors'][0]['message']))
24 return result.json()
25
26 # DisplayNameを更新
27 url = 'https://identitytoolkit.googleapis.com/v1/accounts:update?key={}'.format(api_key)
28 data = json.dumps({
29 'idToken': result.json()['idToken'],
30 'displayName': displayName
31 })
32
33 result = requests.post(url=url,
34 headers=headers,
35 data=data,
36 )
37
38 if 'error' in result.json():
39 print('error: {}'.format(result.json()['error']['errors'][0]['message']))
40 return result.json()
41
42 return result.json()
43
44
45if __name__ == '__main__':
46 _sign_up('contact@sample.com', 'password', 'rightcode')
複雑に見えますが、やっていることは「WebAPI に json を投げている」だけです。
apikey.txt を /private ディレクトリに用意
ここで、API Keyを記述したapikey.txt を「/private」ディレクトリに用意しておいてください。
「プロジェクトの設定」で確認できます。
実行
それでは、実行してみましょう。
そうすると、以下の様にユーザが追加できていることがわかります。
管理者ページとログイン画面を作る
認証機能を実装する前に、先に「管理者ページ」と「ログイン画面」を仮で作っておきます。
管理者ページ
「templates/admin.html」を作り、以下のように記述します。
1<!--
2templates/admin.html
3管理者ページ
4-->
5
6{% extends "layout.html" %}
7{% block content %}
8
9<br>
10<h1>My Blog Name | 管理者ページ</h1>
11<p>こんにちは,{{ name }} さん</p>
12
13{% endblock %}
ひとまず、管理者ページはこれだけです。
ログインページ
次にログインページですが、今回は以下の様にしました。
1<!--
2templates/login.html
3ログインページ
4-->
5
6{% extends "layout.html" %}
7{% block content %}
8
9<br>
10<h1>My Blog Name | ログイン</h1>
11<br>
12<div class="card login">
13 <form action="/admin" method="post">
14 <p>Email</p>
15 <input name="email" type="email">
16 <p>Password</p>
17 <input name="password" type="password">
18 <p class="error">{{ error }}</p>
19 <button type="submit">Login</button>
20 </form>
21</div>
22
23{% endblock %}
CSSも追記します。
1.card{
2 max-width: 500px;
3 height: auto;
4 margin: auto;
5}
6.login{
7 overflow: hidden;
8 border-radius: 8px;
9 box-shadow: 0 4px 15px rgba(0,0,0,.2);
10}
11.login form{
12 margin: 50px 10px;
13}
14.login p{
15 margin: 10px 50px;
16 font-size: 20px;
17 font-weight: bold;
18 line-height: 125%;
19}
20.login input{
21 margin: 5px 50px;
22}
23button{
24 display: block;
25 border-style: none;
26 font-size: 1.4em;
27 background-color: #2b52cd;
28 color: white;
29 margin: 50px 50px;
30 padding: 10px 30px;
31}
32.error{
33 font-size: 0.5em;
34 color: #ce0404;
35}
現時点では処理を書いていないので、まだ何も起きません。
認証する関数
そうしたら、実際の「ルーティングと認証機能を Python で実装」していきます。
ログイン機構
まずは、ログイン機構をauth.py に書きましょう。
1# auth.py内
2from firebase_admin import auth
3from datetime import timedelta
4
5
6def _login(email, password, expires=5):
7 with open('private/apikey.txt') as f:
8 api_key = f.read()
9
10 url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={}'.format(api_key)
11 headers = {'Content-type': 'application/json'}
12 data = json.dumps({'email': email,
13 'password': password,
14 'returnSecureToken': True})
15
16 result = requests.post(url=url,
17 headers=headers,
18 data=data,
19 )
20
21 result = result.json()
22
23 # エラーがある場合
24 if 'error' in result:
25 return result, None
26
27
28 # セッションクッキーの作成
29 session_cookie = auth.create_session_cookie(result['idToken'], timedelta(expires))
30
31 return result, session_cookie
Firebase 側で、セッション Cookie を作成します。
WebAPI が異なるだけで、サインアップとあまり変わりません。
この取得した結果にエラーが含まれていると、認証失敗です。
ルーティング
次に、controllers.py に「/login」と「/admin」をルーティングします。
1# controllers.py内
2from auth import _login, _sign_up
3
4# ~~ 省略 ~~ #
5
6firebase_error_massages = {
7 'EMAIL_NOT_FOUND': 'メールアドレスが正しくありません',
8 'INVALID_PASSWORD': 'パスワードが正しくありません',
9}
10
11@api.route('/login')
12def login(req, resp):
13 # エラーがあれば取得
14 error = req.params.get('error', '')
15 if error in firebase_error_massages:
16 error = firebase_error_massages[error]
17
18 resp.html = api.template('login.html',
19 title='Login',
20 error=error
21 )
22
23
24@api.route('/admin')
25class Admin:
26 async def on_get(self, req, resp):
27 api.redirect(resp, '/login')
28
29 async def on_post(self, req, resp):
30 # POSTデータを取得
31 data = await req.media()
32 email = data['email']
33 password = data['password']
34
35 # 認証
36 res, session = _login(email, password)
37
38 if 'error' not in res:
39 # 認証成功ならば管理者ページへ
40 resp.html = api.template('admin.html',
41 title='管理者ページ',
42 token=res['idToken'],
43 name=res['displayName'])
44 else:
45 # 認証失敗ならばエラーメッセージをログイン画面に渡してリダイレクト
46 api.redirect(resp, '/login?error={}'.format(res['error']['errors'][0]['message']))
「/admin」では、GET でのアクセスはすべて「/login」にアクセスするようにします。
この時、クラス自身にルーティングを施して、on_get() とon_post() で処理を分けることができます。
動作確認
では、実際にログインしてみましょう。
成功すれば、以下の様な画面に遷移されるはずです。
ログインに失敗すると…
例として、失敗した時も見ておきましょう。
第3回へつづく!
今回は、「Firebase 周りのセットアップおよび動作確認」を行いました。
一応認証機能は作ることができましたが、まだまだ不便です。
例えば、一度ログインに成功していれば、直接管理者ページに行けるのが普通です。
しかし、今は全てリダイレクトされてしまいます。
次回は、「認証機能の整備とログアウト機構」もつけてみましょう!
次回の記事はこちら
第1回はこちら
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit
おすすめ記事
immichを知ってほしい
2024.10.31