Flutter Authentication with OAuth 2.0, Google Sign-In, and Biometrics

Flutter Authentication with OAuth 2.0, Google Sign-In, and Biometrics

Build a secure authentication experience using modern identity protocols, Google Sign-In, protected token storage, and biometric verification.

Flutter · Dart · OAuth 2.0 · OpenID Connect · Mobile Security

Level: Intermediate to Advanced | Reading time: 15–20 minutes

Authentication is one of the most important security components of a mobile application. A production-ready implementation must do more than display a login screen: it must validate identity, protect credentials, manage sessions, handle expired tokens, and provide a reliable user experience.

In this guide, we'll explore how OAuth 2.0, Google Sign-In, and device biometrics work together in a Flutter application. We'll also discuss the backend responsibilities and security decisions required before using the implementation in a production environment.

What you'll learn
  • Understand OAuth 2.0 and OpenID Connect.
  • Integrate Google Sign-In in Flutter.
  • Verify Google identity tokens on a backend.
  • Store session credentials securely.
  • Add fingerprint or Face ID verification.
  • Handle logout, expired tokens, and authentication errors.

Table of Contents

  1. Authentication fundamentals
  2. Secure authentication architecture
  3. Project setup
  4. Configure Google Sign-In
  5. Implement Google authentication
  6. Verify identity on the backend
  7. Secure token storage
  8. Implement biometrics
  9. Manage sessions and logout
  10. Production security checklist
  11. Troubleshooting
  12. Frequently asked questions

1. Authentication Fundamentals

What is OAuth 2.0?

OAuth 2.0 is an authorization framework that enables an application to obtain limited access to protected resources. For user login, OpenID Connect (OIDC) adds an identity layer on top of OAuth 2.0.

What is Google Sign-In?

Google Sign-In lets users authenticate using their Google account. Depending on the integration, the Flutter application can obtain an ID token that your backend verifies before creating its own application session.

What is biometric authentication?

Biometric authentication uses supported device capabilities, such as fingerprint recognition or Face ID, to verify that a person can unlock a local protected experience. A successful biometric check alone does not prove identity to your backend or replace server-side authorization.

OAuth 2.0 / OIDC

Establishes delegated access and, through OIDC, provides identity information.

Google Sign-In

Provides a familiar identity flow using a Google account.

Biometrics

Can unlock a locally protected session or key after device verification.

2. Secure Authentication Architecture

A secure architecture separates identity verification, application sessions, and local device authentication.

Flutter Application
Login UI and authentication coordinator
↓
Google Identity / Authorization Provider
User authentication and identity token issuance
↓
Application Backend
Validates identity token and establishes app session
↓
Protected API and User Data
Access controlled by application session and permissions

Separate local path: device biometric check → unlock locally protected session material → authenticated application session.

Important: The Flutter client must not be the sole authority for deciding whether a user is authenticated. Your backend must verify identity credentials and enforce access permissions on every protected resource.

3. Set Up Your Flutter Project

Create a Flutter application:

flutter create secure_auth_demo
cd secure_auth_demo

Add the core packages:

flutter pub add google_sign_in
flutter pub add flutter_secure_storage
flutter pub add local_auth
flutter pub add dio
flutter pub add flutter_riverpod

Use package versions compatible with your Flutter SDK, and follow the setup instructions for the exact versions you install.

Recommended project structure

lib/
 ├── main.dart
 ├── models/
 │    └── auth_session.dart
 ├── services/
 │    ├── google_auth_service.dart
 │    ├── auth_api.dart
 │    └── secure_storage_service.dart
 ├── providers/
 │    └── auth_provider.dart
 └── screens/
      ├── login_screen.dart
      └── home_screen.dart

4. Configure Google Sign-In

  1. Open the Google Cloud Console and create or select a project.
  2. Configure the OAuth consent screen and requested scopes.
  3. Set up the appropriate OAuth client configuration for Android and iOS.
  4. Configure the Android package name and signing certificate fingerprints as required.
  5. Configure the iOS bundle identifier and URL scheme when required by the selected SDK setup.
  6. Follow the current Google Sign-In package instructions for client IDs and platform-specific configuration.

Reference: Google Sign-In for Flutter.

5. Implement Google Authentication

The following example demonstrates the current style of the Google Sign-In Flutter API using GoogleSignIn.instance. It retrieves an ID token for sending to your backend; it does not establish a trusted application session by itself.

Create lib/services/google_auth_service.dart:

import 'package:google_sign_in/google_sign_in.dart';

class GoogleAuthService {
  final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
  bool _initialized = false;

  Future initialize({
    String? serverClientId,
  }) async {
    if (_initialized) return;

    await _googleSignIn.initialize(
      serverClientId: serverClientId,
    );

    _initialized = true;
  }

  Future<String?> signInAndGetIdToken() async {
    if (!_initialized) {
      throw StateError(
        'GoogleAuthService must be initialized first.',
      );
    }

    if (!_googleSignIn.supportsAuthenticate()) {
      throw UnsupportedError(
        'Use the platform-supported Google sign-in flow.',
      );
    }

    final account = await _googleSignIn.authenticate();
    final idToken = account.authentication.idToken;

    if (idToken == null || idToken.isEmpty) {
      throw StateError(
        'Google did not return an ID token.',
      );
    }

    return idToken;
  }

  Future<void> signOut() async {
    await _googleSignIn.signOut();
  }
}

Initialize the service once during app startup with the appropriate configuration for your platform. The server client ID, when required, must be the correct OAuth client ID for your backend audience—not a client secret.

Google Sign-In APIs and platform flows vary by package version. Some platforms use different sign-in mechanisms, so follow the current package documentation rather than assuming that the same interactive method works everywhere.

6. Verify Identity on Your Backend

After Google Sign-In succeeds, send the ID token to your backend over HTTPS. The backend must validate the token using a trusted Google authentication library.

Never trust a client-side token decode.

Decoding a JWT is not the same as validating it. The backend must verify the signature, issuer, audience, expiration, and other required claims before accepting the identity.

Example Flutter API client in lib/services/auth_api.dart:

import 'package:dio/dio.dart';

class AuthApi {
  AuthApi()
      : _dio = Dio(
          BaseOptions(
            baseUrl: 'https://api.example.com',
            connectTimeout: const Duration(seconds: 10),
            receiveTimeout: const Duration(seconds: 10),
          ),
        );

  final Dio _dio;

  Future<Map<String, dynamic>> exchangeGoogleToken(
    String idToken,
  ) async {
    final response = await _dio.post<dynamic>(
      '/auth/google',
      data: {
        'idToken': idToken,
      },
    );

    if (response.data is! Map) {
      throw const FormatException(
        'Unexpected authentication response.',
      );
    }

    return Map<String, dynamic>.from(response.data as Map);
  }
}

The URL above is a placeholder. Replace it with your real backend endpoint. The server should return an application session or another clearly defined authentication result only after successful validation.

Expected authentication sequence

  1. The user signs in with Google.
  2. The Flutter app obtains a Google ID token.
  3. The app sends the token to the backend over HTTPS.
  4. The backend validates the token and its intended audience.
  5. The backend finds or creates the application account according to its account-linking policy.
  6. The backend issues an application session using its defined token strategy.

7. Secure Token Storage

Do not store session tokens in plain text preferences. Use platform-backed secure storage and understand the security properties of the underlying Android and iOS implementations.

Create lib/services/secure_storage_service.dart:

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class SecureStorageService {
  final FlutterSecureStorage _storage =
      const FlutterSecureStorage();

  static const _tokenKey = 'app_access_token';

  Future<void> saveAccessToken(String token) async {
    await _storage.write(
      key: _tokenKey,
      value: token,
    );
  }

  Future<String?> readAccessToken() async {
    return _storage.read(key: _tokenKey);
  }

  Future<void> clearSession() async {
    await _storage.delete(key: _tokenKey);
  }
}

Configure secure storage according to the package's platform-specific security guidance. For sensitive applications, review keychain accessibility, Android backup behavior, device compromise risks, and session expiration.

8. Implement Biometric Authentication

The local_auth package supports local authentication using biometrics and, depending on platform configuration, device credentials. It can be used to gate access to a previously established session.

Create lib/services/biometric_service.dart:

import 'package:local_auth/local_auth.dart';

class BiometricService {
  final LocalAuthentication _auth =
      LocalAuthentication();

  Future<bool> authenticate() async {
    final isSupported = await _auth.isDeviceSupported();

    if (!isSupported) {
      return false;
    }

    final canCheck = await _auth.canCheckBiometrics;
    final biometrics = await _auth.getAvailableBiometrics();

    if (!canCheck || biometrics.isEmpty) {
      return false;
    }

    return _auth.authenticate(
      localizedReason: 'Authenticate to access your account',
      options: const AuthenticationOptions(
        biometricOnly: true,
        stickyAuth: true,
      ),
    );
  }
}

Configure the required Android permissions and iOS Face ID usage description as documented by the installed local_auth package. Test on physical devices because biometric availability differs across devices and simulators.

How biometric unlock should work

Existing application session
↓
Local biometric verification
↓
Unlock protected local access

The backend session must still be valid and authorized.

For stronger protection, use platform-backed cryptographic keys with access controls tied to device authentication. A biometric success boolean should not be treated as a replacement for an access token, and biometrics should not be used to bypass server authorization.

9. Manage Sessions and Logout

Authentication is not complete until session expiration, refresh, and logout behavior are clearly defined.

  • Access tokens: Keep short-lived where appropriate and send them only to authorized APIs.
  • Refresh tokens: Store securely, rotate when supported, and handle revocation according to your backend design.
  • Session restoration: Validate the saved session with the backend rather than trusting that a token merely exists.
  • Logout: Clear local credentials and revoke the server-side session when supported.
  • Google sign-out: Sign out of the Google SDK separately if the intended experience requires it.
  • Biometric unlock: Require a fresh local check before unlocking the protected session when appropriate.

A complete logout implementation should account for in-flight requests, cached sensitive data, refresh-token revocation, and the user's Google account state.

10. Production Security Checklist

✓ Use HTTPS for authentication and API requests.
✓ Validate Google identity tokens on the backend.
✓ Keep client secrets out of the mobile application.
✓ Store credentials using platform-backed secure storage.
✓ Implement token expiration and refresh correctly.
✓ Use biometric authentication only for an appropriate local security purpose.
✓ Handle cancellation, unavailable biometrics, and authentication failures.
✓ Avoid logging tokens, passwords, or sensitive personal data.
✓ Test account linking, logout, token revocation, and recovery flows.

11. Common Issues and Solutions

Issue What to check
Google Sign-In failsOAuth client configuration, package name, signing fingerprints, and SDK setup.
ID token is nullCheck the installed SDK version and the correct client ID configuration.
Backend rejects the tokenVerify signature, issuer, audience, expiry, and backend validation logic.
Biometric prompt unavailableCheck platform permissions, enrolled biometrics, hardware support, and device policy.
User is logged out unexpectedlyInspect token expiry, refresh flow, storage errors, and server-side session revocation.

12. Frequently Asked Questions

Is OAuth 2.0 the same as authentication?

No. OAuth 2.0 is primarily an authorization framework. OpenID Connect adds an identity layer for authentication.

Can I authenticate users with Google Sign-In without a backend?

Google Sign-In can establish a Google identity on the client, but applications that protect their own data or services should verify identity and enforce authorization on a trusted backend.

Can biometrics replace a password or access token?

Biometrics can unlock a local session or protected key. They do not independently establish an authenticated server session or replace backend access controls.

Should I store Google ID tokens permanently?

Generally, use an ID token for its intended identity-verification purpose and exchange it with your backend when required. Manage application session credentials according to their own lifecycle and security requirements.

Is biometric authentication available on every device?

No. Support depends on device hardware, operating system configuration, enrolled biometrics, permissions, and application policy. Always provide an appropriate fallback.

Conclusion

A professional Flutter authentication system combines a correctly configured identity provider, trusted backend validation, secure session management, and a user-friendly login experience. Google Sign-In can simplify identity onboarding, while biometrics can add convenient local protection for returning users.

The most important principle is to keep authentication responsibilities clear: identity providers establish identity, your backend validates credentials and enforces authorization, and device biometrics protect local access according to your security design.

Build Better Flutter Applications

Explore official documentation to keep your authentication implementation secure and compatible with current SDKs.

Explore Flutter Documentation

Official References

Security note: This article provides an implementation overview, not a complete security certification. Review current package documentation and your application's threat model before deploying authentication in production.

Comments