ソーシャルログインについて
ソーシャルログインとはユーザーが自分の所有しているSNSアカウントを使って、ログインや新規登録をする機能です。メールアドレスや電話番号などの入力がなくなるので、その利便さがユーザーエクスペリエンスの向上につながります。そのため、最近ソーシャルログインを実装したアプリが増えています。
国内でよく使われているソーシャルログインはLINE、Google、Facebook、Twitterなどあります。

HUAWEI Auth Service
HUAWEI Auth Serviceはファーウェイが提供しているソーシャルログイン機能です。このソーシャルログイン機能はAndoidでもiOSでも実装可能です。また、GMSのAndroid端末でもHMSのAndroid端末でも使用可能です。HUAWEI Auth ServiceがサポートしているSNSアカウントの認証トークンさえあればログインできます。
現在HUAWEI Auth ServiceがサポートしているSNSアカウントは次の表に示します。
SNSアカウント | Android | iOS | Web |
---|---|---|---|
HUAWEI ID | 〇 | ||
HUAWEI GameCenter account | 〇 | ||
WeChat account | 〇 | 〇 | 〇 |
QQ account | 〇 | 〇 | 〇 |
Weibo account | 〇 | 〇 | |
Apple ID | 〇 | ||
Google account | 〇 | 〇 | |
Google Play Games account | 〇 | ||
Facebook account | 〇 | 〇 | |
Twitter account | 〇 | 〇 |
HUAWEI Auth Serviceの実装方法
ソーシャルログインの流れは次のようになります。
- SNSアカウントの認証サーバーから認証トークンを取得する
- 取得した認証トークンをHUAWEI Auth Serviceの認証サーバーに渡し、ログインする
ここで注意しなければならないのは、HUAWEI Auth Serviceは(2)の機能のみを提供しているということです。(1)の機能は含まれていません。
本稿は(1)と(2)の実装方法をすべて紹介します。
開発準備
- AppGallery Connectに入って、[My project]をクリックする
- 左側のメニューから[Auth Service]をクリックし、Auth Serviceを開く
- [Enable now]をクリックし、Auth Serviceを有効にする
[Huawei account]、[Facebook]、[Twitter]、[Google]を有効にする。SNS開発者アカウントのApp IDやApp Keyもここに設定します。
build.gradleにライブラリを追加します。
implementation 'com.huawei.agconnect:agconnect-core:1.4.2.301'
implementation 'com.huawei.agconnect:agconnect-auth:1.4.2.301'
HUAWEI IDのトークン取得の実装方法
implementation 'com.huawei.hms:hwid:5.0.5.301'
HuaweiIdAuthServiceを生成し、Activityを起動します。
val huaweiIdAuthParamsHelper = HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM).apply {
setScopeList(arrayListOf(Scope(CommonConstant.SCOPE.ACCOUNT_BASEPROFILE)))
}
val huaweiIdAuthParams: HuaweiIdAuthParams = huaweiIdAuthParamsHelper.setAccessToken().createParams()
val huaweiIdAuthService: HuaweiIdAuthService = HuaweiIdAuthManager.getService(activity, huaweiIdAuthParams)
startActivityForResult(huaweiIdAuthService.signInIntent, REQUEST_CODE_SIGN_IN_HUAWEI_ID)
HUAWEI IDのアクセストークンはonActivityResultに返ってきます。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...
when (requestCode) {
REQUEST_CODE_SIGN_IN_HUAWEI_ID -> {
val authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data)
if (authHuaweiIdTask.isSuccessful) {
// HUAWEI IDのアクセストークン = authHuaweiIdTask.result.accessToken
val credential = HwIdAuthProvider.credentialWithToken(authHuaweiIdTask.result.accessToken)
}
}
}
}
Facebookのトークン取得の実装方法
Facebookログインのクイックスタートにしたがって、リソースやソースコードを自分のプロジェクトにコピペします。
implementation 'com.facebook.android:facebook-login:8.1.0'
<string name="facebook_app_id">xxxxxxxxxxxxxx</string>
<string name="fb_login_protocol_scheme">xxxxxxxxxxxxxx</string>
<com.facebook.login.widget.LoginButton
android:id="@+id/button_sign_in_facebook"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
override fun onCreate(savedInstanceState: Bundle?) {
...
FacebookSdk.sdkInitialize(applicationContext)
}
private val facebookCallbackManager: CallbackManager = CallbackManager.Factory.create()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
binding.buttonSignInFacebook.registerCallback(
facebookCallbackManager,
object : FacebookCallback<LoginResult?> {
override fun onSuccess(result: LoginResult?) {
result?.let { result ->
// アクセストークン = result.accessToken.token
val credential = FacebookAuthProvider.credentialWithToken(result.accessToken.token)
}
}
override fun onCancel() {
}
override fun onError(error: FacebookException?) {
}
})
...
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...
facebookCallbackManager.onActivityResult(requestCode, resultCode, data)
}
Twitterのトークン取得の実装方法
TwitterのアプリID、アプリキー、アプリキーシークレットはTwitter Developer Portalから見られます。
implementation 'com.twitter.sdk.android:twitter:3.3.0'
<string name="twitter_app_id">xxxxxx</string>
<string name="twitter_app_key">xxxxxxxxxxxxxxxxxxxxxxxx</string>
<string name="twitter_app_key_secret">xxxxxxxxxxxxxxxxxxxxx</string>
<com.twitter.sdk.android.core.identity.TwitterLoginButton
android:id="@+id/button_sign_in_twitter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
override fun onCreate(savedInstanceState: Bundle?) {
...
val twitterAuthConfig = TwitterAuthConfig(
getString(R.string.twitter_app_key),
getString(R.string.twitter_app_key_secret)
)
val twitterConfig: TwitterConfig = TwitterConfig.Builder(applicationContext).twitterAuthConfig(
twitterAuthConfig
).build()
Twitter.initialize(twitterConfig)
}
// Twitterのログインロジックがフラグメントにある場合
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...
supportFragmentManager.findFragmentByTag(MainFragment.TAG)?.let {
it.onActivityResult(requestCode, resultCode, data)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
binding.buttonSignInTwitter.callback = object : com.twitter.sdk.android.core.Callback<TwitterSession>() {
override fun success(result: Result<TwitterSession>?) {
result?.let { result ->
// アクセストークン = result.data.authToken.token
// アクセストークンシークレット = result.data.authToken.secret
val credential = TwitterAuthProvider.credentialWithToken(
result.data.authToken.token,
result.data.authToken.secret
)
}
}
override fun failure(exception: TwitterException?) {
}
}
...
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...
binding.buttonSignInTwitter.onActivityResult(requestCode, resultCode, data)
}
Googleのトークン取得の実装方法
認証情報の作成
Google Developer Consoleで認証情報を作成します。
ここで注意しなければならないことがあります。HMS端末にGMSが入っていないので、Googleが提供しているSDKが使えません。そのため、Google Developer Consoleで認証情報を作成するときに、ウェブアプリケーションを選ばなければなりません。
作成したら、クライアントIDとクライアントシークレットが表示されます。
また、リダイレクトURI(URI)を設定しなければなりません。このURIは公開されている実在のページやAPIじゃなくてもよいです。httpまたはhttpsから始まるURIであればよいです。
GoogleのSDKを使わないので、build.gradleに追加するコードがありません。
認証コードの取得
https://accounts.google.com/o/oauth2/auth でGoogleアカウントの認証コードを取得します。
https://accounts.google.com/o/oauth2/auth はGETメソッドのAPIです。まずURLを作ります。
companion object {
private const val AUTH_LINK = "https://accounts.google.com/o/oauth2/auth"
private const val CLIENT_ID = "your client id"
private const val SCOPE = "https://www.googleapis.com/auth/userinfo.profile"
const val REDIRECT_URI = "https://your.redirecturi.com/auth"
}
private fun mapToString(map: Map<String, String>): String {
var query = ""
var count = 0
map.forEach { (key, value) ->
if (count > 0) {
query += "&"
}
query += "$key=$value"
count++
}
return query
}
fun createAuthLink(): String {
val map: Map<String, String> = hashMapOf(
"client_id" to CLIENT_ID,
"scope" to SCOPE,
"response_type" to "code",
"redirect_uri" to REDIRECT_URI
)
return AUTH_LINK + "?" + mapToString(map)
}
URLを内部WebViewで開きます。
val googleAuth = GoogleAuth()
val link = googleAuth.createAuthLink()
val intent = Intent(context, WebViewActivity::class.java).apply {
putExtra("link", link)
}
startActivity(intent)
内部WebViewで開くので、WebViewのActivityを作ります。
<activity android:name=".WebViewActivity"></activity>
WebViewを作るときのポイントは次の通りです。
- JavaScriptを有効にする
- ユーザーエージェントを通常のブラウザと同じものにする
- shouldOverrideUrlLoadingを必ずオーバーライドする
- shouldOverrideUrlLoadingのリンクがGoogle Developer Consoleに設定したリダイレクトURIと同じであれば、リダイレクトをせずにMainActivityに転送する
- Google Developer Consoleに設定したリダイレクトURIと違えば、WebView内で開く
class WebViewActivity : AppCompatActivity() {
private lateinit var binding: WebViewActivityBinding
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.inflate<WebViewActivityBinding>(
layoutInflater,
R.layout.web_view_activity,
null,
false
)
setContentView(binding.root)
with(binding) {
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.userAgentString = "Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36"
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
request?.url?.let { url ->
if (url.toString().contains(GoogleAuth.REDIRECT_URI)) {
val intent = Intent(applicationContext, MainActivity::class.java).apply {
data = url
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
return true
}
view?.loadUrl(url.toString())
}
return false
}
}
intent?.getStringExtra("link")?.let { url ->
webView.loadUrl(url)
}
}
window.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
}
MainActivityで認証コードを取得します。
val code = intent?.data?.getQueryParameter("code")
認証コードをトークンに変換
https://oauth2.googleapis.com/token で認証コードをトークンに変換します。
companion object {
private const val TOKEN_LINK = "https://oauth2.googleapis.com/token"
private const val CLIENT_ID = "Your client id"
private const val CLIENT_SECRET = "Your client secret"
private const val SCOPE = "https://www.googleapis.com/auth/userinfo.profile"
const val REDIRECT_URI = "https://your.redirecturi.com/auth"
}
fun getToken(context: Context, code: String, listener: Response.Listener<String>, errorListener: Response.ErrorListener) {
val queue = Volley.newRequestQueue(context)
val postRequest: StringRequest = object : StringRequest(
Method.POST,
TOKEN_LINK,
listener,
errorListener) {
override fun getBodyContentType(): String {
return "application/x-www-form-urlencoded"
}
override fun getParams(): Map<String, String> {
val params: MutableMap<String, String> = HashMap()
params["code"] = code
params["client_id"] = CLIENT_ID
params["client_secret"] = CLIENT_SECRET
params["redirect_uri"] = REDIRECT_URI
params["grant_type"] = "authorization_code"
return params
}
}
queue.add(postRequest)
}
private fun signInGoogleCode(code: String) {
val context: Context = context ?: return
val googleAuth = GoogleAuth()
googleAuth.getToken(
context,
code,
object : Response.Listener<String>{
override fun onResponse(response: String?) {
response?.let { response ->
val jsonObject = JSONObject(response)
if (jsonObject.has("id_token")) {
// トークンはhttps://oauth2.googleapis.com/tokenのレスポンスのid_token
val idToken = jsonObject.getString("id_token")
val credential = GoogleAuthProvider.credentialWithToken(idToken)
}
}
}
},
object : Response.ErrorListener{
override fun onErrorResponse(error: VolleyError?) {
}
}
)
}
各トークンによるHuawei Auth Serviceの認証の実装方法
private fun signIn(credential: AGConnectAuthCredential) {
AGConnectAuth.getInstance().signIn(credential).addOnSuccessListener { result ->
// ユーザーオブジェクト
val user = result.user
// ユーザーのユニークなID
val uid = user.uid
// ユーザーのメールアドレス
val email = user.email
// ユーザーの電話番号
val phone = user.phone
// ユーザーの画像
val photo = user.photoUrl
// ユーザー名
val name = user.displayName
}.addOnFailureListener {
}
}
一言
HMS端末のアプリではGoogleログインが使えないと勘違いするかもしれませんが、ちゃんと使えます!
GitHub
https://github.com/Rei2020GitHub/MyPublicProject/tree/master/AuthDemo
参考
- HMS:https://developer.huawei.com/consumer/jp/
- HMS Auth Service:https://developer.huawei.com/consumer/jp/doc/development/AppGallery-connect-Guides/agc-auth-introduction-0000001053732605
- Huawei Developers:https://forums.developer.huawei.com/forumPortal/en/home
- Facebook Huawei Developersグループ:https://www.facebook.com/Huaweidevs/