diff --git a/docs/06-concepts/11-authentication/01-setup.md b/docs/06-concepts/11-authentication/01-setup.md
index 2198def1..4aa9cbe4 100644
--- a/docs/06-concepts/11-authentication/01-setup.md
+++ b/docs/06-concepts/11-authentication/01-setup.md
@@ -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
+
+
Authentication complete
+Authentication is complete. If this does not happen automatically, please close the window.
+
+```
+
+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.
diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md b/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md
index 3fc3f784..3e871c69 100644
--- a/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md
+++ b/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md
@@ -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`.

@@ -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.
@@ -258,9 +246,11 @@ 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
@@ -268,33 +258,39 @@ Web uses the same server OAuth client you created earlier, so you don't need a s
- `-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`).

-3. **Add the client ID to your Flutter project's `web/index.html`** (e.g., `my_project_flutter/web/index.html`). In the `` 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**.
+ :::
- ```html
-
- ...
-
-
+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 {
@@ -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
@@ -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://.serverpod.space`.
-Replace the URL with your actual production web server address. On Serverpod Cloud, your project is served from `https://.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
@@ -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"]
}
}
```
@@ -453,7 +455,7 @@ Alternatively, set the `SERVERPOD_PASSWORD_googleClientSecret` [environment vari
#### Serverpod Cloud
-Use `https://.serverpod.space` as the redirect URI in the JSON. Save it to a file and use `scloud password set` with `--from-file`:
+Use `https://.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
diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md
index 99c44fbd..661f32e9 100644
--- a/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md
+++ b/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md
@@ -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',
],
},
}),
@@ -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=.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 |
diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/03-customizing-the-ui.md b/docs/06-concepts/11-authentication/04-providers/03-google/03-customizing-the-ui.md
index 4eb8d407..e30a14fb 100644
--- a/docs/06-concepts/11-authentication/04-providers/03-google/03-customizing-the-ui.md
+++ b/docs/06-concepts/11-authentication/04-providers/03-google/03-customizing-the-ui.md
@@ -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
diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md b/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md
index 4d8b68bb..be6d529f 100644
--- a/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md
+++ b/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md
@@ -13,8 +13,8 @@ Go through this before investigating a specific error. Most problems come from a
- [ ] 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 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).
+- [ ] Create a **Web application** OAuth client. For web sign-in, set **Authorized JavaScript origins** to your Flutter web app's origin (e.g., `http://localhost:49660`) and **Authorized redirect URIs** to the full URL of the `auth.html` callback page (e.g., `http://localhost:49660/auth.html`). Copy the **Client ID** and **Client secret**.
+- [ ] Add `googleClientSecret` to `config/passwords.yaml` with your client ID, client secret, and matching `redirect_uris` (the same `auth.html` URL as above). For production with the standard Serverpod template, the URL is `https://your-domain.com/app/auth.html` (the Flutter web build is mounted under `/app/`); see [Publishing to production](./setup#publishing-to-production).
#### Server
@@ -25,26 +25,35 @@ Go through this before investigating a specific error. Most problems come from a
#### Client
-- [ ] Add `client.auth.initializeGoogleSignIn()` after `client.auth.initialize()` in your Flutter app's `main.dart`.
+- [ ] Add `client.auth.initializeGoogleSignIn()` after `client.auth.initialize()` in your Flutter app's `main.dart`. On web, pass `clientId` and `redirectUri` (the full `auth.html` URL).
- [ ] Surface Google sign-in in the UI with `SignInWidget` or `GoogleSignInWidget` (see [Present the authentication UI](./setup#present-the-authentication-ui)).
- [ ] Create an **iOS** OAuth client in the **same** Google Cloud project as the Web client, using the same **Bundle ID** as the app; set `GIDClientID` from the iOS client, `GIDServerClientID` to the **Web** client's ID, and add the reversed-client-ID **URL scheme** in `Info.plist` (*iOS only*).
- [ ] Create an **Android** OAuth client in the **same** project, with the same **package name** and **SHA-1** as the build you run; place `google-services.json` in `android/app/` (*Android only*).
-- [ ] Add the `google-signin-client_id` **meta tag** to `web/index.html` (*Web only*).
-- [ ] On **Web**, list **both** the Serverpod web server origin (e.g., `http://localhost:8082`) and your Flutter app origin under **Authorized JavaScript origins** on the Web OAuth client; use a **fixed** `--web-port` for Flutter so that second origin does not change every run (see [Web setup](./setup#web)).
+- [ ] Create `web/auth.html` in your Flutter project as described in [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml) (*Web only*). The same file is shared with other identity providers that use OAuth2 redirects.
+- [ ] Run Flutter on a **fixed** `--web-port` so the origin does not change every run (see [Web setup](./setup#web)). The port must match both the **Authorized JavaScript origins** entry on the OAuth client and the `redirectUri` passed to `initializeGoogleSignIn`.
## Sign-in fails with redirect_uri_mismatch
**Problem:** The OAuth flow fails with a `redirect_uri_mismatch` error from Google.
-**Cause:** The redirect URI in your OAuth client configuration does not match the URI your app is actually using. Google requires an exact match.
+**Cause:** The redirect URI sent during sign-in does not exactly match one of the URIs registered on the Web OAuth client. Google requires an exact match (scheme, host, port, path, and casing).
-**Resolution:** In the Google Auth Platform, navigate to **Clients**, select your Web application client, and verify that the URIs under **Authorized JavaScript origins** and **Authorized redirect URIs** match your server's address exactly. For local development, both should be `http://localhost:8082` (the Serverpod **web server** port, not the API server port 8080). Trailing slashes, port differences, and `http` vs `https` all count as mismatches.
+**Resolution:** In the Google Auth Platform, navigate to **Clients**, select your Web application client, and verify that the URIs under **Authorized JavaScript origins** and **Authorized redirect URIs** match what your app actually uses:
+
+- **Authorized JavaScript origins** must contain your Flutter web app's origin (e.g., `http://localhost:49660` locally, `https://my-awesome-project.serverpod.space` in production).
+- **Authorized redirect URIs** must contain the full `auth.html` URL (e.g., `http://localhost:49660/auth.html` locally, `https://my-awesome-project.serverpod.space/app/auth.html` in production if you deploy with the standard template).
+
+The same `auth.html` URL must also appear:
+
+- In `config/passwords.yaml` under `googleClientSecret.web.redirect_uris`.
+- In `client.auth.initializeGoogleSignIn(..., redirectUri: ...)` in your Flutter app.
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.
+- Trailing slashes (`http://localhost:49660/auth.html/`), port differences, or `http` vs `https`.
+- Forgetting the `/auth.html` path on the redirect URI; the bare origin is not enough.
+- Forgetting the `/app/` segment in production when the Flutter web build is served under `/app/` by the standard Serverpod template.
+- Flutter web app running on a random port. Always pass `--web-port=` to `flutter run` so the origin is stable.
## Production redirect URIs rejected by Google
@@ -62,19 +71,17 @@ Common mistakes:
**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
+## Sign-in callback never returns to the Flutter app
-**Problem:** Google Sign-In on Flutter web fails with an origin mismatch error, even though you added `http://localhost:8082` to the OAuth client.
+**Problem:** The Google sign-in window completes successfully, but the Flutter app never receives the result. The browser sits on a blank page, or `signIn()` hangs.
-**Cause:** The Flutter web app runs on a different port than the Serverpod web server. The sign-in request originates from the Flutter app's port (e.g., `http://localhost:49660`), which also needs to be listed in **Authorized JavaScript origins**. Flutter also picks a random port by default, so the origin changes on every run.
+**Cause:** The browser was redirected to a URL that does not load the `auth.html` callback page, or the page is loaded but cannot post the result back to the Flutter app.
-**Resolution:** Run Flutter on a fixed port and add that origin to your OAuth client:
-
-```bash
-flutter run -d chrome --web-hostname localhost --web-port=49660
-```
+**Resolution:**
-Then add `http://localhost:49660` to **Authorized JavaScript origins** in the Google Auth Platform.
+1. Confirm `web/auth.html` exists in your Flutter project and contains the script described in [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml). If the file is missing, the redirect URL returns a 404 and the flow cannot complete.
+2. Confirm the `redirectUri` passed to `initializeGoogleSignIn` exactly matches the URL where `auth.html` is actually served. Open the URL directly in a browser tab; you should see the "Authentication complete" page.
+3. If you deploy through the standard Serverpod template, the Flutter web build is mounted under `/app/`, so the production URL is `https://your-domain.com/app/auth.html`, not `https://your-domain.com/auth.html`.
## Server fails to parse googleClientSecret from passwords.yaml
@@ -179,27 +186,35 @@ dart run bin/main.dart --apply-migrations
2. Verify the URL scheme (`CFBundleURLSchemes`) contains the reversed client ID from the iOS plist (the `REVERSED_CLIENT_ID` value).
3. Clean the build and run again.
-## Web sign-in button doesn't render
+## clientId is required when initializing Google Sign-In on web with a redirect URI
-**Problem:** The Google Sign-In button doesn't appear on Web, or the page shows a JavaScript error related to Google Identity Services.
+**Problem:** The Flutter app throws an `ArgumentError` at startup saying `clientId is required when initializing Google Sign-In on web with a redirect URI`.
-**Cause:** The `google-signin-client_id` meta tag is missing from `web/index.html`, or its value doesn't match the server's Web application client ID.
+**Cause:** You passed `redirectUri` to `initializeGoogleSignIn` on web but did not pass `clientId` and did not set the `GOOGLE_CLIENT_ID` `--dart-define`. In redirect mode the package needs the Web OAuth client ID explicitly; it cannot derive it from anywhere else on web.
-**Resolution:** Add or verify the meta tag in `web/index.html`:
+**Resolution:** Either pass `clientId` directly, or pass it via `--dart-define`:
-```html
-
- ...
-
-
+```dart
+client.auth.initializeGoogleSignIn(
+ clientId: kIsWeb
+ ? 'your-web-client-id.apps.googleusercontent.com'
+ : null,
+ redirectUri: kIsWeb
+ ? 'http://localhost:49660/auth.html'
+ : null,
+);
```
-Replace `your_server_client_id` with the `client_id` from your Web application OAuth client JSON file.
+Or:
+
+```bash
+flutter run --dart-define=GOOGLE_CLIENT_ID=your-web-client-id.apps.googleusercontent.com ...
+```
## Google API calls fail after one hour on Web
**Problem:** Your app calls Google APIs (e.g., Calendar, Drive) using the access token from sign-in, but requests start returning `401 Unauthorized` after about an hour. This only affects the Web platform.
-**Cause:** On Web, the `accessToken` returned by the `google_sign_in` package expires after 3,600 seconds (one hour) and is not automatically refreshed.
+**Cause:** On Web, the `accessToken` returned by the underlying sign-in library expires after 3,600 seconds (one hour) and is not automatically refreshed.
-**Resolution:** When making Google API calls on Web, check the token age and prompt the user to re-authenticate if the token has expired. On mobile platforms, the token is refreshed automatically and this is not an issue. See the [google_sign_in_web documentation](https://pub.dev/packages/google_sign_in_web) for details on token lifecycle.
+**Resolution:** When making Google API calls on Web, check the token age and prompt the user to re-authenticate if the token has expired. On mobile platforms, the token is refreshed automatically and this is not an issue.
diff --git a/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md b/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md
index 0e4b8eac..fb8d5695 100644
--- a/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md
+++ b/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md
@@ -163,37 +163,13 @@ In order to capture the callback url, the following activity needs to be added t
### Web
-On the web, you need a specific endpoint to capture the OAuth2 callback. To set this up, create an HTML file (e.g., `auth.html`) inside your project's `./web` folder and add the following content:
-
-```html
-
-Authentication complete
-Authentication is complete. If this does not happen automatically, please close the window.
-
-```
+On the web, GitHub sign-in completes by redirecting the browser to a callback page that hands the result back to your Flutter app. That page is `web/auth.html`, and it is shared across every identity provider that uses an OAuth2 redirect.
-:::note
-You only need a single callback file (e.g. `auth.html`) in your `./web` folder.
-This file is shared across all IDPs that use the OAuth2 utility, as long as your redirect URIs point to it.
-:::
+1. Create the callback page if you have not already. Follow [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml) in the authentication setup guide.
+
+2. Register the full `auth.html` URL (for example, `http://localhost:49660/auth.html`) as the **Authorization callback URL** on your GitHub OAuth app.
+
+3. Pass the same URL to `initializeGitHubSignIn` via the `redirectUri` argument when you initialize the client.
## Present the authentication UI