Firebase Authenticationを、Firebase Admin SDKではなくREST APIで使う方法
2023年06月22日 木曜日 09時00分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を使うなりして、ブラウザで開きます。
メール/パスワードでサインアップ
ドキュメントは こちら です。

リクエスト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sign up with email / password</title>
<style>
.container {
max-width: 400px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.container label {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<h1>Sign up with email / password</h1>
<div class="container">
<label>Email:
<input id="email" type="text" placeholder="Email">
</label>
<label>Password:
<input id="password" type="password" placeholder="Password">
</label>
<button onclick="signUp()">SignUp</button>
<hr>
<label>Response:
<textarea id="response" rows="5"></textarea>
</label>
</div>
<script>
async function signUp() {
try {
const email = document.querySelector('#email').value;
const password = document.querySelector('#password').value;
const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
const url = `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${apiKey}`;
const body = {
email: email,
password: password,
returnSecureToken: true,
};
const option = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
const res = await fetch(url, option);
const response = await res.json();
setResponse(response);
} catch (e) {
console.error(e.message);
}
}
function setResponse(data) {
document.querySelector('#response').value = JSON.stringify(data);
}
</script>
</body>
</html>
レスポンス
{
"kind": "identitytoolkit#SignupNewUserResponse",
"idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhkMDNhZTdmNDczZjJjNmIyNTI3NmMwNjM2MGViOTk4ODdlMjNhYTkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItcmVzdC1hcGktd2VpZHU1IiwiYXVkIjoiZmItcmVzdC1hcGktd2VpZHU1IiwiYXV0aF90aW1lIjoxNjg3MzYxNTgwLCJ1c2VyX2lkIjoiZkx1VlZlTVd1aGY2bWt3RG9JMzhUcXZvNTdzMiIsInN1YiI6ImZMdVZWZU1XdWhmNm1rd0RvSTM4VHF2bzU3czIiLCJpYXQiOjE2ODczNjE1ODAsImV4cCI6MTY4NzM2NTE4MCwiZW1haWwiOiJwYW5vcGx5LXN5bmMwd0BpY2xvdWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbInBhbm9wbHktc3luYzB3QGljbG91ZC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.1LPI49PvK2fXyHxt_Y6j5-DqSJAbNLD3xyjgt_vIH-nv_eMobMS_DH2t55bH8UU3oEPNRSufxDkSldK7hceMwrP7UPGq8sjq_EqRcjwb328aGdDmfUMZ10SPPkBK0_uPxw-c84BJs99iCqo54x4l1O6yBPbQM0ILIj-aL5rKHqsPbcLjaRrYH4cT2_tsBn3185rK15XqDY5mUGz_KgnZhTgBYnwh9U5qP66bat9lGaE2eiXFOqANV8B2ShoXxf2WTtEaMAEGVM7eECov2QeNUvOGfckFmc1WymhXSao9NZmgA0HHlBZHItjqIf7nd2rBKscTiGpQ4Xeytm7Fz8otAA",
"email": "[email protected]",
"refreshToken": "APZUo0RksSngIb3GwemrAgDhSEmUXzJzXaZJpNnqavWk36C2Lz5fPgbbBb5cA-6nrIVd5YwXCtqNXYhNRhD4sBRePJ0N-suje9E0GCCh-aKhcFH77kPJcu64PLOo3vr-cAFgH1i4NBpexJK7svy8uW_dmwnHYH56mn-WuMk_jsPUP2VshZjIPhAcBr_x4GLWhWXo7ucvTnNMVbCHMpaBYKXDTIxn3DV7JuGmiO1LayCI_Xa0rkIdc-g",
"expiresIn": "3600",
"localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2"
}
メール/パスワードでサインイン
ドキュメントは こちら です。

リクエスト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sign in with email / password</title>
<style>
.container {
max-width: 400px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.container label {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<h1>Sign in with email / password</h1>
<div class="container">
<label>Email:
<input id="email" type="text" placeholder="Email">
</label>
<label>Password:
<input id="password" type="password" placeholder="Password">
</label>
<button onclick="signIn()">SignIn</button>
<hr>
<label>Response:
<textarea id="response" rows="5"></textarea>
</label>
</div>
<script>
async function signIn() {
try {
const email = document.querySelector('#email').value;
const password = document.querySelector('#password').value;
const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
const url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${apiKey}`;
const body = {
email: email,
password: password,
returnSecureToken: true,
};
const option = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
const res = await fetch(url, option);
const response = await res.json();
setResponse(response);
} catch (e) {
console.error(e.message);
}
}
function setResponse(data) {
document.querySelector('#response').value = JSON.stringify(data);
}
</script>
</body>
</html>
レスポンス
{
"kind": "identitytoolkit#VerifyPasswordResponse",
"localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2",
"email": "[email protected]",
"displayName": "",
"idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhkMDNhZTdmNDczZjJjNmIyNTI3NmMwNjM2MGViOTk4ODdlMjNhYTkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItcmVzdC1hcGktd2VpZHU1IiwiYXVkIjoiZmItcmVzdC1hcGktd2VpZHU1IiwiYXV0aF90aW1lIjoxNjg3MzYxNjI0LCJ1c2VyX2lkIjoiZkx1VlZlTVd1aGY2bWt3RG9JMzhUcXZvNTdzMiIsInN1YiI6ImZMdVZWZU1XdWhmNm1rd0RvSTM4VHF2bzU3czIiLCJpYXQiOjE2ODczNjE2MjQsImV4cCI6MTY4NzM2NTIyNCwiZW1haWwiOiJwYW5vcGx5LXN5bmMwd0BpY2xvdWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbInBhbm9wbHktc3luYzB3QGljbG91ZC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.40IM5JldAR_BrQSR7eZB7mN7BbpY1aWufr6zOzbXMHNRW_C4Z4O-UBpuhi--p8ysjzM7WoL_GEW9ZY4ZJOl8-QAFXOGTpjMlHn3AA8pXi8gBIZiSqeomiXQrgGP1d5kXbVC6JmSRbFRnrdjuWaQXQAC8xmFZ4oJj4278n4ZrCheRebZeaVTo1inrMXCKlX2Zzj1SuChkKbl8Bi42h1AHQvhm87yaMN-1GRmYXZm3oWhMOVNPnLIp8B1AomGpOO5hm7rx2IxKNVL5g6PIWPyMIa1Bh1fSxOyg3ZIi0zsisvXDdobuyj571nQ8nf_4fw3rLxYMxwvLqEK4c7GL-wSeYA",
"registered": true,
"refreshToken": "APZUo0TAMclAMO6CtNYEj8DcSe_ssct9zxrnoaoZtigvyWREx5ORzwzat1PiD2SdngFFLbFT_FvttjYVf_orjtzrR_-S-yniX9lGEPhA2VS9L7w7REML5s92VSTYKhE13R8E3fXl-O7Qbfd6ip4MrUeZuWR43ZpER8mo_HDAuW7h_yDT1AA6RYAScrNbEyaoBbB2LcE8lXJkpgMXSIvpUIH6L448VZOKjepy-7fAfOK3fPlosA40bPQ",
"expiresIn": "3600"
}
ユーザーデータを取得する
ドキュメントは こちら です。
IdToken
には、↑の メール/パスワードでサインイン
のレスポンスの idToken
を貼り付けてリクエストしてみてください。

リクエスト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Get user data</title>
<style>
.container {
max-width: 400px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.container label {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<h1>Get user data</h1>
<div class="container">
<label>IdToken:
<textarea id="idToken" placeholder="IdToken" rows="10"></textarea>
</label>
<button onclick="getUserData()">GetUserData</button>
<hr>
<label>Response:
<textarea id="response" rows="5"></textarea>
</label>
</div>
<script>
async function getUserData() {
try {
const idToken = document.querySelector('#idToken').value;
const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
const url = `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${apiKey}`;
const body = {
idToken: idToken,
};
const option = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
const res = await fetch(url, option);
const response = await res.json();
setResponse(response);
} catch (e) {
console.error(e.message);
}
}
function setResponse(data) {
document.querySelector('#response').value = JSON.stringify(data);
}
</script>
</body>
</html>
レスポンス
{
"kind": "identitytoolkit#GetAccountInfoResponse",
"users": [
{
"localId": "fLuVVeMWuhf6mkwDoI38Tqvo57s2",
"email": "[email protected]",
"passwordHash": "UkVEQUNURUQ=",
"emailVerified": false,
"passwordUpdatedAt": 1687361580458,
"providerUserInfo": [
{
"providerId": "password",
"federatedId": "[email protected]",
"email": "[email protected]",
"rawId": "[email protected]"
}
],
"validSince": "1687361580",
"lastLoginAt": "1687361580458",
"createdAt": "1687361580458",
"lastRefreshAt": "2023-06-21T15:33:00.458Z"
}
]
}
確認メールを送信する
ドキュメントは こちら です。

リクエスト
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Send email verification</title>
<style>
.container {
max-width: 400px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.container label {
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<h1>Send email verification</h1>
<div class="container">
<label>IdToken:
<textarea id="idToken" placeholder="IdToken" rows="10"></textarea>
</label>
<button onclick="sendEmailVerification()">SendEmailVerification</button>
<hr>
<label>Response:
<textarea id="response" rows="5"></textarea>
</label>
</div>
<script>
async function sendEmailVerification() {
try {
const idToken = document.querySelector('#idToken').value;
const apiKey = 'AIzaSyCLDaLd0GCIKIF0hK1sBW_j0IbBfDpJyS0';
const url = `https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${apiKey}`;
const body = {
requestType: 'VERIFY_EMAIL',
idToken: idToken,
};
const option = {
method: 'POST',
headers: {
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
'X-Firebase-Locale': 'ja',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
const res = await fetch(url, option);
const response = await res.json();
setResponse(response);
} catch (e) {
console.error(e.message);
}
}
function setResponse(data) {
document.querySelector('#response').value = JSON.stringify(data);
}
</script>
</body>
</html>
レスポンス
{
"kind": "identitytoolkit#GetOobConfirmationCodeResponse",
"email": "[email protected]"
}
確認メール

おわり
- 今回、Firebase AuthenticationのREST APIを使ってみましたが、次は、Cloudflare WorkersでMiddlewareを作り、APIを保護したいと思います。