apps/docs/content/guides/auth/social-login/auth-google.mdx
Supabase Auth supports Sign in with Google for the web, native applications (Android, macOS and iOS), and Chrome extensions.
You can use Sign in with Google in two ways:
You need to do some setup to get started with Sign in with Google:
Supabase Auth needs a few scopes granting access to profile data of your end users, which you have to configure in the Data Access (Scopes) screen:
openid (add manually).../auth/userinfo.email (added by default).../auth/userinfo.profile (added by default)If you add more scopes, especially those on the sensitive or restricted list your application might be subject to verification which may take a long time.
It's strongly recommended you set up a custom domain and optionally verify your brand information with Google, as this makes phishing attempts easier to spot by your users.
</Admonition>Google's consent screen is shown to users when they sign in. Optionally configure one of the following to improve the appearance of the screen, increasing the perception of trust by your users:
auth.example.com or api.example.com, if your application is hosted on example.com.<project-id>.supabase.co which does not inspire trust and can make your application more susceptible to successful phishing attempts.To support Sign In with Google, you need to configure the Google provider for your Supabase project.
<Tabs scrollable size="large" type="underlined" defaultActiveId="web" queryGroup="platform"
<TabPanel id="web" label="Web">
Regardless of whether you use application code or Google's pre-built solutions to implement the sign in flow, you need to configure your project by obtaining a Client ID and Client Secret in the Clients section of the Google Auth Platform console:
https://example.com/app add https://example.com.http://localhost:<port> while developing locally. Remember to remove this when your application goes into production.http://127.0.0.1:54321/auth/v1/callback.Create and make sure you save the Client ID and Client Secret.
Skip nonce check option.For iOS add a CFBundleURLTypes key in the <project>/ios/Runner/Info.plist file:
<!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<!-- TODO Replace this value: -->
<!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
<string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
</array>
</dict>
</array>
<!-- End of the Google Sign-in Section -->
Follow the same configuration guide as if your app was a Web application when building a desktop Flutter application.
</TabPanel><$Show if="sdk:swift">
<TabPanel id="swift" label="Swift">Google sign-in with Supabase is done through the [`GoogleSignIn-iOS`](https://github.com/google/GoogleSignIn-iOS) package.
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
Follow the code sample below to implement native Google sign-in with Supabase in your iOS app.
```swift
import GoogleSignIn
class GoogleSignInViewController: UIViewController {
...
func googleSignIn() async throws {
let result = try await GIDSignIn.sharedInstance.signIn(withPresenting: self)
guard let idToken = result.user.idToken?.tokenString else {
print("No idToken found.")
return
}
let accessToken = result.user.accessToken.tokenString
try await supabase.auth.signInWithIdToken(
credentials: OpenIDConnectCredentials(
provider: .google,
idToken: idToken,
accessToken: accessToken
)
)
}
...
}
```
### Configuration [#ios-configuration]
1. Follow the integration instructions on the [get started with Google Sign-In](https://developers.google.com/identity/sign-in/ios/start-integrating) for the iOS guide.
2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service.
3. Add web client ID and iOS client ID from step 1 in the [Google provider on the Supabase Dashboard](/dashboard/project/_/auth/providers), under _Client IDs_, separated by a comma. Enable the `Skip nonce check` option.
To use the Google provider in local development:
SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_SECRET="<client-secret>"
supabase/config.toml:
[auth.external.google]
enabled = true
client_id = "<client-id>"
secret = "env(SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_SECRET)"
skip_nonce_check = false
If you have multiple client IDs, such as one for Web, iOS and Android, concatenate all of the client IDs with a comma but make sure the web's client ID is first in the list.
Use the PATCH /v1/projects/{ref}/config/auth Management API endpoint to configure the project's Auth settings programmatically. For configuring the Google provider send these options:
{
"external_google_enabled": true,
"external_google_client_id": "your-google-client-id",
"external_google_secret": "your-google-client-secret"
}
<Tabs scrollable size="large" type="underlined" defaultActiveId="web" queryGroup="platform"
<TabPanel id="web" label="Web">
To use your own application code for the signin button, call the signInWithOAuth method (or the equivalent for your language).
<$Partial path="create_client_snippet.mdx" />
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('url', 'anonKey')
// ---cut---
supabase.auth.signInWithOAuth({
provider: 'google',
})
For an implicit flow, that's all you need to do. The user will be taken to Google's consent screen, and finally redirected to your app with an access and refresh token pair representing their session.
<$Partial path="oauth_pkce_flow.mdx" />
After a successful code exchange, the user's session will be saved to cookies.
The tokens saved by your application are the Supabase Auth tokens. Your app might additionally need the Google OAuth 2.0 tokens to access Google services on the user's behalf.
On initial login, you can extract the provider_token from the session and store it in a secure storage medium. The session is available in the returned data from signInWithOAuth (implicit flow) and exchangeCodeForSession (PKCE flow).
Google does not send out a refresh token by default, so you will need to pass parameters like these to signInWithOAuth() in order to extract the provider_refresh_token:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://your-project.supabase.co', 'sb_publishable_... or anon key')
// ---cut---
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
queryParams: {
access_type: 'offline',
prompt: 'consent',
},
},
})
Most web apps and websites can utilize Google's personalized sign-in buttons, One Tap or automatic sign-in for the best user experience.
Load the Google client library in your app by including the third-party script:
<script src="https://accounts.google.com/gsi/client" async></script>
Use the HTML Code Generator to customize the look, feel, features and behavior of the Sign in with Google button.
Pick the Swap to JavaScript callback option, and input the name of your callback function. This function will receive a CredentialResponse when sign in completes.
To make your app compatible with Chrome's third-party-cookie phase-out, make sure to set data-use_fedcm_for_prompt to true.
Your final HTML code might look something like this:
<div
id="g_id_onload"
data-client_id="<client ID>"
data-context="signin"
data-ux_mode="popup"
data-callback="handleSignInWithGoogle"
data-nonce=""
data-auto_select="true"
data-itp_support="true"
data-use_fedcm_for_prompt="true"
></div>
<div
class="g_id_signin"
data-type="standard"
data-shape="pill"
data-theme="outline"
data-text="signin_with"
data-size="large"
data-logo_alignment="left"
></div>
Create a handleSignInWithGoogle function that takes the CredentialResponse and passes the included token to Supabase. The function needs to be available in the global scope for Google's code to find it.
async function handleSignInWithGoogle(response) {
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.credential,
})
}
(Optional) Configure a nonce. The use of a nonce is recommended for extra security, but optional. The nonce should be generated randomly each time, and it must be provided in both the data-nonce attribute of the HTML code and the options of the callback function.
async function handleSignInWithGoogle(response) {
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.credential,
nonce: '<NONCE>',
})
}
Note that the nonce should be the same in both places, but because Supabase Auth expects the provider to hash it (SHA-256, hexadecimal representation), you need to provide a hashed version to Google and a non-hashed version to signInWithIdToken.
You can get both versions by using the in-built crypto library:
// Adapted from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
const encoder = new TextEncoder()
const encodedNonce = encoder.encode(nonce)
crypto.subtle.digest('SHA-256', encodedNonce).then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
})
// Use 'hashedNonce' when making the authentication request to Google
// Use 'nonce' when invoking the supabase.auth.signInWithIdToken() method
If you're integrating Google One-Tap with your Next.js application, you can refer to the example below to get started:
'use client'
import Script from 'next/script'
import { createClient } from '@/utils/supabase/client'
import type { accounts, CredentialResponse } from 'google-one-tap'
import { useRouter } from 'next/navigation'
declare const google: { accounts: accounts }
// generate nonce to use for google id token sign-in
const generateNonce = async (): Promise<string[]> => {
const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32))))
const encoder = new TextEncoder()
const encodedNonce = encoder.encode(nonce)
const hashBuffer = await crypto.subtle.digest('SHA-256', encodedNonce)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashedNonce = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
return [nonce, hashedNonce]
}
const OneTapComponent = () => {
const supabase = createClient()
const router = useRouter()
const initializeGoogleOneTap = async () => {
console.log('Initializing Google One Tap')
const [nonce, hashedNonce] = await generateNonce()
console.log('Nonce: ', nonce, hashedNonce)
// check if there's already an existing session before initializing the one-tap UI
const {
data: { claims },
error,
} = await supabase.auth.getClaims()
if (error) {
console.error('Error getting claims', error)
}
if (claims) {
router.push('/')
return
}
/* global google */
google.accounts.id.initialize({
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
callback: async (response: CredentialResponse) => {
try {
// send id token returned in response.credential to supabase
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.credential,
nonce,
})
if (error) throw error
console.log('Session data: ', data)
console.log('Successfully logged in with Google One Tap')
// redirect to protected page
router.push('/')
} catch (error) {
console.error('Error logging in with Google One Tap', error)
}
},
nonce: hashedNonce,
// with chrome's removal of third-party cookies, we need to use FedCM instead (https://developers.google.com/identity/gsi/web/guides/fedcm-migration)
use_fedcm_for_prompt: true,
})
google.accounts.id.prompt() // Display the One Tap UI
}
return <Script onReady={initializeGoogleOneTap} src="https://accounts.google.com/gsi/client" />
}
export default OneTapComponent
Unlike the OAuth flow which requires the use of a web browser, the native Sign in with Google flow on Android uses the Credential Manager library to prompt the user for consent.
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that you then send to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
By default, Supabase Auth implements nonce validation during the authentication flow. This can be disabled in production under Authentication > Providers > Google > Skip Nonce Check in the Dashboard, or when developing locally by setting auth.external.<provider>.skip_nonce_check. Only disable this if your client libraries cannot properly handle nonce verification.
When working with Expo, you can use the @react-native-google-signin/google-signin library to obtain an ID token that you can pass to supabase-js signInWithIdToken method.
Follow the Expo installation docs for installation and configuration instructions. See the supabase-js reference for instructions on initializing the supabase-js client in React Native.
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from '@react-native-google-signin/google-signin'
import { supabase } from '../utils/supabase'
export default function () {
GoogleSignin.configure({
webClientId: 'YOUR CLIENT ID FROM GOOGLE CONSOLE',
})
return (
<GoogleSigninButton
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={async () => {
try {
await GoogleSignin.hasPlayServices()
const response = await GoogleSignin.signIn()
if (isSuccessResponse(response)) {
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: response.data.idToken,
})
console.log(error, data)
}
} catch (error: any) {
if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
}
}}
/>
)
}
Google sign-in with Supabase is done through the google_sign_in package for iOS and Android.
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
Follow the code sample below to implement native Google sign-in with Supabase in your Flutter iOS and Android app.
import 'package:google_sign_in/google_sign_in.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
...
Future<void> _nativeGoogleSignIn() async {
/// TODO: update the Web client ID with your own.
///
/// Web Client ID that you registered with Google Cloud.
const webClientId = 'my-web.apps.googleusercontent.com';
/// TODO: update the iOS client ID with your own.
///
/// iOS Client ID that you registered with Google Cloud.
const iosClientId = 'my-ios.apps.googleusercontent.com';
final scopes = ['email', 'profile'];
final googleSignIn = GoogleSignIn.instance;
await googleSignIn.initialize(
serverClientId: webClientId,
clientId: iosClientId,
);
final googleUser = await googleSignIn.attemptLightweightAuthentication();
// or await googleSignIn.authenticate(); which will return a GoogleSignInAccount or throw an exception
if (googleUser == null) {
throw AuthException('Failed to sign in with Google.');
}
/// Authorization is required to obtain the access token with the appropriate scopes for Supabase authentication,
/// while also granting permission to access user information.
final authorization =
await googleUser.authorizationClient.authorizationForScopes(scopes) ??
await googleUser.authorizationClient.authorizeScopes(scopes);
final idToken = googleUser.authentication.idToken;
if (idToken == null) {
throw AuthException('No ID Token found.');
}
await supabase.auth.signInWithIdToken(
provider: OAuthProvider.google,
idToken: idToken,
accessToken: authorization.accessToken,
);
}
...
Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the signInWithOAuth method.
This method of signing in is web based, and will open a browser window to perform the sign in. For non-web platforms, the user is brought back to the app via deep linking.
await supabase.auth.signInWithOAuth(
OAuthProvider.google,
redirectTo: kIsWeb ? null : 'my.scheme://my-host', // Optionally set the redirect link to bring back the user via deeplink.
authScreenLaunchMode:
kIsWeb ? LaunchMode.platformDefault : LaunchMode.externalApplication, // Launch the auth screen in a new webview on mobile.
);
This call takes the user to Google's consent screen. Once the flow ends, the user's profile information is exchanged and validated with Supabase Auth before it redirects back to your Flutter application with an access and refresh token representing the user's session.
<div className="video-container"> <iframe src="https://www.youtube-nocookie.com/embed/utMg6fVmX0U" frameBorder="1" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen ></iframe> </div> </TabPanel> <TabPanel id="android" label="Android (Kotlin)">The Sign in with Google flow on Android uses the operating system's built-in functionalities to prompt the user for consent.
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
Note: You have to create OAuth client IDs for both a Web and Android application. The Web client ID is the one used in your Android app.
Add the following dependencies to your app. You can find the latest version of credentials here and googleid here.
implementation("androidx.credentials:credentials:<latest version>")
implementation ("com.google.android.libraries.identity.googleid:googleid:<latest version>")
// optional - needed for credentials support from play services, for devices running
// Android 13 and below.
implementation("androidx.credentials:credentials-play-services-auth:<latest version>")
Add the following ProGuard rules to your proguard-rules.pro file:
-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
*;
}
Follow the code sample below to implement native Google sign-in with Supabase using Credential Manager in your Android app.
@Composable
fun GoogleSignInButton() {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val onClick: () -> Unit = {
val credentialManager = CredentialManager.create(context)
// Generate a nonce and hash it with sha-256
// Providing a nonce is optional but recommended
val rawNonce = UUID.randomUUID().toString() // Generate a random String. UUID should be sufficient, but can also be any other random string.
val bytes = rawNonce.toString().toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val digest = md.digest(bytes)
val hashedNonce = digest.fold("") { str, it -> str + "%02x".format(it) } // Hashed nonce to be passed to Google sign-in
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId("WEB_GOOGLE_CLIENT_ID")
.setNonce(hashedNonce) // Provide the nonce if you have one
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
coroutineScope.launch {
try {
val result = credentialManager.getCredential(
request = request,
context = context,
)
val googleIdTokenCredential = GoogleIdTokenCredential
.createFrom(result.credential.data)
val googleIdToken = googleIdTokenCredential.idToken
supabase.auth.signInWith(IDToken) {
idToken = googleIdToken
provider = Google
nonce = rawNonce
}
// Handle successful sign-in
} catch (e: GetCredentialException) {
// Handle GetCredentialException thrown by `credentialManager.getCredential()`
} catch (e: GoogleIdTokenParsingException) {
// Handle GoogleIdTokenParsingException thrown by `GoogleIdTokenCredential.createFrom()`
} catch (e: RestException) {
// Handle RestException thrown by Supabase
} catch (e: Exception) {
// Handle unknown exceptions
}
}
}
Button(
onClick = onClick,
) {
Text("Sign in with Google")
}
}
When using Compose Multiplatform, you can use the compose-auth plugin. On Android it uses the Credential Manager automatically and on other platforms it uses auth.signInWith(Google).
Initialize the Supabase Client
Note: You have to create OAuth credentials for both a Web and Android application. Learn more
val supabaseClient = createSupabaseClient(
supabaseUrl = "SUPABASE_URL",
supabaseKey = "SUPABASE_KEY"
) {
install(Auth)
install(ComposeAuth) {
googleNativeLogin("WEB_GOOGLE_CLIENT_ID") //Use the Web Client ID, not the Android one!
}
}
Use the Compose Auth plugin in your Auth Screen
val authState = supabaseClient.composeAuth.rememberSignInWithGoogle(
onResult = {
when(it) { //handle errors
NativeSignInResult.ClosedByUser -> TODO()
is NativeSignInResult.Error -> TODO()
is NativeSignInResult.NetworkError -> TODO()
NativeSignInResult.Success -> TODO()
}
}
)
Button(onClick = { authState.startFlow() }) {
Text("Sign in with Google")
}
Similar to the native sign in for Android, you can use the Chrome browser's identity APIs to launch an authentication flow.
First, you need to configure your manifest.json file like so:
{
"permissions": ["identity"],
"oauth2": {
"client_id": "<client ID>",
"scopes": ["openid", "email", "profile"]
}
}
Then you should call the chrome.identity.launchWebAuthFlow() function to trigger the sign in flow. On success, call the supabase.auth.signInWithIdToken() function to complete sign in with your Supabase project.
const manifest = chrome.runtime.getManifest()
const url = new URL('https://accounts.google.com/o/oauth2/auth')
url.searchParams.set('client_id', manifest.oauth2.client_id)
url.searchParams.set('response_type', 'id_token')
url.searchParams.set('access_type', 'offline')
url.searchParams.set('redirect_uri', `https://${chrome.runtime.id}.chromiumapp.org`)
url.searchParams.set('scope', manifest.oauth2.scopes.join(' '))
chrome.identity.launchWebAuthFlow(
{
url: url.href,
interactive: true,
},
async (redirectedTo) => {
if (chrome.runtime.lastError) {
// auth was not successful
} else {
// auth was successful, extract the ID token from the redirectedTo URL
const url = new URL(redirectedTo)
const params = new URLSearchParams(url.hash)
const { data, error } = await supabase.auth.signInWithIdToken({
provider: 'google',
token: params.get('id_token'),
})
}
}
)