文章

Naver Login

Naver Login

Naver 集成文档

Naver 为韩国最大搜索引擎公司
https://developers.naver.com/main/

集成文档:https://developers.naver.com/docs/login/android/android.md

My Application 注册

My Application 注册:https://developers.naver.com/apps/#/wizard/register?auth=true
记录:OAUTH_CLIENT_IDOAUTH_CLIENT_SECRETOAUTH_CLIENT_NAME
ygs8g

Gradle引入

1
2
3
4
5
6
7
// groovy
implementation 'com.navercorp.nid:oauth:5.9.0' // jdk 11
implementation 'com.navercorp.nid:oauth-jdk8:5.9.0' // jdk 8

// kts
implementation("com.navercorp.nid:oauth:5.9.0") // jdk 11
implementation("com.navercorp.nid:oauth-jdk8:5.9.0") // jdk 8

Login 代码

initialize

1
2
3
fun init(context: Context) {
    NaverIdLoginSDK.initialize(context, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CLIENT_NAME)
}

Login in

登录分为 2 种方式:

  • NidOAuthLoginButton
  • NaverIdLoginSDK.authenticate()

NidOAuthLoginButton

  1. xml
1
2
3
4
<com.navercorp.nid.oauth.view.NidOAuthLoginButton
      android:id="@+id/buttonOAuthLoginImg"
      android:layout_width="wrap_content"
      android:layout_height="50dp" />
  1. code
1
2
3
binding.buttonOAuthLoginImg.setOAuthLogin(launcher)
// OR
binding.buttonOAuthLoginImg.setOAuthLogin(oauthLoginCallback)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
object NaverHelper {

    private const val TAG = "hacket.naver"

    const val OAUTH_CLIENT_ID = "AEhmbl_uDX5pCOSIIRiq"
    const val OAUTH_CLIENT_SECRET = "VGZ5lyXLUv"
    const val OAUTH_CLIENT_NAME = "KingAssistant"

    fun init(context: Context) {
        NaverIdLoginSDK.showDevelopersLog(BuildConfig.DEBUG)
        NaverIdLoginSDK.initialize(context, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_CLIENT_NAME)
    }

    fun login(
        context: Context,
        onSuccess: (NaverLoginAuthInfo?, NidProfile?) -> Unit = { _, _ -> },
        onFailure: (String?, String?) -> Unit = { _, _ -> }
    ) {
        val oauthLoginCallback = object : OAuthLoginCallback {
            override fun onSuccess() {
                // 네이버 로그인 인증이 성공했을 때 수행할 코드 추가
                getProfile(
                    {
                        val loginAuthInfo = getLoginAuthInfo()
                        onSuccess.invoke(loginAuthInfo, it)
                    }
                ) { err, msg ->
                    onFailure.invoke(err.toString(), msg)
                }
            }

            override fun onFailure(httpStatus: Int, message: String) {
                val errorCode = NaverIdLoginSDK.getLastErrorCode().code
                val errorDescription = NaverIdLoginSDK.getLastErrorDescription()
                Toast.makeText(
                    context,
                    "errorCode:$errorCode, errorDesc:$errorDescription",
                    Toast.LENGTH_SHORT,
                ).show()
                onFailure.invoke(errorCode, errorDescription)
            }

            override fun onError(errorCode: Int, message: String) {
                onFailure(errorCode, message)
            }
        }
        NaverIdLoginSDK.authenticate(context, oauthLoginCallback)
    }

    fun getLoginAuthInfo(): NaverLoginAuthInfo {
        return NaverLoginAuthInfo(
            NaverIdLoginSDK.getAccessToken(),
            NaverIdLoginSDK.getRefreshToken(),
            NaverIdLoginSDK.getExpiresAt(),
            NaverIdLoginSDK.getTokenType(),
            NaverIdLoginSDK.getState(),
        )
    }

    /**
     * Profile API call
     *
     * https://developers.naver.com/docs/login/android/android.md#9--%ED%94%84%EB%A1%9C%ED%95%84-api-%ED%98%B8%EC%B6%9C
     */
    fun getProfile(
        onSuccess: (NidProfile?) -> Unit = {},
        onFailure: (Int, String) -> Unit = { _, _ -> }
    ) {
        NidOAuthLogin().callProfileApi(object : NidProfileCallback<NidProfileResponse> {
            override fun onError(errorCode: Int, message: String) {
                onFailure(errorCode, message)
            }

            override fun onFailure(httpStatus: Int, message: String) {
                onFailure(httpStatus, message)
            }

            override fun onSuccess(result: NidProfileResponse) {
                onSuccess(result.profile)
            }
        })
//  返回的是Map结构
//        NidOAuthLogin().getProfileMap(nidProfileCallback)
    }

    /**
     *  Log out
     *
     *  When the method is called, the token stored on the client is deleted
     *
     *  如果网络不行的只会删除本地的token
     *
     * https://developers.naver.com/docs/login/android/android.md#7--%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83
     */
    fun logout() {
        NaverIdLoginSDK.logout()
    }

    /**
     * Unlink
     *
     * When you unlink, both the tokens stored on the client and the tokens stored on the server will be deleted.
     *
     * 会删除本地和服务器的token
     *
     * https://developers.naver.com/docs/login/android/android.md#8--%EC%97%B0%EB%8F%99-%ED%95%B4%EC%A0%9C
     */
    fun unlink(
        onSuccess: () -> Unit = {},
        onFailure: (Int, String) -> Unit = { _, _ -> }
    ) {
        NidOAuthLogin().callDeleteTokenApi(object : OAuthLoginCallback {
            override fun onSuccess() {
                // 서버에서 토큰 삭제에 성공한 상태입니다.
                onSuccess.invoke()
            }

            override fun onFailure(httpStatus: Int, message: String) {
                // 서버에서 토큰 삭제에 실패했어도 클라이언트에 있는 토큰은 삭제되어 로그아웃된 상태입니다.
                // 클라이언트에 토큰 정보가 없기 때문에 추가로 처리할 수 있는 작업은 없습니다.
                onFailure(httpStatus, message)
                loge("errorCode: ${NaverIdLoginSDK.getLastErrorCode().code}: ${NaverIdLoginSDK.getLastErrorDescription()}")
            }

            override fun onError(errorCode: Int, message: String) {
                // 서버에서 토큰 삭제에 실패했어도 클라이언트에 있는 토큰은 삭제되어 로그아웃된 상태입니다.
                // 클라이언트에 토큰 정보가 없기 때문에 추가로 처리할 수 있는 작업은 없습니다.
                onFailure(errorCode, message)
            }
        })
    }

    fun loge(msg: String, e: Throwable? = null) {
        Log.e(TAG, msg, e)
    }

    fun logi(msg: String) {
        Log.i(TAG, msg)
    }

    fun logd(msg: String) {
        Log.d(TAG, msg)
    }
}

data class NaverLoginAuthInfo(
    val accessToken: String?,
    val refreshToken: String?,
    val expire: Long?,
    val type: String?,
    val state: NidOAuthLoginState?,
)
  • NaverIdLoginSDK.authenticate() 获取到 accessToken
  • 通过 callProfileApi 获取到 NidProfile 的 id,传给中间层登录

Naver SDK 评估

背景

亚洲站点、韩国市场,添加三方登录 Naver

版本分析

新接入的 sdk,没有历史接入版本

影响范围

新功能,功能异常情况下会影响到 Naver 用户的三方登录,需要做 abt 降级

价值评估

提升亚洲站点、韩国市场用户登录转化率

影响评估

App 质量

  • 存在 4 个 bug 相关的 issues

Naver 5.9.0 存在的issues

  • 未发现有 ANR 的 issues

性能

待测试

包大小

  • aar 大小 (com.navercorp.nid:oauth:5.9.0) 226KB

85kt6

  • 文档很久未更新,国际化适配做的不好

兼容性

  • SDK 最低版本兼容到 21,和 xxx app 最低版本一致
  • 其他兼容问题待测试

业务功能影响

新功能,功能异常情况下会影响到 Naver 用户的三方登录,需要做 abt 降级

技术方案评估

6.1 UI、UE

无影响

6.2 接口

无影响

6.3 效果监控

不实施

风险评估

7.1 电子应用市场审核规则风险评估

待评估

7.2 安全评估

  • 获取用户的 email, phone,隐私协议是否需要更新?

7.3 降级方案

隐藏 Naver 登录方式

本文由作者按照 CC BY 4.0 进行授权