Android で Firebase 電話番号認証を使用して Firebase 認証を行う

Firebase PNV パブリック プレビューでは、Firebase Authentication はログイン用の Firebase PNV トークンを直接受け入れることはできませんが、Firebase Authentication のカスタム認証機能を使用して、ユーザーが Firebase PNV でログインできるようにすることはできます。

トークン交換エンドポイントを作成する

Firebase でカスタム認証ソリューションを実装するうえで重要な手順は、Firebase PNV トークンを受け取って検証し、Firebase カスタム認証トークンを発行できるエンドポイントを作成することです。アプリケーションは、このカスタム トークンを使用してユーザーをログインさせることができます。

このエンドポイントは任意のプラットフォームでホストできますが、次の例では、Cloud Functions for Firebase を使用してホストされています。

Node.js

import { JwtVerifier } from "aws-jwt-verify";
import { getApp } from "firebase-admin/app";
import { getAuth, UserRecord } from "firebase-admin/auth";
import { onRequest } from "firebase-functions/https";

// Because we're deploying to Cloud Functions for Firebase, admin credentials
// are automatically available.
const app = getApp();
const authAdmin = getAuth(app);

// Find your Firebase project number in the Firebase console.
const FIREBASE_PROJECT_NUMBER = "123456789";

// The issuer and audience claims of the FPNV token are specific to your
// project.
const issuer = `https://fpnv.googleapis.com/projects/${FIREBASE_PROJECT_NUMBER}`;
const audience = `https://fpnv.googleapis.com/projects/${FIREBASE_PROJECT_NUMBER}`;

// The JWKS URL contains the current public signing keys for FPNV tokens.
const jwksUri = "https://fpnv.googleapis.com/v1beta/jwks";

// Configure a JWT verifier to check the following:
// - The token is signed by Google
// - The issuer and audience claims match your project
// - The token has not yet expired (default begavior)
const fpnvVerifier = JwtVerifier.create({ issuer, audience, jwksUri });

// This Cloud Function is your token exchange endpoint. You pass the endpoint an
// FPNV token, and the Cloud Function verifies it and exchanges it for a
// Firebase Auth token corresponding to the same user.
export const signInWithFpnv = onRequest(async (req, res) => {
    // Get the FPNV token from the request body.
    const fpnvToken = req.body?;
    if (!fpnvToken) {
        res.sendStatus(400);
        return;
    }

    let verifiedPhoneNumber;
    try {
        // Attempt to verify the token using the verifier configured above.
        const verifiedPayload = await fpnvVerifier.verify(fpnvToken);

        // If verification succeeds, the subject claim of the token contains the
        // verified phone number.
        verifiedPhoneNumber = verifiedPayload.sub;
    } catch {
        // If verification fails, reject the token.
        res.sendStatus(403);
        return;
    }

    // Now that you have a verified phone number, look it up in your Firebase
    // project's user database.
    let user: UserRecord;
    try {
        // If a user account already exists with the phone number, retrieve it.
        user = await authAdmin.getUserByPhoneNumber(verifiedPhoneNumber);
    } catch {
        // Otherwise, create a new user account using the phone number.
        user = await authAdmin.createUser({phoneNumber: verifiedPhoneNumber});
    }

    // Finally, mint a Firebase custom auth token containing the UID of the user
    // you looked up or created. Return this token to the caller.
    const authToken = await authAdmin.createCustomToken(user.uid);
    res.status(200).send(authToken);
    return;
});

カスタム認証トークンでログインする

エンドポイントをデプロイしたら、次の手順に沿ってユーザーを Firebase にログインさせます。

  1. Firebase Phone Number Verification の利用を開始するページに記載されているフローを使用して Firebase PNV トークンを取得します。

  2. このトークンを Cloud Functions エンドポイントに渡します。エンドポイントは、アプリにカスタム認証トークンを返します。このトークンは signInWithCustomToken() に渡すことができます。

    たとえば、Retrofit を使用して、Firebase の signin メソッドの 1 つと同様のインターフェースを持つメソッド signInWithFpnvToken() を記述できます。

    Kotlin

    class FpnvSigninExample {
        interface FPNVTokenExchangeService {
            @POST("signInWithFpnv")
            suspend fun signInWithFpnv(@Body fpnvToken: String): String
        }
    
        val retrofit = Retrofit.Builder()
            .baseUrl("https://example-project.cloudfunctions.net/")
            .build()
        val service: FPNVTokenExchangeService = retrofit.create(FPNVTokenExchangeService::class.java)
    
        suspend fun signInWithFpnvToken(fpnvToken: String): Task<AuthResult?> = coroutineScope {
            val authToken = service.signInWithFpnv(fpnvToken)
            return@coroutineScope Firebase.auth.signInWithCustomToken(authToken)
        }
    }