techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

FirebaseでAuthを用いたパスワード認証を行う

Firebaseではユーザ認証を行うことでデータの安全性を確保することができます。特にRealtime Databaseを利用する際に、データにアクセスしているのが誰なのかを確認することはとても重要です。

Firebaseでは幾つかのユーザ認証方法が用意されていますが、今回はAndroidからパスワード認証を試してみたいと思います。

初めに

Realtime Databaseのコンソールからルールを確認すると、デフォルトでは以下のようになっているかと思います。

{
  "rules": {
    ".write": "auth != null",
    ".read" : "auth != null"
  }
}

ここで使用されている"auth"は、クライアントからRealtime Databaseにアクセスがあった時に自動で生成されるオブジェクトで、この中にアクセスユーザの認証情報が入ります。
この例では、"auth != null"、つまり認証情報がNULLでない場合のみRealtime Databaseに対するWriteとReadの権限が与えられるということになります。このように、ユーザの認証情報を用いてRealtime Databaseに保持されているデータへのアクセスを制限することができます。
ちなみに、authオブジェクトには以下のような情報が入っています。
・auth.provider・・・・使用した認証方法(今回はパスワード認証なので"password"が入るはず)
・auth.uid・・・・アクセスユーザのuid。すべての認証方法に渡ってユニークとなる値。
・auth.token・・・・FirebaseのAuthトークン。中には認証に用いたemailも入っている。詳細はこちら

FirebaseのAuthを利用してログインせずにRealtimeDatabaseにアクセスするとauthオブジェクトがNULLとなってしまうので、そうならないように今回はパスワード認証を用いてログインする方法について調べてみた。

ちなみに、ほとんど公式を見てそのままやっただけです。

事前準備

  1. build.gradle(Module:app)に以下を追記します。
dependencies {
    ・・・略・・・
    compile 'com.google.firebase:firebase-auth:9.4.0'
    ・・・略・・・
}
  1. パスワード認証を有効にするには、Firebase Consoleを開いてAuthセクションからパスワード認証を有効にします。
    f:id:uentseit:20160818234749p:plain

アカウント作成

ユーザが新規にアカウントを作成できるようにします。今回は、サンプルとして新規アカウント作成用の画面(SignUpActivity)を用意しました。
f:id:uentseit:20160818235130p:plain
Activityのソースコードは以下の通りです。

public class SignUpActivity extends AppCompatActivity {

    private String TAG = "SignUpActivity";
    private FirebaseAuth mAuth;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private ActivitySignUpBinding mBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinder = DataBindingUtil.setContentView(this, R.layout.activity_sign_up);
        mBinder.btnSignUp.setOnClickListener(mOnClickListener);

        mAuth = FirebaseAuth.getInstance();   ・・・①
        // リスナの初期化  
        mAuthListener = new FirebaseAuth.AuthStateListener() {    ・・・②
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {   ・・・⑤
                // ログイン状態の変化
                FirebaseUser user = firebaseAuth.getCurrentUser();   ・・・⑥
                if (user != null) {
                    // ログイン状態
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // ログアウト
                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
            }
        };
    }

    View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String email = mBinder.newUserName.getText().toString() == null ? "" : mBinder.newUserName.getText().toString();
            String password = mBinder.newPassword.getText().toString();
            createUserWithEmailAndPassword(email,password);
        }
    };

    private void createUserWithEmailAndPassword(String email, String password) {
        // emailとパスワードでアカウント作成
        mAuth.createUserWithEmailAndPassword(email, password)  ・・・④
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "createUserWithEmail:onComplete:" + task.isSuccessful());

                        if (!task.isSuccessful()) {
                            Log.d("Login#onComplete", task.getException().toString());
                            // アカウント作成あるいはログイン失敗時にはメッセージを表示する
                            Toast.makeText(SignUpActivity.this, R.string.auth_fail,
                                    Toast.LENGTH_SHORT).show();
                        }else{
                            Log.d("onComplete", "アカウント作成及びログインに成功しました。");
                        }
                    }
                });
    }

    @Override
    public void onStart(){
        super.onStart();

        // リスナの追加
        mAuth.addAuthStateListener(mAuthListener);・・・③
    }

    @Override
    public void onStop(){
        super.onStop();
        // リスナの削除
        if(mAuthListener != null) {
            mAuth.removeAuthStateListener(mAuthListener);
        }
    }

まず、SignUpActivity#onCreateでFirebaseAuthオブジェクトを取得します(①)。
次に、同じくSignUpActivity#onCreateでAuthStateListenerをセットします。このリスナは、ユーザのログイン状態の変化を受け取りことができます(②、③)。
あとはSign Upボタンが押されたタイミングで、createUserWithEmailAndPasswordメソッドを用いて新規アカウント作成を行います。引数にはメールアドレスとパスワードを渡します(④)。
新規アカウント作成の結果はonCompleteメソッドで受け取りことができます。この時、新規のメールアドレスやパスワードに以下に示すような問題が発生した場合、エクセプションが発生します。
・パスワードが十分な長さでない・・・ FirebaseAuthWeakPasswordException
・emialアドレスが不正な形式・・・ FirebaseAuthInvalidCredentialsException
・同じemailのアカウントがすでに存在・・・ FirebaseAuthUserCollisionException
エクセプションは、onCompleteメソッド内で以下のように取得できます。

task.getException();

新規アカウント作成に成功すると、そのままログインも行われ、⑤のコールバックメソッドが呼ばれます。⑥はログインに成功したユーザの情報を取得しています。

以上で新規アカウントが作成されるので、コンソールで確認してみます。
f:id:uentseit:20160819001113p:plain
f:id:uentseit:20160819001236p:plain

メルアドとパスワードでログインする

さて、新規アカウントは作成できたので、次はログインしてみます。
ただし、ログインは新規アカウント作成とほとんど同じです。違いはログインボタン押下時に、以下のようなcreateUserWithEmailAndPasswordメソッドではなくsignInWithEmailAndPasswordメソッドを用いる点です。

        // emailとパスワードでログイン
        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "createUserWithEmail:onComplete:" + task.isSuccessful());

                        if (!task.isSuccessful()) {
                            Log.d("Login#onComplete", task.getException().toString());
                            // ログイン失敗
                            Toast.makeText(LoginActivity.this, R.string.auth_fail,
                                    Toast.LENGTH_SHORT).show();
                        }else{
                            Log.d("Login#onComplete", "ログインに成功しました。");
                            Intent i = new Intent(getApplicationContext(), MainActivity.class);
                            startActivity(i);
                        }
                    }
                });
    }

引数は画面からユーザが入力したメールアドレスとパスワードを指定しています。
ログイン結果はonCompleteのコールバックメソッドで受け取ります。以下のような場合、ログインに失敗してエクセプションが発生します。
・ 入力されたemailと一致するアカウントがないか、そのアカウントが使用不可になっている・・・FirebaseAuthInvalidUserException
・ パスワードが間違っている・・・FirebaseAuthInvalidCredentialsException
ログイン処理が完了すると、アカウント作成時と同様、onAuthStateChangedのコールバックメソッドが呼ばれます。

ログインに成功した状態でRealtime Databaseにアクセスすると、authオブジェクトがNULLではなくなるので、以下のルールでもRealtime Databaseからデータが取得できるようになります。
f:id:uentseit:20160819002714p:plain