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

Kakao Login

Flutter

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

To learn about each API in detail, refer to Understand concepts.

For a Kakao Login button, you can download the resources that Kakao provides or customize it according to your service UI by referring to the Design guide.

Before you begin

Before using Kakao Login APIs with the Flutter SDK,

  1. Complete the prerequisites.
  2. Add modules.
  3. Set Custom URL scheme.

Add modules

To use the features of Kakao Login, you need to add kakao_flutter_sdk_auth and kakao_flutter_sdk_user modules in pubspec.yaml by referring to Install SDK. After that, add the following libraries in your dart file.

import 'package:kakao_flutter_sdk_auth/kakao_flutter_sdk_auth.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';

Set Custom URL scheme

To receive an authorization code through redirection, you must specify the Redirect URI by setting custom URL scheme. If you do not set a custom URL scheme, the login process stops with no further UI response. When the authorization server sends an authorization code to the specified Redirect URI, the SDK receives the authorization code and requests an access token with the passed authorization code. To see the login process in details, refer to Understand concepts .

  1. Go to your app's Android/app/src/main/AndroidManifest.xml file, and then specify com.kakao.sdk.flutter.AuthCodeCustomTabsActivity inside the <application> tag.
  2. Add data of scheme and host inside the <intent-filter> tag.
  3. Set scheme to your Native app key in kakao${YOUR_NATIVE_APP_KEY} format.
  4. 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.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          package="your.package.name">
    <application
      ...
      >
      ...
        <activity android:name="com.kakao.sdk.flutter.AuthCodeCustomTabsActivity"
                  android:exported="true">
                  
          <intent-filter android:label="flutter_web_auth">
              <action android:name="android.intent.action.VIEW" />
              <category android:name="android.intent.category.DEFAULT" />
              <category android:name="android.intent.category.BROWSABLE" />
              <!-- For Kakao Login, set Redirect URI: "kakao${YOUR_NATIVE_APP_KEY}://oauth" -->
              <data android:scheme="kakao${YOUR_NATIVE_APP_KEY}" android:host="oauth"/>
          </intent-filter>
        </activity>
        ...
      </application>
</manifest>

Login

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

Login with Kakao Talk

To allow users to log in with Kakao Talk, call the loginWithKakaoTalk() method that launches the Kakao Talk application and prompts the Consent screen asking consent.

Parameter
Name Type Description Required
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
// Login with Kakao Talk
try {
  OAuthToken token = await UserApi.instance.loginWithKakaoTalk();
  print('Login succeeds. ${token.accessToken}');
} catch (e) {
  print('Login fails. $e')
}

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

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 DateTime 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 DateTime Validity period in seconds until the refresh token expires. O
scope 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

To allow users to log in with Kakao Account, call the loginWithKakaoAccount() method that opens a web browser and prompts the Consent screen asking consent.

Parameter
Name Type Description Required
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
//  Login with Kakao Account
try {
  OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
  print('Login succeeds. ${token.accessToken}');
} catch (e) {
  print('Login fails. $e')
}

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 when you call loginWithKakaoAccount. Then, the login screen is prompted even though a user has already been logged in on the same web browser on the device.

try {
  OAuthToken token = await UserApi.instance
    .loginWithKakaoAccount(prompts: [Prompt.login]);
  print('Login succeeds. ${token.accessToken}');
} catch (e) {
    print('Login fails. $e')
  }

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 isKakaoTalkInstalled() method to check if Kakao Talk has been installed on a user's device so that the user can log in with Kakao Talk.

// Login combination sample
try {
  bool talkInstalled = await isKakaoTalkInstalled();

  // If Kakao Talk has been installed, log in with Kakao Talk. Otherwise, log in with Kakao Account.
  OAuthToken token = talkInstalled
    ? await UserApi.instance.loginWithKakaoTalk()
    : await UserApi.instance.loginWithKakaoAccount();
  print('Login succeeds. ${token.accessToken}');
} catch (e) {
  print('Login fails. $e')
}

The code snippet below shows the logic of Kakao Login depending on whether Kakao Talk is installed and a user's action. You can see how to handle errors that may occur during the login process.

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, the CANCELED 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.

// Login combination sample + Detailed error handling callback
bool talkInstalled = await isKakaoTalkInstalled();
// If Kakao Talk has been installed, log in with Kakao Talk. Otherwise, log in with Kakao Account.
if (talkInstalled) {
  try {
    OAuthToken token = await UserApi.instance.loginWithKakaoTalk();
    print('Login succeeded. ${token.accessToken}');
  } catch (e) {
    print('Login failed. $e');

    // 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 (e is PlatformException && e.code == 'CANCELED') {
      return;
    }

    // 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.
    try {
      OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
      print('Login succeeded. ${token.accessToken}');
    } catch (e) {
      print('Login failed. $e');
    }
  }
} else {
  try {
    OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
    print('Login succeeded. ${token.accessToken}');
  } catch (e) {
    print('Login failed. $e');
  }
}

Check token presence

To check if a user has obtained an access token through Kakao Login, call the hasToken() method defined in the UserApi class. This API returns the presence of tokens as a boolean type.

Note that calling hasToken() to check the token presence does not ensure the user's login status.

Sample

if (await AuthApi.instance.hasToken()) {
  try {
    AccessTokenInfo tokenInfo = await UserApi.instance.accessTokenInfo();
    print('Succeeded in validating token ${tokenInfo.id} ${tokenInfo.expiresIn}');
  } catch (e) {
    if (e is KakaoException && e.isInvalidTokenError()) {
      print('Token has expired.')
    } else {
      print('Failed to retireve token information.')
    }

    try {
      // Log in with Kakao Account
      OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
      print('Login succeeds. ${token.accessToken}');
    } catch (e) {
      print('Login fails. $e')
    }
  }
} else {
  print('There is no token.');
    try {
      OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
      print('Login succeeds. ${token.accessToken}');
    } catch (e) {
      print('Login fails. $e')
    }
}

Return data

If the user has a valid access token, hasToken() returns the token information through the AccessTokenInfo class. Then, you can call the token-based APIs with the user's access token.

If false is returned, it means that the token is not valid, so you cannot retrieve the token information. For this case, implement a process for the user to log in to obtain tokens and the error handling process.

AccessTokenInfo
Name Type Description Required
id Int Service user ID. X
expiresIn Int Validity period in seconds until the access token expires. O
appId Int App ID that an access token has been issued for. O

Logout

To enable a user to log out, call the logout() method in the UserApi class. Then, the access and refresh token issued through the login process is deleted, and the user is logged out.

// Logout
try {
  await UserApi.instance.logout();
  print('Logout succeeds. Tokens are deleted from SDK.');
} catch (e) {
  print('Logout fails. Tokens are deleted from SDK.')
}

Regardless of the result of the logout request, the Flutter SDK deletes the access token and refresh token so that the login session expires.

Unlink

To unlink a user's Kakao Account from a service app, call the unlink() method in the UserApi class.

/// Unlink current user from the app.
try {
  await UserApi.instance.unlink();
  print('Unlink succeeds. Tokens are deleted from SDK.');
} catch (e) {
  print('Unlink fails.')
}

If the request is successful, the session between an app and a user is disconnected, and the user is logged out as the issued access token and refresh token are deleted.

Retrieve token information

To retrieve the token information of the user who is currently logged in, call the accessTokenInfo() method in the UserApi class. To check the validity of the tokens only, use this API rather than retrieving all user information.

Sample

// Retrieving token information
try {
  AccessTokenInfo tokenInfo = await UserApi.instance.accessTokenInfo();
  print('Succeeded in retrieving token information\nService user ID: ${tokenInfo.id}\nValidity period: ${tokenInfo.expiresIn} seconds');
} catch (e) {
  print('Failed to retrieve token information.')
}

Return data

If the request is successful, accessTokenInfo() returns the token information through the AccessTokenInfo class. If the access token expires, the access token is refreshed and new token information is returned.

Token information on the client-side cannot be trusted since it could expire if

  • User changes the Kakao Account password and invalidates tokens
  • User unlinks from your app

Retrieve user information

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 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 UserApi class.

Sample

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

// Retrieving user information
try {
  User user = await UserApi.instance.me();
  print('Succeeded in retrieving user information'
        '\nService user ID: ${user.id}'
        '\nEmail: ${user.kakaoAccount?.email}'
        '\nNickname: ${user.kakaoAccount?.profile?.nickname}'
        '\nProfile Thumbnail Image URI: ${user.kakaoAccount?.profile?.thumbnailImageUrl}');
} catch (e) {
  print('Failed to retrieve user information');
}

Return data

me() returns the user information through the User class.

User
Name Type Description Required
id Int 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 DateTime The time when the user is linked with a service in UTC*. X
synchedAt DateTime 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 > 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

* The time is based on Coordinated Universal Time(UTC), being 9 hours behind Korean Standard Time(KST). For the format of time, refer to RFC3339: Date and Time on the Internet.

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.

Store user information

You can store or update the additional user information needed for your service. Call the updateProfile() method in the UserApi class by passing properties.

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 User properties.
You can also add new property keys up to five.
O

Sample: Updating age range

// Storing or updating user's additional information
// Check the names of property keys that you can use in [My Application] > [User Properties] in Kakao Developers.

try {
  // Data to be updated
  Map<String, String> properties = {'age_range':'20~30'};
  await UserApi.instance.updateProfile(properties);
  print('Storing user information succeeds.');
} catch (e) {
  print('Storing user information fails.')
}

To check if the user information is successfully updated under the designated property keys, call the Retrieving user information API.

Request additional consent

IMPORTANT

To use this API, you must configure consent items for the required scopes.

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

To request additional consent, call the loginWithNewScopes() method in the UserApi class. Pass the scopes of user information you want to request additional consent as arguments.

Parameter

Name Type Description Required
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 that checks which scopes are required to get additional consent by checking the value of {FIELD_NAME}NeedsAgreement obtained through the Retrieving user information API.

/// Requesting 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. 

User user;
try {
  user = await UserApi.instance.me();
} catch (e) {
  print('Failed to retrieve user information.')
  return;
}

// Check which scope are required to get additional consent.
List<String> scopes = [];

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?.ciNeedsAgreement == true) { scopes.add("account_ci"); }
if (user.kakaoAccount?.legalNameNeedsAgreement == true) { scopes.add("legal_name"); }
if (user.kakaoAccount?.legalBirthDateNeedsAgreement == true) { scopes.add("legal_birth_date"); }
if (user.kakaoAccount?.legalGenderNeedsAgreement == true) { scopes.add("legal_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 a user has not granted permission to provide user information that is needed for your service, request additional consent with the `loginWithNewScopes()` method. 
if (scopes.length > 0) {
  print('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");

  OAuthToken token;
  try {
    token = await UserApi.instance.loginWithNewScopes(scopes);
    print('allowed scopes: ${token.scopes}');
  } catch (e) {
    print('Failed to obtain additional consent.')
    return;
  }

  // Retrieve the user information after obtaining consent. 
  try {
    User user = await UserApi.instance.me();
    print('Succeeded in retrieving user information.'
          '\nID: ${user.id}'
          '\nEmail: ${user.kakaoAccount?.email}'
          '\nNickname: ${user.kakaoAccount?.profile?.nickname}'
          '\nProfileImage: ${user.kakaoAccount?.profile?.thumbnailImageUrl}');
  } catch (e) {
    print('Failed to retrieve user information.')
  }
}

Return data

loginWithNewScopes() presents the Consent screen asking 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.

Retrieve consent details

To retrieve the detailed information of the scopes (consent items) that a user has agreed to, call the scopes() method in the UserApi class.

To retrieve the details of specific scopes only, you need to pass the scopes parameter as an argument when calling scopes().

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: Retrieving all scopes

try {
  ScopeInfo scopeInfo = await UserApi.instance.scopes();
  print('Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: ${scopeInfo.scopes}');
} catch (e) {
  print('Failed to retrieve consent details.')
}

Sample: Retrieving specific scope

// List of the scope IDs that you want to retrieve
List<String> scopes = ['account_email', 'friends'];

try {
  ScopeInfo scopeInfo = await UserApi.instance.scopes(scopes: scopes);
  print('Succeeded in retrieving consent details succeeds.\n Scopes being used or agreed: ${scopeInfo.scopes}');
} catch (e) {
  print('Failed to retrieve consent details.')
}

Return data

If the request is successful, the API returns the details of the scopes that you enabled in [My Application] > [Kakao Login] > [Consent Items] and that the user has consented through the scopeInfo object. Even though your app is currently not using the scope but if a user has consented to the scope before, the scope is included in the response.

ScopeInfo
Name Type Description Required
id Int Service user ID. O
scopes 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 Scope type. PRIVACY or SERVICE.
PRIVACY: scopes for Personal Information
SERVICE: scopes for Permission
O
using Bool 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 Bool Whether the user has agreed to the scope.
true: The user has agreed.
false: The user has not agreed.
O
revocable Bool 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

To revoke the scope that a user has agreed to, call the revokeScopes() method in the UserApi class. You can only revoke 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.

You must pass the list of scope ID through the scopes parameter when calling revokeScopes().

Parameter

Name Type Description Required
scopes List<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.
O

Sample

// List of the scope IDs that you want to revoke
List<String> scopes = ['account_email', 'legal_birth_date', 'friends'];

try {
  ScopeInfo scopeInfo = await UserApi.instance.revokeScopes(scopes: scopes);
  print('Succeeded in revoking consent.\n Scopes being used or agreed: ${scopeInfo.scopes}');
} catch (e) {
  print('Failed to revoke consent.')
}

Return data

If the request is successful, the API returns the scopeInfo object that includes the changed details of each scope and whether a user has agreed to the scope.

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 by calling the Retrieving user information API and handle each case:

Return value Status Action
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.
// Checking if user is linked with your app by calling the Retrieving user information API.
try {
  User user = await UserApi.instance.me();
  print('Succeeded in retrieving user information.'
        '\nService user ID: ${user.id}'
        '\nLinked status:: ${user.hasSignedUp}');
} catch (e) {
  print('Failed to retrieve user information.');
}

If the return value of hasSignedUp is false and the user is ready to sign up, call the signup() method to complete the signup. You can also request to store user information needed for your service with the properties parameter.

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 User properties.
X

Sample:

// Requesting signup 
try {
  await UserApi.instance.signup();
  print('signup succeeds.');
} catch (e) {
  print('signup fails.')
}

Return data

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

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

See more