In recent years, the development of cloud services has created an environment where individuals can easily start their own membership-based online services.
In particular, using a Backend as a Service (BaaS) like Firebase allows developers to greatly simplify the complex process of building a backend.
Firebase provides many of the features needed to build an online service, such as authentication, database, file storage, and serverless functions, allowing developers to focus on the core functionality of their service.
Firebase also automatically scales with service growth, which can significantly reduce operational costs.
In this article, I will explain the specific steps for starting a membership-based online service as an individual using Firebase, while creating a fictitious service with slightly relaxed security as an example.
The source code is available on GitHub and service is available at https://exp-fb-c223c.web.app/.
Firebase is a powerful platform that facilitates app development. However, it's currently undergoing frequent updates, notably transitioning to v9, which introduces the Modular API. This new API significantly alters the syntax from the traditional one.
While the Modular API offers enhanced flexibility and efficiency, it breaks compatibility with legacy code. Although compat versions are provided by the Firebase team, migrating to the Modular API is recommended for future-proofing your projects.
Firebase includes Firebase-UI, a handy UI library for implementing features like login. However, Firebase-UI doesn't support the Modular API yet, necessitating a potentially time-consuming migration process.
Given Firebase's ongoing evolution, anticipate further significant changes in the future.
Considering this evolving landscape, directly utilizing Firebase's features to implement login functionality emerges as the most sustainable approach.
By embracing the latest Firebase developments and implementing login functionality directly, developers can ensure their applications' longevity and adaptability.
For the code, HTML and CSS are provided on CodePen. JavaScript follows separately since Firebase doesn't function within CodePen. You can view a working version here.
See the Pen Firebase AUTH by Satachito (@satachito) on CodePen.
If a user creates an account using createUserWithEmailAndPassword, signInWithPopup, or signInWithRedirect, the email may not be verified immediately (except when using GoogleAuthProvider).
The signOut method can be used to reflect email verification status changes in the auth user object, as it requires the user to log in again.
Since September 2023, Firebase has introduced Email Enumeration Protection, a feature that fortifies login functionality against traditional attacks, enhancing user security.
In traditional login systems, attackers try various email addresses with random passwords. By analyzing error messages, they can discern whether an email address is registered, thus exploiting the system's vulnerabilities.
This feature ensures that all login attempts, regardless of password accuracy, return a generic "Invalid credentials" message. Consequently, attackers cannot determine email address validity solely based on error responses, mitigating enumeration attacks.
The following methods are affected by Email Enumeration Protection:
signInWithEmailAndPasswordfetchSignInMethodsForEmailsendPasswordResetEmailWhen enabled, fetchSignInMethodsForEmail returns an empty array, and sendPasswordResetEmail shows no error for non-existent email addresses to avoid revealing address existence.
This article assumes Email Enumeration Protection is enabled. However, note that the Firebase Auth Emulator does not support this feature, necessitating thorough deployment and debugging for authentication functionality.
In our fictional service, connecting with external APIs is crucial for functionality. However, directly connecting a client-side (web) application to external APIs presents security challenges. To overcome these hurdles, a backend service plays a vital role.
See the Pen Firebase Functions 1 by Satachito (@satachito) on CodePen.
これで動作しますが、2つの大きな問題があります。 まずは、この API が世界中のどこからでもアクセスできてしまうことです。 これを避ける方法はいくつかありますが、最も安全な Web 側で Firebase Authenticate を使って ユーザー情報を含む Token を作成して、 Authorization ヘッダーにセットして、Functions の中で Authorization ヘッダーから Token を取り出して verify する方法です。 Web側は以下のようなコードになります。Web 側
const
app = initializeApp (
// Your configuration here
)
const
auth = getAuth( app )
getIdToken( auth.currentUser, true ).then(
token => {
fetch(
'https://......./ticker'
, { headers: { 'Authorization': 'Bearer ' + token } }
).then(
r => {
if ( !r.ok ) throw new Error( r.statusText )
return r.json()
}
).then(
_ => // Do something with JavaScript Object
).catch(
_ => alert( _.message )
)
}
)
Functions側
( q, s ) => admin.auth().verifyIdToken( q.headers.authorization.split( 'Bearer ' )[ 1 ] ).then(
user => fetch( 'https://example.com/ticker' ).then(
r => {
if ( !r.ok ) throw new Error( r.statusText )
return r.json()
}
).then(
j => send( j )
)
)
まずはWeb からアクセスすると Same Origin Policyに引っ掛かります。
これを回避するためには onRequest の中にCORS (オリジン間リソース共有、 Cross-Origin Resource Sharing)用のコードをインプリします。
functions/index.js
( q, s ) => (
s.set( 'Access-Control-Allow-Origin', '*' )
, q.method === 'OPTIONS'
? ( s.set( 'Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization' )
, s.set( 'Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE' )
, s.status( 200 ).end()
)
: fetch( 'https://example.com/ticker' ).then(
r => {
if ( !r.ok ) throw new Error( r.statusText )
return r.json()
}
).then(
j => send( j )
)
;
)
const admin = require( 'firebase-admin' )
admin.initializeApp()
const { onSchedule } = require( 'firebase-functions/v2/scheduler' )
exports.perDay = onSchedule(
'0 0 * * *'
, _ => fetch( 'https://example.com/metals.html' ).then(
r => {
if ( !r.ok ) throw new Error( r.statusText )
return r.text()
}
).thcn(
html => fs.collection( 'mmc' ).doc( String( Math.floor( Date.now() / ( 1000 * 60 * 60 * 24 ) ) ) ).set(
{ html }
)
)
)
APIがアクセストークンを必要とする場合、ソースコード中にアクセストークンをハードコードすると、漏洩の可能性が出てきます。
例えばソースコードを公開レポジトリにおくと、そこからアクセストークンが漏洩します。
そのような場合は Google Secret Manager を使うとアクセストークンを隠蔽できます。これは Firebase の機能ではありませんが、Firebase は現在 Google に統合されているので、
Firebase からシームレスに SecreteManager を使うことができます。
Thank you for reading this article. I hope this article will be helpful for those who want to start their own membership-based online service using Firebase!