HelloJohn / docs
Authentication

OAuth / Social Login

Add Google, GitHub, Apple, Microsoft, Discord, and 4 more OAuth providers to HelloJohn. Setup instructions, callback URLs, and SDK integration for all 9 providers.

HelloJohn supports 9 OAuth providers out of the box. Users can sign in with any enabled provider, and their account is automatically linked or created.

Supported providers

ProviderEnvironment VariablesOAuth App URL
GoogleHELLOJOHN_GOOGLE_CLIENT_ID/SECRETconsole.cloud.google.com
GitHubHELLOJOHN_GITHUB_CLIENT_ID/SECRETgithub.com/settings/developers
AppleHELLOJOHN_APPLE_CLIENT_ID/KEY_ID/TEAM_ID/PRIVATE_KEYdeveloper.apple.com
MicrosoftHELLOJOHN_MICROSOFT_CLIENT_ID/SECRET/TENANT_IDportal.azure.com
DiscordHELLOJOHN_DISCORD_CLIENT_ID/SECRETdiscord.com/developers
Twitter/XHELLOJOHN_TWITTER_CLIENT_ID/SECRETdeveloper.twitter.com
FacebookHELLOJOHN_FACEBOOK_CLIENT_ID/SECRETdevelopers.facebook.com
LinkedInHELLOJOHN_LINKEDIN_CLIENT_ID/SECRETlinkedin.com/developers
SlackHELLOJOHN_SLACK_CLIENT_ID/SECRETapi.slack.com/apps

Configure a provider

1. Create an OAuth app

Create an OAuth app at your provider's developer console. Set the callback URL to:

https://your-hellojohn-instance.com/v1/oauth/callback/{provider}

Examples:

https://auth.yourdomain.com/v1/oauth/callback/google
https://auth.yourdomain.com/v1/oauth/callback/github
https://auth.yourdomain.com/v1/oauth/callback/apple

2. Set environment variables

.env
# Google
HELLOJOHN_GOOGLE_CLIENT_ID=123456789-abc.apps.googleusercontent.com
HELLOJOHN_GOOGLE_CLIENT_SECRET=GOCSPX-XXXXXXXXXXXXXXXX

# GitHub
HELLOJOHN_GITHUB_CLIENT_ID=Iv1.XXXXXXXXXXXXXXXX
HELLOJOHN_GITHUB_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Apple (more complex — see Apple section below)
HELLOJOHN_APPLE_CLIENT_ID=com.yourdomain.app
HELLOJOHN_APPLE_TEAM_ID=XXXXXXXXXX
HELLOJOHN_APPLE_KEY_ID=XXXXXXXXXX
HELLOJOHN_APPLE_PRIVATE_KEY_PATH=/secrets/AuthKey_XXXXXXXXXX.p8

3. Add the sign-in button

import { useAuth } from '@hellojohn/react'

function SocialSignIn() {
  const { signInWithOAuth } = useAuth()

  return (
    <div>
      <button onClick={() => signInWithOAuth({ provider: 'google' })}>
        Continue with Google
      </button>
      <button onClick={() => signInWithOAuth({ provider: 'github' })}>
        Continue with GitHub
      </button>
      <button onClick={() => signInWithOAuth({ provider: 'apple' })}>
        Continue with Apple
      </button>
    </div>
  )
}
import { useAuth } from '@hellojohn/vue'

const { signInWithOAuth } = useAuth()

// In your template:
// <button @click="signInWithOAuth({ provider: 'google' })">Continue with Google</button>
import { hellojohn } from './hellojohn'

await hellojohn.auth.signInWithOAuth({
  provider: 'google',
  redirectUrl: '/dashboard',
})

The SDK handles the OAuth redirect flow automatically. After the user authorizes your app, they're redirected back and the session is established.

Account linking

When a user signs in with OAuth using an email that already exists from a previous sign-up, HelloJohn automatically links the OAuth account to the existing user.

You can configure this behavior:

.env
# Behavior when OAuth email matches existing account:
# "link" (default) — link the OAuth provider to existing account
# "require_password" — require the user to confirm with their existing password
# "reject" — block OAuth login if email already exists via email/password
HELLOJOHN_OAUTH_ACCOUNT_LINK_STRATEGY=link

Apple Sign-In (special setup)

Apple Sign-In requires a .p8 private key file instead of a client secret:

.env
HELLOJOHN_APPLE_CLIENT_ID=com.yourdomain.app
HELLOJOHN_APPLE_TEAM_ID=XXXXXXXXXX        # 10-char Team ID from Apple Developer
HELLOJOHN_APPLE_KEY_ID=XXXXXXXXXX         # Key ID from your Sign in with Apple key
HELLOJOHN_APPLE_PRIVATE_KEY_PATH=/secrets/AuthKey_XXXXXXXXXX.p8

Apple requires https:// redirect URLs. You cannot use http://localhost in production. For local development, use a tunneling service like ngrok.

Using pre-built UI components

Each SDK provides pre-built OAuth buttons:

import { OAuthButton } from '@hellojohn/react'

// Single button
<OAuthButton provider="google" />

// All enabled providers
import { OAuthButtons } from '@hellojohn/react'
<OAuthButtons />
<template>
  <OAuthButton provider="google" />
  <!-- or all enabled providers: -->
  <OAuthButtons />
</template>

<script setup>
import { OAuthButton, OAuthButtons } from '@hellojohn/vue'
</script>

Accessing the OAuth access token

The OAuth provider's access token is stored (encrypted) and available for making provider-specific API calls:

import { useUser } from '@hellojohn/react'

const { user } = useUser()

// Get connected OAuth providers
console.log(user.oauthProviders)
// [{ provider: 'google', connected: true }, ...]

To get the raw OAuth access token on the server (for e.g. reading Google Calendar):

GET /v1/users/me/oauth-tokens/{provider}
Authorization: Bearer {user_access_token}

# Response
{
  "provider": "google",
  "access_token": "ya29.XXXXXXX",
  "expires_at": "2026-01-15T11:00:00Z"
}

Common errors

ErrorCauseSolution
oauth_provider_not_configuredProvider env vars not setAdd the provider credentials to .env
oauth_callback_url_mismatchCallback URL in OAuth app doesn't matchUpdate the callback URL in the provider's developer console
oauth_state_mismatchCSRF protection triggeredUsually caused by cross-tab issues; ask user to retry
oauth_email_requiredProvider didn't return an emailSome providers require explicit scope; add email scope

Next steps

On this page