OAuth2 authentication
Configure an enabled OAuth2 provider and exchange a redirect code for an authenticated PocketBase record.
Use OAuth2 to authenticate records in an auth collection with a configured provider. This guide covers the provider configuration boundary, the SDK popup flow, and the manual redirect-and-code-exchange flow.
Before you begin
- Create an OAuth2 application with your provider and obtain its client credentials.
- Use an auth collection such as
users. OAuth2 is not supported for the_superuserscollection. - Enable the provider in the auth collection's OAuth2 options. PocketBase rejects a provider that is missing or disabled.
- Register
https://yourdomain.example/api/oauth2-redirectas the provider callback. For local testing, usehttp://127.0.0.1:8090/api/oauth2-redirect.
Understand the flow
The SDK flow creates a temporary realtime connection for the provider popup. The manual flow uses a page that starts authorization and a redirect page that validates state before sending the code to PocketBase.
Steps
Open the auth collection options and enable the provider you registered. Keep the provider name exactly as PocketBase reports it from listAuthMethods().
The provider appears in the returned OAuth2 provider list. If it does not, check that the provider exists and is enabled before attempting authentication.
Start authentication from a user gesture so the browser can open the provider window:
import PocketBase from "pocketbase";
const pb = new PocketBase("http://127.0.0.1:8090");
pb.collection("users").authWithOAuth2({ provider: "google" })
.then((authData) => {
console.log(pb.authStore.isValid);
console.log(authData.record.id);
})
.catch((error) => console.error("OAuth2 sign-in failed", error));On success, authStore.isValid is true and the response contains the authenticated record. If a browser blocks the popup, call this code directly from the click handler.
Request the enabled providers and send the user to the selected provider URL. Store the provider metadata, including its state and code verifier, in short-lived local state for the redirect page.
const methods = await pb.collection("users").listAuthMethods();
const provider = methods.oauth2.providers.find((item) => item.name === "google");
if (!provider) throw new Error("OAuth2 provider is not enabled");
sessionStorage.setItem("oauth2-provider", JSON.stringify(provider));
window.location.assign(provider.authURL + "http://127.0.0.1:8090/redirect.html");The provider displays its consent screen and redirects back with an authorization code and state.
In the redirect page, compare the returned state with the stored provider state before exchanging the code:
const params = new URL(window.location).searchParams;
const provider = JSON.parse(sessionStorage.getItem("oauth2-provider"));
const redirectURL = "http://127.0.0.1:8090/redirect.html";
if (provider.state !== params.get("state")) {
throw new Error("OAuth2 state parameters do not match");
}
const authData = await pb.collection("users").authWithOAuth2Code(
provider.name,
params.get("code"),
provider.codeVerifier,
redirectURL,
{ emailVisibility: false },
);
console.log(authData.record.id);PocketBase exchanges the code and returns the authenticated record and token. Remove the temporary provider metadata after the exchange.
Troubleshoot failures
If PocketBase reports that the provider is missing or not enabled, enable that exact provider in the auth collection and obtain a fresh provider list. If the state values differ, stop the exchange and restart authorization; do not bypass the comparison. If the callback does not arrive, compare the registered callback with the URL used by the flow, including scheme, host, port, and path.
Apple manual code exchange requires a POST-capable redirect handler when the provider sends the user's name and email with response_mode=form_post. A GET handler is sufficient only when the flow uses query parameters and you do not need those fields.
Next step
Continue with verification, password reset, and email change to complete the account lifecycle.