Skip to content
Draft
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
44 changes: 44 additions & 0 deletions docs/06-concepts/11-authentication/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,50 @@ This is equivalent to calling `restore()` followed by `validateAuthentication()`

See [Client-side authentication](./basics#client-side-authentication) for more details on how to interact with the authentication state from the client.

### Web callback page (`auth.html`)

:::note
You only need this if your app targets the **web** platform and uses an identity provider that signs the user in through an OAuth2 redirect. That includes **GitHub** and **Google** (when configured for redirect-based web sign-in). Skip this section if your app does not target web, or if it only uses email, passkey, or anonymous sign-in.
:::

When the user finishes signing in at the provider's page (for example, `accounts.google.com`), the provider redirects the browser to a URL on your site with the sign-in result attached. Your Flutter app cannot receive that redirect directly because the browser navigates fully away from it. `auth.html` is a small static page that catches the redirect, reads the result, and hands it back to your running Flutter app through `postMessage` (or `localStorage`, depending on how the sign-in was launched).

You create one `auth.html` and share it across every identity provider that needs it.

In your Flutter project's `web/` folder, add a file named `auth.html` with this content:

```html
<!DOCTYPE html>
<title>Authentication complete</title>
<p>Authentication is complete. If this does not happen automatically, please close the window.</p>
<script>
function postAuthenticationMessage() {
const message = {
'flutter-web-auth-2': window.location.href
};

if (window.opener) {
window.opener.postMessage(message, window.location.origin);
window.close();
} else if (window.parent && window.parent !== window) {
window.parent.postMessage(message, window.location.origin);
} else {
localStorage.setItem('flutter-web-auth-2', window.location.href);
window.close();
}
}

postAuthenticationMessage();
</script>
```

When you set up a provider that uses this callback, you will register the full URL of `auth.html` in **two** places, and they must match exactly:

- **In the provider's OAuth client configuration** for example, **Authorized redirect URIs** in Google Cloud Console, or **Authorization callback URL** in a GitHub OAuth app.
- **In the Flutter sign-in initializer**, via the `redirectUri` argument (e.g., `client.auth.initializeGoogleSignIn(..., redirectUri: ...)`).

The URL is your Flutter web app's origin plus `/auth.html`. For example, `http://localhost:49660/auth.html` during local development or `https://yourdomain.com/auth.html` in production. The provider's setup page walks through the exact values for that provider.

### Present the authentication UI

The `serverpod_auth_idp_flutter` package provides a `SignInWidget` that automatically detects enabled authentication providers and displays the appropriate sign-in options.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,7 @@ All platforms (iOS, Android, and Web) require a **Web application** OAuth client

2. Select **Web application** as the application type.

3. Add the following URIs:

- **Authorized JavaScript origins**: The origin that is allowed to make requests to Google's OAuth servers. For Serverpod, this is your **web server** address.
- **Authorized redirect URIs**: The URL Google redirects the user back to after they sign in. Serverpod handles this callback on the web server as well.

Serverpod runs three servers locally (see `config/development.yaml`): the API server on port 8080, the Insights server on 8081, and the **web server on port 8082**. The Google OAuth flow uses the web server, so both fields should point to port 8082:

| Environment | Authorized JavaScript origins | Authorized redirect URIs |
| --- | --- | --- |
| Local development | `http://localhost:8082` | `http://localhost:8082` |
| Production | Your web server's public URL (e.g., `https://my-awesome-project.serverpod.space`) | Your web server's public URL |

You can find these ports in your server's `config/development.yaml` under `webServer`.
3. Leave **Authorized JavaScript origins** and **Authorized redirect URIs** empty for now if you only target iOS or Android. If you target web, you will fill them in under [Web](#web) below, where the exact values depend on the port you choose for `flutter run`.

![Clients configuration](/img/authentication/providers/google/5-clients.png)

Expand All @@ -106,12 +94,12 @@ development:
"web": {
"client_id": "your-client-id.apps.googleusercontent.com",
"client_secret": "your-client-secret",
"redirect_uris": ["http://localhost:8082"]
"redirect_uris": ["http://localhost:49660/auth.html"]
}
}
```

Replace `your-client-id` and `your-client-secret` with the values from the Google Auth Platform. The `redirect_uris` must match the **Authorized redirect URIs** you configured in the previous step.
Replace `your-client-id` and `your-client-secret` with the values from the Google Auth Platform. The `redirect_uris` must match the **Authorized redirect URIs** you configured in the previous step. For web sign-in, this is the full URL where your Flutter app's `auth.html` is served.

For production, add the same `googleClientSecret` entry to the `production:` section of `passwords.yaml` (with your production redirect URI), or set the `SERVERPOD_PASSWORD_googleClientSecret` environment variable on your production server.

Expand Down Expand Up @@ -258,43 +246,51 @@ The downloaded `google-services.json` may not include a web OAuth client entry,

### Web

Web uses the same server OAuth client you created earlier, so you don't need a separate client. However, for web, the sign-in request originates from the Flutter app running in the browser, not from the Serverpod web server. Google requires this origin to be listed as well.
On web, Google signs the user in through an OAuth2 redirect: the browser is sent to `accounts.google.com`, the user signs in, and Google redirects back to a callback page hosted by your Flutter app. Your Flutter web app's origin must be registered with Google, and so must the callback URL.

1. **Choose a fixed port for your Flutter web app.** Google OAuth requires exact origin matches, and Flutter picks a random port on each run by default. To keep things consistent, run Flutter on a fixed port using `--web-port`:
1. **Create the `auth.html` callback page** in your Flutter project's `web/` folder. This page receives the OAuth redirect and hands the result back to your running Flutter app. The same file is shared with every Serverpod identity provider that uses an OAuth2 redirect, so create it once. Follow [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml) in the authentication setup guide if you have not already.

2. **Choose a fixed port for your Flutter web app.** Google OAuth requires exact URI matches, and Flutter picks a random port on each run by default. Run Flutter on a fixed port using `--web-port`:

```bash
flutter run -d chrome --web-hostname localhost --web-port=49660
```

- `-d chrome`: Run on the Chrome browser.
- `--web-hostname localhost`: Bind to localhost.
- `--web-port=49660`: Use a fixed port (pick any available port). This is the value you will add to **Authorized JavaScript origins** in the next step.

2. **Update the server OAuth client.** Go back to the server OAuth client you created in the [previous section](#create-the-server-oauth-client-web-application) and add your Flutter web app's origin to **Authorized JavaScript origins**:
- `--web-port=49660`: Use a fixed port (pick any available port). This is the value you will add to **Authorized JavaScript origins** and **Authorized redirect URIs** in the next step.

- For local development: `http://localhost:49660` (or whichever port you chose)
- For production: your Flutter web app's domain (e.g., `https://my-awesome-project.serverpod.space`)
3. **Update the server OAuth client.** Go back to the server OAuth client you created in the [previous section](#create-the-server-oauth-client-web-application) and add the following:

The **Authorized redirect URIs** should already contain your Serverpod web server's address (`http://localhost:8082`) from the earlier setup. You don't need to change it.
- **Authorized JavaScript origins**: your Flutter web app's origin. For local development, `http://localhost:49660` (or whichever port you chose). For production, your Flutter web app's domain (e.g., `https://my-awesome-project.serverpod.space`).
- **Authorized redirect URIs**: the full URL where `auth.html` is served. For local development, `http://localhost:49660/auth.html`. For production, the same URL with your production origin (e.g., `https://my-awesome-project.serverpod.space/auth.html`).

![Web credentials configuration](/img/authentication/providers/google/2-credentials.png)

3. **Add the client ID to your Flutter project's `web/index.html`** (e.g., `my_project_flutter/web/index.html`). In the `<head>` section, add:
:::note
On the standard Serverpod project template, the Flutter web build is copied into the Serverpod web server's `web/app/` folder when you run the `flutter_build` script. If you deploy that way, the production callback lives at `https://your-domain.com/app/auth.html`, not at the domain root. Register that exact URL in **Authorized redirect URIs**.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be nice, if you mentioned serverpod run flutter_build.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do great.

:::

```html
<head>
...
<meta name="google-signin-client_id" content="your_server_client_id">
</head>
4. **Pass the web client ID and redirect URI to `initializeGoogleSignIn`.** When you initialize Google sign-in in your Flutter app's `main.dart` (covered under [Initialize the Google sign-in service](#initialize-the-google-sign-in-service) below), pass these two arguments so sign-in uses the redirect flow:

```dart
client.auth.initializeGoogleSignIn(
clientId: 'your-web-client-id.apps.googleusercontent.com',
redirectUri: 'http://localhost:49660/auth.html',
);
```

Replace `your_server_client_id` with the client ID from your Web application OAuth client.
Use the same `clientId` and `auth.html` URL you registered above. iOS and Android do not need these arguments because they read their client IDs from `Info.plist` and `google-services.json`.

:::tip
To keep these values out of `main.dart` and vary them per build, read them from `--dart-define`. See [Configuring the Web redirect URI](./customizations#configuring-the-web-redirect-uri) for the pattern.
:::

## Present the authentication UI

### Initialize the Google sign-in service

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:
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:

```dart
void main() async {
Expand All @@ -313,7 +309,9 @@ void main() async {
}
```

`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.
`initializeGoogleSignIn()` registers a sign-out hook so signing out of Serverpod also signs the user out of their Google session, and initializes the underlying sign-in SDK with the client IDs you configured (`Info.plist` on iOS, `google-services.json` on Android). `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 target **web**, pass the `clientId` and `redirectUri` arguments described in step 4 of the [Web](#web) section above.

### Show the Google sign-in button

Expand Down Expand Up @@ -417,12 +415,16 @@ A single verified root authorizes all of its subdomains. If Google rejects a dom

### 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**:
Go back to the [server OAuth client](#create-the-server-oauth-client-web-application) in the Google Auth Platform and add your production URLs:

- **Authorized JavaScript origins**: your production Flutter web app's origin (e.g., `https://my-awesome-project.serverpod.space`).
- **Authorized redirect URIs**: the full URL of the production `auth.html` callback. With the standard Serverpod template, the Flutter web app is served under `/app/` by the `flutter_build` script, so the callback is `https://my-awesome-project.serverpod.space/app/auth.html`.

- **Authorized JavaScript origins**: `https://my-awesome-project.serverpod.space`
- **Authorized redirect URIs**: `https://my-awesome-project.serverpod.space`
Replace the URLs with your actual production address. On Serverpod Cloud, your project is served from `https://<project-id>.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`.
:::note
If you host the Flutter web build at the domain root instead of under `/app/` (for example, when you deploy it to a separate static host rather than alongside the Serverpod server), use `https://my-awesome-project.serverpod.space/auth.html` instead.
:::

### 3. Set production credentials

Expand All @@ -444,7 +446,7 @@ production:
"web": {
"client_id": "your-client-id.apps.googleusercontent.com",
"client_secret": "your-client-secret",
"redirect_uris": ["https://my-awesome-project.serverpod.space"]
"redirect_uris": ["https://my-awesome-project.serverpod.space/app/auth.html"]
}
}
```
Expand All @@ -453,7 +455,7 @@ Alternatively, set the `SERVERPOD_PASSWORD_googleClientSecret` [environment vari

#### 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`:
Use `https://<project-id>.serverpod.space/app/auth.html` as the redirect URI in the JSON (the `/app/` segment is where the standard template mounts the Flutter web build). 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final googleIdpConfig = GoogleIdpConfig(
'client_id': 'your-client-id.apps.googleusercontent.com',
'client_secret': 'your-client-secret',
'redirect_uris': [
'http://localhost:8082',
'http://localhost:49660/auth.html',
],
},
}),
Expand Down Expand Up @@ -216,6 +216,31 @@ This approach is useful when you need to:
You can also set these environment variables in your IDE's run configuration or CI/CD pipeline to avoid passing them manually each time.
:::

### Configuring the Web redirect URI

On web, `initializeGoogleSignIn` accepts a `redirectUri` argument that switches sign-in into the OAuth2 redirect flow described in [Web setup](./setup#web). When `redirectUri` is null on web, the package falls back to Google's built-in popup/iframe button rendered by `google_sign_in_web`.

The same `--dart-define` pattern recommended for client IDs works for the redirect URI, so the same `main.dart` builds correctly for local development and production:

```dart
const _googleClientId = String.fromEnvironment('GOOGLE_CLIENT_ID');
const _googleRedirectUri = String.fromEnvironment('GOOGLE_WEB_REDIRECT_URI');
client.auth.initializeGoogleSignIn(
clientId: kIsWeb ? _googleClientId : null,
redirectUri: kIsWeb ? _googleRedirectUri : null,
);
```

```bash
flutter run \
-d chrome --web-port=49660 \
--dart-define="GOOGLE_CLIENT_ID=<web_client_id>.apps.googleusercontent.com" \
--dart-define="GOOGLE_WEB_REDIRECT_URI=http://localhost:49660/auth.html"
```

`GOOGLE_CLIENT_ID` is also read automatically by the package if `clientId` is null, so you can omit the explicit `clientId:` argument and rely on the environment variable. There is no equivalent automatic env var for `redirectUri` because it is purely a web concern.

## GoogleIdpConfig parameter reference

| Parameter | Type | Required | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ final controller = GoogleAuthController(
await controller.signIn();
```

:::warning
When using Google Sign-In on web, be mindful that the button will be rendered by the underlying `google_sign_in` package, so customizing the button might not work as expected. The included `GoogleSignInWidget` is a wrapper around the original widgets that already applies some customizations to make its design compatible between all platforms.
:::note
On web, the button you can customize depends on which web sign-in mode you use. If you pass `redirectUri` to `initializeGoogleSignIn`, sign-in runs through the OAuth2 redirect flow and your custom widget renders directly. If you do not pass `redirectUri`, the underlying `google_sign_in` package renders Google's built-in button instead and most visual customizations have no effect. Set up `redirectUri` as described in the [Web setup](./setup#web) to control the button yourself.
:::

### GoogleAuthController State Management
Expand Down
Loading
Loading