angel_auth_oauth2 2.1.0

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 66

auth_oauth2 #

Pub

package:angel_auth strategy for OAuth2 login, i.e. Facebook or Github.

Usage #

First, create an options object:

configureServer(Angel app) async {
  // Load from a Map, i.e. app config:
  var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);
  
  // Create in-place:
  var opts = ExternalAuthOptions(
      clientId: '<client-id>',
      clientSecret: '<client-secret>',
      redirectUri: Uri.parse('<callback>'));
}

After getting authenticated against the remote server, we need to be able to identify users within our own application.

typedef FutureOr<User> OAuth2Verifier(oauth2.Client, RequestContext, ResponseContext);

/// You might use a pure function to create a verifier that queries a
/// given service.
OAuth2Verifier oauth2verifier(Service<User> userService) {
  return (client) async {
     var response = await client.get('https://api.github.com/user');
      var ghUser = json.decode(response.body);
      var id = ghUser['id'] as int;

      var matchingUsers = await mappedUserService.index({
        'query': {'github_id': id}
      });

      if (matchingUsers.isNotEmpty) {
        // Return the corresponding user, if it exists.
        return matchingUsers.first;
      } else {
        // Otherwise,create a user
        return await mappedUserService.create(User(githubId: id));
      }
   };
}

Now, initialize an OAuth2Strategy, using the options and verifier. You'll also need to provide a name for this instance of the strategy. Consider using the name of the remote authentication provider (ex. facebook).

configureServer(Angel app) {
  auth.strategies['github'] = OAuth2Strategy(
    options,
    authorizationEndpoint,
    tokenEndpoint,
    yourVerifier,

    // This function is called when an error occurs, or the user REJECTS the request.
    (e, req, res) async {
      res.write('Ooops: $e');
      await res.close();
    },
  );
}

Lastly, connect it to an AngelAuth instance, and wire it up to an Angel server. Set up two routes:

  1. Redirect users to the external provider
  2. Acts as a callback and handles an access code

In the case of the callback route, you may want to display an HTML page that closes a popup window. In this case, use confirmPopupAuthentication, which is bundled with package:angel_auth, as a callback function:

configureServer(Angel app) async {
  // ...
  var auth = AngelAuth<User>();
  auth.strategies['github'] = oauth2Strategy;
  
  // Redirect
  app.get('/auth/github', auth.authenticate('github'));
  
  // Callback
  app.get('/auth/github/callback', auth.authenticate(
    'github',
    AngelAuthOptions(callback: confirmPopupAuthentication())
  ));
  
  // Connect the plug-in!!!
  await app.configure(auth);
}

Custom Scope Delimiter #

This package should work out-of-the-box for most OAuth2 providers, such as Github or Dropbox. However, if your OAuth2 scopes are separated by a delimiter other than the default (' '), you can add it in the OAuth2Strategy constructor:

configureServer(Angel app) async {
  OAuth2Strategy(..., delimiter: ' ');
}

Handling non-JSON responses #

Many OAuth2 providers do not follow the specification, and do not return application/json responses.

You can add a getParameters callback to parse the contents of any arbitrary response:

OAuth2Strategy(
    // ...
    getParameters: (contentType, body) {
      if (contentType.type == 'application') {
        if (contentType.subtype == 'x-www-form-urlencoded')
          return Uri.splitQueryString(body);
        else if (contentType.subtype == 'json') return JSON.decode(body);
      }

      throw FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
    }
);

2.1.0 #

  • Angel 2 + Dart 2 update
  • Support for handling errors + rejections.
  • Use ExternalAuthOptions.

2.0.0+1 #

  • Meta update to improve Pub score.

2.0.0 #

  • Angel 2 + Dart 2 updates.

1.0.2 #

Added getParameters to AngelOAuth2Options.

example/main.dart

import 'dart:convert';
import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:angel_auth_oauth2/angel_auth_oauth2.dart';
import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';

var authorizationEndpoint =
    Uri.parse('http://github.com/login/oauth/authorize');

var tokenEndpoint = Uri.parse('https://github.com/login/oauth/access_token');

var options = ExternalAuthOptions(
  clientId: '6caeaf5d4c04936ec34f',
  clientSecret: '178360518cf9de4802e2346a4b6ebec525dc4427',
  redirectUri: Uri.parse('http://localhost:3000/auth/github/callback'),
);

/// Github doesn't properly follow the OAuth2 spec, so here's logic to parse their response.
Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
  if (contentType.type == 'application') {
    if (contentType.subtype == 'x-www-form-urlencoded')
      return Uri.splitQueryString(body);
    else if (contentType.subtype == 'json')
      return (json.decode(body) as Map).cast<String, String>();
  }

  throw FormatException(
      'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
}

main() async {
  // Create the server instance.
  var app = Angel();
  var http = AngelHttp(app);
  app.logger = Logger('angel')
    ..onRecord.listen((rec) {
      print(rec);
      if (rec.error != null) print(rec.error);
      if (rec.stackTrace != null) print(rec.stackTrace);
    });

  // Create a service that stores user data.
  var userService = app.use('/users', MapService()).inner;
  var mappedUserService = userService.map(User.parse, User.serialize);

  // Set up the authenticator plugin.
  var auth =
      AngelAuth<User>(jwtKey: 'oauth2 example secret', allowCookie: false);
  auth.serializer = (user) async => user.id;
  auth.deserializer = (id) => mappedUserService.read(id.toString());
  app.fallback(auth.decodeJwt);

  /// Create an instance of the strategy class.
  auth.strategies['github'] = OAuth2Strategy(
    options,
    authorizationEndpoint,
    tokenEndpoint,

    // This function is called when the user ACCEPTS the request to sign in with Github.
    (client, req, res) async {
      var response = await client.get('https://api.github.com/user');
      var ghUser = json.decode(response.body);
      var id = ghUser['id'] as int;

      var matchingUsers = await mappedUserService.index({
        'query': {'github_id': id}
      });

      if (matchingUsers.isNotEmpty) {
        // Return the corresponding user, if it exists.
        return matchingUsers.first;
      } else {
        // Otherwise,create a user
        return await mappedUserService.create(User(githubId: id));
      }
    },

    // This function is called when an error occurs, or the user REJECTS the request.
    (e, req, res) async {
      res.write('Ooops: $e');
      await res.close();
    },

    // We have to pass this parser function when working with Github.
    getParameters: parseParamsFromGithub,
  );

  // Mount some routes
  app.get('/auth/github', auth.authenticate('github'));
  app.get(
      '/auth/github/callback',
      auth.authenticate('github',
          AngelAuthOptions(callback: (req, res, jwt) async {
        // In real-life, you might include a pop-up callback script.
        //
        // Use `confirmPopupAuthentication`, which is bundled with
        // `package:angel_auth`.
        var user = req.container.make<User>();
        res.write('Your user info: ${user.toJson()}\n\n');
        res.write('Your JWT: $jwt');
        await res.close();
      })));

  // Start listening.
  await http.startServer('127.0.0.1', 3000);
  print('Listening on ${http.uri}');
  print('View user listing: ${http.uri}/users');
  print('Sign in via Github: ${http.uri}/auth/github');
}

class User extends Model {
  @override
  String id;

  int githubId;

  User({this.id, this.githubId});

  static User parse(Map map) =>
      User(id: map['id'] as String, githubId: map['github_id'] as int);

  static Map<String, dynamic> serialize(User user) => user.toJson();

  Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId};
}

Use this package as a library

1. Depend on it

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


dependencies:
  angel_auth_oauth2: ^2.1.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or 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:angel_auth_oauth2/angel_auth_oauth2.dart';
  
Version Uploaded Documentation Archive
2.1.0 Jan 6, 2019 Go to the documentation of angel_auth_oauth2 2.1.0 Download angel_auth_oauth2 2.1.0 archive
2.0.0+1 Sep 12, 2018 Go to the documentation of angel_auth_oauth2 2.0.0+1 Download angel_auth_oauth2 2.0.0+1 archive
2.0.0 Sep 12, 2018 Go to the documentation of angel_auth_oauth2 2.0.0 Download angel_auth_oauth2 2.0.0 archive
1.0.2 Mar 30, 2018 Go to the documentation of angel_auth_oauth2 1.0.2 Download angel_auth_oauth2 1.0.2 archive
1.0.1 Jun 3, 2017 Go to the documentation of angel_auth_oauth2 1.0.1 Download angel_auth_oauth2 1.0.1 archive
1.0.0 Feb 23, 2017 Go to the documentation of angel_auth_oauth2 1.0.0 Download angel_auth_oauth2 1.0.0 archive
0.0.0 Jan 12, 2017 Go to the documentation of angel_auth_oauth2 0.0.0 Download angel_auth_oauth2 0.0.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
34
Health:
Code health derived from static analysis. [more]
99
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
66
Learn more about scoring.

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

  • Dart: 2.3.1
  • pana: 0.12.16

Platforms

Detected platforms: Flutter, other

Primary library: package:angel_auth_oauth2/angel_auth_oauth2.dart with components: io.

Health suggestions

Fix lib/angel_auth_oauth2.dart. (-1.49 points)

Analysis of lib/angel_auth_oauth2.dart reported 3 hints:

line 55 col 9: DO use curly braces for all flow control structures.

line 57 col 9: DO use curly braces for all flow control structures.

line 68 col 5: Future results in async function bodies must be awaited or marked unawaited using package:pedantic.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
angel_auth ^2.0.0 2.1.5+1
angel_framework ^2.0.0-alpha 2.0.1
http_parser ^3.0.0 3.1.3
oauth2 ^1.0.0 1.2.3
Transitive dependencies
angel_container 1.0.4
angel_http_exception 1.1.0
angel_model 1.0.3
angel_route 3.0.6
async 2.2.0
charcode 1.1.2
code_buffer 1.0.1
collection 1.14.11
combinator 1.1.0
convert 2.1.1
crypto 2.0.6
dart2_constant 1.0.2+dart2
file 5.0.8
http 0.12.0+2
http2 1.0.0
http_server 0.9.8+1
intl 0.15.8
matcher 0.12.5
merge_map 1.0.2
meta 1.1.7
mime 0.9.6+2
mock_request 1.0.5
path 1.6.2
quiver 2.0.3
quiver_hashcode 2.0.0
source_span 1.5.5
stack_trace 1.9.3
string_scanner 1.0.4
term_glyph 1.1.0
tuple 1.0.2
typed_data 1.1.6
uuid 2.0.1
Dev dependencies
logging ^0.11.0 0.11.3+2
pedantic ^1.0.0 1.7.0