Firebase Authenticationを、Firebase Admin SDKではなくREST APIで使う方法
Cloudflare Workersで、APIを作っているのですが、当然ですが、なにかしらの制限をかけないと、だれでもリクエストできてしまいます。
そこで、よく利用している、Firebase Authenticationを使い、APIを保護したいと思いました。
しかし、Cloudflare Workersは、まだNode.jsを完全にサポートしていないと思いますので、今回は、Firebase Admin SDKを使うのではなく、REST APIを使うことにしました。
色々なライブラリを見ていると、結局、REST APIをコールしているだけのようですし…
このブログは、基本的に自分用のメモなので、簡易的なHTMLを書いて実行しました。
間違いがあれば、コメント頂ければと思います。
環境
名称 | バージョンなど |
---|---|
OS | macOS Monterey 12.6.6 |
ドキュメント
Firebaseの準備
Firebaseでプロジェクトを作成します。
Authentication -> ログイン プロバイダ -> メール / パスワード を追加します。
アプリを追加します。
APIキーをメモしておきます。
これで、Firebaseの準備がおわりました。
APIをリクエスト
簡単なHTMLを準備しました。
エディタから開くなり、live-serverを使うなりして、ブラウザで開きます。
メール/パスワードでサインアップ
ドキュメントは こちら です。
リクエスト
1<!DOCTYPE html>
2<html lang="ja">
3<head>
4 <meta charset="UTF-8">
5 <title>Sign up with email / password</title>
6 <style>
7 .container {
8 max-width: 400px;
9 display: flex;
10 flex-direction: column;
11 gap: 1rem;
12 }
13
14 .container label {
15 display: flex;
16 flex-direction: column;
17 }
18 </style>
19</head>
20<body>
21<h1>Sign up with email / password</h1>
22<div class="container">
23 <label>Email:
24 <input id="email" type="text" placeholder="Email">
25 </label>
26 <label>Password:
27 <input id="password" type="password" placeholder="Password">
28 </label>
29 <button onclick="signUp()">SignUp</button>
30 <hr>
31 <label>Response:
32 <textarea id="response" rows="5"></textarea>
33 </label>
34</div>
35<script>
36 async function signUp() {
37 try {
38 const email = document.querySelector('#email').value;
39 const password = document.querySelector('#password').value;
40
41 const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
42 const url = `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
43 const body = {
44 email: email,
45 password: password,
46 returnSecureToken: true,
47 };
48 const option = {
49 method: 'POST',
50 headers: {
51 'Content-Type': 'application/json',
52 },
53 body: JSON.stringify(body),
54 };
55
56 const res = await fetch(url, option);
57 const response = await res.json();
58 setResponse(response);
59 } catch (e) {
60 console.error(e.message);
61 }
62 }
63
64 function setResponse(data) {
65 document.querySelector('#response').value = JSON.stringify(data);
66 }
67</script>
68</body>
69</html>
レスポンス
1{
2 "kind": "identitytoolkit#SignupNewUserResponse",
3 "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhkMDNhZTdmNDczZjJjNmIyNTI3NmMwNjM2MGViOTk4ODdlMjNhYTkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItcmVzdC1hcGktd2VpZHU1IiwiYXVkIjoiZmItcmVzdC1hcGktd2VpZHU1IiwiYXV0aF90aW1lIjoxNjg3MzYxNTgwLCJ1c2VyX2lkIjoiZkx1VlZlTVd1aGY2bWt3RG9JMzhUcXZvNTdzMiIsInN1YiI6ImZMdVZWZU1XdWhmNm1rd0RvSTM4VHF2bzU3czIiLCJpYXQiOjE2ODczNjE1ODAsImV4cCI6MTY4NzM2NTE4MCwiZW1haWwiOiJwYW5vcGx5LXN5bmMwd0BpY2xvdWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbInBhbm9wbHktc3luYzB3QGljbG91ZC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.1LPI49PvK2fXyHxt_Y6j5-DqSJAbNLD3xyjgt_vIH-nv_eMobMS_DH2t55bH8UU3oEPNRSufxDkSldK7hceMwrP7UPGq8sjq_EqRcjwb328aGdDmfUMZ10SPPkBK0_uPxw-c84BJs99iCqo54x4l1O6yBPbQM0ILIj-aL5rKHqsPbcLjaRrYH4cT2_tsBn3185rK15XqDY5mUGz_KgnZhTgBYnwh9U5qP66bat9lGaE2eiXFOqANV8B2ShoXxf2WTtEaMAEGVM7eECov2QeNUvOGfckFmc1WymhXSao9NZmgA0HHlBZHItjqIf7nd2rBKscTiGpQ4Xeytm7Fz8otAA",
4 "email": "[email protected]",
5 "refreshToken": "APZUo0RksSngIb3GwemrAgDhSEmUXzJzXaZJpNnqavWk36C2Lz5fPgbbBb5cA-6nrIVd5YwXCtqNXYhNRhD4sBRePJ0N-suje9E0GCCh-aKhcFH77kPJcu64PLOo3vr-cAFgH1i4NBpexJK7svy8uW_dmwnHYH56mn-WuMk_jsPUP2VshZjIPhAcBr_x4GLWhWXo7ucvTnNMVbCHMpaBYKXDTIxn3DV7JuGmiO1LayCI_Xa0rkIdc-g",
6 "expiresIn": "3600",
7 "localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2"
8}
メール/パスワードでサインイン
ドキュメントは こちら です。
リクエスト
1<!DOCTYPE html>
2<html lang="ja">
3<head>
4 <meta charset="UTF-8">
5 <title>Sign in with email / password</title>
6 <style>
7 .container {
8 max-width: 400px;
9 display: flex;
10 flex-direction: column;
11 gap: 1rem;
12 }
13
14 .container label {
15 display: flex;
16 flex-direction: column;
17 }
18 </style>
19</head>
20<body>
21<h1>Sign in with email / password</h1>
22<div class="container">
23 <label>Email:
24 <input id="email" type="text" placeholder="Email">
25 </label>
26 <label>Password:
27 <input id="password" type="password" placeholder="Password">
28 </label>
29 <button onclick="signIn()">SignIn</button>
30 <hr>
31 <label>Response:
32 <textarea id="response" rows="5"></textarea>
33 </label>
34</div>
35<script>
36 async function signIn() {
37 try {
38 const email = document.querySelector('#email').value;
39 const password = document.querySelector('#password').value;
40
41 const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
42 const url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${apiKey}`;
43 const body = {
44 email: email,
45 password: password,
46 returnSecureToken: true,
47 };
48 const option = {
49 method: 'POST',
50 headers: {
51 'Content-Type': 'application/json',
52 },
53 body: JSON.stringify(body),
54 };
55
56 const res = await fetch(url, option);
57 const response = await res.json();
58 setResponse(response);
59 } catch (e) {
60 console.error(e.message);
61 }
62 }
63
64 function setResponse(data) {
65 document.querySelector('#response').value = JSON.stringify(data);
66 }
67</script>
68</body>
69</html>
レスポンス
1{
2 "kind": "identitytoolkit#VerifyPasswordResponse",
3 "localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2",
4 "email": "[email protected]",
5 "displayName": "",
6 "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhkMDNhZTdmNDczZjJjNmIyNTI3NmMwNjM2MGViOTk4ODdlMjNhYTkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItcmVzdC1hcGktd2VpZHU1IiwiYXVkIjoiZmItcmVzdC1hcGktd2VpZHU1IiwiYXV0aF90aW1lIjoxNjg3MzYxNjI0LCJ1c2VyX2lkIjoiZkx1VlZlTVd1aGY2bWt3RG9JMzhUcXZvNTdzMiIsInN1YiI6ImZMdVZWZU1XdWhmNm1rd0RvSTM4VHF2bzU3czIiLCJpYXQiOjE2ODczNjE2MjQsImV4cCI6MTY4NzM2NTIyNCwiZW1haWwiOiJwYW5vcGx5LXN5bmMwd0BpY2xvdWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbInBhbm9wbHktc3luYzB3QGljbG91ZC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.40IM5JldAR_BrQSR7eZB7mN7BbpY1aWufr6zOzbXMHNRW_C4Z4O-UBpuhi--p8ysjzM7WoL_GEW9ZY4ZJOl8-QAFXOGTpjMlHn3AA8pXi8gBIZiSqeomiXQrgGP1d5kXbVC6JmSRbFRnrdjuWaQXQAC8xmFZ4oJj4278n4ZrCheRebZeaVTo1inrMXCKlX2Zzj1SuChkKbl8Bi42h1AHQvhm87yaMN-1GRmYXZm3oWhMOVNPnLIp8B1AomGpOO5hm7rx2IxKNVL5g6PIWPyMIa1Bh1fSxOyg3ZIi0zsisvXDdobuyj571nQ8nf_4fw3rLxYMxwvLqEK4c7GL-wSeYA",
7 "registered": true,
8 "refreshToken": "APZUo0TAMclAMO6CtNYEj8DcSe_ssct9zxrnoaoZtigvyWREx5ORzwzat1PiD2SdngFFLbFT_FvttjYVf_orjtzrR_-S-yniX9lGEPhA2VS9L7w7REML5s92VSTYKhE13R8E3fXl-O7Qbfd6ip4MrUeZuWR43ZpER8mo_HDAuW7h_yDT1AA6RYAScrNbEyaoBbB2LcE8lXJkpgMXSIvpUIH6L448VZOKjepy-7fAfOK3fPlosA40bPQ",
9 "expiresIn": "3600"
10}
ユーザーデータを取得する
ドキュメントは こちら です。
IdToken
には、↑の メール/パスワードでサインイン
のレスポンスの idToken
を貼り付けてリクエストしてみてください。
リクエスト
1<!DOCTYPE html>
2<html lang="ja">
3<head>
4 <meta charset="UTF-8">
5 <title>Get user data</title>
6 <style>
7 .container {
8 max-width: 400px;
9 display: flex;
10 flex-direction: column;
11 gap: 1rem;
12 }
13
14 .container label {
15 display: flex;
16 flex-direction: column;
17 }
18 </style>
19</head>
20<body>
21<h1>Get user data</h1>
22<div class="container">
23 <label>IdToken:
24 <textarea id="idToken" placeholder="IdToken" rows="10"></textarea>
25 </label>
26 <button onclick="getUserData()">GetUserData</button>
27 <hr>
28 <label>Response:
29 <textarea id="response" rows="5"></textarea>
30 </label>
31</div>
32<script>
33 async function getUserData() {
34 try {
35 const idToken = document.querySelector('#idToken').value;
36
37 const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
38 const url = `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${apiKey}`;
39 const body = {
40 idToken: idToken,
41 };
42 const option = {
43 method: 'POST',
44 headers: {
45 'Content-Type': 'application/json',
46 },
47 body: JSON.stringify(body),
48 };
49
50 const res = await fetch(url, option);
51 const response = await res.json();
52 setResponse(response);
53 } catch (e) {
54 console.error(e.message);
55 }
56 }
57
58 function setResponse(data) {
59 document.querySelector('#response').value = JSON.stringify(data);
60 }
61</script>
62</body>
63</html>
レスポンス
1{
2 "kind": "identitytoolkit#GetAccountInfoResponse",
3 "users": [
4 {
5 "localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2",
6 "email": "[email protected]",
7 "passwordHash": "UkVEQUNURUQ=",
8 "emailVerified": false,
9 "passwordUpdatedAt": 1687361580458,
10 "providerUserInfo": [
11 {
12 "providerId": "password",
13 "federatedId": "[email protected]",
14 "email": "[email protected]",
15 "rawId": "[email protected]"
16 }
17 ],
18 "validSince": "1687361580",
19 "lastLoginAt": "1687361580458",
20 "createdAt": "1687361580458",
21 "lastRefreshAt": "2023-06-21T15:33:00.458Z"
22 }
23 ]
24}
確認メールを送信する
ドキュメントは こちら です。
リクエスト
1<!DOCTYPE html>
2<html lang="ja">
3<head>
4 <meta charset="UTF-8">
5 <title>Send email verification</title>
6 <style>
7 .container {
8 max-width: 400px;
9 display: flex;
10 flex-direction: column;
11 gap: 1rem;
12 }
13
14 .container label {
15 display: flex;
16 flex-direction: column;
17 }
18 </style>
19</head>
20<body>
21<h1>Send email verification</h1>
22<div class="container">
23 <label>IdToken:
24 <textarea id="idToken" placeholder="IdToken" rows="10"></textarea>
25 </label>
26 <button onclick="sendEmailVerification()">SendEmailVerification</button>
27 <hr>
28 <label>Response:
29 <textarea id="response" rows="5"></textarea>
30 </label>
31</div>
32<script>
33 async function sendEmailVerification() {
34 try {
35 const idToken = document.querySelector('#idToken').value;
36
37 const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
38 const url = `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
39 const body = {
40 requestType: 'VERIFY_EMAIL',
41 idToken: idToken,
42 };
43 const option = {
44 method: 'POST',
45 headers: {
46 // https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
47 'X-Firebase-Locale': 'ja',
48 'Content-Type': 'application/json',
49 },
50 body: JSON.stringify(body),
51 };
52
53 const res = await fetch(url, option);
54 const response = await res.json();
55 setResponse(response);
56 } catch (e) {
57 console.error(e.message);
58 }
59 }
60
61 function setResponse(data) {
62 document.querySelector('#response').value = JSON.stringify(data);
63 }
64</script>
65</body>
66</html>
レスポンス
1{
2 "kind": "identitytoolkit#GetOobConfirmationCodeResponse",
3 "email": "[email protected]"
4}
確認メール
おわり
- 今回、Firebase AuthenticationのREST APIを使ってみましたが、次は、Cloudflare WorkersでMiddlewareを作り、APIを保護したいと思います。
Tags: