react-native-naver-login-turbo
v0.1.5
Published
React Native TurboModule for Naver OAuth (네이버 아이디로 로그인) on iOS and Android, targeting the New Architecture.
Downloads
316
Maintainers
Readme
react-native-naver-login-turbo
React Native New Architecture (TurboModule) library for Naver OAuth 2.0 login on iOS and Android.
Designed to be API-compatible with @react-native-seoul/naver-login, so it can serve as a drop-in replacement in existing projects.
New Architecture only. This library does not support the legacy Bridge (Old Architecture).
Table of Contents
- Requirements
- Naver Developer Console Setup
- Installation
- iOS Setup
- Android Setup
- Usage
- API Reference
- Behavior Notes
- Running the Example App
- Migrating from @react-native-seoul/naver-login
- Troubleshooting
- License
Requirements
| Item | Minimum version | | --------------------- | --------------------------------- | | React Native | 0.76+ (New Architecture required) | | iOS | 15.1+ | | Android | API 24 (Android 7.0)+ | | Xcode | 15+ | | Android Gradle Plugin | 8.x+ |
Naver Developer Console Setup
Before using this library, register your application at the Naver Developer Console.
- Register an application → Select Naver Login as the API to use.
- Configure the login service environment:
- iOS: Enter your iOS Bundle ID (e.g.,
com.example.myapp) - Android: Enter your Android package name (e.g.,
com.example.myapp)
- iOS: Enter your iOS Bundle ID (e.g.,
- After registration, note your Client ID (
consumerKey) and Client Secret (consumerSecret). - For iOS, decide on a URL Scheme for the OAuth callback (e.g.,
naverlogin.myapp).
Installation
npm install react-native-naver-login-turbo
# or
yarn add react-native-naver-login-turboiOS
cd ios && pod installThe podspec automatically pulls in naveridlogin-sdk-ios via CocoaPods. No manual SDK download or framework setup is needed.
Android
com.navercorp.nid:oauth:5.10.0 is automatically added from Maven Central via android/build.gradle. No additional steps required.
iOS Setup
Info.plist — Register URL Scheme
Register a URL Scheme so your app can receive the OAuth callback from the Naver app.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>YOUR_URL_SCHEME</string>
</array>
</dict>
</array>Info.plist — Naver App Allowlist
Allow the system to query whether the Naver app is installed.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>naversearchapp</string>
<string>naversearchthirdlogin</string>
</array>No AppDelegate Changes Needed
This library subscribes to RCTOpenURLNotification internally to handle the OAuth callback URL. You do not need to add an openURL: handler to your AppDelegate.
Android Setup
AndroidManifest.xml
Register the Naver login activity.
<application ...>
<activity
android:name="com.navercorp.nid.oauth.OAuthLoginActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
</application>ProGuard
If you use ProGuard, add the following rule.
-keep class com.navercorp.nid.** { *; }Usage
Initialize
Call this once at app startup, typically inside useEffect in your root component.
import NaverLogin from 'react-native-naver-login-turbo';
NaverLogin.initialize({
consumerKey: 'YOUR_CLIENT_ID',
consumerSecret: 'YOUR_CLIENT_SECRET',
appName: 'MyApp',
serviceUrlSchemeIOS: 'YOUR_URL_SCHEME', // iOS only
disableNaverAppAuthIOS: false, // iOS only, default false
});Login
const result = await NaverLogin.login();
if (result.isSuccess && result.successResponse) {
const { accessToken, refreshToken, tokenType, expiresAtUnixSecondString } =
result.successResponse;
console.log('Login succeeded:', accessToken);
} else if (result.failureResponse?.isCancel) {
console.log('User cancelled login.');
} else {
console.log('Login failed:', result.failureResponse?.message);
}Important:
login()never rejects. Both errors and cancellations resolve as{ isSuccess: false, failureResponse: { ... } }. Checkresult.isSuccess— do not usetry/catchto detect failure.
Get Profile
const profile = await NaverLogin.getProfile(accessToken);
const {
id,
name,
email,
nickname,
profile_image,
birthday,
birthyear,
mobile,
gender,
} = profile.response;Logout (local token removal only)
Removes the locally stored token without revoking it on the server.
await NaverLogin.logout();Unlink (server revocation + local removal)
Sends a revocation request to the Naver server and then removes the local token. This fully severs the connection between your app and the user's Naver account.
try {
await NaverLogin.deleteToken();
} catch (e) {
console.error('Unlink failed:', e);
}Full Example
import { useEffect, useState } from 'react';
import { Button, Text, View } from 'react-native';
import NaverLogin, {
type NaverLoginSuccessResponse,
} from 'react-native-naver-login-turbo';
export default function LoginScreen() {
const [token, setToken] = useState<NaverLoginSuccessResponse | null>(null);
useEffect(() => {
NaverLogin.initialize({
consumerKey: 'YOUR_CLIENT_ID',
consumerSecret: 'YOUR_CLIENT_SECRET',
appName: 'MyApp',
serviceUrlSchemeIOS: 'YOUR_URL_SCHEME',
});
}, []);
const handleLogin = async () => {
const result = await NaverLogin.login();
if (result.isSuccess && result.successResponse) {
setToken(result.successResponse);
}
};
const handleLogout = async () => {
await NaverLogin.logout();
setToken(null);
};
return (
<View>
{token ? (
<>
<Text>{token.accessToken}</Text>
<Button title="Logout" onPress={handleLogout} />
</>
) : (
<Button title="Login with Naver" onPress={handleLogin} />
)}
</View>
);
}API Reference
NaverLogin.initialize(params)
| Parameter | Type | Required | Description |
| ------------------------ | --------- | ------------ | ------------------------------------------------- |
| consumerKey | string | ✓ | Client ID from Naver Developer Console |
| consumerSecret | string | ✓ | Client Secret from Naver Developer Console |
| appName | string | ✓ | App name displayed on the login screen |
| serviceUrlSchemeIOS | string | iOS required | URL Scheme for the iOS OAuth callback |
| disableNaverAppAuthIOS | boolean | — | Disable native Naver app login (default: false) |
Returns: void (synchronous)
NaverLogin.login()
Returns: Promise<NaverLoginResponse> — never rejects.
type NaverLoginResponse = {
isSuccess: boolean;
successResponse?: NaverLoginSuccessResponse;
failureResponse?: NaverLoginFailureResponse;
};
type NaverLoginSuccessResponse = {
accessToken: string;
refreshToken: string;
expiresAtUnixSecondString: string; // Unix timestamp in seconds, as a string
tokenType: string; // typically "Bearer"
};
type NaverLoginFailureResponse = {
message: string;
isCancel: boolean;
lastErrorCodeFromNaverSDK?: string; // Android only
lastErrorDescriptionFromNaverSDK?: string; // Android only
};NaverLogin.refreshToken()
Returns: Promise<NaverLoginResponse> — never rejects (same contract as login()).
Reissues the access token using the refresh token stored by the SDK after login.
Naver access tokens expire after 1 hour, so call this to obtain a fresh accessToken without prompting the user to log in again.
On success it resolves with a new successResponse token bundle (same shape as login()).
If the refresh token is missing or expired, it resolves as { isSuccess: false, failureResponse: {...} } — treat that as a signal to run login() again.
const result = await NaverLogin.refreshToken();
if (result.isSuccess) {
const { accessToken, expiresAtUnixSecondString } = result.successResponse!;
} else {
// refresh token expired — fall back to a full login
await NaverLogin.login();
}Uses
requestAccessTokenWithRefreshToken(iOS) /NidOAuthLogin().callRefreshAccessTokenApi(Android). This method is additive — it does not exist on@react-native-seoul/naver-login.
NaverLogin.logout()
Returns: Promise<void> — can reject if the native layer throws.
Removes the locally cached token. The token remains valid on the server.
NaverLogin.deleteToken()
Returns: Promise<void> — rejects if the server request fails.
Sends a revocation request to Naver's server, then removes the local token. Unlike logout(), this can reject on network failure.
NaverLogin.getProfile(accessToken)
| Parameter | Type | Description |
| ------------- | -------- | --------------------------------- |
| accessToken | string | Access token obtained after login |
Returns: Promise<GetProfileResponse> — rejects on HTTP or network errors.
type GetProfileResponse = {
resultcode: string; // "00" = success
message: string; // "success"
response: NaverProfileData;
};
type NaverProfileData = {
id: string;
email: string;
name: string;
profile_image: string | null;
nickname: string | null;
birthday: string | null; // "MM-DD" format
birthyear: number | null;
age: string | null; // e.g. "20-29"
gender: string | null; // "M" | "F" | "U"
mobile: string | null;
mobile_e164: string | null;
ci?: string | null; // CI (Connecting Information); only present if the Naver app has business-info + CI scope
};Behavior Notes
login() never rejects
All failure cases — errors, cancellations, network issues — resolve as { isSuccess: false, failureResponse: {...} }. A try/catch block will never catch a login failure.
// ✅ Correct
const result = await NaverLogin.login();
if (!result.isSuccess) {
const { isCancel, message } = result.failureResponse!;
}
// ❌ Wrong — catch is never called
try {
const result = await NaverLogin.login();
} catch (e) {
// unreachable
}Concurrent login protection
On both iOS and Android, if a second login() call arrives while one is already in progress, it immediately resolves as { isSuccess: false }. Wait for the current login to finish before issuing a new one.
logout vs deleteToken
| | logout() | deleteToken() |
| -------------------- | --------------- | ------------------- |
| Removes local token | ✓ | ✓ |
| Revokes server token | ✗ | ✓ |
| Requires network | ✗ | ✓ |
| Can reject | ✓ | ✓ |
| Use case | Simple sign-out | Full account unlink |
No AppDelegate changes needed (iOS)
When initialize() is called, the library subscribes to RCTOpenURLNotification to automatically process the return URL from the Naver app. If you previously added an openURL: handler in your AppDelegate for Naver, remove it to avoid conflicts.
Naver app not installed (iOS)
When disableNaverAppAuthIOS is false (default), the library attempts native app login if the Naver app is installed. If it is not installed, it automatically falls back to an in-app WebView login.
Parsing expiresAtUnixSecondString
const expiresAt = new Date(parseInt(expiresAtUnixSecondString, 10) * 1000);Running the Example App
The example/ directory contains a working sample app. Complete the checklist below before running it.
Checklist
Step 1: Register your app at the Naver Developer Console
Register an application at developers.naver.com.
| Field | Value |
| --------------------------------- | --------------------------------------------------------------------------- |
| API | Naver Login |
| iOS Bundle ID | Bundle ID from Xcode (e.g., org.reactjs.native.example.NaverLoginExample) |
| Android package name | com.naverloginexample |
| Android app signature (keyhash) | Debug keystore hash extracted via adb shell or keytool |
After registration you will receive a Client ID and Client Secret.
Step 2: Set credentials in example/src/App.tsx
Replace the three constants at the top of the file with your actual values.
// example/src/App.tsx
const CLIENT_ID = 'YOUR_CLIENT_ID'; // ← your Client ID
const CLIENT_SECRET = 'YOUR_CLIENT_SECRET'; // ← your Client Secret
const URL_SCHEME = 'naverloginexample'; // ← already configured, no change neededStep 3: iOS Info.plist (already configured)
example/ios/NaverLoginExample/Info.plist already includes the following.
<!-- URL Scheme: receives the OAuth callback from the Naver app -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>naverloginexample</string>
</array>
</dict>
</array>
<!-- Allows querying whether the Naver app is installed -->
<key>LSApplicationQueriesSchemes</key>
<array>
<string>naversearchapp</string>
<string>naversearchthirdlogin</string>
</array>A missing
CFBundleURLSchemesentry is the most common mistake. Without it, the app never receives the OAuth callback, resulting inFailed to open URL naverloginexample://thirdPartyLoginResult.
Step 4: Android AndroidManifest.xml (already configured)
example/android/app/src/main/AndroidManifest.xml already includes the following activity.
<activity
android:name="com.navercorp.nid.oauth.OAuthLoginActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />Step 5: Run the example app
# Install dependencies (first time only)
yarn
cd example/ios && pod install && cd ../..
# Start Metro bundler (separate terminal)
yarn example start
# Run on a platform
yarn example ios # iOS Simulator
yarn example android # Android Emulator or physical devicePhysical device testing — iOS
The iOS Simulator does not have the Naver app, so login falls back to WebView. To test native Naver app login on a real device, ensure you have a valid Team and Provisioning Profile set in Xcode.
Physical device testing — Android
Extract the app signature hash to register with the Naver Developer Console.
# Extract debug keystore hash (for development)
keytool -exportcert -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass android | \
openssl sha1 -binary | openssl base64Paste the output into the Android app registration field labeled "SHA-1 fingerprint of the app signing certificate" in the Naver Developer Console.
Migrating from @react-native-seoul/naver-login
What changed
| Item | Before (@react-native-seoul) | This library |
| --------------------- | ------------------------------ | --------------------------------- |
| Architecture | Old Bridge | New Architecture (TurboModule) |
| getAgreement | Supported | Not ported (see note below) |
| AppDelegate setup | Required | Not required |
| iOS openURL handler | Added in AppDelegate | Handled internally by the library |
Note on
getAgreement. It is not deprecated —GET https://openapi.naver.com/v1/nid/agreementis still live and documented, and the upstream package still ships it. It is a niche feature usable only by apps enrolled in Naver's "약관 동의 대행" (consent-delegation) program, and it is a plain REST call that never touches native code. We deliberately left it out because wrapping a barefetchin this library adds little value: an app that needs it can call the endpoint directly with its own API client (sameAuthorization: Bearer <accessToken>header used bygetProfile). An earlier version of this table incorrectly labelled it "deprecated"; that claim was unsourced and is wrong.
What stayed the same
initialize(),login(),logout(),deleteToken(),getProfile()signatures are identicalNaverLoginResponse,NaverLoginSuccessResponse,NaverLoginFailureResponse,GetProfileResponse,NaverProfileDatatypes are identical- The
login()never-rejects contract is preserved
Troubleshooting
iOS build error: naveridlogin-sdk-ios not found
cd ios && pod install --repo-updateiOS build error: missing ReactCodegen generated files
If Xcode reports missing files under ios/build/generated/ios/ReactCodegen after pod install, check ios/.xcode.env.local. React Native sources .xcode.env.local after .xcode.env, so a stale hard-coded NODE_BINARY path can prevent the Codegen script phase from running with the Node version you expect. Delete ios/.xcode.env.local or regenerate it from the active shell:
cd ios
echo 'export NODE_BINARY="$(command -v node)"' > .xcode.env.localiOS: app does not return after login
- Verify that the URL Scheme in
Info.plistmatches theserviceUrlSchemeIOSvalue passed toinitialize(). - Verify that the iOS Bundle ID registered in the Naver Developer Console matches your actual Bundle ID.
- Verify that
LSApplicationQueriesSchemescontains bothnaversearchappandnaversearchthirdlogin.
Android build error: Manifest merger conflict
Check whether OAuthLoginActivity is declared more than once. The current library manifest is empty, so your app should declare OAuthLoginActivity exactly once in the app's AndroidManifest.xml.
Android: login() immediately returns isSuccess: false
This happens when login() is called while the app is in the background (no current Activity). Only call login() in response to a user interaction.
deleteToken() rejects
The request failed due to a network error or the token was already expired. If you want to sign the user out locally regardless of server-side revocation, use logout() instead.
License
MIT © Dongmin Yu
