> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pocketbase/pocketbase/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth2 authentication

> Set up social login with OAuth2 providers in PocketBase

OAuth2 authentication allows users to sign in using their existing accounts from popular providers like Google, GitHub, Facebook, and more.

## Configuration

You can enable OAuth2 authentication in your auth collection settings:

```go theme={null}
type OAuth2Config struct {
    Providers    []OAuth2ProviderConfig `json:"providers"`
    MappedFields OAuth2KnownFields      `json:"mappedFields"`
    Enabled      bool                   `json:"enabled"`
}
```

### Provider configuration

Each OAuth2 provider requires specific configuration:

```go theme={null}
type OAuth2ProviderConfig struct {
    Name         string         `json:"name"`          // Provider name (e.g., "google", "github")
    ClientId     string         `json:"clientId"`      // OAuth2 client ID
    ClientSecret string         `json:"clientSecret"`  // OAuth2 client secret
    AuthURL      string         `json:"authURL"`       // Authorization URL (optional)
    TokenURL     string         `json:"tokenURL"`      // Token exchange URL (optional)
    UserInfoURL  string         `json:"userInfoURL"`   // User info URL (optional)
    DisplayName  string         `json:"displayName"`   // Custom display name (optional)
    PKCE         *bool          `json:"pkce"`          // Override PKCE setting (optional)
    Extra        map[string]any `json:"extra"`         // Additional provider options
}
```

<Note>
  For standard providers (Google, GitHub, etc.), you only need to provide the `name`, `clientId`, and `clientSecret`. The URLs are automatically configured.
</Note>

### Supported providers

PocketBase supports 15+ OAuth2 providers out of the box:

* Google
* GitHub
* Facebook
* GitLab
* Discord
* Twitter
* Microsoft
* Spotify
* Kakao
* Twitch
* Strava
* Gitee
* LiveChat
* Gitea
* OIDC (OpenID Connect)
* Apple
* Instagram
* VK
* Yandex
* Patreon

## OAuth2 flow

The OAuth2 authentication process follows these steps:

<Steps>
  <Step title="Get auth methods">
    Retrieve available OAuth2 providers and their authorization URLs.

    ```bash theme={null}
    curl http://localhost:8090/api/collections/users/auth-methods
    ```

    **Response:**

    ```json theme={null}
    {
      "oauth2": {
        "enabled": true,
        "providers": [
          {
            "name": "google",
            "displayName": "Google",
            "state": "abc123random",
            "authURL": "https://accounts.google.com/o/oauth2/auth?...",
            "codeVerifier": "...",
            "codeChallenge": "...",
            "codeChallengeMethod": "S256"
          }
        ]
      },
      "password": { "enabled": true, "identityFields": ["email"] },
      "otp": { "enabled": false, "duration": 0 },
      "mfa": { "enabled": false, "duration": 0 }
    }
    ```
  </Step>

  <Step title="Redirect to provider">
    Redirect the user to the provider's authorization URL with your redirect URI appended.

    ```javascript theme={null}
    // The authURL needs a redirect_uri parameter
    const redirectURL = 'https://yourdomain.com/auth/callback';
    const authURL = provider.authURL + encodeURIComponent(redirectURL);
    window.location.href = authURL;
    ```
  </Step>

  <Step title="Handle callback">
    After user authorizes, the provider redirects back with an authorization code.

    ```javascript theme={null}
    // Extract code from URL
    const urlParams = new URLSearchParams(window.location.search);
    const code = urlParams.get('code');
    ```
  </Step>

  <Step title="Exchange code for token">
    Send the authorization code to PocketBase to complete authentication.

    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/auth-with-oauth2 \
      -H "Content-Type: application/json" \
      -d '{
        "provider": "google",
        "code": "AUTHORIZATION_CODE",
        "codeVerifier": "CODE_VERIFIER_FROM_STEP_1",
        "redirectURL": "https://yourdomain.com/auth/callback",
        "createData": {}
      }'
    ```
  </Step>

  <Step title="Receive auth token">
    PocketBase returns the auth token and user record.

    ```json theme={null}
    {
      "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "record": {
        "id": "RECORD_ID",
        "email": "user@example.com",
        "verified": true,
        ...
      },
      "meta": {
        "id": "oauth2_user_id",
        "name": "John Doe",
        "username": "johndoe",
        "email": "user@example.com",
        "avatarURL": "https://...",
        "isNew": true
      }
    }
    ```
  </Step>
</Steps>

## Authentication endpoint

**POST** `/api/collections/{collection}/auth-with-oauth2`

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST http://localhost:8090/api/collections/users/auth-with-oauth2 \
    -H "Content-Type: application/json" \
    -d '{
      "provider": "google",
      "code": "4/0AWtgzh7...",
      "codeVerifier": "M6H2xWGzJ...",
      "redirectURL": "http://localhost:3000/auth/callback"
    }'
  ```

  ```javascript JavaScript theme={null}
  const pb = new PocketBase('http://localhost:8090');

  // Get the auth methods to retrieve provider info
  const authMethods = await pb.collection('users').listAuthMethods();

  // Find your provider
  const googleProvider = authMethods.oauth2.providers.find(
    (p) => p.name === 'google'
  );

  // Redirect user to provider
  const redirectURL = 'http://localhost:3000/auth/callback';
  window.location.href = googleProvider.authURL + encodeURIComponent(redirectURL);

  // After callback, exchange code for token
  const authData = await pb.collection('users').authWithOAuth2Code(
    'google',
    code,
    googleProvider.codeVerifier,
    redirectURL
  );
  ```

  ```go Go theme={null}
  // Handle OAuth2 callback
  func handleOAuth2Callback(e *core.RequestEvent) error {
      code := e.Request.URL.Query().Get("code")
      provider := e.Request.URL.Query().Get("provider")
      
      collection, _ := e.App.FindCachedCollectionByNameOrId("users")
      
      // The OAuth2 flow is handled by the API endpoint
      // This is an example of manual token exchange
      providerConfig, _ := collection.OAuth2.GetProviderConfig(provider)
      client, _ := providerConfig.InitProvider()
      
      token, _ := client.FetchToken(code)
      authUser, _ := client.FetchAuthUser(token)
      
      // Find or create auth record
      return nil
  }
  ```
</CodeGroup>

**Request body:**

```go theme={null}
type recordOAuth2LoginForm struct {
    Provider     string         `json:"provider"`     // Required: provider name
    Code         string         `json:"code"`         // Required: authorization code
    CodeVerifier string         `json:"codeVerifier"` // Required for PKCE
    RedirectURL  string         `json:"redirectURL"`  // Required: same as initial request
    CreateData   map[string]any `json:"createData"`   // Optional: additional data for new users
}
```

## Field mapping

You can map OAuth2 user data to your collection fields:

```go theme={null}
type OAuth2KnownFields struct {
    Id        string `json:"id"`        // Map OAuth2 user ID
    Name      string `json:"name"`      // Map OAuth2 user name
    Username  string `json:"username"`  // Map OAuth2 username
    AvatarURL string `json:"avatarURL"` // Map OAuth2 avatar URL
}
```

**Example:**

```go theme={null}
collection.OAuth2.MappedFields.Name = "fullName"
collection.OAuth2.MappedFields.Username = "handle"
collection.OAuth2.MappedFields.AvatarURL = "profilePicture"
```

When a new user signs up via OAuth2, PocketBase automatically populates these fields:

```go theme={null}
// From apis/record_auth_with_oauth2.go:262
if _, ok := payload[e.Collection.OAuth2.MappedFields.Name]; !ok && 
   e.Collection.OAuth2.MappedFields.Name != "" {
    payload[e.Collection.OAuth2.MappedFields.Name] = e.OAuth2User.Name
}
```

<Note>
  If the mapped field for `avatarURL` is a file field, PocketBase automatically downloads the avatar image. Otherwise, it stores the URL as a string.
</Note>

## New user creation

When a user authenticates with OAuth2 for the first time, PocketBase creates a new record. You can provide additional data:

```javascript theme={null}
await pb.collection('users').authWithOAuth2Code(
  'google',
  code,
  codeVerifier,
  redirectURL,
  {
    // Additional fields for new user creation
    role: 'member',
    preferences: { theme: 'dark' }
  }
);
```

The implementation handles new user creation:

```go theme={null}
// From apis/record_auth_with_oauth2.go:250
payload := maps.Clone(e.CreateData)
if payload == nil {
    payload = map[string]any{}
}

// Assign OAuth2 user email if not submitted
if v, _ := payload[core.FieldNameEmail].(string); v == "" {
    payload[core.FieldNameEmail] = e.OAuth2User.Email
}

// Map known fields
if _, ok := payload[e.Collection.OAuth2.MappedFields.Id]; !ok && 
   e.Collection.OAuth2.MappedFields.Id != "" {
    payload[e.Collection.OAuth2.MappedFields.Id] = e.OAuth2User.Id
}
```

## PKCE (Proof Key for Code Exchange)

PocketBase supports PKCE for enhanced security. PKCE is automatically enabled for providers that support it:

```go theme={null}
// From apis/record_auth_methods.go:147
if provider.PKCE() {
    info.CodeVerifier = security.RandomString(43)
    info.CodeChallenge = security.S256Challenge(info.CodeVerifier)
    info.CodeChallengeMethod = "S256"
    urlOpts = append(urlOpts,
        oauth2.SetAuthURLParam("code_challenge", info.CodeChallenge),
        oauth2.SetAuthURLParam("code_challenge_method", info.CodeChallengeMethod),
    )
}
```

<Info>
  You can override the default PKCE setting for any provider using the `pkce` field in the provider configuration.
</Info>

## Existing user linking

When a user authenticates with OAuth2, PocketBase tries to link the OAuth2 account to an existing user in this order:

<Steps>
  <Step title="Check for existing OAuth2 link">
    Search for an existing `ExternalAuth` record with the same provider and provider ID.
  </Step>

  <Step title="Use authenticated user">
    If the user is already authenticated (e.g., linking additional OAuth2 account), use the current auth record.
  </Step>

  <Step title="Match by email">
    If the OAuth2 user has an email, search for an auth record with that email.
  </Step>

  <Step title="Create new user">
    If no existing user is found, create a new auth record.
  </Step>
</Steps>

```go theme={null}
// From apis/record_auth_with_oauth2.go:120
switch {
case err == nil && externalAuthRel != nil:
    authRecord, err = e.App.FindRecordById(form.collection, externalAuthRel.RecordRef())
case fallbackAuthRecord != nil && fallbackAuthRecord.Collection().Id == form.collection.Id:
    authRecord = fallbackAuthRecord
case authUser.Email != "":
    authRecord, err = e.App.FindAuthRecordByEmail(form.collection.Id, authUser.Email)
}
```

## Email verification

OAuth2 authenticated users are automatically verified if their email matches the OAuth2 provider's email:

```go theme={null}
// From apis/record_auth_with_oauth2.go:306
if e.Record.Email() == e.OAuth2User.Email && !e.Record.Verified() {
    e.Record.SetVerified(true)
    if err := txApp.Save(e.Record); err != nil {
        return err
    }
}
```

## Provider-specific notes

### Apple

Apple returns the user's name only during the first authorization. PocketBase handles this by storing the name temporarily:

```go theme={null}
// From apis/record_auth_with_oauth2.go:96
if form.Provider == auth.NameApple && authUser.Name == "" {
    nameKey := oauth2RedirectAppleNameStoreKeyPrefix + form.Code
    name, ok := e.App.Store().Get(nameKey).(string)
    if ok {
        e.App.Store().Remove(nameKey)
        authUser.Name = name
    }
}
```

Apple also uses `response_mode=form_post`:

```go theme={null}
// From apis/record_auth_methods.go:143
case auth.NameApple:
    urlOpts = append(urlOpts, 
        oauth2.SetAuthURLParam("response_mode", "form_post"))
```

### LinkedIn OIDC

Some providers like LinkedIn OIDC may require manual PKCE adjustment:

```go theme={null}
// Disable PKCE for LinkedIn if needed
pkce := false
providerConfig.PKCE = &pkce
```

## Security considerations

<Warning>
  Always validate the `redirectURL` to prevent authorization code interception attacks. Only allow URLs from your application's domain.
</Warning>

### Best practices

1. **Use HTTPS**: Always use HTTPS for redirect URLs in production.

2. **Validate state parameter**: Although PocketBase handles this internally, ensure your client validates the state parameter to prevent CSRF attacks.

3. **Store secrets securely**: Never expose OAuth2 client secrets in client-side code.

4. **Limit provider scopes**: Only request the minimum required OAuth2 scopes.

5. **Handle email conflicts**: Consider what happens when an OAuth2 email matches an existing password-authenticated user.

## Custom OAuth2 hooks

You can customize OAuth2 authentication behavior:

```go theme={null}
app.OnRecordAuthWithOAuth2Request().Bind(&hook.Handler[*core.RecordAuthWithOAuth2RequestEvent]{
    Func: func(e *core.RecordAuthWithOAuth2RequestEvent) error {
        // Add custom fields for new users
        if e.IsNewRecord {
            if e.CreateData == nil {
                e.CreateData = make(map[string]any)
            }
            e.CreateData["role"] = "oauth2_user"
            e.CreateData["verified"] = true
        }
        
        // Log OAuth2 authentications
        log.Printf("OAuth2 auth: %s via %s", 
            e.OAuth2User.Email, e.ProviderName)
        
        return e.Next()
    },
})
```

## OAuth2 redirect handler

PocketBase provides a global OAuth2 redirect handler for subscription redirects:

```go theme={null}
// From apis/record_auth.go:11
rg.GET("/oauth2-redirect", oauth2SubscriptionRedirect).Bind(
    SkipSuccessActivityLog(),
)
rg.POST("/oauth2-redirect", oauth2SubscriptionRedirect).Bind(
    SkipSuccessActivityLog(),
)
```

This endpoint handles OAuth2 callbacks and forwards them to your application.

## Related topics

<CardGroup cols={2}>
  <Card title="Email/Password auth" icon="envelope" href="/auth/email-password">
    Set up traditional password authentication
  </Card>

  <Card title="MFA" icon="shield-halved" href="/auth/mfa">
    Require multiple auth methods
  </Card>

  <Card title="OTP authentication" icon="key" href="/auth/otp">
    Enable one-time password login
  </Card>

  <Card title="API rules" icon="shield" href="/auth/api-rules">
    Configure access control
  </Card>
</CardGroup>
