<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jamiu Okanlawon</title>
    <description>The latest articles on DEV Community by Jamiu Okanlawon (@developerjamiu).</description>
    <link>https://dev.to/developerjamiu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1233428%2F3903dc85-7623-4e2a-a37b-39f6748ed3f0.jpg</url>
      <title>DEV Community: Jamiu Okanlawon</title>
      <link>https://dev.to/developerjamiu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/developerjamiu"/>
    <language>en</language>
    <item>
      <title>Accessing Google APIs server-side from a Flutter app</title>
      <dc:creator>Jamiu Okanlawon</dc:creator>
      <pubDate>Thu, 13 Aug 2026 16:51:14 +0000</pubDate>
      <link>https://dev.to/serverpod/accessing-google-apis-server-side-from-a-flutter-app-3ip2</link>
      <guid>https://dev.to/serverpod/accessing-google-apis-server-side-from-a-flutter-app-3ip2</guid>
      <description>&lt;p&gt;Your Flutter app needs data that lives in a user’s Google account. Their calendar, their contacts, their files. Google’s APIs will hand it over, but not to just anyone: the user has to consent in your app, and your backend needs an access token that lets it act on their behalf.&lt;/p&gt;

&lt;p&gt;That second part is where most Flutter developers get stuck, because it means OAuth credentials, token verification, authenticated sessions, and a backend to hold them.&lt;/p&gt;

&lt;p&gt;With Serverpod, that backend comes with Google sign-in built in. When a user signs in, your server gets their verified identity and receives an access token. Reading their calendar is an HTTP call away.&lt;/p&gt;

&lt;p&gt;In this article, we’ll wire it end-to-end: Google sign-in for a Flutter app, and a backend that reads the user’s upcoming calendar events the moment they sign in. We will demonstrate it with the calendar, but the pattern is the same for any Google API that the user can grant you access to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the flow
&lt;/h2&gt;

&lt;p&gt;Before configuring anything, it’s worth understanding the whole journey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28h6a5gbfzayr3pgeue2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F28h6a5gbfzayr3pgeue2.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a user signs in, your app asks Google for two things: proof of who the user is, and permission to read their calendar. Google shows the consent screen, and what comes back reaches your Serverpod backend.&lt;/p&gt;

&lt;p&gt;The server verifies that the identity token was issued by Google, creates an authenticated session, and receives the access token that carries the user’s consent. That token is what lets your backend call Google’s APIs for this user.&lt;/p&gt;

&lt;p&gt;The user grants access in your app. API access occurs on your server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;We’ll begin with a new Serverpod project created using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serverpod create my_project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During project creation, keep the recommended defaults and make sure Authentication remains enabled when prompted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00mylv1xt81e5x484pq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00mylv1xt81e5x484pq1.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The command gives you a Flutter app (&lt;code&gt;my_project_flutter&lt;/code&gt;), the backend it talks to (&lt;code&gt;my_project_server&lt;/code&gt;), and a generated client package that connects the two, so your app calls the server using plain Dart methods.&lt;/p&gt;

&lt;p&gt;With the project in place, we’re ready to connect it to Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Google
&lt;/h2&gt;

&lt;p&gt;Every Google integration begins in Google Cloud, regardless of which backend you’re using.&lt;/p&gt;

&lt;p&gt;You’ll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Google Cloud project.&lt;/li&gt;
&lt;li&gt;Configure the Google Auth Platform.&lt;/li&gt;
&lt;li&gt;Enable the required APIs: the People API, which Serverpod uses for basic profile data, and the Calendar API, which is used in this article.&lt;/li&gt;
&lt;li&gt;Under Data access, add the &lt;code&gt;userinfo.email&lt;/code&gt;, &lt;code&gt;userinfo.profile&lt;/code&gt;, and &lt;code&gt;calendar.readonly&lt;/code&gt; scopes.&lt;/li&gt;
&lt;li&gt;Create a Web OAuth client for your backend, and any platform-specific clients your app needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The Calendar scope is sensitive, so Google may ask you to verify your app before you publish it. While it’s in testing mode, any account you add as a test user can sign in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The complete walkthrough, including platform-specific configuration for Android, iOS, and the web, is covered in the Serverpod &lt;a href="https://docs.serverpod.dev/next/concepts/authentication/providers/google/setup#get-your-google-credentials" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once those credentials are ready, it’s time to connect them to Serverpod.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Google to Serverpod
&lt;/h2&gt;

&lt;p&gt;The first step in connecting your backend to Google is making your Google credentials available to Serverpod.&lt;/p&gt;

&lt;p&gt;Copy your Web OAuth credentials into &lt;code&gt;config/passwords.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;googleClientSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"web": {&lt;/span&gt;
        &lt;span class="s"&gt;"client_id": "your-client-id.apps.googleusercontent.com",&lt;/span&gt;
        &lt;span class="s"&gt;"client_secret": "your-client-secret",&lt;/span&gt;
        &lt;span class="s"&gt;"redirect_uris": []&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Serverpod reads these credentials at startup and uses them to verify Google identity tokens.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The credential block is a YAML block scalar, so an indentation error silently produces malformed JSON and surfaces later as an authentication failure rather than an error at startup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The complete credential structure, along with production configuration options, is covered in the &lt;a href="https://docs.serverpod.dev/next/concepts/authentication/providers/google/setup#store-your-credentials" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Register the Google provider
&lt;/h2&gt;

&lt;p&gt;Next, register Google as one of your application’s identity providers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializeAuthServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;tokenManagerBuilders:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;JwtConfigFromPasswords&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nl"&gt;identityProviderBuilders:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;GoogleIdpConfigFromPasswords&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GoogleIdpConfigFromPasswords()&lt;/code&gt; reads the credentials from &lt;code&gt;passwords.yaml&lt;/code&gt; and configures Google as an available authentication provider.&lt;/p&gt;

&lt;p&gt;The final server-side step is exposing that provider through an endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:serverpod_auth_idp_server/providers/google.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GoogleIdpEndpoint&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;GoogleIdpBaseEndpoint&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the entire endpoint.&lt;/p&gt;

&lt;p&gt;Serverpod already provides the implementation. Your application exposes it, making the Google authentication flow available to your Flutter client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the calendar from your server
&lt;/h2&gt;

&lt;p&gt;When a user signs in, Serverpod passes your code the access token via a callback in the provider config. Whatever you do with it runs on your server, with the user’s consent.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;http&lt;/code&gt; package to your server, then extend the provider registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart pub add http    &lt;span class="c"&gt;# from my_project_server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// my_project_server/lib/server.dart&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:http/http.dart'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializeAuthServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// ... tokenManagerBuilders stay as they are ...&lt;/span&gt;
  &lt;span class="nl"&gt;identityProviderBuilders:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;GoogleIdpConfigFromPasswords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;getExtraGoogleInfoCallback:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;accountDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;https&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'www.googleapis.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'/calendar/v3/calendars/primary/events'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="s"&gt;'maxResults'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;'singleEvents'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;'orderBy'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'startTime'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="s"&gt;'timeMin'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUtc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toIso8601String&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="nl"&gt;headers:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'Authorization'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Bearer &lt;/span&gt;&lt;span class="si"&gt;$accessToken&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Calendar request failed: &lt;/span&gt;&lt;span class="si"&gt;${response.statusCode}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nl"&gt;level:&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s"&gt;'items'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Upcoming: &lt;/span&gt;&lt;span class="si"&gt;${event['summary'] ?? '(no title)'}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Calendar read skipped: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;level:&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s a real Google API call, made by your backend, for the user who just signed in. This example logs their next five events. Yours might store them instead.&lt;/p&gt;

&lt;p&gt;There are two things to know about this callback.&lt;/p&gt;

&lt;p&gt;First, it runs as part of the sign-in flow. If the calendar request fails and the error goes uncaught, the entire sign-in fails too. Treat the calendar read as optional: log the error and let sign-in continue.&lt;/p&gt;

&lt;p&gt;Second, the access token passed to the callback is short-lived. It works well for reading calendar data immediately during sign-in. If you need to sync calendar data later, such as in a scheduled or background job, you’ll need to store the credentials and handle token refresh separately.&lt;/p&gt;

&lt;p&gt;The same token works against any Google API covered by the scopes the user granted. The &lt;a href="https://docs.serverpod.dev/next/concepts/authentication/providers/google/customizations#accessing-google-apis" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; covers how to request additional scopes and use the token in your own code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the application
&lt;/h2&gt;

&lt;p&gt;With the backend configured, it’s time to start the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serverpod start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the only command it takes.&lt;/p&gt;

&lt;p&gt;Besides launching your backend, Flutter application, and development services, &lt;code&gt;serverpod start&lt;/code&gt; automatically generates any required code before starting the project.&lt;/p&gt;

&lt;p&gt;If your authentication setup introduces new database tables, there’s no need to stop the server. While &lt;code&gt;serverpod start&lt;/code&gt; is running, press &lt;strong&gt;M&lt;/strong&gt; to create and apply the migration. Once it has been applied, development continues without interrupting your workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr6ztdabzff7j91qn413s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr6ztdabzff7j91qn413s.png" alt=" " width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the backend running, the remaining setup happens in Flutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Flutter
&lt;/h2&gt;

&lt;p&gt;The template already initializes authentication when the app starts. Adding Google is one more call in &lt;code&gt;main.dart&lt;/code&gt;, right after &lt;code&gt;client.auth.initialize()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializeGoogleSignIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;serverClientId:&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;web-client-id&amp;gt;.apps.googleusercontent.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;serverClientId&lt;/code&gt; is the Web application client ID from earlier, not one of the platform-specific ones. That’s deliberate: the sign-in happens in your app, but the tokens are for your server, and this value tells Google which server that is. On the web, the same value is used as &lt;code&gt;clientId&lt;/code&gt;, along with a redirect URI. The &lt;a href="https://docs.serverpod.dev/next/concepts/authentication/providers/google/setup#initialize-the-google-sign-in-service" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; includes the exact call for each platform.&lt;/p&gt;

&lt;p&gt;Then ask for the calendar scope when the user signs in. The sign-in screen that ships with the template uses &lt;code&gt;SignInWidget&lt;/code&gt;, which accepts a customized Google button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;SignInWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;client:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;googleSignInWidget:&lt;/span&gt; &lt;span class="n"&gt;GoogleSignInWidget&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;client:&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;scopes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GoogleAuthController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;defaultScopes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'https://www.googleapis.com/auth/calendar.readonly'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scopes here are what Google puts on the consent screen and what your server’s access token is allowed to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;Run the app and tap the Google button.&lt;/p&gt;

&lt;p&gt;The consent screen now asks for calendar access alongside the usual profile permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw135jjfsfj9nbkxi1lr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw135jjfsfj9nbkxi1lr.png" alt=" " width="799" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Approve it, and watch your server’s logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo84g6o298fvkjlpj9gn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo84g6o298fvkjlpj9gn.png" alt=" " width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your backend just called Google on the user’s behalf. Sign-in, verification, session, and a working API call, and the only authentication code you wrote was a callback.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pattern you’ll reuse
&lt;/h2&gt;

&lt;p&gt;The calendar was just an example. Change the scopes you request, and the endpoint you call, and the same flow reads Google Drive files, Contacts, or YouTube data. Add another identity provider, and the same four steps repeat: configure the provider, register it with Serverpod, expose its endpoint, and initialize it in Flutter.&lt;/p&gt;

&lt;p&gt;The provider-specific configuration changes, but the authentication architecture stays the same. That consistency is one of the advantages of Serverpod’s authentication system. As your application grows, you don’t have to rethink your authentication flow. You just plug in another provider, or another API, and keep building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Accessing Google APIs from a Flutter app has a reputation for being complicated, and most of that reputation stems from the requirements Google imposes, no matter what backend you use: credentials, consent, and verification. Once those are in place, Serverpod’s side is a config entry, an empty endpoint class, and a callback that receives a ready-to-use access token.&lt;/p&gt;

&lt;p&gt;From there, Serverpod handles verifying identity tokens, creating authenticated sessions, and maintaining consistent authentication across your application. What you do with the APIs is up to you.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>backend</category>
      <category>google</category>
    </item>
  </channel>
</rss>
