Edited at

Firebase Authenticationを使ってみる🔥

More than 1 year has passed since last update.

初めまして。普段様々な機会を頂いてAndroid開発をやっている大学院生です。

もう早いもので12月も半分以上が過ぎてしまいましたね。

2016年も様々なことがありましたが、2016/05/19(日本時間)にGoogle I/O 2016にてFirebaseの新バージョンが発表されました。おかげで、Firebaseを使ってできることが更に増え、GoogleのいくつかのサービスがFirebaseに統合されました。

そんな中で、今年Firebaseの一つの機能である認証機能を触る機会がありました。もちろん、公式ドキュメントがあるのですが、公式ドキュメントだけではつまづくポイントがいくつかありましたので(特にTwitterとの連携周りで)、この機会にまとめておこうと思います。

今回認証の連携に用いたプロバイダサービスは、Google、Facebook、Twitterの3つです。これらに加えて、emailとpasswordの入力による認証を実装しました。

それぞれのプロバイダごとに説明していこうと思います。


まず初めに

各プロバイダとの連携を説明する前に、まずはFirebaseをアプリに追加しましょう。

こちらは説明すると言っても、公式ドキュメントをなぞるだけになってしまうので、公式ドキュメントをご覧ください。

いきなり手抜いてすみません。笑


Googleログインを用いて認証する

Firebaseをアプリに追加したら、いよいよプロバイダを用いた認証の実装です!ワクワクですね!!

まず、Firebase consoleでGoogleログインを有効にします。

そして、build.gradleに以下を追加します。

compile 'com.google.firebase:firebase-auth:9.6.1'

compile 'com.google.android.gms:play-services-auth:9.6.1'

GoogleSignInOptionオブジェクトにサインインの設定を追加していきます。

GoogleSignInOptions googleSignInOptions =

new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.web_client_id))
.requestEmail()
.build();

requestIdTokenメソッドを設定するのを忘れないでください!この時の引数の文字列はこちらのページ内のOAuth 2.0 クライアントIDを設定してください。二つクライアントIDがあると思いますが、ウェブアプリケーションタイプのクライアントIDを設定しましょう。

DEFAULT_SIGN_INオプションではログインするアカウントのIDbasic profileのみしか取得できません。適宜オプションを追加してください。今回はメールアドレスも取得したかったので、requestEmail()を設定しています。

次に、GoogleApiClientオブジェクトを生成します。

googleApiClient = new GoogleApiClient.Builder(this)

.enableAutoManage(this/* FragmentActivity*/, this/*OnConnectionFailedListener*/)
.addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
.build();

enableAutoManageメソッドの第一引数はFragmentActivityを継承したActivityを渡してください。AppCompatActivityFragmentActivityのサブクラスですので、多くの場合はAppCompatActivityを渡すことになるかと思います。第二引数には、OnConnectionFailedListenerを渡します。このリスナーは読んで字のごとくGoogleApiClientへの接続に失敗した時にコールバックされます。

GoogleのSignInButtonをレイアウトして、ログイン時の挙動を設定していきます。

SignInButton googleSignInButton = (SignInButton) findViewById(R.id.google_sign_in_button);

googleSignInButton.setSize(SignInButton.SIZE_STANDARD);
googleSignInButton.setOnClickListener(this);

今回はonClickメソッド内から次のsignInWithGoogle()を呼んでいます。

private void signInWithGoogle() {

Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, REQUEST_CODE_GOOGLE_SIGN_IN);
}

そして、OnActivityResultメソッド内でsign inの結果を受け取ります。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == REQUEST_CODE_GOOGLE_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
handleSignInWithGoogleResult(result);
}
}

onActivityResultの引数であるIntentからgetSignInResultFromIntentメソッドを使ってGoogleSignInResultオブジェクトを取得します。このオブジェクトを利用して、認証をハンドリングしていきます。

private void handleSignInWithGoogleResult(GoogleSignInResult result) {

if (result.isSuccess()) {
// succeed sign in
GoogleSignInAccount account = result.getSignInAccount();
firebaseAuthWithGoogle(account);
} else {
// failed sign in
DialogManager.createDialog(MainActivity.this,
CommonStatusCodes.getStatusCodeString(result.getStatus().getStatusCode()))
.show();
}
}

private void firebaseAuthWithGoogle(GoogleSignInAccount account) {
AuthCredential credential = GoogleAuthProvider.getCredential(account.getIdToken(), null);
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this/*Activity*/, this/*OnCompleteListener*/);
}

Googleアカウントへのログインが成功したら、先ほど取得したGoogleSignInResultオブジェクトからIdTokenを取得し、Firebaseの認証情報と交換し、Firebaseの認証情報を使用してFirebaseでの認証を行います。

Firebaseでの認証が完了したら(成功・失敗に関わらず)、addOnCompleteListenerメソッドの第二引数に指定しているOnCompleteListeneronCompleteメソッドがコールバックされます。このメソッド内では、失敗した時の処理だけ書いておけばいいです。今回は、認証の流れの中で何かしらのエラーが発生した場合は、ダイアログを表示しています。

@Override

public void onComplete(@NonNull Task<AuthResult> task) {
if (!task.isSuccessful()) {
DialogManager.createDialog(MainActivity.this, task.getException())
.show();
}
}

では、成功した場合はどうするかというところですが、

@Override

protected void onStart() {
super.onStart();
firebaseAuth.addAuthStateListener(this);
}

@Override
protected void onStop() {
super.onStop();
firebaseAuth.removeAuthStateListener(this);
}

このようにして、ActivityにimplementしたAuthStateListenerを設定しておきます。

@Override

public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User has already signed in
} else {
// User has already signed out
}
}

先ほどのリスナーを設定しておくことで、onAuthStateChangedメソッドがコールバックされます。この中で、Firebaseでの認証が成功した時の処理を書いていきます。

ちなみに、このメソッドは以下のタイミングでUIスレッドで呼ばれます。


  • リスナーがセットされた直後 (Right after the listener has been registered)

  • ユーザがサインインした時 (When a user is signed in)

  • ログイン中のユーザがサインアウトした時(When the current user is signed out)

  • ログインユーザが変更された時 (When the current user changes)

  • ログイン中のユーザのtokenが変わった時 (When there is a change in the current user's token)

以上が、Googleログインを用いたFirebase認証の実装です。


Facebookログインを用いて認証する

お次は、Facebookログインとの連携です!!

Facebook for Developersのクイックスタートに従って、アプリケーションをFacebookに登録します。

登録が済んだら、Googleログインとの連携時と同様にFirebase consoleでFacebookログインを有効にします。この時に、アプリケーションIDアプリシークレットが必要になります。先ほどのFacebook for Developersで登録したアプリケーションのページで取得できます。

また今度は、Firebase consoleで生成されたOAuthリダイレクトURIをFacebook for Developersのアプリケーションページへ設定します。Facebook for Developersのアプリケーションページの左側にある[+製品を追加]からFacebookログインを追加することで、設定ページを開くことができます。

それでは、Facebookログインを用いた認証を実装していきましょう。

まず、Facebook SDKを初期化します。

FacebookSdk.sdkInitialize(getApplicationContext());

そして、FacebookのLoginButtonをレイアウトし、設定していきます。

callbackManager = CallbackManager.Factory.create();

FacebookCallback<LoginResult> facebookLoginCallback = new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
firebaseAuthWithFacebook(loginResult.getAccessToken());
}

@Override
public void onCancel() {
}

@Override
public void onError(FacebookException error) {
if (error != null) {
DialogManager.createDialog(MainActivity.this, error).show();
}
}
};
LoginButton facebookLoginButton = (LoginButton) findViewById(R.id.facebook_login_button);
facebookLoginButton.setReadPermissions("email", "public_profile");
facebookLoginButton.registerCallback(callbackManager, facebookLoginCallback);

setReadPermissionsメソッドで取得したい情報の権限を設定しておきます。registerCallbackメソッドでFacebookへのログイン時のCallbackManagerFacebookLoginCallbackを設定します。Facebookへのログイン結果に応じて各コールバックが呼ばれます。

ログインに成功した場合は、Googleログインの時と同様に、FacebookログインのトークンをFirebaseの認証情報と交換し、Firebaseの認証情報を使用してFirebaseでの認証を行います。

private void firebaseAuthWithFacebook(AccessToken token) {

AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this/*Activity*/, this/*OnCompleteListener*/);
}

Firebaseでの認証が成功した場合には、Googleログイン時と同様にAuthStateListenerの各メソッドがコールバックされます。

また、onActivityResult内で、次のようにCallbackManagerOnAcvityResultに値を渡してあげることも忘れないでください。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

callbackManager.onActivityResult(requestCode, resultCode, data);
}

Googleログインの時と流れはだいたい同じですね!

この勢いでどんどん連携プロバイダを増やしていきます。


Twitterを使用して認証する

今度は色々苦戦したTwitterでの認証です。

まず、Twitter Application Managementにアプリケーションを登録します。

ここで注意して欲しいのが、Firebaseのログイン情報からメールアドレスを取得したい場合には、Android StudioにFabricのプラグインが入ってる人でも、プラグイン上からアプリケーションを登録せずに、Twitter Application Management上でアプリケーションを登録してください。

登録が済んだら、Facebookログインとの連携と同じ流れでFirebase consoleでTwitterを有効にします。この時に、Twitter Application ManagementからAPI KeyAPI Secretを取得して、Firebase consoleに設定します。

また同様に、Firebase consoleで生成されたOAuthリダイレクトURIをTwitter Application Managementのアプリケーションページへ設定してください。

ここまで済んだら、Twitter連携のfirebase認証を実装していきます。

Twitter連携の実装をするにあたり、Android StudioのFabricのプラグインを使用することをお勧めします!これを用いることで、Twitter Loginの実装をすいすいと進めていくことができます。

今回もFabricのプラグインを使用した前提で話を進めていきます。

Fabricのプラグインを起動し、画面に従って登録するアプリケーションを選択していきます。



この画面で[Twitter]を選択し、[Install]をクリックします。



この画面になったら、[I already have a Twitter account]を選択します。決して、[Create Account]を押しちゃダメですよ!!次の画面で、Twitter KeyTwitter Secretを求められるので、先ほどのTwitter Application Managementで取得したApi KeyとApi Secretをそれぞれ入力します。

あとは、画面の指示に従って進んでいくのみです!

ここまできたら、GoogleとTwitterの時と同じ流れです。

TwitterAuthConfig authConfig = new TwitterAuthConfig(getString(R.string.twitter_key),

getString(R.string.twitter_secret));
Fabric.with(this, new Twitter(authConfig));

このコードがFabricプラグインによって自動的に追加されているかと思います。

Callback<TwitterSession> twitterLoginCallback = new Callback<TwitterSession>() {

@Override
public void success(Result<TwitterSession> result) {
TwitterSession session = result.data;
firebaseAuthWithTwitter(session);
}

@Override
public void failure(TwitterException exception) {
if (exception != null) {
DialogManager.createDialog(MainActivity.this, exception).show();
}
}
};
twitterLoginButton = (TwitterLoginButton) findViewById(R.id.twitter_login_button);
twitterLoginButton.setCallback(twitterLoginCallback);

TwitterLoginButtonをレイアウトし、コールバックを設定してあげます。

そして、ログイン結果を次のメソッドでハンドリングしています。

private void firebaseAuthWithTwitter(TwitterSession session) {

AuthCredential credential = TwitterAuthProvider.getCredential(session.getAuthToken().token,
session.getAuthToken().secret);
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this/*Activity*/, this/*OnCompleteListener*/);
}

ここの処理もGoogleとFacebookの時とだいたい同じです。

TwitterもFacebook同様、OnAcvitityResult内でTwitterLoginButtonOnActivityResultに値を渡す必要があります。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

twitterLoginButton.onActivityResult(requestCode, resultCode, data);
}

Firebaseの認証に成功した場合は、この時もAuthStateListenerの各メソッドがコールバックされます。


パスワードベースのアカウントを使用して認証する

最後は、メールアドレスとパスワードをユーザに入力させてFirebase認証を行う場合について説明していきます。

まずは、Firebase consoleで[Email/password]を有効にします。

この場合は、あらかじめ特別なボタン等が用意されていないので、自分でButtonをレイアウトし、ボタンが押された時の処理を適宜ハンドリングしていきます。

今回は、ボタンを押すと別のActivityが生成され、そのActivity内でユーザにメールアドレスとパスワードを入力してもらうという流れにしました。画面としてはこんな感じです。

アプリを始めて使う場合はもちろんアカウントがないので、[sign up]ボタンを選択してもらい、既にアカウントがある場合には[sign in]ボタンを選択してもらいます。

このActivityで入力した情報を最初のActivityのonActivityResultで受け取り、Firebaseにその情報を登録していきます。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == REQUEST_CODE_EMAIL_SIGN_IN && data != null) {
firebaseAuthWithEmailAndPassword(data);
}
}

private void firebaseAuthWithEmailAndPassword(Intent data) {
if (data.getBooleanExtra(FormActivity.KEY_HAS_ACCOUNT, true)) {
firebaseAuth.signInWithEmailAndPassword(data.getStringExtra(FormActivity.KEY_EMAIL),
data.getStringExtra(FormActivity.KEY_PASSWORD))
.addOnCompleteListener(this/*Activity*/, this/*OnCompleteListener*/);
} else {
firebaseAuth.createUserWithEmailAndPassword(data.getStringExtra(FormActivity.KEY_EMAIL),
data.getStringExtra(FormActivity.KEY_PASSWORD))
.addOnCompleteListener(this/*Activity*/, this/*OnCompleteListener*/);
}
}

firebaseAuthWithEmailAndPasswordメソッド内で、[sign in]か[sign up]かを判定して処理を分けています。新しくアカウントを作成する場合は、createUserWithEmailAndPasswordメソッドを、既にあるアカウントにログインする場合には、signInWithEmailAndPasswordメソッドを呼びます。引数にはそれぞれ、メールアドレスとパスワードを渡す必要があります。

この時、メールアドレスのフォーマットを、Firebaseの方で判定をしてくれるため、メールアドレスのフォーマットが普通でない(?)場合には、アカウントの作成が弾かれます。



優しい世界ですね🍵

Firebaseでメールアドレスとパスワードが認証された場合には、例のコールバックが呼ばれます。

この方法は、他のプロバイダを使用した認証よりは敷居が低いような感じがしますが、自分で入力フォーム等を実装して処理を行わないといけないので、また違った面倒がありますね。

以上が各プロバイダやメールアドレス、パスワードを使ってFirebase認証を実装する一通りの流れになります。

どうでしょう!結構簡単ですね!!ありがたみが強いです!!!


ログインユーザの情報を取得する

各プロバイダを用いてFirebase認証する方法が分かったので、最後にユーザの情報を取得する方法をまとめておきます。

現在ログイン中のユーザ情報をFirebaseから取得するのは非常に簡単です。

FirebaseAuthオブジェクトからgetCurrentUserメソッドを使用して、得られたFirebaseUserオブジェクトからメソッドを使用して各情報を取得するだけです。

例えば、現在ログイン中のユーザの[UID]、[Email]、[Provider]情報を取得したい場合は次のようにします。

FirebaseUser user = firebaseAuth.getCurrentUser();

if (user != null) {
user.getUid());
user.getEmail());
if (user.getProviders() != null && !user.getProviders().isEmpty()) {
userInfoBundle.putString(ProfileActivity.KEY_USER_PROVIDER, user.getProviders().get(0));
} else {
userInfoBundle.putString(ProfileActivity.KEY_USER_PROVIDER, getString(R.string.unknown_user_provider));
}
}

すごく簡単ですね!

現在のところ、FirebaseUserオブジェクトから取れるユーザ情報は、


  • display name

  • email

  • providers

  • provider id

  • photo url

  • provider data

  • token

  • uid

です。

しかし、これらのユーザ情報が必ずしも取れるとは限りません。各プロバイダの設定で権限を適切に設定しておく必要があります。

今回は、Twitterからemail情報を取得しようとした時に苦戦してしまったので、その取得方法をここに書いておきます。


Twitterからユーザのメールアドレスを取得する

先ほど説明した方法でTwitterからユーザのメールアドレスを取得しようとしても、取得することができません。そこで、Twitter Application Managementの設定を変更します。

自分の登録したアプリケーションのページを開き、[Permissions]タブを選択します。

ここで、emaiのパーミッションを解放する必要があります。


These additional permissions require that you provide URLs to your application or service's privacy policy and terms of service. You can configure these fields in your Application Settings.


とあるので、プライバシーポリシーと利用規約をApplication Settingsのページで設定しておく必要があります。今回仮で自分のTwitterのURLを登録したのですが、特に問題なかったです。

これらを設定したら、[Request email addresses from users]にチェックをして、パーミッションをアップデートします。

現在、Fabricプラグインから登録したアプリケーションの場合、このメールアドレスの権限を取得するのが難しくなっています。一方で、Twitter Application Managementから登録したアプリケーションであれば、この権限を取得するのが容易です。Fabricプラグインからアプリケーションを登録しないように言ったのはこのためです。

今回Twitterからメールアドレスの権限をリクエストするためにTwitter Supportに問い合わせをしたところ、次のようなメールが自動返信されてきました。

このパーミッションをアップデートすることで、Firebaseにユーザのアドレスが登録されるようになるはずですが、このままでは単にFirebaseUserオブジェクトからユーザのメールアドレスを取得しようとしても、できません。

そこで、アプリケーション側では次の処理を行う必要があります。

TwitterSession session = Twitter.getSessionManager().getActiveSession();

TwitterAuthClient authClient = new TwitterAuthClient();
authClient.requestEmail(session, new Callback<String>() {
@Override
public void success(Result<String> result) {

}

@Override
public void failure(TwitterException exception) {

}
});

正しくリクエストが行えた場合、この画面が出てきます。

リクエストの結果に合わせて各コールバック内で処理を行えばいいです。


最後に

今回はFirebase Authenticationを用いてアプリに認証機能を追加する方法を、つまづいたポイントを踏まえて説明しました。

めちゃくちゃ長くなってしまい、申し訳ないです。

初めてのQiitaパブリック投稿だったので、大目に見ていただけると幸いです🙇

今回作成したサンプルアプリはGitHubで公開していますので、ご参考までにどうぞ。

FirebaseLoginSample

各種keyの設定をお忘れなく。

Firebase認証と連携できるプロバイダはこれらだけでなく、GitHubもあります。カスタム認証を使用することで、LINEと連携させることもできるようです。Google Developers Japanのブログに記事がありますので、こちらをご参考ください。

今回お話したFirebaseの機能は氷山の一角に過ぎないので、Firebaseをもっともっと使い倒していきたいですね!!

それでは、良い年末をお過ごしください!


参考