電話SMS認証(上手くいかない)(Firebase Authentication)

Kotlin

Firebase Authentication

無料。(Twilioの場合は1階につき$0.0079ドルかかる)

Firebaseコンソールでプロジェクトを作成し、

「Authentication」>「電話番号」を有効化

「プロジェクトの設定」>「アプリを追加」>「Androidアプリ」

google-services.jsonをAndroidアプリのappフォルダ直下に配置します

app/build.gradle

dependencies {
    implementation 'com.google.firebase:firebase-auth:22.1.2'
    implementation 'com.google.android.gms:play-services-auth:20.7.0'
}

MyApp.kt(MainActivity.ktと同階層)

package com.example.application0326

import android.app.Application
import com.google.firebase.FirebaseApp

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        FirebaseApp.initializeApp(this)
    }
}

AndroidManifest.xml

<application
    android:name=".MyApp"  <!-- ここを追加 -->
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.App">

PhoneActivity.kt

class LoginActivity : AppCompatActivity() {

    private lateinit var auth: FirebaseAuth
    private lateinit var storedVerificationId: String
    private lateinit var resendToken: PhoneAuthProvider.ForceResendingToken

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        auth = FirebaseAuth.getInstance()

        val btnSendCode = findViewById<Button>(R.id.btnSendCode)
        val etPhone = findViewById<EditText>(R.id.etPhone)

        btnSendCode.setOnClickListener {
            val phoneNumber = etPhone.text.toString().trim()
            if (phoneNumber.isNotEmpty()) {
                sendVerificationCode(phoneNumber)
            } else {
                Toast.makeText(this, "電話番号を入力してください", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun sendVerificationCode(phoneNumber: String) {
        val options = PhoneAuthOptions.newBuilder(auth)
            .setPhoneNumber(phoneNumber)
            .setTimeout(60L, TimeUnit.SECONDS)
            .setActivity(this)
            .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                    signInWithPhoneAuthCredential(credential)
                }

                override fun onVerificationFailed(e: FirebaseException) {
                    Toast.makeText(applicationContext, "認証失敗: ${e.message}", Toast.LENGTH_LONG).show()
                }

                override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
                    storedVerificationId = verificationId
                    resendToken = token
                    Toast.makeText(applicationContext, "認証コードが送信されました", Toast.LENGTH_LONG).show()
                }
            })
            .build()

        PhoneAuthProvider.verifyPhoneNumber(options)
    }
}

ユーザーが認証コードを入力してログインします

val btnVerify = findViewById<Button>(R.id.btnVerify)
val etCode = findViewById<EditText>(R.id.etCode)

btnVerify.setOnClickListener {
    val code = etCode.text.toString().trim()
    if (code.isNotEmpty()) {
        val credential = PhoneAuthProvider.getCredential(storedVerificationId, code)
        signInWithPhoneAuthCredential(credential)
    } else {
        Toast.makeText(this, "認証コードを入力してください", Toast.LENGTH_SHORT).show()
    }
}

private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
    auth.signInWithCredential(credential)
        .addOnCompleteListener(this) { task ->
            if (task.isSuccessful) {
                val user = auth.currentUser
                Toast.makeText(this, "ログイン成功: ${user?.phoneNumber}", Toast.LENGTH_LONG).show()

                // WordPressに登録する場合はここでAPIを呼ぶ
                registerUserToWordPress(user?.uid, user?.phoneNumber)
            } else {
                Toast.makeText(this, "ログイン失敗", Toast.LENGTH_SHORT).show()
            }
        }
}

WordPressにFirebaseユーザーを登録します

fun registerUserToWordPress(uid: String?, phone: String?) {
    val url = "https://example.com/wp-admin/admin-ajax.php?action=register_firebase_user"
    val requestBody = "uid=$uid&phone=$phone"

    val request = Request.Builder()
        .url(url)
        .post(requestBody.toRequestBody("application/x-www-form-urlencoded".toMediaType()))
        .build()

    val client = OkHttpClient()
    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            runOnUiThread {
                Toast.makeText(this@LoginActivity, "エラー: ${e.message}", Toast.LENGTH_SHORT).show()
            }
        }

        override fun onResponse(call: Call, response: Response) {
            runOnUiThread {
                Toast.makeText(this@LoginActivity, "WordPressに登録しました", Toast.LENGTH_SHORT).show()
            }
        }
    })
}

Functions.php

function register_firebase_user() {
    global $wpdb;

    $uid = $_POST['uid'];
    $phone = $_POST['phone'];

    if (empty($uid) || empty($phone)) {
        echo json_encode(["status" => "error", "message" => "UIDまたは電話番号が空です"]);
        wp_die();
    }

    // 既存ユーザーをチェック
    if ($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM wp_users WHERE user_login = %s", $phone))) {
        echo json_encode(["status" => "error", "message" => "既に登録されています"]);
        wp_die();
    }

    // WordPressに登録
    $user_id = wp_insert_user([
        'user_login' => $phone,
        'user_pass' => wp_generate_password(),
        'role' => 'subscriber',
        'meta_input' => ['firebase_uid' => $uid]
    ]);

    if (is_wp_error($user_id)) {
        echo json_encode(["status" => "error", "message" => "登録失敗"]);
    } else {
        echo json_encode(["status" => "success", "message" => "登録成功"]);
    }
    wp_die();
}
add_action("wp_ajax_register_firebase_user", "register_firebase_user");
add_action("wp_ajax_nopriv_register_firebase_user", "register_firebase_user");
BACK