KAHIBARO
Discord Login Register

13.12. Social Login

Why Social Login Matters

Social login lets users sign in with an existing account from a third‑party provider, such as Google, GitHub, Facebook, or Apple, instead of creating a new username and password.

From a backend perspective, social login is simply a special way to perform authentication using an external identity provider. Your application delegates the job of identifying the user to that provider, then uses the information you get back to create or look up a local user.

Some benefits:

Some downsides:

Your goal as a backend developer is to integrate these providers safely and predictably, not to fully trust them for everything.

Key idea: Social login is still your own authentication system. The provider only proves the user’s identity. You are responsible for:

  • Mapping that identity to a local user.
  • Creating and issuing your own session or tokens.
  • Enforcing your own authorization rules.

Core Social Login Flow

Most modern social login systems use OAuth 2.0 and often OpenID Connect on top. The specific details belong in the dedicated OAuth 2.0 and OpenID Connect chapters, but the core flow is always similar.

High‑Level Flow

Typical steps:

  1. User clicks “Continue with X” on your site.
  2. Your backend redirects the user to the provider’s authorization page.
  3. User signs in (if needed) and consents.
  4. Provider redirects back to your backend with a short‑lived authorization code.
  5. Your backend exchanges that code for:
    • An access token (to call the provider’s APIs),
    • Often an ID token (OpenID Connect) that describes the user.
  6. Your backend:
    • Validates the tokens.
    • Reads the user’s identity (id, email, etc.).
    • Creates or finds a corresponding local user.
    • Issues your own session or JWT access token to the client.

Example Sequence

Imagine a “Sign in with Google” flow for a web app:

  1. Frontend calls your backend: GET /auth/google/login.
  2. Backend builds a Google OAuth URL and replies with a redirect.
  3. Browser goes to Google, user logs in and consents.
  4. Google redirects back to https://your-api.com/auth/google/callback?code=XYZ.
  5. Backend receives code=XYZ, sends a POST request to Google’s token endpoint:
    • Includes client_id, client_secret, redirect_uri, and the code.
  6. Google responds with JSON, for example:
json
{
  "access_token": "ya29.a0Af...",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
  "expires_in": 3599,
  "token_type": "Bearer"
}
  1. Backend validates id_token, extracts the user email and provider user id.
  2. Backend looks up or creates a local user.
  3. Backend issues its own JWT access token and refresh token.
  4. Frontend now uses those local tokens to call your API.

Provider Identifiers and Local Users

Your database should never fully depend on an external provider structure. The provider is just a way to authenticate.

Local vs External Identity

You usually have:

Example tables:

users table

columntypedescription
idUUID / intPrimary key, local user id
emailstringUser’s main email
namestringDisplay name
created_atdatetimeCreation timestamp
is_activeboolActive flag

social_accounts table

columntypedescription
idUUID / intPrimary key
user_idFKReferences users.id
providerstringExample: "google", "github"
provider_user_idstringId given by the provider
emailstringEmail from provider (optional, may be null)
access_tokenstringOptional, if you call provider APIs
refresh_tokenstringOptional, if you need long‑term access
created_atdatetimeWhen this link was created

Important rule: The pair (provider, provider_user_id) must be unique and must always refer to one and only one local user.

Creating or Linking Users

When you receive a successful social login response, typical logic is:

  1. Extract:
    • provider (for example "google"),
    • provider_user_id,
    • primary email (if available),
    • name or profile info (optional).
  2. Try to find existing social account:
    • If you find one, you already know the user_id, log the user in.
  3. If not found:
    • Option A: Create a new local user and a new social account link.
    • Option B: If email matches an existing user, ask user to link this provider to that account.
    • Option C: If email missing or unverified, require extra steps (for example ask for an email).

Example pseudocode:

python
def handle_social_login(provider, provider_user_id, email, name):
    social = db.social_accounts.get(provider=provider, provider_user_id=provider_user_id)
    if social:
        user = db.users.get(id=social.user_id)
        return issue_tokens_for_user(user)
    if email:
        user = db.users.get_by_email(email)
    else:
        user = None
    if user:
        # Optional: ask user confirmation before linking
        social = db.social_accounts.create(
            provider=provider,
            provider_user_id=provider_user_id,
            user_id=user.id,
            email=email,
        )
    else:
        user = db.users.create(email=email, name=name)
        social = db.social_accounts.create(
            provider=provider,
            provider_user_id=provider_user_id,
            user_id=user.id,
            email=email,
        )
    return issue_tokens_for_user(user)

Common Providers and Data You Receive

Different providers give you different fields. You must read their documentation and adapt.

Google Example (OpenID Connect)

With Google, you often get an ID token that is a JWT. Its payload might look like:

json
{
  "iss": "https://accounts.google.com",
  "sub": "110169484474386276334",
  "email": "user@example.com",
  "email_verified": true,
  "name": "John Doe",
  "picture": "https://lh3.googleusercontent.com/a-/AOh14Gh...",
  "aud": "YOUR_CLIENT_ID.apps.googleusercontent.com",
  "exp": 1614077559,
  "iat": 1614073959
}

Important fields:

GitHub Example

GitHub usually gives you an access token and then you use its API to get user info:

http
GET https://api.github.com/user
Authorization: Bearer <access_token>

Response:

json
{
  "id": 123456,
  "login": "octocat",
  "email": "octocat@github.com",
  "name": "The Octocat"
}

You can treat:

Apple Example

Apple can behave differently. You often receive:

Because of that, you must be prepared that long‑term you can only rely on provider_user_id, not always on email.

Security Considerations

Social login adds external tokens and redirects. You must validate and protect every step.

Validating ID Tokens (JWTs)

For providers that use OpenID Connect, such as Google, you get an ID token that is a JWT. You must validate:

Always validate external tokens. Never trust any JWT or token from the client unless you verify:

  • Who signed it,
  • Who it is for,
  • That it has not expired.

CSRF and State Parameter

Avoid simple “click and redirect” flows that can be abused by cross‑site request forgery.

Use a state parameter:

  1. Generate a cryptographically random state value.
  2. Store it in the user’s session or a secure cookie.
  3. Include it in the authorization URL to the provider.
  4. When provider redirects back, verify that state matches what you stored.

Example:

python
state = generate_random_string()
store_state_in_session(state)
authorization_url = (
    f"https://accounts.google.com/o/oauth2/v2/auth"
    f"?client_id={CLIENT_ID}"
    f"&redirect_uri={REDIRECT_URI}"
    f"&response_type=code"
    f"&scope=openid%20email%20profile"
    f"&state={state}"
)

If incoming state does not match the stored value, reject the request.

HTTPS Required

You must use HTTPS in production:

This protects tokens and codes while they travel between browser, provider, and backend.

Token Storage

You can store the provider’s access token and refresh token in your database if you plan to call the provider’s APIs later, for example to access user data or contacts.

Keep in mind:

If you do not need long‑term provider access, you can avoid storing these tokens and only keep the identity mapping.

UX and Account Linking

Poorly designed social login can confuse users and produce duplicate accounts.

Avoiding Duplicate Accounts

Common problem:

  1. User signs up with email + password: user@example.com.
  2. Later clicks “Sign in with Google” using the same email.
  3. Your backend creates a second user with the same email.

To avoid that:

Example strategy:

Supporting Multiple Providers Per User

Users may want to connect several social accounts with the same local user, for example Google and GitHub.

Your social_accounts table should allow many records per user_id. This allows flows like:

  1. User logs in with email + password.
  2. From account settings, user clicks “Connect Google”.
  3. After social flow, you add a new social account entry for that local user.

Pseudocode:

python
def link_social_account(current_user, provider, provider_user_id, email):
    existing = db.social_accounts.get(
        provider=provider,
        provider_user_id=provider_user_id,
    )
    if existing and existing.user_id != current_user.id:
        raise ConflictError("This social account is already linked to another user.")
    if not existing:
        db.social_accounts.create(
            user_id=current_user.id,
            provider=provider,
            provider_user_id=provider_user_id,
            email=email,
        )

Social Login in API‑Only Backends

If your backend only exposes a REST API and your frontend is separate, the flow is slightly more complex, but the principles are the same.

Frontend vs Backend Redirects

Two common patterns:

  1. Backend handles redirects directly
    • Frontend calls your API endpoint to start social login.
    • Backend returns a redirect URL.
    • Browser goes directly to provider.
    • Provider returns to backend callback URL.
    • Backend responds with a redirect to your frontend, including an authorization code or a short‑lived token.
    • Frontend exchanges that for long‑lived tokens via another API call.
  2. Frontend handles provider SDKs (for example using Google or Facebook JS SDK)
    • Frontend talks directly to the provider, gets an ID token or access token.
    • Frontend sends that token to your backend in an API call.
    • Backend verifies the token with the provider, then creates or finds the user and issues local tokens.

The second approach is often simpler for SPAs, but you must be careful:

Example Backend‑Only API Flow (Simplified)

  1. GET /auth/google/url
    Backend responds:
json
   {
     "authorization_url": "https://accounts.google.com/o/oauth2/v2/auth?...&state=abc123"
   }
  1. Frontend redirects the user to that URL.
  2. After Google callback to /auth/google/callback?code=XYZ&state=abc123, backend:
    • Validates state.
    • Exchanges code for tokens.
    • Identifies or creates the user.
    • Issues its own JWT access and refresh tokens.
    • Redirects the user to a frontend URL like:

https://app.example.com/auth/callback?access_token=...&refresh_token=...

or better, uses a short code and lets the frontend call an API to exchange it for tokens, so you do not put long‑lived tokens in the URL.

Handling Errors and Edge Cases

Social login can fail at several points. Your backend must produce clear, consistent error responses.

Typical Error Scenarios

Consistent API Responses

Treat social login endpoints as part of your normal authentication API:

Example success response:

json
{
  "access_token": "local-jwt-token",
  "refresh_token": "local-refresh-token",
  "token_type": "Bearer",
  "user": {
    "id": 42,
    "email": "user@example.com",
    "name": "John Doe"
  }
}

Example error response:

json
{
  "error": "SOCIAL_LOGIN_FAILED",
  "message": "Google login failed: access denied by user."
}

Summary

Social login is an authentication feature where you:

If you keep these principles in mind, social login becomes:

Views: 11

Comments

Please login to add a comment.

Don't have an account? Register now!