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:
type OAuth2Config struct {
Providers [] OAuth2ProviderConfig `json:"providers"`
MappedFields OAuth2KnownFields `json:"mappedFields"`
Enabled bool `json:"enabled"`
}
Provider configuration
Each OAuth2 provider requires specific configuration:
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
}
For standard providers (Google, GitHub, etc.), you only need to provide the name, clientId, and clientSecret. The URLs are automatically configured.
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:
Get auth methods
Retrieve available OAuth2 providers and their authorization URLs. curl http://localhost:8090/api/collections/users/auth-methods
Response: {
"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 }
}
Redirect to provider
Redirect the user to the provider’s authorization URL with your redirect URI appended. // The authURL needs a redirect_uri parameter
const redirectURL = 'https://yourdomain.com/auth/callback' ;
const authURL = provider . authURL + encodeURIComponent ( redirectURL );
window . location . href = authURL ;
Handle callback
After user authorizes, the provider redirects back with an authorization code. // Extract code from URL
const urlParams = new URLSearchParams ( window . location . search );
const code = urlParams . get ( 'code' );
Exchange code for token
Send the authorization code to PocketBase to complete authentication. 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": {}
}'
Receive auth token
PocketBase returns the auth token and user record. {
"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
}
}
Authentication endpoint
POST /api/collections/{collection}/auth-with-oauth2
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"
}'
Request body:
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:
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:
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:
// 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
}
If the mapped field for avatarURL is a file field, PocketBase automatically downloads the avatar image. Otherwise, it stores the URL as a string.
New user creation
When a user authenticates with OAuth2 for the first time, PocketBase creates a new record. You can provide additional data:
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:
// 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:
// 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 ),
)
}
You can override the default PKCE setting for any provider using the pkce field in the provider configuration.
Existing user linking
When a user authenticates with OAuth2, PocketBase tries to link the OAuth2 account to an existing user in this order:
Check for existing OAuth2 link
Search for an existing ExternalAuth record with the same provider and provider ID.
Use authenticated user
If the user is already authenticated (e.g., linking additional OAuth2 account), use the current auth record.
Match by email
If the OAuth2 user has an email, search for an auth record with that email.
Create new user
If no existing user is found, create a new auth record.
// 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:
// 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:
// 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:
// 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:
// Disable PKCE for LinkedIn if needed
pkce := false
providerConfig . PKCE = & pkce
Security considerations
Always validate the redirectURL to prevent authorization code interception attacks. Only allow URLs from your application’s domain.
Best practices
Use HTTPS : Always use HTTPS for redirect URLs in production.
Validate state parameter : Although PocketBase handles this internally, ensure your client validates the state parameter to prevent CSRF attacks.
Store secrets securely : Never expose OAuth2 client secrets in client-side code.
Limit provider scopes : Only request the minimum required OAuth2 scopes.
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:
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:
// 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.
Email/Password auth Set up traditional password authentication
MFA Require multiple auth methods
OTP authentication Enable one-time password login
API rules Configure access control