페이지 이동경로
  • Docs>
  • Kakao Login>
  • Android

Kakao Login

Android

This document describes how to integrate Kakao Login APIs into your service with the Kakao SDK for Android.

To learn about each API in detail, refer to Concepts.

For a Kakao Login button, you can download the resources provided by Kakao or customize buttons according to your service user interface by referring to the Design Guide.

Before you begin

Before using Kakao Login APIs with the Android SDK,

  1. Register the Android platform.
  2. Add modules.
  3. Set Redirect URI.

Add modules

To use the Kakao Login feature, you need to add the v2-user module for Kakao Login and user management in the module-level build.gradle file by referring to Add modules.

IMPORTANT: Changes on Kakao Login module

- Less than Android 2.4.0: You should call 'AuthApiClient' in the v2-auth module and 'UserApiClient' in the v2-user module to use the Kakao Login APIs. - Since Android 2.4.0 or higher: You can call 'UserApiClient' in the v2-user module only to use the Kakao Login APIs. However, to call the Checking token presence API related to user authentication, call 'AuthApiClient'. The code snippets have been updated along with this change.

Set Redirect URI

To implement 'Kakao Login' feature, your app must be able to receive the authorization code through redirection. To make this state, add AuthCodeHandlerActivity in AndroidManifest.xml and specify redirect_uri by adding host and scheme data in <intent-filter>.

<activity android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
          android:exported="true">
          
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- Redirect URI: "kakao${YOUR_NATIVE_APP_KEY}://oauth" -->
        <data android:scheme="kakao${YOUR_NATIVE_APP_KEY}" android:host="oauth" />
    </intent-filter>
</activity>

When the authorization server sends an authorization code to the specified redirect_uri in kakao${YOUR_NATIVE_APP_KEY}://oauth?code=${AUTHORIZATION_CODE} format, the SDK receives the authorization code and requests an access token with the passed authorization code. Refer to Concepts to see the login process.

If your app is targeting Android 12 or higher, you must declare android:exported and set it to true. Refer to Behavior changes: Apps targeting Android 12.

Login

This API enables users to log in through Kakao Talk or their Kakao Account information.

If Kakao Talk is not installed or a browser is not available on a user's device, the ClientErrorCause.NotSupported error occurs. To see more about the error cases that you may encounter during the login process, refer to Troubleshooting > Android. You can also refer to Kakao Login Sample.

Login with Kakao Talk

For 'Login with Kakao Talk', call the loginWithKakaoTalk() method that runs Kakao Talk and prompts the Consent screen that asks consent.

Parameter
Name Type Description Required
context Context Current activity context to invoke Kakao Talk login. O
nonce String Parameter to strengthen security.
To prevent replay attacks, generate random strings and pass them as an argument.

IMPORTANT: Allowed to set only when you integrate Kakao Login with OpenID Connect.
X
Sample
Kotlin
RxKotlin
// Login with Kakao Talk
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
    if (error != null) {
        Log.e(TAG, "Login failed.", error)
    }
    else if (token != null) {
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }
}
var disposables = CompositeDisposable()

// Login with Kakao Talk
UserApiClient.rx.loginWithKakaoTalk(context)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ token ->
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }, { error ->
        Log.e(TAG, "Login failed.", error)
    })
    .addTo(disposables)

When a user consents, Kakao identifies the user with the user's Kakao Account information linked to Kakao Talk, and then issues tokens. The Android SDK provides the issued tokens through the OAuthToken object.

OAuthToken

The issued tokens are stored through the TokenManagerProvider class. The stored tokens are automatically added to the authorization header when calling the token-based APIs.

Name Type Description Required
accessToken String One of the tokens that authorizes you to call Kakao APIs and identifies users. O
accessTokenExpiresAt Date Validity period in seconds until the access token expires. O
refreshToken String One of the tokens that is used to gain new tokens. O
refreshTokenExpiresAt Date Validity period in seconds until the refresh token expires. O
scopes List<String> List of scopes of user information to be retrieved with the issued access token. X
idToken String JSON Web Token that contain user's authentication information, encoded using Base64 algorithm.
For more details, refer to ID Token.

IMPORTANT: Only returned when you integrate Kakao Login with OpenID Connect.
If you call the Requesting additional consent API, only returned when openid is added to scope in the request.
X

Login with Kakao Account

For 'Login with Kakao Account', you must set Redirect URI in advance and call the loginWithKakaoAccount() method that opens a default web browser and prompts the Consent screen that asks consent.

Parameter
Name Type Description Required
context Context Current activity context to open CustomTabs. O
prompts List<Prompt> Used to request reauthentication by presenting the Kakao Account Login screen to a user even though the default web browser already retains cookies about the user's login information through Kakao Account. X
nonce String Parameter to strengthen security.
To prevent replay attacks, generate random strings and pass them as an argument.

IMPORTANT: Allowed to set only when you integrate Kakao Login with OpenID Connect.
X
Sample
Kotlin
RxKotlin
//  Login with Kakao Account
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
    if (error != null) {
        Log.e(TAG, "Login failed.", error)
    }
    else if (token != null) {
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }
}
var disposables = CompositeDisposable()

//  Login with Kakao Account
UserApiClient.rx.loginWithKakaoAccount(context)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ token ->
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }, { error ->
        Log.e(TAG, "Login failed.", error)
    })
    .addTo(disposables)

When a user consents, Kakao identifies the user through the user's Kakao Account cookie stored on the default web browser and then issues tokens through the OAuthToken class.

Advanced: Request reauthentication

You can request reauthentication regardless of a user's login status to enhance security. Set prompts to Prompt.LOGIN, and pass it along with context. Then, the login screen is prompted even though a user has already been logged in on the same web browser on the device.

Sample
Kotlin
RxKotlin
UserApiClient.instance.loginWithKakaoAccount(context, prompts = listOf(Prompt.LOGIN)) { token, error ->
    if (error != null) {
        Log.e(TAG, "Login failed.", error)
    }
    else if (token != null) {
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }
}
var disposables = CompositeDisposable()

UserApiClient.rx.loginWithKakaoAccount(context, prompts = listOf(Prompt.LOGIN))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ token ->
        Log.i(TAG, "Login succeeded.${token.accessToken}")
    }, { error ->
        Log.e(TAG, "Login failed.", error)
    })
    .addTo(disposables)

Kakao Login Sample

You can implement Kakao Login by combining 'Kakao Login with Kakao Talk' and 'Kakao Login with Kakao Account' together, as exemplified below. You can also use the isKakaoTalkLoginAvailable() method to check if Kakao Talk has been installed on a user's device so that the user can log in with Kakao Talk. The code snippet below shows the logic of Kakao Login depending on whether Kakao Talk is installed and a user's action.

After a user initially installs Kakao Talk and does not complete app permission for Kakao Talk, the user can cancel to log in with Kakao Talk. In this case, ClientErrorCause.Cancelled error occurs. Thus, you need to implement the subsequent process after the user cancels the login, such as redirecting the user to the main page or going back to the previous page by referring to this code snippet.

Sample
Kotlin
RxKotlin
// Login common callback 
val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
    if (error != null) {
        Log.e(TAG, "Login failed.", error)
    } else if (token != null) {
        Log.i(TAG, "Login succeeded. ${token.accessToken}")
    }
}

// If Kakao Talk is installed on user's device, proceed to log in with Kakao Talk. Otherwise, implement to log in with Kakao Account.
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
    UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
        if (error != null) {
            Log.e(TAG, "Login failed.", error)

            // After installing Kakao Talk, if a user does not complete app permission and cancels Login with Kakao Talk, skip to log in with Kakao Account, considering that the user does not want to log in. 
            // You could implement other actions such as going back to the previous page.
            if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
                return@loginWithKakaoTalk
            }

            // If a user is not logged into Kakao Talk after installing Kakao Talk and allowing app permission, make the user log in with Kakao Account.
            UserApiClient.instance.loginWithKakaoAccount(context, callback = callback)
        } else if (token != null) {
            Log.i(TAG, "Login succeeded. ${token.accessToken}")
        }
    }
} else {
    UserApiClient.instance.loginWithKakaoAccount(context, callback = callback)
}
var disposables = CompositeDisposable()

// If Kakao Talk has been installed, log in with Kakao Talk. Otherwise, log in with Kakao Account.
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
    UserApiClient.rx.loginWithKakaoTalk(context)
        .observeOn(AndroidSchedulers.mainThread())
        .onErrorResumeNext { error ->
            // After installing Kakao Talk, if a user does not complete app permission and cancels Login with Kakao Talk, skip to log in with Kakao Account, considering that the user does not want to log in.
            // You could implement other actions such as going back to the previous page.
            if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
                Single.error(error)
            } else {
                // If a user is not logged into Kakao Talk after installing Kakao Talk and allowing app permission, make the user log in with Kakao Account.
                UserApiClient.rx.loginWithKakaoAccount(context)
            }
        }.observeOn(AndroidSchedulers.mainThread())
        .subscribe({ token ->
            Log.i(TAG, "Login succeeded. ${token.accessToken}")
        }, { error ->
            Log.e(TAG, "Login failed.", error)
        }).addTo(disposables)
} else {
    UserApiClient.rx.loginWithKakaoAccount(context)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe({ token ->
            Log.i(TAG, "Login succeeded. ${token.accessToken}")
        }, { error ->
            Log.e(TAG, "Login failed.", error)
        }).addTo(disposables)
}

Check token presence

To check if a user has obtained an access token through Kakao Login, call the hasToken() method in AuthApiClient. This API returns the presence of an access token or refresh token as a boolean type. However, note that the return value true does not guarantee that the user is in a logged-in state.

Sample
Kotlin
RxKotlin
if (AuthApiClient.instance.hasToken()) {
    UserApiClient.instance.accessTokenInfo { _, error ->
        if (error != null) {
            if (error is KakaoSdkError && error.isInvalidTokenError() == true) {
                //Login is required.
            }
            else {
                //Handle other errors.
            }
        }
        else {
            //Succeeded in validating token (Token is refreshed if needed).
        }
    }
}
else {
    //Login is required.
}
var disposables = CompositeDisposable()

if (AuthApiClient.instance.hasToken()) {
    UserApiClient.rx.accessTokenInfo()
            .subscribe({ tokenInfo ->
                //Succeeded in validating token (Token is refreshed if needed).

            }, { error ->
                if (error != null) {
                    if (error is KakaoSdkError && error.isInvalidTokenError() == true) {
                        //Login is required.
                    }
                    else {
                        //Handle other errors.
                    }
                }
                else {
                    //Succeeded in validating token (Token is refreshed if needed).
                }

            })
            .addTo(disposables)
}
else {
    //Login is required.
}

If the value false is returned, it means that tokens do not exist. In this case, implement a process for a user to log in to issue tokens. On the other hand, if the return value is true, you can validate the access token that the user has through the accessTokenInfo() method in UserApiClient, and then proceed as follows depending on the request result:

  • If the request is successful, the access token information is returned,
    • Access token is valid, which means the user does not need to log in.
    • You can call the Kakao APIs with the access token.
  • If an error occurs,
    • Access and refresh tokens are invalid, which means the user needs to log in.
    • You need to handle errors by referring to API Reference.

Logout

The Logout API deletes the access and refresh token issued through the login process to have a user log out. Call the logout() method in the UserApiClient class.

Regardless of the result of the logout request, the Android SDK deletes the access and refresh tokens and has the login session end.

Sample
Kotlin
RxKotlin
// Logout
UserApiClient.instance.logout { error ->
    if (error != null) {
        Log.e(TAG, "Logout fail. Tokens are deleted from SDK", error)
    }
    else {
        Log.i(TAG, "Logout success. Tokens are deleted from SDK")
    }
}
var disposables = CompositeDisposable()

// Logout
UserApiClient.rx.logout()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
        Log.i(TAG, "Logout success. Tokens are deleted from SDK.")
    }, { error ->
        Log.e(TAG, "Logout fail. Tokens are deleted from SDK.", error)
    }).addTo(disposables)

Unlink

The Unlink API unlinks a user's Kakao Account from a service app. Call the unlink() method in the UserApiClient class.

If the request is successful, the Android SDK deletes the access and refresh tokens. As the issued tokens are deleted, the session between an app and a user is disconnected, and the user is logged out and unlinked from your app.

Sample
Kotlin
RxKotlin
// Unlink
UserApiClient.instance.unlink { error ->
    if (error != null) {
        Log.e(TAG, "Unlink fail", error)
    }
    else {
        Log.i(TAG, "Unlink success. Tokens are deleted from SDK.")
    }
}
var disposables = CompositeDisposable()

// Unlink
UserApiClient.rx.unlink()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
        Log.i(TAG, "Unlink success. Tokens are deleted from SDK.")
    }, { error ->
        Log.e(TAG, "Unlink fail", error)
    }).addTo(disposables)

Retrieve token information

You can simply check the validity of the access token and refresh token, instead of requesting user information. Call the accessTokenInfo() method in the UserApiClient.

Parameter
Name Type Description Required
tokenInfo (AccessTokenInfo?, Throwable?) -> Unit If the request is successful, the token information stored in the current cache is returned through the AccessTokenInfo object.
If failed, error is returned.
O
Sample
Kotlin
RxKotlin
// Retrieving token information
UserApiClient.instance.accessTokenInfo { tokenInfo, error ->
    if (error != null) {
        Log.e(TAG, "Failed to retrieve token information.", error)
    }
    else if (tokenInfo != null) {
        Log.i(TAG, "Retrieving token information success" +
                "\nService user ID: ${tokenInfo.id}" +
                "\nValidity period: ${tokenInfo.expiresIn} seconds")
    }
}
var disposables = CompositeDisposable()

// Retrieving token information
UserApiClient.rx.accessTokenInfo()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ tokenInfo ->
        Log.i(TAG, "Retrieving token information success" +
                "\nService user ID: ${tokenInfo.id}" +
                "\nValidity period: ${tokenInfo.expiresIn} seconds")
    }, { error ->
        Log.e(TAG, "Failed to retrieve token information.", error)
    })
    .addTo(disposables)
Return data
AccessTokenInfo
Name Type Description Required
id Long Service user ID. X
appId Int App ID that the access token has been issued for. O
expiresIn Long Validity period in seconds until the access token expires.

NOTE: expiresInMillis has been deprecated and replaced with expiresIn.
O

Retrieve user information Consent required

IMPORTANT

To retrieve user data, you must set consent items and obtain user's consent for the data that your service needs. If a user does not consent, you cannot get the user data. To check which scopes a user has already agreed, you can call the Retrieving consent details API and check the agreed scopes first.

To retrieve the information of the user who is currently logged in, call the me() method in the UserApiClient class.

Sample

Here is an example of retrieving user information — Service user ID, email, nickname and profile thumbnail image URI.

Kotlin
RxKotlin
// Retrieving user information (Default) 
UserApiClient.instance.me { user, error ->
    if (error != null) {
        Log.e(TAG, "Retrieving user information fails", error)
    }
    else if (user != null) {
        Log.i(TAG, "Retrieving user information succeeds" +
                "\nService user ID: ${user.id}" +
                "\nEmail: ${user.kakaoAccount?.email}" +
                "\nNickname: ${user.kakaoAccount?.profile?.nickname}" +
                "\nProfile Thumbnail Image URI: ${user.kakaoAccount?.profile?.thumbnailImageUrl}")
    }
}
var disposables = CompositeDisposable()

// Retrieving user information (Default) 
UserApiClient.rx.me()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ user ->
        Log.i(TAG, "Retrieving user information succeeds" +
                "\nService user ID: ${user.id}" +
                "\nEmail: ${user.kakaoAccount?.email}" +
                "\nNickname: ${user.kakaoAccount?.profile?.nickname}" +
                "\nProfile Thumbnail Image URI: ${user.kakaoAccount?.profile?.thumbnailImageUrl}")
    }, { error ->
        Log.e(TAG, "Retrieving user information fails", error)
    })
    .addTo(disposables)

The user information in response to this request is passed through the User class defined in the com.kakao.sdk.user.model package. For example, you can access user.id to retrieve Service user ID, user.kakaoAccount.profile for Kakao Account's profile information, user.kakaoAccount.email for email.

User
Name Type Description Required
id Long Service user ID. O
hasSignedUp Bool Only returned when the Auto-link setting is disabled.
Whether the user is completely linked with (signed up) your app.
false: Preregistered
true: Registered
X
connectedAt Date The time when the user is linked with a service in UTC*. X
synchedAt Date The time when the user is logged in through Kakao Sync Simple Signup in UTC*.
This value is only passed for Kakao Sync service users. In this case, its value is the same as connected_at.
X
properties Map<String, String> Additional user information saved on the Kakao platform to use it later.
Refer to Prerequisites > Manage user properties.
X
kakaoAccount Account Kakao Account information.
To see what user data you can get, refer to User information included in Kakao Account.
X

Ensure that you need to handle exceptions for the following cases in which you cannot get the user information:

  • If you do not enable the consent item for some user information.
  • If a user has not agreed to provide the user information to the third-party service.
  • If a user has not provided the user information to Kakao.

Retrieve shipping address Consent required

This API enables you to retrieve shipping addresses saved in user's Kakao Account.

To retrieve shipping address,

  • You must enable the 'Shipping information (shipping_address)' scope.
  • Users must consent to the scope. If a user does not consent, you cannot get the user data even though you enabled the scope. If Kakao does not retain a user's shipping address, Kakao cannot provide the information to your service even when a user consents.

The shipping_address scope is inactivated by default. To set the scope to 'Optional consent', connect your app to a Biz Channel. If your service must get users' shipping addresses, set the scope to 'Required consent' by going through the Review for Provision of Personal Information provided by Kakao Sync and use the Provision after collecting information option.

To retrieve a user's shipping addresses, call the shippingAddresses() method defined in the UserApiClient class. You can also pass the following parameters.

Parameter
Name Type Description Required
fromUpdatedAt Date If multiple shipping addresses are returned through multiple pages, only the shipping addresses that are changed after updated_at time return.
The last shipping address on the previous page is used for an input value for the next page.
If set to 0, shipping addresses are retrieved from the beginning.
X
pageSize Int Number of (two or more) shipping addresses displayed on a page.
(Default: 10)
X
Sample
Kotlin
RxKotlin
// Retrieve shipping address with requesting additional consent
UserApiClient.instance.shippingAddresses { userShippingAddresses, error ->
    if (error != null) {
        Log.e(TAG, "Failed to retrieve shipping address", error)
    }
    else if (userShippingAddresses != null) {
        if (userShippingAddresses.shippingAddresses != null) {
            Log.i(TAG, "Succeeded in retrieving shipping addresses" +
                    "\nService user ID: ${userShippingAddresses.userId}" +
                    "\nShipping addresses: \n${userShippingAddresses.shippingAddresses?.joinToString("\n")}")
        }
        else if (userShippingAddresses.needsAgreement == false) {
            Log.e(TAG, "User account does not have a shipping address. If required, select 'Provision after collecting information' option in [My Application] > [Kakao Login] > [Consent items].")
        }
        else if (userShippingAddresses.needsAgreement == true) {
            Log.d(TAG, "You must obtain consent to providing shipping address from a user.")

            val scopes = listOf("shipping_address")

            // Request consent to providing shipping address 
            UserApiClient.instance.loginWithNewScopes(context, scopes) { token, error ->
                if (error != null) {
                    Log.e(TAG, "Failed to get consent to providing shipping address", error)
                }
                else if (token != null) {
                    Log.d(TAG, "allowed scopes: ${token.scopes}")

                    // Retry the request retrieving shipping address
                    UserApiClient.instance.shippingAddresses { userShippingAddresses, error ->
                        if (error != null) {
                            Log.e(TAG, "Failed to retrieve shipping address", error)
                        }
                        else if (userShippingAddresses != null) {
                            Log.i(TAG, "Succeeded in retrieving shipping addresses" +
                                    "\nService user ID: ${userShippingAddresses.userId}" +
                                    "\nShipping addresses: \n${userShippingAddresses.shippingAddresses?.joinToString("\n")}")
                        }
                    }
                }
            }
        }
    }
}
var disposables = CompositeDisposable()

// Retrieve shipping address with requesting additional consent
UserApiClient.rx.shippingAddresses()
    .flatMap { userShippingAddresses ->
        if (userShippingAddresses.needsAgreement == true) {
            Log.d(TAG, "You must obtain consent to providing shipping address from a user.")

            // If InsufficientScope error occurs
            Single.error(ApiError.fromScopes(listOf("shipping_address")))
        }
        else {
            Single.just(userShippingAddresses)
        }
    }
    .retryWhen(
        //  Retry the request the Retrieving shipping address API after requesting additional consent to resolve the InsufficientScope error.
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ userShippingAddresses ->
        if (userShippingAddresses.shippingAddresses != null) {
            Log.i(TAG, "Succeeded in retrieving shipping addresses" +
                    "\nService user ID: ${userShippingAddresses.userId}" +
                    "\nShipping addresses: \n${userShippingAddresses.shippingAddresses?.joinToString("\n")}")
        } else {
            Log.e(TAG, "User account does not have a shipping address. If required, select 'Provision after collecting information' option in [My Application] > [Kakao Login] > [Consent items].")
        }
    }, { error ->
        Log.e(TAG, "Failed to retrieve shipping address", error)
    })
    .addTo(disposables)
Return data

shippingAddresses() returns the UserShippingAddresses object.

UserShippingAddresses
Name Type Description Required
userId Long Service user ID of a user who requested shipping addresses. X
shippingAddresses List<ShippingAddress> List of shipping addresses that the user added.
The default shipping address is displayed on the uppermost, and the rest addresses are sorted by last updated date in decending order.
X
needsAgreement Boolean Whether consent to shipping addresses is required. O
ShippingAddress
Name Type Description Required
id Long Shipping address ID. O
name String Shipping address name. X
isDefault Boolean Whether the shipping address is a default address or not. O
updatedAt Date The time when a user updated the shipping address. X
type ShippingAddressType Shipping address type.
- OLD: Administrative address
- NEW : Road name address.
X
baseAddress String Base address that is automatically input when searching for a zipcode. X
detailAddress String Detailed address that a user adds to the base address. X
receiverName String Recipient name. X
receiverPhoneNumber1 String Recipient phone number. X
receiverPhoneNumber2 String Additional recipient phone number. X
zoneNumber String New type of 5-digit postal code for a road name address system.
Required for the road name address system.
X
zipCode String Old type of 6-digit postal code for an administrative address system.
Optional for the administrative address system since some old type of addresses may not have a zip code.
X

Store user information

You can store or update additional user information into the user property keys that you designated in [My Application] > [User Properties].

Call the updateProfile() method in the UserApiClient class. You must pass the custom property keys and values that you want to upadate through properties in a key-value pair using mapOf. For example, if you want to update a user's clothing size, set properties to mapOf("clothing_size" to "small").

Parameter
Name Type Description Required
properties Map<String, String> User information to be updated.
Check the property keys of user information that you want to update in [My Application] > [User Properties] by referring to Manage user properties.
You can also add new property keys up to five.
O
Sample
Kotlin
RxKotlin
// Storing user information

// Properties to be updated
val properties = mapOf("${CUSTOM_PROPERTY_KEY}" to "${CUSTOM_PROPERTY_VALUE}")

UserApiClient.instance.updateProfile(properties) { error ->
    if (error != null) {
        Log.e(TAG, "Storing user information fails", error)
    }
    else {
        Log.i(TAG, "Storing user information succeeds")
    }
}
var disposables = CompositeDisposable()

// Storing user information

// Properties to be updated
val properties = mapOf("${CUSTOM_PROPERTY_KEY}" to "${CUSTOM_PROPERTY_VALUE}")

UserApiClient.rx.updateProfile(properties)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({
        Log.i(TAG, "Storing user information succeeds")
    }, { error ->
        Log.e(TAG, "Storing user information fails", error)
    })

Request additional consent Consent required

You can request consent to access permission or specific personal information if the user has not agreed when the user logged in with Kakao. Before using this API, read Concepts > Request additional consent thoroughly for a better understanding.

To use this API,

  1. Enable the consent items for the scopes you want to obtain additional consent.
  2. Call the loginWithNewScopes() method in the UserApiClient class. Pass the scopes of user information you want to request additional consent as a list of strings.

loginWithNewScopes() presents the Consent screen asking for consent to the requested scope. When a user chooses to provide the scope and selects [Accept and Continue] on the Consent screen, new tokens are issued, and the scope information is updated in the OAuthToken class.

Parameter
Name Type Description Required
context Context Current activity context to open CustomTabs. O
scopes List<String> Pass the scope IDs of the User information. You can figure out each scope's ID in [My Application] > [Kakao Login] > [Consent Items].

IMPORTANT: If you implement OpenID Connect (OIDC), you must add openid to scopes along with the scope values you want to obtain consent. If not, OAuth is applied even though OIDC is enabled.
O
nonce String Parameter to strengthen security.
To prevent replay attacks, generate random strings and pass them as an argument.

IMPORTANT: Allowed to set only when you integrate Kakao Login with OpenID Connect.
X
Sample

Here is an example of checking which scopes are required to get consent with the me() method and requesting additional consent by passing scopes when calling loginWithNewScopes.

Kotlin
RxKotlin
// Retrieving user information (additional consent)

// You can request additional consent when the user attempts to use a service 
// that requires specific user information and then obtain the user information as follows. 

//  * CAUTION: Consider the case that users refuse to consent to 'Optional consent' item 
// to prevent a problem with the use of the service. 

// Example that checks available scopes and requests consent to all available scopes.
UserApiClient.instance.me { user, error ->
    if (error != null) {
        Log.e(TAG, "Failed to retrieve user information.", error)
    }
    else if (user != null) {
        var scopes = mutableListOf<String>()

        if (user.kakaoAccount?.emailNeedsAgreement == true) { scopes.add("account_email") }
        if (user.kakaoAccount?.birthdayNeedsAgreement == true) { scopes.add("birthday") }
        if (user.kakaoAccount?.birthyearNeedsAgreement == true) { scopes.add("birthyear") }
        if (user.kakaoAccount?.genderNeedsAgreement == true) { scopes.add("gender") }
        if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) { scopes.add("phone_number") }
        if (user.kakaoAccount?.profileNeedsAgreement == true) { scopes.add("profile") }
        if (user.kakaoAccount?.ageRangeNeedsAgreement == true) { scopes.add("age_range") }
        if (user.kakaoAccount?.ciNeedsAgreement == true) { scopes.add("account_ci") }

        if (scopes.count() > 0) {
            Log.d(TAG, "Need to obtain consent from user.")

            // If OpenID Connect (OIDC) is enabled,
            // - When "openid" is added to `scopes`, OIDC is applied.
            // - When "openid" is not added to `scopes`, OAuth 2.0 is applied. 
                                                
            // To use OIDC, add "openid" to `scopes`.
            // scopes.add("openid")

            UserApiClient.instance.loginWithNewScopes(context, scopes) { token, error ->
                if (error != null) {
                    Log.e(TAG, "Failed to obtain additional consent.", error)
                } else {
                    Log.d(TAG, "allowed scopes: ${token!!.scopes}")

                    // Re-request user information
                    UserApiClient.instance.me { user, error ->
                        if (error != null) {
                            Log.e(TAG, "Failed to retrieve user information.", error)
                        }
                        else if (user != null) {
                            Log.i(TAG, "Succeeded in retrieving user information.")
                        }
                    }
                }
            }
        }
    }
}
var disposables = CompositeDisposable()

// Retrieving user information (additional consent)

// You can request additional consent when the user attempts to use a service 
// that requires specific user information and then obtain the user information as follows. 


//  * CAUTION: Consider the case that users refuse to consent to 'Optional consent' item 
// to prevent a problem with the use of the service. 

// Example that checks available scopes and requests consent to all available scopes.
UserApiClient.rx.me()
    .flatMap { user ->

        var scopes = mutableListOf<String>()

        if (user.kakaoAccount?.emailNeedsAgreement == true) { scopes.add("account_email") }
        if (user.kakaoAccount?.birthdayNeedsAgreement == true) { scopes.add("birthday") }
        if (user.kakaoAccount?.birthyearNeedsAgreement == true) { scopes.add("birthyear") }
        if (user.kakaoAccount?.genderNeedsAgreement == true) { scopes.add("gender") }
        if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) { scopes.add("phone_number") }
        if (user.kakaoAccount?.profileNeedsAgreement == true) { scopes.add("profile") }
        if (user.kakaoAccount?.ageRangeNeedsAgreement == true) { scopes.add("age_range") }
        if (user.kakaoAccount?.ciNeedsAgreement == true) { scopes.add("account_ci") }

        if (scopes.count() > 0) {
            Log.d(TAG, "Need to obtain consent from user.")

            // If OpenID Connect (OIDC) is enabled,
            // - When "openid" is added to `scopes`, OIDC is applied.
            // - When "openid" is not added to `scopes`, OAuth 2.0 is applied. 
                                                
            // To use OIDC, add "openid" to `scopes`.
            // scopes.add("openid")

            // InsufficientScope  error occurs
            Single.error(ApiError.fromScopes(scopes))
        }
        else {
            Single.just(user)
        }
    }
    .retryWhen(
        // Request additional consent for InsufficientScope.
        RxAuthOperations.instance.incrementalAuthorizationRequired(context)
    )
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ user ->
        Log.i(TAG, "Succeeded in retrieving user information.")
    }, { error ->
        Log.e(TAG, "Failed to retrieve user information.", error)
    })
    .addTo(disposables)
Return data

If the request is successful, the newly issued tokens are returned through the OAuthToken object.

Retrieve consent details

This API enables you to retrieve the detailed information of the scopes (consent items) that a user has agreed to. You can check all scopes set in [My Application] > [Kakao Login] > [Consent Items] and the details of the scopes. If a user has consented to the scope before, the scope is included in the response even though your app is currently not using the scope.

Call the scopes() method in the UserApiClient class.

Parameter
Name Type Description Required
scopes List<String> Used when you retrieve specific scopes only.
List of scope IDs you want to retrieve by referring to the IDs set in [My Application] > [Kakao Login] > [Consent Items].
X
Sample
Kotlin
RxKotlin
UserApiClient.instance.scopes { scopeInfo, error->
    if (error != null) {
        Log.e(TAG, "Failed to retrieve consent details.", error)
    }else if (scopeInfo != null) {
        Log.i(TAG, "Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: $scopeInfo")
    }
}
var disposables = CompositeDisposable()

UserApiClient.rx.scopes()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ scopeInfo: ScopeInfo?->
        Log.i(TAG, "Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: $scopeInfo")
    }, { error: Throwable? ->
        Log.e(TAG, "Failed to retrieve consent details.", error)
    }).addTo(disposables)

You can also retrieve the information of specific scopes by passing scope IDs. In this case, the response includes only the detailed information of the specified scopes if the request is successful.

Kotlin
RxKotlin
// List of the scope IDs that you want to retrieve
val scopes = mutableListOf("account_email", "friends")

UserApiClient.instance.scopes(scopes) { scopeInfo, error->
    if (error != null) {
        Log.e(TAG, "Failed to retrieve consent details.", error)
    }else if (scopeInfo != null) {
        Log.i(TAG, "Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: $scopeInfo")
    }
}
var disposables = CompositeDisposable()

// List of the scope IDs that you want to retrieve
val scopes = mutableListOf("account_email", "friends")

UserApiClient.rx.scopes(scopes)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ scopeInfo: ScopeInfo?->
        Log.i(TAG, "Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: $scopeInfo")
    }, { error: Throwable? ->
        Log.e(TAG, "Failed to retrieve consent details.", error)
    }).addTo(disposables)
Return data

If the request is successful, scopes() returns the ScopeInfo object that includes the changed details of each scope and whether a user has agreed to the scope.

ScopeInfo
Name Type Description Required
id Long Service user ID. O
scopes List<Scope> List of scope information. X
scope
Name Type Description Required
id String Scope ID. O
displayName String Name or description of the scope (consent item) displayed on the Consent screen. O
type ScopeType Enum class for Scope type.
- PRIVACY: scopes for Personal Information
- SERVICE: scopes for Permission
O
using Boolean Whether your app is using the scope.
true: Using the scope.
false: Not using the scope even though the user has agreed to.
O
agreed Boolean Whether the user has agreed to the scope.
true: The user has agreed.
false: The user has not agreed.
O
revocable Boolean Whether you can revoke this scope.
Only returned if the user has agreed to the scope.("agreed"=true)
true: Possible to revoke the scope.
false: Impossible to revoke the scope.
X

Revoke consent

This API revokes the scope that a user has agreed to. Call the revokeScopes() method in the UserApiClient class.

Parameter
Name Type Description Required
scopes String[] List of scope IDs you want to revoke.
You can revoke only the scope with "revocable":true among the scopes retrieved through the Retrieving consent details API. If you request to revoke the scope that is not revocable, an error is returned.
O
Sample
Kotlin
RxKotlin
// List of the scope IDs that you want to revoke
val scopes = mutableListOf("account_email", "legal_birth_date", "friends")

UserApiClient.instance.revokeScopes(scopes) { scopeInfo, error->
    if (error != null) {
        Log.e(TAG, "Failed to revoke consent.", error)
    }else if (scopeInfo != null) {
        Log.i(TAG, "Succeeded in revoking consent.\n Scopes being used or agreed: $scopeInfo")
    }
}
var disposables = CompositeDisposable()

// List of the scope IDs that you want to revoke
val scopes = mutableListOf("account_email", "legal_birth_date", "friends")

UserApiClient.rx.revokeScopes(scopes)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ scopeInfo: ScopeInfo?->
        Log.i(TAG, "Succeeded in revoking consent.\n Scopes being used or agreed: $scopeInfo")
    }, {error: Throwable? ->
        Log.e(TAG, "Failed to revoke consent.", error)
    }).addTo(disposables)
Return data

If the request is successful, revokeScopes() returns the ScopeInfo object that includes the changed details of each scope and whether a user has agreed to the scope.

Check user agreed terms

IMPORTANT

This API is only allowed for the service that adopted Kakao Sync. To see the advantages of Kakao Sync, refer to Concept > Kakao Sync. Before implementing this API, read Design terms and policies.

To check which terms a user has consented to when the user logs in, call the serviceTerms() method defined in the UserApiClient class.

Parameter
Name Type Description Required
extra String Used to retrieve all of the terms registered in your app.
In this case, set it to app_service_terms.
X
Sample
Kotlin
RxKotlin
// Check the terms that a user has consented
UserApiClient.instance.serviceTerms { userServiceTerms, error ->
    if (error != null) {
        Log.e(TAG, "Failed to check the agreed terms", error)
    }
    else if (userServiceTerms != null) {
        Log.i(TAG, "Succeeded in checking the agreed terms" +
                "\nService user ID: ${userServiceTerms.userId}" +
                "\nAgreed terms: \n${userServiceTerms.allowedServiceTerms?.joinToString("\n")}")
    }
}
var disposables = CompositeDisposable()

// the terms that a user has consented
UserApiClient.rx.serviceTerms()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ userServiceTerms ->
        Log.i(TAG, "Succeeded in check the agreed terms" +
                "\nService user ID: ${userServiceTerms.userId}" +
                "\nAgreed terms: \n${userServiceTerms.allowedServiceTerms?.joinToString("\n")}")
    }, { error ->
        Log.e(TAG, "Failed to check the agreed terms", error)
    })
    .addTo(disposables)
Return data

serviceTerms() returns the UserServiceTerms object.

UserServiceTerms
Name Type Description Required
userId Long Service user ID. X
appServiceTerms List<AppServiceTerms> A list of terms registered for an app. X
allowedServiceTerms List<ServiceTerms> A list of terms that a user has consented to. X
AppServiceTerms
Name Type Description Required
tag String Tag that is specified in [My Application] > [Simple Signup] for each term. O
createdAt Date The time when the term was registered.
(RFC3339 internet date/time format)
O
updatedAt Date The time when the term was modified.
(RFC3339 internet date/time format)
O
ServiceTerms
Name Type Description Required
tag String Tag of the term that a user consented to. O
agreedAt Date The last time when a user consented to the term.
(RFC3339 internet date/time format)
O

Get consent to desired terms

IMPORTANT

This API is only allowed for the service that adopted Kakao Sync. To see the advantages of Kakao Sync, refer to Concept > Kakao Sync. Before implementing this API, read Design terms and policies.

This API enables you to request consent to a specific term that a user has not consented, regardless of whether the user has already signed up or not.

If your app is used for multiple services and each service requires consent to different terms, or if a new required term is added to your service, you can use this API. For more details, Design terms and policies.

To request consent to specific scopes from a user, pass the list of term tags through serviceTerms as an argument when you call loginWithKakaoTalk() or loginWithKakaoAccount().

If you do not add serviceTerms when requesting an authorization code, the general Kakao Login API proceeds. In this case, you cannot request consent to the desired terms. Also, if a user has already consented to all required terms, the user is logged in without the Consent screen prompted. For more details, refer to FAQ.

Parameter
Name Type Description Required
serviceTerms List<String> Tags for the terms that are required to consent. O
Sample
Kotlin
RxKotlin
// Get consent to desired terms

// Designate tags for the terms that you want to get consent
// among the terms registered in [My Application] > [Simple Signup] in Kakao Developers. 
val serviceTerms = listOf("service")

// Request login with Kakao Talk using the serviceTerms parameter (same for Login with Kakao Account)
UserApiClient.instance.loginWithKakaoTalk(
    context = context,
    serviceTerms = serviceTerms
) { token, error ->
    if (error != null) {
        Log.e(TAG, "Login fail", error)
    }
    else if (token != null) {
        Log.i(TAG, "Login success ${token.accessToken}")
    }
}
var disposables = CompositeDisposable()

// Get consent to desired terms

// Designate tags for the terms that you want to get consent
// among the terms registered in [My Application] > [Simple Signup] in Kakao Developers. 
val serviceTerms = listOf("service")

// Request login with Kakao Talk using the serviceTerms parameter (same for Login with Kakao Account)
UserApiClient.rx.loginWithKakaoTalk(
    context = context,
    serviceTerms = serviceTerms
)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ token ->
        Log.i(TAG, "Login success ${token.accessToken}")
    }, { error ->
        Log.e(TAG, "Login fail", error)
    })
    .addTo(disposables)
Return data

The response is returned through the OAuthToken object in the same as the Kakao Login.

Advanced: Manual signup

IMPORTANT

The Manual signup API is only for the app with the Auto-link option disabled. Before using this API, check out when to use this API and the cautions in REST API.

The Manual signup API manually links a user with your app to complete signup when the Auto-link is disabled.

To figure out if the currently logged-in user needs to be linked with your app, check the value of hasSignedUp in the response of the Retrieving user information API and handle each case:

  • true: The user is already linked with your app. You do not need to call the Manual signup API.
  • false: The user is not linked with your app. You must call signup() to link the user with your app manually.
  • null: Your app is using the Auto-link feature. You do not need to call the Manual signup API.
Kotlin
RxKotlin
UserApiClient.instance.me { user, error ->
    if (error != null) {
        Log.e(TAG, "Retrieving user information fails", error)
    } else {
        Log.i(TAG, "Retrieving user information succeeds" +
                "\nService user ID: ${user.id}" +
                "\nLinked status: ${user.hasSignedUp}")
    }
}
var disposables = CompositeDisposable()

UserApiClient.rx.me()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe({ user ->
        Log.i(TAG, "Retrieving user information succeeds" +
                "\nService user ID: ${user.id}" +
                "\nLinked status: ${user.hasSignedUp}")
    }, { error ->
        Log.e(TAG, "Retrieving user information fails", error)
    }).addTo(disposables)

If the return value of hasSignedUp is false and the user is ready to sign up, call the signup() method to complete the signup.

Parameter
Name Type Description Required
properties Map<String, String> A list of user information.
Used when you want to store user information needed for your service when linking a user.
Refer to Manage user properties.
X
Sample
Kotlin
RxKotlin
UserApiClient.instance.signup { error ->
    if (error != null) {
        Log.e(TAG, "signup fails", error)
    } else {
        Log.i(TAG, "signup succeeds")
    }
}
var disposables = CompositeDisposable()

UserApiClient.rx.signup()
        .subscribeOn(Schedulers.io())
        .subscribe(Action { showSnackbar("signup() succeeds") }, onError).addTo(disposables)

If the request is successful, the user's service ID is returned.

To check the request result, request the Retrieving user information API again and check the value of hasSignedUp since the user's linked status is not provided even when the request is successful.

See more

Legacy

To see how to integrate Kakao Login using the Legacy Kakao SDK for Android, see Kakao Login guide for Legacy Android SDK.

It is highly recommended to migrate to the new version of Android SDK as soon as possible because the Legacy version may not be supported anymore.