Skip to main content
The AdonisJS Starter Kit includes built-in social authentication support using the @adonisjs/ally package. This allows users to sign in with their Google, GitHub, or other social provider accounts.

Overview

The social authentication flow:
  1. User clicks “Sign in with Google” (or another provider)
  2. User is redirected to the provider’s OAuth page
  3. User authorizes your application
  4. Provider redirects back to your app with user data
  5. User is automatically created or logged in

Setting Up Google Authentication

1

Create Google OAuth credentials

  1. Go to the Google Cloud Console
  2. Create a new project or select an existing one
  3. Navigate to APIs & ServicesCredentials
  4. Click Create CredentialsOAuth client ID
  5. Select Web application as the application type
  6. Add authorized redirect URIs:
    • http://localhost:3333/google/callback (for development)
    • https://yourdomain.com/google/callback (for production)
  7. Copy the Client ID and Client Secret
2

Configure environment variables

Add your Google credentials to the .env file:
.env
GOOGLE_CLIENT_ID=your_client_id_here
GOOGLE_CLIENT_SECRET=your_client_secret_here
VITE_API_URL=http://localhost:3333
Never commit your .env file to version control. Always use .env.example as a template.
3

Verify configuration

The Google provider is already configured in config/ally.ts:
config/ally.ts
import env from '#start/env'
import { defineConfig, services } from '@adonisjs/ally'

const allyConfig = defineConfig({
  google: services.google({
    clientId: env.get('GOOGLE_CLIENT_ID'),
    clientSecret: env.get('GOOGLE_CLIENT_SECRET'),
    callbackUrl: env.get('VITE_API_URL') + '/google/callback',
  }),
})

export default allyConfig
4

Test the integration

  1. Start your development server:
    pnpm run dev
    
  2. Navigate to the sign-up page
  3. Click the “Sign in with Google” button
  4. Complete the OAuth flow
  5. Verify that you’re logged in

How Social Authentication Works

The social authentication is handled by the SocialController:
app/auth/controllers/social_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import { afterAuthRedirectRoute } from '#config/auth'
import User from '#users/models/user'

export default class SocialController {
  // Redirect to OAuth provider
  async redirect({ ally, params }: HttpContext) {
    const driverInstance = ally.use(params.provider)
    return driverInstance.redirect()
  }

  // Handle OAuth callback
  async callback({ ally, auth, params, response, session }: HttpContext) {
    const social = ally.use(params.provider)

    // Handle access denied
    if (social.accessDenied()) {
      session.flash('errors', 'auth.social.error.access_denied')
      return response.redirect().toRoute('auth.sign_up.show')
    }

    // Handle state mismatch (CSRF protection)
    if (social.stateMisMatch()) {
      session.flash('errors', 'auth.social.error.state_mismatch')
      return response.redirect().toRoute('auth.sign_up.show')
    }

    // Handle other errors
    if (social.hasError()) {
      session.flash('errors', 'auth.social.error.uknown')
      return response.redirect().toRoute('auth.sign_up.show')
    }

    // Get user info from provider
    const socialUser = await social.user()

    // Find or create user
    let user = await User.findBy('email', socialUser.email)

    if (!user) {
      user = await User.create({
        fullName: socialUser.name,
        email: socialUser.email,
        password: null,
        avatarUrl: socialUser.avatarUrl,
      })
    }

    // Log in the user
    await auth.use('web').login(user)

    return response.redirect().toRoute(afterAuthRedirectRoute)
  }
}

Routes Configuration

Social authentication routes are defined in app/auth/routes.ts:
app/auth/routes.ts
import router from '@adonisjs/core/services/router'

const SocialController = () => import('#auth/controllers/social_controller')

router
  .get('/:provider/redirect', [SocialController, 'redirect'])
  .where('provider', /google/)
  .as('social.create')

router
  .get('/:provider/callback', [SocialController, 'callback'])
  .where('provider', /google/)

Adding More Providers

1

Install provider support (if needed)

AdonisJS Ally supports many providers out of the box:
  • Google
  • GitHub
  • Twitter
  • Facebook
  • Discord
  • LinkedIn
  • And more…
2

Add provider configuration

Update config/ally.ts to include the new provider:
config/ally.ts
const allyConfig = defineConfig({
  google: services.google({
    clientId: env.get('GOOGLE_CLIENT_ID'),
    clientSecret: env.get('GOOGLE_CLIENT_SECRET'),
    callbackUrl: env.get('VITE_API_URL') + '/google/callback',
  }),
  
  github: services.github({
    clientId: env.get('GITHUB_CLIENT_ID'),
    clientSecret: env.get('GITHUB_CLIENT_SECRET'),
    callbackUrl: env.get('VITE_API_URL') + '/github/callback',
  }),
})
3

Update routes

Modify the route regex to include the new provider:
router
  .get('/:provider/redirect', [SocialController, 'redirect'])
  .where('provider', /google|github/)
  .as('social.create')

router
  .get('/:provider/callback', [SocialController, 'callback'])
  .where('provider', /google|github/)
4

Add environment variables

.env
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret

Security Considerations

CSRF Protection

The Ally package includes built-in CSRF protection through state verification. Never disable this in production.

Email Verification

Social providers verify email addresses, so users authenticated via OAuth can be considered verified.

Profile Updates

Consider syncing user profile data (name, avatar) on each login to keep it up to date.

Troubleshooting

Ensure the callback URL in your OAuth provider settings exactly matches the one in your config:
http://localhost:3333/google/callback  ✓
http://localhost:3333/google/callback/ ✗ (trailing slash)
This usually happens when:
  • Session cookies are not being saved
  • You’re testing across different domains (localhost vs 127.0.0.1)
  • Session expires between redirect and callback
Check your session configuration in config/session.ts.
The user clicked “Cancel” or “Deny” on the OAuth consent screen. This is normal behavior and should be handled gracefully with a helpful error message.
Verify that:
  • Client ID and Secret are correct
  • OAuth app is enabled in provider settings
  • Environment variables are loaded correctly

Environment Variables Reference

.env.example
# Google OAuth
GOOGLE_CLIENT_ID=<your-key>
GOOGLE_CLIENT_SECRET=<your-key>

# Application URL (used for callbacks)
VITE_API_URL=http://localhost:3333

Resources

AdonisJS Ally Docs

Official social authentication guide

Google OAuth Setup

Google Cloud Console

GitHub OAuth Apps

Create GitHub OAuth apps

Supported Providers

View all supported providers

Build docs developers (and LLMs) love