Firebase の電話番号の確認を使用して Android で 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)
        }
    }