Skip to content

Social Login Providers — Google, GitHub, and Apple Authentication for APIs

DodaTech Updated 2026-06-28 6 min read

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

  1. How does the social login flow differ from standard OAuth2?
  2. Why must the ID token be verified server-side?
  3. How does account linking work across multiple providers?
  4. What provider-specific differences exist between Google and Apple sign-in?
  5. How do you handle email changes from the provider?

Answers:

  1. Social login uses the same OAuth2 Authorization Code flow, but the identity provider issues tokens containing user profile information that your app trusts.
  2. The client could receive a manipulated token. Server-side verification (checking the signature, issuer, and audience) ensures the token is genuine.
  3. 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.
  4. 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.
  5. 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

Is social login secure?

Yes, when properly implemented. The OAuth2 flow uses server-side token exchange. Verify the ID token server-side and always use HTTPS.

Can users sign in with multiple providers?

Yes. Link multiple social accounts to a single user profile. Users can then sign in with any linked provider.

What data does each provider share?

Google: email, name, profile picture. GitHub: email, username, avatar. Apple: email, name (with user consent).

How do I handle users who want to use both social and password auth?

Offer both options. When the user adds a password, they can bypass social login. The social accounts remain linked.

What is the best provider to start with?

Start with Google for the widest reach. Add GitHub for developer-focused audiences. Add Apple specifically for iOS users.

Do I need all three providers?

Start with Google. Add GitHub if your audience includes developers. Add Apple if you have an iOS app (Apple requires Sign in with Apple for apps using social login).

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