Skip to content

A complete OAuth2 solution for Flutter apps. Handles auth, token storage, and token refresh.

License

Notifications You must be signed in to change notification settings

IO-Design-Team/oauth_flutter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A complete OAuth2 solution for Flutter apps. Handles auth, token storage, and token refresh.

Features

  • Handles dio client setup
  • Securely stores tokens
  • Automatically refreshes tokens when expired
  • Refresh token expiration handler
  • Nonce, PKCE, and state verification
  • OIDC support
    • Endpoint discovery
    • Access to the ID token and raw nonce
    • Works with Firebase OIDC implicit flow

Getting started

The most relevant setup information for iOS/Android/web apps is copied below. See the individual plugin readmes for more details:

iOS

Set up Universal Links

Android

Set up App Links

android/app/build.gradle

android {
    defaultConfig {
        minSdk = 23
    }
}

In order to capture the callback url, the following activity needs to be added to your AndroidManifest.xml. Be sure to replace YOUR_CALLBACK_URL_SCHEME_HERE with your actual callback url scheme.

<manifest>
  <application>

    <activity
      android:name="com.linusu.flutter_web_auth_2.CallbackActivity"
      android:exported="true">
      <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="YOUR_CALLBACK_URL_SCHEME_HERE" />
      </intent-filter>
    </activity>

  </application>
</manifest>

Web

Ensure that the web server serves the Flutter app with a Strict-Transport-Security header. Firebase Hosting sets this header by default. See the documentation. Here is an example header:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

An endpoint must be created that captures the callback URL and sends it to the application using the JavaScript postMessage method. In the web folder of the project, create an HTML file named, e.g. oauth2/callback with content:

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

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

  postAuthenticationMessage();
</script>

Usage

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:oauth_flutter/oauth_flutter.dart';

void main() async {
  final client = OAuth2Client(
    key: 'fitbit',
    // The `baseUrl` is the OAuth `aud` parameter
    dio: Dio(BaseOptions(baseUrl: 'https://api.fitbit.com/1/user')),
    endpoints: OAuth2Endpoints(
      authorization: 'https://fitbit.com/oauth2/authorize',
      token: 'https://api.fitbit.com/oauth2/token',
      revocation: 'https://api.fitbit.com/oauth2/revoke',
    ),
    // Use `OAuth2Endpoints.base` for services with a consistent base URL
    // endpoints: OAuth2Endpoints.base('https://api.fitbit.com/oauth2'),
    redirectUri: Uri.parse('https://your-app.com/oauth2/callback'),
    // Do not pass client credentials if they are injected by the server
    credentials: const OAuth2ClientCredentials(
      id: 'your-client-id',
      secret: 'your-client-secret',
    ),
    scope: {
      'activity',
      'heartrate',
      'nutrition',
      'oxygen_saturation',
      'respiratory_rate',
      'settings',
      'sleep',
      'temperature',
      'weight',
    },
  );

  final token = await client.authenticate();
  debugPrint(token.idToken); // Fitbit doesn't actually support OIDC

  final response =
      await client.dio.get('/GGNJL9/activities/heart/date/today/1d.json');
  debugPrint(response.data);

  final isAuthenticated = await client.isAuthenticated();
  debugPrint(isAuthenticated.toString());
}

About

A complete OAuth2 solution for Flutter apps. Handles auth, token storage, and token refresh.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages