diff --git a/.idea/auth_oauth2.iml b/.idea/auth_oauth2.iml
index 7fe256fb..eae13016 100644
--- a/.idea/auth_oauth2.iml
+++ b/.idea/auth_oauth2.iml
@@ -5,8 +5,6 @@
-
-
diff --git a/.idea/runConfigurations/Github_Auth_Server.xml b/.idea/runConfigurations/Github_Auth_Server.xml
index c68cda20..d3ff1581 100644
--- a/.idea/runConfigurations/Github_Auth_Server.xml
+++ b/.idea/runConfigurations/Github_Auth_Server.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..d08eb72d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,2 @@
+# 1.0.2
+Added `getParameters` to `AngelOAuth2Options`.
\ No newline at end of file
diff --git a/README.md b/README.md
index edb4b7cd..e7b14ff7 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# auth_oauth2
-[![version 1.0.1](https://img.shields.io/badge/pub-1.0.1-brightgreen.svg)](https://pub.dartlang.org/packages/angel_auth_oauth2)
+[![Pub](https://img.shields.io/pub/v/angel_auth_oauth2.svg)](https://pub.dartlang.org/packages/angel_auth_oauth2)
`package:angel_auth` strategy for OAuth2 login, i.e. Facebook or Github.
@@ -13,7 +13,7 @@ configureServer(Angel app) async {
var opts = new AngelOAuth2Options.fromJson(map);
// Create in-place:
- const AngelAuthOAuth2Options OAUTH2_CONFIG = const AngelAuthOAuth2Options(
+ var opts = const AngelAuthOAuth2Options(
callback: '',
key: '',
secret: '',
@@ -98,9 +98,31 @@ you can add it in the `AngelOAuth2Options` constructor:
```dart
configureServer(Angel app) async {
- const AngelOAuth2Options OPTS = const AngelOAuth2Options(
+ var opts = const AngelOAuth2Options(
// ...
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:
+
+```dart
+var opts = const AngelOAuth2Options(
+ // ...
+ 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 new FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
+ }
+);
```
\ No newline at end of file
diff --git a/.analysis-options b/analysis_options.yaml
similarity index 100%
rename from .analysis-options
rename to analysis_options.yaml
diff --git a/example/github.dart b/example/main.dart
similarity index 66%
rename from example/github.dart
rename to example/main.dart
index cb92b891..d84141e5 100644
--- a/example/github.dart
+++ b/example/main.dart
@@ -1,31 +1,43 @@
import 'dart:convert';
import 'dart:io';
import 'package:angel_auth/angel_auth.dart';
-import 'package:angel_diagnostics/angel_diagnostics.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/common.dart';
import 'package:angel_auth_oauth2/angel_auth_oauth2.dart';
+import 'package:logging/logging.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
-const AngelAuthOAuth2Options OAUTH2_CONFIG = const AngelAuthOAuth2Options(
+final AngelAuthOAuth2Options oAuth2Config = new AngelAuthOAuth2Options(
callback: 'http://localhost:3000/auth/github/callback',
key: '6caeaf5d4c04936ec34f',
secret: '178360518cf9de4802e2346a4b6ebec525dc4427',
authorizationEndpoint: 'http://github.com/login/oauth/authorize',
- tokenEndpoint: 'https://github.com/login/oauth/access_token');
+ tokenEndpoint: 'https://github.com/login/oauth/access_token',
+ 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 new FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
+ });
main() async {
var app = new Angel();
app.lazyParseBodies = true;
app.use('/users', new MapService());
- var auth = new AngelAuth(jwtKey: 'oauth2 example secret', allowCookie: false);
+ var auth =
+ new AngelAuth(jwtKey: 'oauth2 example secret', allowCookie: false);
+
+ auth.deserializer =
+ (id) => app.service('users').read(id).then((u) => User.parse(u));
- auth.deserializer = app.service('users').read;
auth.serializer = (User user) async => user.id;
auth.strategies.add(
- new OAuth2Strategy('github', OAUTH2_CONFIG, (oauth2.Client client) async {
+ new OAuth2Strategy('github', oAuth2Config, (oauth2.Client client) async {
var response = await client.get('https://api.github.com/user');
var ghUser = JSON.decode(response.body);
var id = ghUser['id'];
@@ -41,7 +53,7 @@ main() async {
// Otherwise,create a user
return await app
.service('users')
- .create({'githubId': id}).then(User.parse);
+ .create({'githubId': id}).then((u) => User.parse(u));
}
}));
@@ -57,10 +69,12 @@ main() async {
res.write('Your JWT: $jwt');
})));
- await app.configure(auth);
- await app.configure(logRequests());
+ await app.configure(auth.configureServer);
- var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
+ app.logger = new Logger('angel')..onRecord.listen(print);
+
+ var http = new AngelHttp(app);
+ var server = await http.startServer(InternetAddress.LOOPBACK_IP_V4, 3000);
var url = 'http://${server.address.address}:${server.port}';
print('Listening on $url');
print('View user listing: $url/users');
diff --git a/lib/angel_auth_oauth2.dart b/lib/angel_auth_oauth2.dart
index 226ef36a..54d0a824 100644
--- a/lib/angel_auth_oauth2.dart
+++ b/lib/angel_auth_oauth2.dart
@@ -2,21 +2,18 @@ library angel_auth_oauth2;
import 'dart:async';
import 'package:angel_auth/angel_auth.dart';
-import 'package:angel_framework/src/http/response_context.dart';
-import 'package:angel_framework/src/http/request_context.dart';
+import 'package:angel_framework/angel_framework.dart';
import 'package:angel_validate/angel_validate.dart';
+import 'package:http_parser/http_parser.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
-/// Loads a user profile via OAuth2.
-typedef Future OAuth2Verifier(oauth2.Client client);
-
final Validator OAUTH2_OPTIONS_SCHEMA = new Validator({
'key*': isString,
'secret*': isString,
- 'authorizationEndpoint*': isString,
- 'tokenEndpoint*': isString,
+ 'authorizationEndpoint*': anyOf(isString, const isInstanceOf()),
+ 'tokenEndpoint*': anyOf(isString, const isInstanceOf()),
'callback*': isString,
- 'scopes': new isInstanceOf>()
+ 'scopes': const isInstanceOf>()
}, defaultValues: {
'scopes': []
}, customErrorMessages: {
@@ -32,10 +29,10 @@ class AngelAuthOAuth2Options {
final String secret;
/// The remote endpoint that prompts external users for authentication credentials.
- final String authorizationEndpoint;
+ final authorizationEndpoint;
/// The remote endpoint that exchanges auth codes for access tokens.
- final String tokenEndpoint;
+ final tokenEndpoint;
/// The callback URL that the OAuth2 server should redirect authenticated users to.
final String callback;
@@ -44,6 +41,8 @@ class AngelAuthOAuth2Options {
final String delimiter;
final Iterable scopes;
+ final Map Function(MediaType, String) getParameters;
+
const AngelAuthOAuth2Options(
{this.key,
this.secret,
@@ -51,7 +50,8 @@ class AngelAuthOAuth2Options {
this.tokenEndpoint,
this.callback,
this.delimiter: ' ',
- this.scopes: const []});
+ this.scopes: const [],
+ this.getParameters});
factory AngelAuthOAuth2Options.fromJson(Map json) =>
new AngelAuthOAuth2Options(
@@ -62,7 +62,7 @@ class AngelAuthOAuth2Options {
callback: json['callback'],
scopes: json['scopes'] ?? []);
- Map toJson() {
+ Map toJson() {
return {
'key': key,
'secret': secret,
@@ -74,19 +74,14 @@ class AngelAuthOAuth2Options {
}
}
-class OAuth2Strategy implements AuthStrategy {
- String _name;
+class OAuth2Strategy implements AuthStrategy {
+ final FutureOr Function(oauth2.Client) verifier;
+ String name;
+
AngelAuthOAuth2Options _options;
- final OAuth2Verifier verifier;
-
- @override
- String get name => _name;
-
- @override
- set name(String value) => _name = name;
/// [options] can be either a `Map` or an instance of [AngelAuthOAuth2Options].
- OAuth2Strategy(this._name, options, this.verifier) {
+ OAuth2Strategy(this.name, options, this.verifier) {
if (options is AngelAuthOAuth2Options)
_options = options;
else if (options is Map)
@@ -102,7 +97,8 @@ class OAuth2Strategy implements AuthStrategy {
Uri.parse(_options.authorizationEndpoint),
Uri.parse(_options.tokenEndpoint),
secret: _options.secret,
- delimiter: _options.delimiter ?? ' ');
+ delimiter: _options.delimiter ?? ' ',
+ getParameters: _options.getParameters);
@override
Future authenticate(RequestContext req, ResponseContext res,
diff --git a/pubspec.yaml b/pubspec.yaml
index f34a9ce7..06c39ae7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: angel_auth_oauth2
description: angel_auth strategy for OAuth2 login, i.e. Facebook.
-version: 1.0.1
+version: 1.0.2
author: Tobe O
environment:
sdk: ">=1.19.0"
@@ -8,6 +8,4 @@ homepage: https://github.com/angel-dart/auth_oauth2.git
dependencies:
angel_auth: ^1.0.0-dev
angel_validate: ^1.0.0-beta
- oauth2: ^1.0.0
-dev_dependencies:
- angel_diagnostics: ^1.0.0-dev
\ No newline at end of file
+ oauth2: ^1.0.0
\ No newline at end of file