2.0.0 done
This commit is contained in:
parent
e86f6d1427
commit
db505a470c
3 changed files with 85 additions and 23 deletions
|
@ -1,3 +1,5 @@
|
|||
# 2.0.0
|
||||
* Angel 2 + Dart 2 suppport.
|
||||
* Use `package:twitter` instead of `package:twit`.
|
||||
* Add `TwitterAuthorizationException`.
|
||||
* Add `onError` callback.
|
|
@ -38,11 +38,19 @@ main() async {
|
|||
'http://localhost:3000/auth/twitter/callback',
|
||||
),
|
||||
(twit, req, res) async {
|
||||
var response =
|
||||
await twit.twitterClient.get('/account/verify_credentials.json');
|
||||
var response = await twit.twitterClient
|
||||
.get('https://api.twitter.com/1.1/account/verify_credentials.json');
|
||||
var userData = json.decode(response.body) as Map;
|
||||
return _User(userData['screen_name'] as String);
|
||||
},
|
||||
(e, req, res) async {
|
||||
// When an error occurs, i.e. the user declines to approve the application.
|
||||
if (e.isDenial) {
|
||||
res.write("Why'd you say no???");
|
||||
} else {
|
||||
res.write("oops: ${e.message}");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
app
|
||||
|
@ -69,6 +77,7 @@ main() async {
|
|||
(req, res) {
|
||||
var user = req.container.make<_User>();
|
||||
res.write('Your Twitter handle is ${user.handle}');
|
||||
return false;
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:angel_auth/angel_auth.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
@ -17,6 +18,10 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
final FutureOr<User> Function(Twitter, RequestContext, ResponseContext)
|
||||
verifier;
|
||||
|
||||
/// A callback that is triggered when an OAuth2 error occurs (i.e. the user declines to login);
|
||||
final FutureOr<dynamic> Function(
|
||||
TwitterAuthorizationException, RequestContext, ResponseContext) onError;
|
||||
|
||||
/// The root of Twitter's API. Defaults to `'https://api.twitter.com'`.
|
||||
final Uri baseUrl;
|
||||
|
||||
|
@ -25,7 +30,7 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
/// The underlying [oauth.Client] used to query Twitter.
|
||||
oauth.Client get client => _client;
|
||||
|
||||
TwitterStrategy(this.options, this.verifier,
|
||||
TwitterStrategy(this.options, this.verifier, this.onError,
|
||||
{http.BaseClient client, Uri baseUrl})
|
||||
: this.baseUrl = baseUrl ?? Uri.parse('https://api.twitter.com') {
|
||||
var tokens = oauth.Tokens(
|
||||
|
@ -38,8 +43,16 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
var body = rs.body;
|
||||
|
||||
if (rs.statusCode != 200) {
|
||||
throw new AngelHttpException.notAuthenticated(
|
||||
message: 'Twitter authentication error: $body');
|
||||
var err = json.decode(rs.body) as Map;
|
||||
var errors = err['errors'] as List;
|
||||
|
||||
if (errors.isNotEmpty) {
|
||||
throw TwitterAuthorizationException(
|
||||
errors[0]['message'] as String, false);
|
||||
} else {
|
||||
throw StateError(
|
||||
'Twitter returned an error response without an error message: ${rs.body}');
|
||||
}
|
||||
}
|
||||
|
||||
return Uri.splitQueryString(body);
|
||||
|
@ -49,6 +62,9 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
Future<Map<String, String>> getAccessToken(String token, String verifier) {
|
||||
return _client.post(
|
||||
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/access_token')),
|
||||
headers: {
|
||||
'accept': 'application/json'
|
||||
},
|
||||
body: {
|
||||
'oauth_token': token,
|
||||
'oauth_verifier': verifier
|
||||
|
@ -61,6 +77,9 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
Future<Map<String, String>> getRequestToken() {
|
||||
return _client.post(
|
||||
baseUrl.replace(path: p.join(baseUrl.path, 'oauth/request_token')),
|
||||
headers: {
|
||||
'accept': 'application/json'
|
||||
},
|
||||
body: {
|
||||
"oauth_callback": options.redirectUri.toString()
|
||||
}).then(handleUrlEncodedResponse);
|
||||
|
@ -69,8 +88,13 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
@override
|
||||
Future<User> authenticate(RequestContext req, ResponseContext res,
|
||||
[AngelAuthOptions options]) async {
|
||||
try {
|
||||
if (options != null) {
|
||||
return await authenticateCallback(req, res, options);
|
||||
var result = await authenticateCallback(req, res, options);
|
||||
if (result is User)
|
||||
return result;
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
var result = await getRequestToken();
|
||||
var token = result['oauth_token'];
|
||||
|
@ -80,16 +104,43 @@ class TwitterStrategy<User> extends AuthStrategy<User> {
|
|||
res.redirect(url);
|
||||
return null;
|
||||
}
|
||||
} on TwitterAuthorizationException catch (e) {
|
||||
var result = await onError(e, req, res);
|
||||
await req.app.executeHandler(result, req, res);
|
||||
await res.close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<User> authenticateCallback(
|
||||
Future authenticateCallback(
|
||||
RequestContext req, ResponseContext res, AngelAuthOptions options) async {
|
||||
// TODO: Handle errors
|
||||
try {
|
||||
if (req.queryParameters.containsKey('denied')) {
|
||||
throw TwitterAuthorizationException(
|
||||
'The user denied the Twitter authorization attempt.', true);
|
||||
}
|
||||
|
||||
var token = req.queryParameters['oauth_token'] as String;
|
||||
var verifier = req.queryParameters['oauth_verifier'] as String;
|
||||
var loginData = await getAccessToken(token, verifier);
|
||||
var twitter = Twitter(this.options.clientId, this.options.clientSecret,
|
||||
loginData['oauth_token'], loginData['oauth_token_secret']);
|
||||
return await this.verifier(twitter, req, res);
|
||||
} on TwitterAuthorizationException catch (e) {
|
||||
return await onError(e, req, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TwitterAuthorizationException implements Exception {
|
||||
/// The message associated with this exception.
|
||||
final String message;
|
||||
|
||||
/// Whether the user denied the authorization attempt.
|
||||
final bool isDenial;
|
||||
|
||||
TwitterAuthorizationException(this.message, this.isDenial);
|
||||
|
||||
@override
|
||||
String toString() => 'TwitterAuthorizationException: $message';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue