2017-10-16 06:38:46 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'package:angel_framework/angel_framework.dart';
|
2018-11-08 15:32:36 +00:00
|
|
|
import 'package:angel_framework/http.dart';
|
2017-10-16 06:38:46 +00:00
|
|
|
import 'package:angel_oauth2/angel_oauth2.dart';
|
2017-10-16 16:46:01 +00:00
|
|
|
import 'package:logging/logging.dart';
|
|
|
|
import 'package:oauth2/oauth2.dart' as oauth2;
|
2017-10-16 06:38:46 +00:00
|
|
|
import 'package:test/test.dart';
|
|
|
|
import 'common.dart';
|
|
|
|
|
|
|
|
main() {
|
2017-10-16 16:46:01 +00:00
|
|
|
Angel app;
|
|
|
|
Uri tokenEndpoint;
|
2017-10-16 06:38:46 +00:00
|
|
|
|
|
|
|
setUp(() async {
|
2019-05-02 07:28:38 +00:00
|
|
|
app = Angel();
|
|
|
|
var auth = _AuthorizationServer();
|
2017-10-16 06:38:46 +00:00
|
|
|
|
|
|
|
app.group('/oauth2', (router) {
|
|
|
|
router
|
2017-10-16 16:46:01 +00:00
|
|
|
..get('/authorize', auth.authorizationEndpoint)
|
|
|
|
..post('/token', auth.tokenEndpoint);
|
2017-10-16 06:38:46 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
app.errorHandler = (e, req, res) async {
|
|
|
|
res.json(e.toJson());
|
|
|
|
};
|
|
|
|
|
2019-05-02 07:28:38 +00:00
|
|
|
app.logger = Logger('password_test')..onRecord.listen(print);
|
2017-10-16 16:46:01 +00:00
|
|
|
|
2019-05-02 07:28:38 +00:00
|
|
|
var http = AngelHttp(app);
|
2018-07-09 15:38:29 +00:00
|
|
|
var server = await http.startServer();
|
2017-10-16 16:46:01 +00:00
|
|
|
var url = 'http://${server.address.address}:${server.port}';
|
|
|
|
tokenEndpoint = Uri.parse('$url/oauth2/token');
|
2017-10-16 06:38:46 +00:00
|
|
|
});
|
|
|
|
|
2017-10-16 16:46:01 +00:00
|
|
|
tearDown(() => app.close());
|
2017-10-16 06:38:46 +00:00
|
|
|
|
|
|
|
test('authenticate via username+password', () async {
|
2017-10-16 16:46:01 +00:00
|
|
|
var client = await oauth2.resourceOwnerPasswordGrant(
|
|
|
|
tokenEndpoint,
|
|
|
|
'michael',
|
|
|
|
'jackson',
|
|
|
|
identifier: 'foo',
|
|
|
|
secret: 'bar',
|
2017-10-16 06:38:46 +00:00
|
|
|
);
|
2017-10-16 16:46:01 +00:00
|
|
|
print(client.credentials.toJson());
|
|
|
|
client.close();
|
|
|
|
expect(client.credentials.accessToken, 'foo');
|
|
|
|
expect(client.credentials.refreshToken, 'bar');
|
|
|
|
});
|
2017-10-16 06:38:46 +00:00
|
|
|
|
2017-10-16 16:46:01 +00:00
|
|
|
test('force correct username+password', () async {
|
|
|
|
oauth2.Client client;
|
|
|
|
|
|
|
|
try {
|
|
|
|
client = await oauth2.resourceOwnerPasswordGrant(
|
|
|
|
tokenEndpoint,
|
|
|
|
'michael',
|
|
|
|
'jordan',
|
|
|
|
identifier: 'foo',
|
|
|
|
secret: 'bar',
|
|
|
|
);
|
2017-10-16 06:38:46 +00:00
|
|
|
|
2019-05-02 07:28:38 +00:00
|
|
|
throw StateError('should fail');
|
2017-10-16 16:46:01 +00:00
|
|
|
} on oauth2.AuthorizationException catch (e) {
|
|
|
|
expect(e.error, ErrorResponse.accessDenied);
|
|
|
|
} finally {
|
|
|
|
client?.close();
|
|
|
|
}
|
2017-10-16 06:38:46 +00:00
|
|
|
});
|
|
|
|
|
2017-10-16 16:46:01 +00:00
|
|
|
test('can refresh token', () async {
|
|
|
|
var client = await oauth2.resourceOwnerPasswordGrant(
|
|
|
|
tokenEndpoint,
|
|
|
|
'michael',
|
|
|
|
'jackson',
|
|
|
|
identifier: 'foo',
|
|
|
|
secret: 'bar',
|
2017-10-16 06:38:46 +00:00
|
|
|
);
|
2017-10-16 16:46:01 +00:00
|
|
|
client = await client.refreshCredentials();
|
|
|
|
print(client.credentials.toJson());
|
|
|
|
client.close();
|
|
|
|
expect(client.credentials.accessToken, 'baz');
|
|
|
|
expect(client.credentials.refreshToken, 'bar');
|
2017-10-16 06:38:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AuthorizationServer
|
|
|
|
extends AuthorizationServer<PseudoApplication, PseudoUser> {
|
|
|
|
@override
|
|
|
|
PseudoApplication findClient(String clientId) {
|
|
|
|
return clientId == pseudoApplication.id ? pseudoApplication : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<bool> verifyClient(
|
|
|
|
PseudoApplication client, String clientSecret) async {
|
|
|
|
return client.secret == clientSecret;
|
|
|
|
}
|
|
|
|
|
2017-10-16 16:46:01 +00:00
|
|
|
@override
|
|
|
|
Future<AuthorizationTokenResponse> refreshAuthorizationToken(
|
|
|
|
PseudoApplication client,
|
|
|
|
String refreshToken,
|
|
|
|
Iterable<String> scopes,
|
|
|
|
RequestContext req,
|
|
|
|
ResponseContext res) async {
|
2019-05-02 07:28:38 +00:00
|
|
|
return AuthorizationTokenResponse('baz', refreshToken: 'bar');
|
2017-10-16 16:46:01 +00:00
|
|
|
}
|
|
|
|
|
2017-10-16 06:38:46 +00:00
|
|
|
@override
|
|
|
|
Future<AuthorizationTokenResponse> resourceOwnerPasswordCredentialsGrant(
|
|
|
|
PseudoApplication client,
|
|
|
|
String username,
|
|
|
|
String password,
|
|
|
|
Iterable<String> scopes,
|
|
|
|
RequestContext req,
|
|
|
|
ResponseContext res) async {
|
|
|
|
var user = pseudoUsers.firstWhere(
|
|
|
|
(u) => u.username == username && u.password == password,
|
|
|
|
orElse: () => null);
|
|
|
|
|
|
|
|
if (user == null) {
|
2018-12-14 06:55:36 +00:00
|
|
|
var body = await req.parseBody().then((_) => req.bodyAsMap);
|
2019-05-02 07:28:38 +00:00
|
|
|
throw AuthorizationException(
|
|
|
|
ErrorResponse(
|
2017-10-16 06:38:46 +00:00
|
|
|
ErrorResponse.accessDenied,
|
|
|
|
'Invalid username or password.',
|
2018-11-08 15:32:36 +00:00
|
|
|
body['state']?.toString() ?? '',
|
2017-10-16 06:38:46 +00:00
|
|
|
),
|
|
|
|
statusCode: 401,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-05-02 07:28:38 +00:00
|
|
|
return AuthorizationTokenResponse('foo', refreshToken: 'bar');
|
2017-10-16 06:38:46 +00:00
|
|
|
}
|
|
|
|
}
|