Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 130 additions & 43 deletions docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Sign in with Google requires a Google Cloud project. You also need platform-specific OAuth credentials depending on which platforms you target.

## Prerequisites

Before following this guide, make sure you have:

- A Google account with access to [Google Cloud Console](https://console.cloud.google.com/).
- A running Serverpod project (server, client, and Flutter app packages from `serverpod create`).
- The Serverpod auth module installed and configured per the [authentication setup](../../setup). If your project was generated with an older Serverpod version, follow that guide first to add `serverpod_auth_idp_server` and `serverpod_auth_idp_flutter` and to configure `pod.initializeAuthServices()` before continuing.

## Get your Google credentials

All platforms require a Web application OAuth client (used by the server). iOS and Android additionally require their own platform-specific OAuth clients.
Expand Down Expand Up @@ -36,7 +44,9 @@ The People API is required for Serverpod to access basic user profile data durin

3. **Branding**: After completing the wizard, navigate to the [Branding](https://console.cloud.google.com/auth/branding) page from the sidebar. Fill in the remaining fields: app logo, app homepage link, privacy policy link, terms of service link, developer contact email, and **authorized domains**. These details appear on the OAuth consent screen shown to users during sign-in.

Add any domains you will use in production (e.g., `my-awesome-project.serverpod.space`) to **Authorized domains**. Google will reject redirect URIs that use domains not listed here.
Add the **root domain** you will deploy under to **Authorized domains**. Google stores only the top private domain, so a single root entry covers every subdomain you deploy under it.

If you deploy on Serverpod Cloud, add `serverpod.space`. It is already verified by Serverpod, so you only need to add it here, no DNS verification is required on your end. For custom domains, see [Verify your authorized domain](#1-verify-your-authorized-domain).

![Branding configuration](/img/authentication/providers/google/10-branding.png)

Expand All @@ -53,7 +63,7 @@ The People API is required for Serverpod to access basic user profile data durin
![Audience and test users](/img/authentication/providers/google/7-audience.png)

:::tip
Leave the app in **Testing** mode for now. You can [publish it](#publishing-to-production) after verifying that sign-in works end to end.
Leave the app in **Testing** mode for now. You can [publish it](#5-publish-the-oauth-consent-screen) after verifying that sign-in works end to end.
:::

### Create the server OAuth client (Web application)
Expand Down Expand Up @@ -284,51 +294,106 @@ Web uses the same server OAuth client you created earlier, so you don't need a s

### Initialize the Google sign-in service

In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client` and calls `client.auth.initialize()`. Add `client.auth.initializeGoogleSignIn()` right after it:
Open your Flutter app's `main.dart` (e.g., `my_project_flutter/lib/main.dart`). The Serverpod template already creates the `Client` and calls `client.auth.initialize()` inside `main()`. Add `client.auth.initializeGoogleSignIn()` on the line immediately after it. With the new line added, `main()` looks like this:

```dart
client.auth.initialize();
client.auth.initializeGoogleSignIn();
void main() async {
WidgetsFlutterBinding.ensureInitialized();

final serverUrl = await getServerUrl();

client = Client(serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

client.auth.initialize();
client.auth.initializeGoogleSignIn(); // add this line

runApp(const MyApp());
}
```

### Add the sign-in widget
`initializeGoogleSignIn()` initializes the underlying `google_sign_in` SDK (loading the client IDs you configured) and registers a sign-out hook so signing out of Serverpod also signs the user out of their Google session. `SignInWidget` can lazily initialize Google on first use, but calling this at startup wires the sign-out hook early and avoids a delay on the first tap.

If you have configured the `SignInWidget` as described in the [setup section](../../setup#present-the-authentication-ui), the Google identity provider will be automatically detected and displayed in the sign-in widget.
### Show the Google sign-in button

You can also use the `GoogleSignInWidget` directly in your widget tree to include the Google authentication flow in your own custom UI:
The Serverpod template ships with a `SignInScreen` widget at `lib/screens/sign_in_screen.dart`. It listens to `client.auth.authInfoListenable` and swaps between `SignInWidget` while the user is signed out and the `child` you pass it once they sign in. `SignInWidget` auto-detects which identity provider endpoints are registered on the server, so once `GoogleIdpEndpoint` is exposed and `serverpod generate` has run, the Google button appears inside it.

```dart
import 'package:flutter/material.dart';
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';

GoogleSignInWidget(
client: client,
onAuthenticated: () {
// Do something when the user is authenticated.
//
// NOTE: You should not navigate to the home screen here, otherwise
// the user will have to sign in again every time they open the app.
},
onError: (error) {
// Handle errors
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $error')),
);
},
)
import '../main.dart';

class SignInScreen extends StatefulWidget {
final Widget child;
const SignInScreen({super.key, required this.child});

@override
State<SignInScreen> createState() => _SignInScreenState();
}

class _SignInScreenState extends State<SignInScreen> {
bool _isSignedIn = false;

@override
void initState() {
super.initState();
client.auth.authInfoListenable.addListener(_updateSignedInState);
_isSignedIn = client.auth.isAuthenticated;
}

@override
void dispose() {
client.auth.authInfoListenable.removeListener(_updateSignedInState);
super.dispose();
}

void _updateSignedInState() {
setState(() {
_isSignedIn = client.auth.isAuthenticated;
});
}

@override
Widget build(BuildContext context) {
return _isSignedIn
? widget.child
: Center(
child: SignInWidget(
client: client,
onError: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Authentication failed: $error')),
);
},
),
);
}
}
```

This renders a Google sign-in button like this:
:::warning
The `SignInScreen` listener is what swaps to your authenticated UI. If you also pass an `onAuthenticated` callback to `SignInWidget`, use it for transient feedback only (snackbars, analytics). Driving navigation from `onAuthenticated` instead of the listener sends the user back to sign-in on every app restart even though their session is still valid.
:::

![Google sign-in button](/img/authentication/providers/google/3-button.png)
In `main.dart`, the template wires this into `MyHomePage.build()`'s `Scaffold` behind a commented block. Comment out `body: const GreetingsScreen()` and uncomment the `SignInScreen(...)` block beneath it:

The widget automatically handles:
```dart
body: SignInScreen(
child: GreetingsScreen(
onSignOut: () async {
await client.auth.signOutDevice();
},
),
),
```

- Google Sign-In flow for iOS, Android, and Web.
- Lightweight sign-in (One Tap, FedCM) support.
- Token management.
- Underlying Google Sign-In package error handling.
`SignInWidget` renders the standard Google sign-in button:

For details on how to customize the Google Sign-In UI in your Flutter app, see the [customizing the UI section](./customizing-the-ui).
![Google sign-in button](/img/authentication/providers/google/3-button.png)

To change the button's theme or build a fully custom UI, see [Customizing the UI](./customizing-the-ui).

:::tip
If you run into issues, see the [troubleshooting guide](./troubleshooting).
Expand All @@ -338,18 +403,38 @@ If you run into issues, see the [troubleshooting guide](./troubleshooting).

Before going live, complete the following steps:

### 1. Update the OAuth redirect URIs
### 1. Verify your authorized domain

Google's **Authorized domains** field on the [Branding](https://console.cloud.google.com/auth/branding) page accepts only the **top private domain** (the root). Once the root is verified, every subdomain under it is automatically authorized, and you do not need to add each project subdomain separately.
Comment thread
Swiftaxe marked this conversation as resolved.

If you deploy on Serverpod Cloud under a `*.serverpod.space` subdomain, `serverpod.space` is already verified by Serverpod. Just add `serverpod.space` to **Authorized domains** in the Google Auth Platform, no DNS verification is required on your end.

For a custom domain, verify ownership of your root domain (e.g., `example.com`) at [Google Search Console](https://search.google.com/search-console) by adding the DNS TXT record Google provides. After verification completes, add the root to **Authorized domains** in the Google Auth Platform.

Comment thread
Swiftaxe marked this conversation as resolved.
:::tip
A single verified root authorizes all of its subdomains. If Google rejects a domain you add, you are likely entering a full subdomain instead of the root.
:::

### 2. Update the OAuth redirect URIs

Go back to the [server OAuth client](#create-the-server-oauth-client-web-application) in the Google Auth Platform and add your production server's public URL to both **Authorized JavaScript origins** and **Authorized redirect URIs**:

- **Authorized JavaScript origins**: `https://your-domain.serverpod.space`
- **Authorized redirect URIs**: `https://your-domain.serverpod.space`
- **Authorized JavaScript origins**: `https://my-awesome-project.serverpod.space`
- **Authorized redirect URIs**: `https://my-awesome-project.serverpod.space`

Replace the URL with your actual production web server address. On Serverpod Cloud, your project is served from `https://<project-id>.serverpod.space`.

### 3. Set production credentials

Replace the URL with your actual production web server address.
Production runs out of the `production:` section of `passwords.yaml`, which is separate from the `development:` section you populated during setup. Adding production credentials does not replace your development ones, both stay in place and Serverpod picks the right set based on the run mode.

### 2. Store the production credentials
The production `googleClientSecret` reuses the same web client ID and secret from setup, but lists your production redirect URI rather than the development one. If you use a different OAuth client for production, [create another web client](#create-the-server-oauth-client-web-application) first and use its values below.

Add the `googleClientSecret` entry to the `production:` section of `config/passwords.yaml`, using the production redirect URI:
Pick the path that matches your deployment:

#### Self-hosted

Add `googleClientSecret` to the `production:` section of `passwords.yaml` with the production redirect URI:

```yaml
production:
Expand All @@ -359,22 +444,24 @@ production:
"web": {
"client_id": "your-client-id.apps.googleusercontent.com",
"client_secret": "your-client-secret",
"redirect_uris": ["https://your-domain.serverpod.space"]
"redirect_uris": ["https://my-awesome-project.serverpod.space"]
}
}
```

Alternatively, set the `SERVERPOD_PASSWORD_googleClientSecret` [environment variable](../../../07-configuration.md#2-via-environment-variables) on your production server with the same JSON value.

If you're deploying to Serverpod Cloud, set the password with the `scloud` CLI instead. Save the JSON to a file and run:
#### Serverpod Cloud

Use `https://<project-id>.serverpod.space` as the redirect URI in the JSON. Save it to a file and use `scloud password set` with `--from-file`:

```bash
scloud password set googleClientSecret --from-file path/to/google-client-secret.json
```

See the [Serverpod Cloud passwords guide](https://docs.serverpod.dev/cloud/guides/passwords) for more details.
Run this from your linked server project directory, or pass `--project <project-id>` on the call. See the [Serverpod Cloud passwords guide](https://docs.serverpod.dev/cloud/guides/passwords) for project linking and other options.

### 3. Update the Android OAuth client with the release SHA-1 (Android only)
### 4. Update the Android OAuth client with the release SHA-1

The Android OAuth client you created during setup uses your debug SHA-1 fingerprint. Release builds are signed with a different key, so you need to add the release SHA-1 as well.

Expand All @@ -392,8 +479,8 @@ Once you have the SHA-1, go back to your Android OAuth client in the Google Auth
Forgetting this step is one of the most common reasons Google Sign-In works in debug builds but silently fails after publishing to the Play Store.
:::

### 4. Publish the OAuth consent screen
### 5. Publish the OAuth consent screen

While the app is in **Testing** mode, only the test users you added on the [Audience](https://console.cloud.google.com/auth/audience) page, in the Google Auth Platform, can sign in. All other users will see an error.
While the app is in **Testing** mode, only the test users you added on the [Audience](https://console.cloud.google.com/auth/audience) page, in the Google Auth Platform, can sign in. All other users will see an error.

Navigate to the **Audience** page and click **Publish App** to allow any Google account to sign in. If your app uses sensitive or restricted scopes, Google may require a verification review before publishing.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Go through this before investigating a specific error. Most problems come from a
- [ ] Create a **Google Cloud project** in the [Google Cloud Console](https://console.cloud.google.com/).
- [ ] Enable the **People API** in your project.
- [ ] In **Google Auth Platform**, complete the initial setup (wizard) and add the required scopes on **Data Access** (`.../auth/userinfo.email` and `.../auth/userinfo.profile`).
- [ ] On **Branding** ([Branding](https://console.cloud.google.com/auth/branding)), complete the OAuth consent screen (logo, homepage, privacy policy, terms of service, and developer contact) and add every hostname you will use under **Authorized domains** (redirect URIs must use a listed domain).
- [ ] On **Branding** ([Branding](https://console.cloud.google.com/auth/branding)), complete the OAuth consent screen (logo, homepage, privacy policy, terms of service, and developer contact) and add the **root domain** (top private domain) under **Authorized domains**. Google stores only the root, so a single verified entry covers all of its subdomains. On Serverpod Cloud, add `serverpod.space` (already verified by Serverpod, no DNS setup needed). For custom domains, see [Verify your authorized domain](./setup#1-verify-your-authorized-domain).
- [ ] Add **test users** on **Audience** while in **Testing** mode ([Audience](https://console.cloud.google.com/auth/audience)), or **Publish app** when everyone should be able to sign in.
- [ ] Create a **Web application** OAuth client with **Authorized JavaScript origins** and **Authorized redirect URIs** set to your Serverpod **web server** (`http://localhost:8082` locally, not port `8080`). Copy the **Client ID** and **Client secret**.
- [ ] Add `googleClientSecret` to `config/passwords.yaml` with your client ID, client secret, and matching `redirect_uris`. For production, use the live web server URL in Google Cloud and in `production:` (or env vars) as in [Publishing to production](./setup#publishing-to-production).
Expand Down Expand Up @@ -42,25 +42,25 @@ Go through this before investigating a specific error. Most problems come from a

Common mistakes:

* Using port `8080` (the API server) instead of `8082` (the web server). Check `config/development.yaml` under `webServer` for the correct port.
* Adding a trailing slash (e.g., `http://localhost:8082/` instead of `http://localhost:8082`).
* For Web apps: not adding the Flutter web app's origin (e.g., `http://localhost:49660`) to **Authorized JavaScript origins**. This is separate from the Serverpod web server address. See the [Web setup section](./setup#web) for details.
- Using port `8080` (the API server) instead of `8082` (the web server). Check `config/development.yaml` under `webServer` for the correct port.
- Adding a trailing slash (e.g., `http://localhost:8082/` instead of `http://localhost:8082`).
- For Web apps: not adding the Flutter web app's origin (e.g., `http://localhost:49660`) to **Authorized JavaScript origins**. This is separate from the Serverpod web server address. See the [Web setup section](./setup#web) for details.

## Sign-in works for you but not for other users
## Production redirect URIs rejected by Google

**Problem:** Sign-in works for your Google account but other users get an error screen from Google saying the app is not verified or access is denied.
**Problem:** When adding your production domain to **Authorized redirect URIs** on the Web OAuth client, Google rejects it with an error about unauthorized domains.

**Cause:** Your Google Auth Platform app is still in **Testing** mode. Only users explicitly added as test users can sign in (up to 100).
**Cause:** The redirect URI's root domain (the top private domain) has not been verified and added to **Authorized domains** on the Branding page. Google requires the root to be a verified Authorized Domain before it accepts redirect URIs that use it.

**Resolution:** Navigate to the [Audience](https://console.cloud.google.com/auth/audience) page and click **Publish App** to allow any Google account to sign in. If your app uses sensitive or restricted scopes, Google may require a verification review before publishing.
**Resolution:** Add the root domain (e.g., `serverpod.space` or your custom root) to **Authorized domains**, then retry the redirect URI. On Serverpod Cloud, the `serverpod.space` root is already verified, so you only need to add `serverpod.space` to **Authorized domains**. For a custom domain, verify ownership at [Google Search Console](https://search.google.com/search-console) first. See [Verify your authorized domain](./setup#1-verify-your-authorized-domain).

## Production redirect URIs rejected by Google
## Sign-in works for you but not for other users

**Problem:** When adding your production domain to Authorized redirect URIs, Google rejects it with an error about unauthorized domains.
Comment thread
Swiftaxe marked this conversation as resolved.
**Problem:** Sign-in works for your Google account but other users get an error screen from Google saying the app is not verified or access is denied.

**Cause:** Your production domain is not listed under **Authorized domains** on the Branding page.
**Cause:** Your Google Auth Platform app is still in **Testing** mode. Only users explicitly added as test users can sign in (up to 100).

**Resolution:** Navigate to the [Branding](https://console.cloud.google.com/auth/branding) page and add your production domain (e.g., `my-awesome-project.serverpod.space`) to **Authorized domains**. Google requires redirect URIs to use domains listed here.
**Resolution:** Navigate to the [Audience](https://console.cloud.google.com/auth/audience) page and click **Publish App** to allow any Google account to sign in. If your app uses sensitive or restricted scopes, Google may require a verification review before publishing.

## Flutter web sign-in fails with origin mismatch

Expand Down
Loading