Social Login Providers — Google, GitHub, and Apple Authentication for APIs
In this tutorial, you will learn about Social Login Providers. We cover key concepts, practical examples, and best practices to help you master this topic.
Social login lets users authenticate with existing accounts from Google, GitHub, Apple, or other identity providers, reducing friction and eliminating the need for application-specific passwords.
What You'll Learn
Integrating Google, GitHub, and Apple sign-in, OAuth2-based social login flow, provider-specific differences, account linking across providers, and handling provider-issued tokens.
Why It Matters
Social login increases conversion rates by eliminating registration friction. Users prefer using existing accounts. Supporting multiple providers also improves Accessibility and user choice.
Real-World Use
Almost every major platform supports social login. Notion, Figma, and Vercel offer Google and GitHub sign-in. Durga Antivirus Pro offers Google and GitHub login for its dashboard, with Apple sign-in for iOS users.
sequenceDiagram
participant User as User
participant App as Your App
participant Provider as Identity Provider (Google/GitHub/Apple)
User->>App: Click "Sign in with Google"
App->>Provider: Redirect to provider's OAuth2 endpoint
Provider->>User: Authenticate + consent
User->>Provider: Grant permission
Provider->>App: Redirect with authorization code
App->>Provider: Exchange code for tokens
Provider->>App: { id_token, access_token }
App->>App: Verify token, create/link account
App->>User: Session established
Code Example: Google Sign-In Integration
from flask import Flask, request, redirect, jsonify
from google.oauth2 import id_token
from google.auth.transport import requests as google_requests
import secrets, jwt, datetime
app = Flask(__name__)
SECRET = "social-login-secret"
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", "your-google-client-id")
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET")
@app.route("/api/auth/google/login")
def google_login():
"""Redirect user to Google's OAuth2 endpoint."""
params = {
"client_id": GOOGLE_CLIENT_ID,
"redirect_uri": "https://api.durga-antivirus.com/api/auth/google/callback",
"response_type": "code",
"scope": "openid email profile",
"state": secrets.token_urlsafe(16),
"access_type": "offline",
"prompt": "select_account"
}
url = "https://accounts.google.com/o/oauth2/v2/auth?" + \
"&".join(f"{k}={v}" for k, v in params.items())
return redirect(url)
@app.route("/api/auth/google/callback")
def google_callback():
"""Handle Google's OAuth2 callback."""
code = request.args.get("code")
if not code:
return jsonify({"error": "Missing authorization code"}), 400
# Exchange code for tokens
token_resp = requests.post("https://oauth2.googleapis.com/token", data={
"code": code,
"client_id": GOOGLE_CLIENT_ID,
"client_secret": GOOGLE_CLIENT_SECRET,
"redirect_uri": "https://api.durga-antivirus.com/api/auth/google/callback",
"grant_type": "authorization_code"
})
tokens = token_resp.json()
# Verify the ID token
try:
id_info = id_token.verify_oauth2_token(
tokens["id_token"],
google_requests.Request(),
GOOGLE_CLIENT_ID
)
except ValueError:
return jsonify({"error": "Invalid ID token"}), 401
# Extract user info
user_data = {
"sub": id_info["sub"],
"email": id_info.get("email"),
"name": id_info.get("name"),
"picture": id_info.get("picture"),
"provider": "google"
}
# Create or link account
session_token = handle_social_user(user_data)
return jsonify({
"access_token": session_token,
"user": {"email": user_data["email"], "name": user_data["name"]}
})
Code Example: GitHub and Apple Sign-In
# GitHub OAuth2
@app.route("/api/auth/github/login")
def github_login():
params = {
"client_id": os.environ.get("GITHUB_CLIENT_ID"),
"redirect_uri": "https://api.durga-antivirus.com/api/auth/github/callback",
"scope": "read:user user:email",
"state": secrets.token_urlsafe(16)
}
url = "https://github.com/login/oauth/authorize?" + \
"&".join(f"{k}={v}" for k, v in params.items())
return redirect(url)
@app.route("/api/auth/github/callback")
def github_callback():
code = request.args.get("code")
# Exchange code for access token
token_resp = requests.post(
"https://github.com/login/oauth/access_token",
headers={"Accept": "application/json"},
data={
"client_id": os.environ.get("GITHUB_CLIENT_ID"),
"client_secret": os.environ.get("GITHUB_CLIENT_SECRET"),
"code": code
}
)
access_token = token_resp.json().get("access_token")
# Fetch user info
user_resp = requests.get(
"https://api.github.com/user",
headers={"Authorization": f"Bearer {access_token}"}
)
github_user = user_resp.json()
user_data = {
"sub": str(github_user["id"]),
"email": github_user.get("email") or fetch_github_email(access_token),
"name": github_user.get("name", github_user["login"]),
"picture": github_user.get("avatar_url"),
"provider": "github"
}
session_token = handle_social_user(user_data)
return jsonify({"access_token": session_token, "user": user_data})
# Apple Sign-In
@app.route("/api/auth/apple/login")
def apple_login():
params = {
"client_id": os.environ.get("APPLE_CLIENT_ID"),
"redirect_uri": "https://api.durga-antivirus.com/api/auth/apple/callback",
"response_type": "code id_token",
"scope": "name email",
"state": secrets.token_urlsafe(16),
"response_mode": "form_post"
}
url = "https://appleid.apple.com/auth/authorize?" + \
"&".join(f"{k}={v}" for k, v in params.items())
return redirect(url)
Code Example: Account Linking Across Providers
# User account store
user_accounts = {}
user_links = {} # identity_provider:sub -> user_id
def handle_social_user(user_data):
"""Create or link a social login user account."""
provider = user_data["provider"]
provider_sub = user_data["sub"]
# Check if this social identity is already linked
link_key = f"{provider}:{provider_sub}"
existing_user = user_links.get(link_key)
if existing_user:
return issue_session_token(existing_user)
# Check if a user with this email exists
email = user_data.get("email")
if email:
for uid, ua in user_accounts.items():
if ua.get("email") == email:
# Link this social account to existing user
user_links[link_key] = uid
ua.setdefault("linked_providers", []).append(provider)
return issue_session_token(uid)
# Create new user
user_id = f"user_{secrets.token_hex(8)}"
user_accounts[user_id] = {
"email": email,
"name": user_data.get("name"),
"picture": user_data.get("picture"),
"linked_providers": [provider],
"created_at": datetime.datetime.utcnow().isoformat()
}
user_links[link_key] = user_id
return issue_session_token(user_id)
def issue_session_token(user_id):
return jwt.encode({
"sub": user_id,
"auth_method": "social",
"iat": datetime.datetime.utcnow(),
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=24)
}, SECRET, algorithm="HS256")
Common Mistakes
1. Trusting Provider Access Tokens Without Verification
Always verify tokens from the provider (ID token validation, userinfo endpoint call). Never trust a token just because it came from the provider.
2. Not Handling Email Changes
Users may change their email on the provider side. Handle this by periodically re-verifying the primary email or allowing email updates within your app.
3. Breaking the Login Flow on Mobile
Mobile apps need proper redirect URI configuration (custom schemes, universal links). Test the flow on actual devices.
4. No Account Unlinking
Users should be able to unlink a social provider from their account. Implement explicit unlinking with confirmation.
5. Missing Error Handling for Provider Downtime
Social login providers may be unavailable. Provide alternative login methods and show clear error messages.
Practice Questions
- How does the social login flow differ from standard OAuth2?
- Why must the ID token be verified server-side?
- How does account linking work across multiple providers?
- What provider-specific differences exist between Google and Apple sign-in?
- How do you handle email changes from the provider?
Answers:
- Social login uses the same OAuth2 Authorization Code flow, but the identity provider issues tokens containing user profile information that your app trusts.
- The client could receive a manipulated token. Server-side verification (checking the signature, issuer, and audience) ensures the token is genuine.
- When a user signs in with a new provider, check if the email matches an existing account. If so, link the new provider to the existing account.
- Apple requires the private key (not client secret) for token exchange and always returns the user's email in a JWT. Google uses simpler client_secret_basic authentication.
- Periodically re-fetch user info from the provider. If the email changed, update your records and send a notification to both old and new emails.
Challenge: Build a social login system supporting Google and GitHub with account linking, profile synchronization, and provider unlinking.
FAQ
Mini Project
Build a social login system with Google, GitHub, and Apple integration. Include server-side token verification, account linking, profile synchronization, and a unified login widget that offers all three options.
What's Next
Now learn about LDAP Authentication Bind for integrating enterprise directory services with your API.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro