Skip to main content

Auth Providers

Auth providers handle user authentication and identity management. They support various authentication methods including email/password, OAuth providers (Google, GitHub), and custom authentication flows.

Available Auth Providers

Medusa includes three authentication providers:

Email/Password Provider

@medusajs/medusa/auth-emailpass - Traditional email and password authentication. Features:
  • User registration with email and password
  • Secure password hashing using Scrypt
  • Password updates
  • Email-based authentication

Google OAuth Provider

@medusajs/medusa/auth-google - Google OAuth 2.0 authentication. Features:
  • Google Sign-In integration
  • OAuth 2.0 flow
  • Email verification enforcement
  • User profile metadata (name, picture, etc.)

GitHub OAuth Provider

@medusajs/medusa/auth-github - GitHub OAuth authentication. Features:
  • GitHub Sign-In integration
  • OAuth flow
  • User profile metadata

Installation

All auth providers are included in the core Medusa package:
npm install @medusajs/medusa

Configuration

Configure email/password authentication:
medusa-config.ts
import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
modules: [
{
  resolve: "@medusajs/medusa/auth",
  options: {
    providers: [
      {
        resolve: "@medusajs/medusa/auth-emailpass",
        id: "emailpass",
        options: {
          // Optional: customize password hashing
          hashConfig: {
            logN: 15, // CPU cost (default: 15)
            r: 8,     // Memory cost (default: 8)
            p: 1,     // Parallelization (default: 1)
          },
        },
      },
    ],
  },
},
],
})
Higher logN values increase security but slow down hashing. The default (15) provides a good balance for most applications.

Auth Provider Interface

All auth providers extend AbstractAuthModuleProvider and implement the following methods:

authenticate(input: AuthenticationInput, authIdentityService: AuthIdentityProviderService): Promise<AuthenticationResponse>

Authenticates a user. Email/Password Example:
const result = await authProvider.authenticate(
  {
    body: {
      email: "[email protected]",
      password: "securepassword",
    },
  },
  authIdentityService
)

if (result.success) {
  // Authentication successful
  console.log(result.authIdentity)
} else {
  // Authentication failed
  console.error(result.error)
}
OAuth Example (Google/GitHub):
// Initial authentication - redirects to OAuth provider
const result = await authProvider.authenticate(
  {
    query: {},
    body: {
      callback_url: "http://localhost:3000/auth/callback",
    },
  },
  authIdentityService
)

if (result.success && result.location) {
  // Redirect user to OAuth provider
  res.redirect(result.location)
}

register(input: AuthenticationInput, authIdentityService: AuthIdentityProviderService): Promise<AuthenticationResponse>

Registers a new user (email/password only).
const result = await authProvider.register(
  {
    body: {
      email: "[email protected]",
      password: "securepassword",
    },
  },
  authIdentityService
)

if (result.success) {
  // Registration successful
  console.log(result.authIdentity)
}
OAuth providers (Google, GitHub) throw an error when register is called. Use authenticate for OAuth flows.

validateCallback(input: AuthenticationInput, authIdentityService: AuthIdentityProviderService): Promise<AuthenticationResponse>

Validates OAuth callback (OAuth providers only).
// After OAuth redirect with code
const result = await authProvider.validateCallback(
  {
    query: {
      code: "oauth-code",
      state: "state-key",
    },
  },
  authIdentityService
)

if (result.success) {
  // User authenticated via OAuth
  console.log(result.authIdentity)
}

update(data: any, authIdentityService: AuthIdentityProviderService): Promise<AuthenticationResponse>

Updates auth identity (email/password only).
// Update password
const result = await authProvider.update(
  {
    entity_id: "[email protected]",
    password: "newsecurepassword",
  },
  authIdentityService
)

Using the Auth Module

Access auth providers through the Auth Module:
import { Modules } from "@medusajs/framework/utils"

const authModule = container.resolve(Modules.AUTH)

// Authenticate with email/password
const authUser = await authModule.authenticate("emailpass", {
  body: {
    email: "[email protected]",
    password: "password",
  },
})

// Register new user
const newUser = await authModule.register("emailpass", {
  body: {
    email: "[email protected]",
    password: "password",
  },
})

Authentication Flows

Email/Password Flow

  1. Registration:
    POST /auth/emailpass/register
    {
      "email": "[email protected]",
      "password": "securepassword"
    }
    
  2. Login:
    POST /auth/emailpass
    {
      "email": "[email protected]",
      "password": "securepassword"
    }
    
  3. Password Update:
    POST /auth/emailpass/update
    {
      "email": "[email protected]",
      "password": "newsecurepassword"
    }
    

OAuth Flow (Google/GitHub)

  1. Initiate OAuth:
    GET /auth/google
    // Redirects to Google OAuth consent screen
    
  2. Handle Callback:
    GET /auth/google/callback?code=xxx&state=xxx
    // Validates OAuth code and creates/updates auth identity
    
  3. Access User Data:
    // Auth identity contains user metadata
    {
      "entity_id": "google-user-id",
      "provider_identities": [
        {
          "provider": "google",
          "user_metadata": {
            "email": "[email protected]",
            "name": "John Doe",
            "picture": "https://...",
            "given_name": "John",
            "family_name": "Doe"
          }
        }
      ]
    }
    

Creating Custom Auth Providers

Create a custom auth provider by extending AbstractAuthModuleProvider:
packages/modules/providers/auth-custom/src/services/custom-auth.ts
import {
  AbstractAuthModuleProvider,
  MedusaError,
} from "@medusajs/framework/utils"
import {
  AuthenticationInput,
  AuthenticationResponse,
  AuthIdentityProviderService,
  Logger,
} from "@medusajs/framework/types"

type InjectedDependencies = {
  logger: Logger
}

interface CustomAuthConfig {
  apiKey: string
  apiSecret: string
}

export class CustomAuthService extends AbstractAuthModuleProvider {
  static identifier = "custom-auth"
  static DISPLAY_NAME = "Custom Authentication"

  protected config_: CustomAuthConfig
  protected logger_: Logger

  static validateOptions(options: CustomAuthConfig) {
    if (!options.apiKey) {
      throw new Error("API key is required")
    }
    if (!options.apiSecret) {
      throw new Error("API secret is required")
    }
  }

  constructor(
    { logger }: InjectedDependencies,
    options: CustomAuthConfig
  ) {
    // @ts-ignore
    super(...arguments)
    this.config_ = options
    this.logger_ = logger
  }

  async authenticate(
    input: AuthenticationInput,
    authIdentityService: AuthIdentityProviderService
  ): Promise<AuthenticationResponse> {
    const { email, token } = input.body ?? {}

    if (!email || !token) {
      return {
        success: false,
        error: "Email and token are required",
      }
    }

    try {
      // Validate token with your auth service
      const userData = await this.validateToken(token)

      // Get or create auth identity
      let authIdentity
      try {
        authIdentity = await authIdentityService.retrieve({
          entity_id: userData.id,
        })
      } catch (error) {
        if (error.type === MedusaError.Types.NOT_FOUND) {
          authIdentity = await authIdentityService.create({
            entity_id: userData.id,
            user_metadata: {
              email: userData.email,
              name: userData.name,
            },
          })
        } else {
          throw error
        }
      }

      return {
        success: true,
        authIdentity,
      }
    } catch (error) {
      return {
        success: false,
        error: error.message,
      }
    }
  }

  async register(
    input: AuthenticationInput,
    authIdentityService: AuthIdentityProviderService
  ): Promise<AuthenticationResponse> {
    // Implement registration logic
    throw new MedusaError(
      MedusaError.Types.NOT_ALLOWED,
      "Registration not supported"
    )
  }

  private async validateToken(token: string) {
    // Validate token with your auth service
    // Return user data
  }
}
Register your custom provider:
packages/modules/providers/auth-custom/src/index.ts
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
import { CustomAuthService } from "./services/custom-auth"

export default ModuleProvider(Modules.AUTH, {
  services: [CustomAuthService],
})

Reference

  • Email/Password: packages/modules/providers/auth-emailpass/src/services/emailpass.ts
  • Google OAuth: packages/modules/providers/auth-google/src/services/google.ts
  • GitHub OAuth: packages/modules/providers/auth-github/src/services/github.ts
  • Base class: packages/core/utils/src/auth/abstract-auth-module-provider.ts
  • Types: packages/core/types/src/auth/provider.ts

Next Steps

Providers Overview

Explore other provider types

Notification Providers

Send welcome emails to new users

Build docs developers (and LLMs) love