Social Login Setup
Wire up Google and Apple social login in minutes.
At a Glance
The Unveily SDK supports different social login providers depending on your plan.
| Social | Basic | Standard | Pro |
|---|---|---|---|
| ✅ | ✅ | ✅ | |
| Apple (iOS) | ✅ | ✅ | ✅ |
| Apple (Android) | ✅ | ✅ | ✅ |
| Kakao | — | ✅ | ✅ |
| Naver | — | ✅ | ✅ |
| LINE | — | ✅ | ✅ |
| Meta | — | — | ✅ |
Google and Apple both go through Firebase Auth — no custom backend required.
Both providers are configured in Firebase Console and the same google-services.json covers both.
The SDK returns a Firebase ID Token (idToken) for Google and Apple logins,
which your server can verify using the Firebase Admin SDK.
iOS provider availability
On iOS today, only Apple and Google login work. Calling Kakao, Naver, LINE, or Meta on iOS may return the SDK_NOT_READY error until a future iOS SDK update. Android supports all providers. Apple/Google work on both platforms.
Also, Apple login on iOS returns an authorizationCode (Apple-only) in addition to idToken. The "Google + Apple return idToken" statement above refers to the Firebase path (Android, and the Firebase-handled portion on iOS).
1. Key Configuration File
Enter the SDK keys for each social provider in assets/config/social_login_config.json.
{
"kakao": {
"nativeAppKey": "your-kakao-native-app-key-here"
},
"naver": {
"clientId": "your-naver-client-id-here",
"clientSecret": "your-naver-client-secret-here",
"appName": "App Name"
},
"line": {
"channelId": "your-line-channel-id-here"
},
"meta": {
"appId": "your-meta-app-id-here",
"clientToken": "your-meta-client-token-here"
}
}- If a key is empty, that social provider is automatically disabled.
- Google and Apple: Configured via
google-services.json+ Firebase Console. No entries needed in this file.
2. Update build.gradle.kts Placeholders
Kakao and Meta require updating the manifest placeholders in build.gradle.kts for deep link callbacks.
// app/build.gradle.kts — inside defaultConfig
defaultConfig {
// Kakao: "kakao" + nativeAppKey
manifestPlaceholders["kakaoScheme"] = "kakaoyour-kakao-native-app-key-here"
// Meta: "fb" + appId
manifestPlaceholders["facebookLoginProtocolScheme"] = "fbyour-meta-app-id-here"
}3. Control Social Button Visibility
Use the socialLogin section in assets/config.json to turn each button on or off.
{
"socialLogin": {
"google": true,
"apple": true,
"kakao": true,
"naver": true,
"line": false,
"meta": false
}
}true: Button is shown (also requires the plan to allow it for it to actually work)false: Button is hidden
4. Developer Console Registration Steps
- Firebase Console → Create a project
- Authentication → Sign-in method → Enable Google
- Download
google-services.json→ Place in theapp/folder
Apple (iOS and Android)
Both iOS and Android Apple Sign-In go through Firebase Auth — the same Firebase project covers both platforms.
- Apple Developer → Certificates, Identifiers & Profiles:
- Create a Services ID (reverse-domain format, e.g.
com.yourcompany.app) - Create a Key with Sign In with Apple enabled → note the Team ID, Key ID, and download the
.p8key
- Create a Services ID (reverse-domain format, e.g.
- Firebase Console → Authentication → Sign-in method → Enable Apple
- Enter the Team ID, Services ID, Key ID, and paste the
.p8private key content
- Enter the Team ID, Services ID, Key ID, and paste the
- The same
google-services.jsoncovers both Google and Apple — no extra file needed.
Firebase handles the entire OAuth flow with Apple internally (including the Chrome Custom Tab on Android). No server-side callback endpoint or custom URL scheme is required.
Kakao
- Kakao Developers Console → My Applications → Add Application
- App Keys → Copy the Native App Key
- Platform → Android → Register package name and market URL
- Kakao Login → Activate → Configure consent items (nickname, email, etc.)
social_login_config.json→ Enterkakao.nativeAppKeybuild.gradle.kts→ UpdatemanifestPlaceholders["kakaoScheme"] = "kakao{NativeAppKey}"
Naver
- Naver Developers Center → Application → Register Application
- Use API → Select Naver ID Login
- Android → Register package name
- Copy Client ID / Client Secret
social_login_config.json→ Enternaver.clientId,naver.clientSecret,naver.appName
LINE
- LINE Developers → Providers → Create → Create a Channel
- Channel type → Select LINE Login
- Register Android Package Name
- Copy Channel ID
social_login_config.json→ Enterline.channelId
Meta (Facebook)
- Meta for Developers → My Apps → Create App
- Add Product → Select Facebook Login
- Settings → Basic → Copy App ID and Client Token
- Android → Register package name and key hash
social_login_config.json→ Entermeta.appId,meta.clientTokenbuild.gradle.kts→ UpdatemanifestPlaceholders["facebookLoginProtocolScheme"] = "fb{AppID}"
5. JS Bridge API
Login
window.unveilyBridge.auth.socialLogin({
provider: 'google' // 'apple' | 'kakao' | 'naver' | 'line' | 'meta'
});Login Callback
Declare the following function on your page to receive the result.
function onSocialLoginResult(result) {
if (result.success) {
console.log(result.provider); // 'google' | 'apple' | 'kakao' | ...
console.log(result.uid); // Firebase UID (Google/Apple) or social unique ID
console.log(result.displayName); // Name (can be null)
console.log(result.email); // Email (can be null)
console.log(result.profileImage); // Profile image URL (can be null)
console.log(result.idToken); // Firebase ID Token (Google + Apple only; null for Kakao/Naver/LINE/Meta)
// Use idToken to verify the login server-side via Firebase Admin SDK
} else {
console.error(result.error);
// Key error codes:
// FEATURE_NOT_ALLOWED → Tier requirement not met (includes result.requiredTier)
// PROVIDER_DISABLED → Set to false in config.json
// SDK_NOT_CONFIGURED → Key not entered in social_login_config.json
// SDK_NOT_READY → Provider not yet available on iOS (Kakao/Naver/LINE/Meta)
// LOGIN_CANCELLED → User closed the login screen
}
}Logout
window.unveilyBridge.auth.logout({ provider: 'google' });Revoke (Unlink Social Provider)
Disconnects the social provider from the user's account. The account itself is not deleted.
window.unveilyBridge.auth.revoke({ provider: 'kakao' });Google / Apple Revoke
revoke calls Firebase user.unlink() — it disconnects the provider only.
The Firebase account remains intact.
Account deletion (withdrawal) must be handled separately via user.delete() on your server.
Logout / Revoke Callback
function onSocialAuthResult(result) {
// result.success — true/false
// result.action — 'logout' | 'revoke'
// result.provider — 'google' | 'apple' | 'kakao' | ...
// result.error — error code on failure
}6. Error Code Reference
| Error Code | Description |
|---|---|
FEATURE_NOT_ALLOWED | Social provider not allowed on current plan (includes requiredTier) |
PROVIDER_DISABLED | That social provider is set to false in config.json |
SDK_NOT_CONFIGURED | Key is empty in social_login_config.json |
SDK_NOT_READY | Provider not yet supported on iOS (Kakao/Naver/LINE/Meta — planned for a future iOS SDK update) |
LOGIN_CANCELLED | User closed the login screen |
UNKNOWN_PROVIDER | Unsupported provider value |
GOOGLE_ID_TOKEN_NULL | Google ID token issuance failed |
FIREBASE_AUTH_FAILED | Firebase sign-in credential rejected |
APPLE_NO_USER | Apple sign-in succeeded but Firebase returned no user |
APPLE_ERROR | Apple sign-in failed (general) |
GOOGLE_REVOKE_FAILED | Google provider unlink failed |
APPLE_REVOKE_FAILED | Apple provider unlink failed |
NAVER_NO_TOKEN | No Naver access token |
LINE_INIT_FAILED | LINE SDK initialization failed |
7. profileImage Handling Rules
| Social | Availability |
|---|---|
| Almost always available (null if not) | |
| Apple | Always null (not provided by Apple) |
| Kakao | Available if consent item is configured |
| Naver | Available if consent item is configured |
| LINE | Available if user profile is public |
| Meta | Available if public profile is set |
If null, display a default image on your side.
function onSocialLoginResult(result) {
const avatar = result.profileImage
?? '/images/default-avatar.png';
document.getElementById('avatar').src = avatar;
}8. Apple displayName Cache
Apple only provides the user's name on the first login. The SDK caches this locally.
| Action | Behavior |
|---|---|
| First login (displayName available) | Saved to cache and passed along |
| Re-login (displayName is null) | Replaced with cached value |
| Logout | Cache retained (for re-login) |
| Revoke | Cache deleted (Apple provides it again on re-link) |
| Platform | Cache Storage | Key Format |
|---|---|---|
| iOS | UserDefaults | apple_name_{uid} |
| Android | SharedPreferences("unveily_apple_name_cache") | apple_name_{uid} |
9. Complete Example
// Login button click
document.getElementById('btn-google').addEventListener('click', () => {
window.unveilyBridge.auth.socialLogin({ provider: 'google' });
});
// Handle the result
function onSocialLoginResult(result) {
if (!result.success) {
if (result.error === 'FEATURE_NOT_ALLOWED') {
alert(`Available on ${result.requiredTier} plan or higher.`);
} else if (result.error !== 'LOGIN_CANCELLED') {
alert('Login failed.');
}
return;
}
// Send to your server — use idToken for server-side verification (Google/Apple)
sendToServer(result);
}
async function sendToServer(result) {
await fetch('/api/auth/social', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
provider: result.provider,
uid: result.uid,
idToken: result.idToken, // Firebase ID Token (Google/Apple) — verify server-side
email: result.email,
displayName: result.displayName
})
});
}Server-side Firebase ID Token Verification
For Google and Apple logins, verify the idToken on your server using the Firebase Admin SDK.
// npm install firebase-admin
import admin from 'firebase-admin';
admin.initializeApp({ credential: admin.credential.applicationDefault() });
app.post('/api/auth/social', async (req, res) => {
const { provider, uid, idToken, email, displayName } = req.body;
// Verify Firebase ID Token for Google and Apple
if (idToken) {
const decoded = await admin.auth().verifyIdToken(idToken);
if (decoded.uid !== uid) return res.status(401).json({ error: 'UID mismatch' });
}
// Find or create user by uid
let user = await db.users.findOne({ socialProvider: provider, socialId: uid });
if (!user) {
user = await db.users.create({ socialProvider: provider, socialId: uid, email, name: displayName });
}
req.session.userId = user.id;
res.json({ ok: true, user: { id: user.id, name: user.name } });
});// NuGet: FirebaseAdmin
using FirebaseAdmin;
using FirebaseAdmin.Auth;
[HttpPost("auth/social")]
public async Task<IActionResult> SocialLogin([FromBody] SocialLoginRequest request)
{
// Verify Firebase ID Token for Google and Apple
if (!string.IsNullOrEmpty(request.IdToken))
{
var decoded = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(request.IdToken);
if (decoded.Uid != request.Uid)
return Unauthorized(new { error = "UID mismatch" });
}
var user = await _userService.FindOrCreateBySocialAsync(
request.Provider, request.Uid, request.Email, request.DisplayName);
HttpContext.Session.SetInt32("UserId", user.Id);
return Ok(new { ok = true, user = new { user.Id, user.Name } });
}// Maven: com.google.firebase:firebase-admin
@PostMapping("/api/auth/social")
public ResponseEntity<?> socialLogin(@RequestBody SocialLoginRequest request,
HttpSession session) throws FirebaseAuthException {
// Verify Firebase ID Token for Google and Apple
if (request.getIdToken() != null) {
FirebaseToken decoded = FirebaseAuth.getInstance().verifyIdToken(request.getIdToken());
if (!decoded.getUid().equals(request.getUid()))
return ResponseEntity.status(401).body(Map.of("error", "UID mismatch"));
}
User user = userService.findOrCreateBySocial(
request.getProvider(), request.getUid(), request.getEmail(), request.getDisplayName());
session.setAttribute("userId", user.getId());
return ResponseEntity.ok(Map.of("ok", true, "userId", user.getId()));
}<?php
// composer require kreait/firebase-php
use Kreait\Firebase\Factory;
$data = json_decode(file_get_contents('php://input'), true);
$provider = $data['provider'];
$uid = $data['uid'];
$idToken = $data['idToken'] ?? null;
$email = $data['email'] ?? null;
$displayName = $data['displayName'] ?? null;
// Verify Firebase ID Token for Google and Apple
if ($idToken) {
$firebase = (new Factory)->withServiceAccount('/path/to/serviceAccount.json');
$auth = $firebase->createAuth();
$decoded = $auth->verifyIdToken($idToken);
if ($decoded->claims()->get('sub') !== $uid) {
http_response_code(401);
echo json_encode(['error' => 'UID mismatch']);
exit;
}
}
$stmt = $pdo->prepare('SELECT * FROM users WHERE social_provider = ? AND social_id = ?');
$stmt->execute([$provider, $uid]);
$user = $stmt->fetch();
if (!$user) {
$pdo->prepare('INSERT INTO users (social_provider, social_id, email, name) VALUES (?, ?, ?, ?)')
->execute([$provider, $uid, $email, $displayName]);
$userId = $pdo->lastInsertId();
} else {
$userId = $user['id'];
}
$_SESSION['user_id'] = $userId;
echo json_encode(['ok' => true, 'userId' => $userId]);