oauth2_custom_uri_scheme 0.3.6

  • Readme
  • Changelog
  • Example
  • Installing
  • 72

oauth2_custom_uri_scheme #

An implementation of OAuth 2.0 authorization code grant with redirection to application specific custom URI scheme.

To make the implementation safer (but not perfect), it implements the following features:

So the implementation fully works on Android with API level >= 23 and iOS >= 11.0.

Installation #

dependencies:
  oauth2_custom_uri_scheme: ^0.3.6

Getting Started #

import 'package:oauth2_custom_uri_scheme/oauth2_custom_uri_scheme.dart';
import 'package:oauth2_custom_uri_scheme/oauth2_token_holder.dart';

...

//
// OAuth2Config can be app global to keep the OAuth configuration
//
final oauth2Config = OAuth2Config(
  uniqueId: 'example.com#1', // NOTE: ID to identify the credential for box session
  authorizationEndpoint: Uri.parse('https://example.com/authorize'),
  tokenEndpoint: Uri.parse('https://example.com/token'),
  // revocationEndpoint is optional
  revocationEndpoint: Uri.parse('https://example.com/revoke'),
  // NOTE: For Android, we also have corresponding intent-filter entry on example/android/app/src/main/AndroidManifest.xml
  redirectUri: Uri.parse('com.example.redirect43763246328://callback'),
  clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
  useBasicAuth: false);

...

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Simple OAuth Sample'),
    ),
    body: Center(
      // OAuth2TokenHolder is the easiest way to create [authorize] button.
      child: OAuth2TokenHolder(
        config: oauth2Config,
        builder: (context, accessToken, state, authorize, deauthorize, child) => ListTile(title: RaisedButton(
          onPressed:() => accessToken == null ? authorize() : deauthorize(),
          child: state == OAuth2TokenAvailability.Authorizing
          ? const CircularProgressIndicator()
          : Text(accessToken == null ? 'Authorize' : 'Deauthorize'))
        )
      )
    )
  );
}

// After [Authorize] on the UI, we can get the access token from cache.
AccessToken token = await oauth2Config.getAccessTokenFromCache();

// Or, of course, you can authorize the app
// If reset=false, it may use the cache and the method returns immediately;
// otherwise reset=true, it always tries to authorize the app.
AccessToken token = await oauth2Config.authorize(reset: true);

// OK, we can execute GET query
final foobarResult = await token.getJsonFromUri('https://example.com/api/2.0/foobar');

Lower level API #

To use the plugin with your OAuth service provider, call AccessToken.authorize with your OAuth service's endpoints and client configuration:

final AccessToken token = await AccessToken.authorize(
    authorizationEndpoint: Uri.parse('https://example.com/authorize'),
    tokenEndpoint: Uri.parse('https://example.com/token'),
    // NOTE: For Android, we also have corresponding intent-filter entry on example/android/app/src/main/AndroidManifest.xml
    redirectUri: Uri.parse('com.example.redirect43763246328://callback'),
    clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
    useBasicAuth: false, // certain services such as Box does not support Basic Auth
    idForCache: 'example.com#1' // used when caching access token
);
if (token == null) {
    // error handling
}

// OK, we can execute GET query
final foobarResult = await token.getJsonFromUri('https://example.com/api/2.0/foobar');

// POST query
final request = await token.createRequest('POST', 'https://example.com/api/2.0/zzzzz');
request.body = '....';
final response = await request.send();

Additional settings on Android #

For Android, we should update several configurations:

We should add AndroidManifest.xml to include additional <intent-filter> under <activity>.

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <!-- Should match to the one on example/lib/main.dart -->
    <data android:scheme="com.example.redirect43763246328" android:host="callback" />
</intent-filter>

In android/app/build.gradle set minSdkVersion to >= 18.

android {
    ...
    defaultConfig {
        ...
        minSdkVersion 18
        ...
    }
}

API reference #

API reference on pub.dev.

Security considerations #

On every platform, we can define a custom URI scheme to launch our app on URI redirects.

Although the URI may be something like myapp://localhost, the URI scheme here, myapp is not suitable for accepting authorization code redirect. If any other apps may use the same URI scheme, the authorization code may be intercepted by them without launching our app.

Basically, it's almost impossible to hide our custom URI scheme from others, apparently, we should choose one carefully not to conflict with other apps.

At least, we should use app scheme like com.example.mwl5oodcb9, which contains some random characters (but they should be lowercase anyway).

To reduce the risk of others intercepting authorization code, the plugin implements RFC 7636: Proof Key for Code Exchange (PKCE) by OAuth Public Clients. But not every OAuth service implements it.

0.3.6 #

  • token endpoint also needs redirect_uri.

0.3.5 #

  • code_challenge is not correctly calculated.

0.3.4 #

  • refresh throws exception if it could not refresh the token.
  • Add AccessToken.printHandler static variable to customize debug log verbosity.

0.3.2 #

  • OAuth2Config.authorize: reset should be false by default.

0.3.1 #

  • Minor updates.

0.3.0 #

  • BREAKING CHANGE: OAuth2Config is moved to oauth2_custom_uri_scheme.dart.
  • Add several helper methods for POST/PUT requests.

0.2.0 #

  • Introducing easy to use widgets; OAuth2Config and OAuth2TokenHolder.

0.1.1 #

  • Fix build issue on iOS caused by podspec.

example/README.md

oauth2_custom_uri_scheme_example #

Demonstrates how to use the oauth2_custom_uri_scheme plugin.

Getting Started #

You should update example/lib/main.dart and example/android/app/src/main/AndroidManifest.xml (for Android) to use your OAuth server and client configurations:

final oauth2Config = OAuth2Config(
  uniqueId: 'example.com#1', // NOTE: ID to identify the credential for box session
  authorizationEndpoint: Uri.parse('https://example.com/authorize'),
  tokenEndpoint: Uri.parse('https://example.com/token'),
  // revocationEndpoint is optional
  revocationEndpoint: Uri.parse('https://example.com/revoke'),
  // NOTE: For Android, we also have corresponding intent-filter entry on example/android/app/src/main/AndroidManifest.xml
  redirectUri: Uri.parse('com.example.redirect43763246328://callback'),
  clientId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy',
  useBasicAuth: false);
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <!-- Should match to the one on example/lib/main.dart -->
    <data android:scheme="com.example.redirect43763246328" android:host="callback" />
</intent-filter>

For choosing good URL scheme, see Security considerations.

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  oauth2_custom_uri_scheme: ^0.3.6

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:oauth2_custom_uri_scheme/oauth2_custom_uri_scheme.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
49
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
72
Learn more about scoring.

We analyzed this package on Dec 13, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.0
  • pana: 0.13.1+4
  • Flutter: 1.12.13+hotfix.4

Health suggestions

Format lib/oauth2_custom_uri_scheme.dart.

Run flutter format to format lib/oauth2_custom_uri_scheme.dart.

Format lib/oauth2_token_holder.dart.

Run flutter format to format lib/oauth2_token_holder.dart.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (rxdart).

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
crypto ^2.1.2 2.1.4
flutter 0.0.0
flutter_custom_tabs ^0.6.0 0.6.0
flutter_secure_storage ^3.2.1+1 3.3.1+1
http ^0.12.0+2 0.12.0+2
rxdart ^0.22.2 0.22.6 0.23.1
Transitive dependencies
async 2.4.0
charcode 1.1.2
collection 1.14.11 1.14.12
convert 2.1.1
flutter_web_plugins 0.0.0
http_parser 3.1.3
meta 1.1.8
path 1.6.4
pedantic 1.9.0
plugin_platform_interface 1.0.1
sky_engine 0.0.99
source_span 1.5.5
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
url_launcher 5.4.1
url_launcher_macos 0.0.1+2
url_launcher_platform_interface 1.0.4
url_launcher_web 0.1.0+2
vector_math 2.0.8
Dev dependencies
flutter_test