platform/packages/oauth2/test/auth_code_test.dart

197 lines
6 KiB
Dart
Raw Permalink Normal View History

2017-09-29 02:16:44 +00:00
import 'dart:async';
2018-11-08 15:32:36 +00:00
import 'dart:collection';
2018-11-08 15:34:49 +00:00
import 'dart:convert';
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_framework/http.dart';
import 'package:angel3_oauth2/angel3_oauth2.dart';
import 'package:angel3_test/angel3_test.dart';
2017-09-29 02:16:44 +00:00
import 'package:logging/logging.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:test/test.dart';
import 'package:uuid/uuid.dart';
import 'common.dart';
void main() {
2017-09-29 02:16:44 +00:00
Angel app;
late Uri authorizationEndpoint, tokenEndpoint, redirectUri;
late TestClient testClient;
2017-09-29 02:16:44 +00:00
setUp(() async {
2019-05-02 07:28:38 +00:00
app = Angel();
2017-09-29 02:16:44 +00:00
app.configuration['properties'] = app.configuration;
app.container!.registerSingleton(AuthCodes());
2017-09-29 02:16:44 +00:00
2019-05-02 07:28:38 +00:00
var server = _Server();
2017-09-29 02:16:44 +00:00
app.group('/oauth2', (router) {
router
..get('/authorize', server.authorizationEndpoint)
..post('/token', server.tokenEndpoint);
});
2019-05-02 07:28:38 +00:00
app.logger = Logger('angel')
2017-09-29 02:16:44 +00:00
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
if (rec.stackTrace != null) print(rec.stackTrace);
});
2019-05-02 07:28:38 +00:00
var http = AngelHttp(app);
2018-07-09 15:38:29 +00:00
var s = await http.startServer();
var url = 'http://${s.address.address}:${s.port}';
2017-09-29 02:16:44 +00:00
authorizationEndpoint = Uri.parse('$url/oauth2/authorize');
tokenEndpoint = Uri.parse('$url/oauth2/token');
redirectUri = Uri.parse('http://foo.bar/baz');
testClient = await connectTo(app);
});
tearDown(() async {
await testClient.close();
});
group('auth code', () {
oauth2.AuthorizationCodeGrant createGrant() =>
2019-05-02 07:28:38 +00:00
oauth2.AuthorizationCodeGrant(
2017-09-29 02:16:44 +00:00
pseudoApplication.id,
authorizationEndpoint,
tokenEndpoint,
secret: pseudoApplication.secret,
);
test('show authorization form', () async {
var grant = createGrant();
var url = grant.getAuthorizationUrl(redirectUri, state: 'hello');
var response = await testClient.client!.get(url);
2017-09-29 02:16:44 +00:00
print('Body: ${response.body}');
expect(
response.body,
2018-07-09 15:38:29 +00:00
json.encode(
2017-09-29 02:16:44 +00:00
'Hello ${pseudoApplication.id}:${pseudoApplication.secret}'));
});
test('preserves state', () async {
var grant = createGrant();
var url = grant.getAuthorizationUrl(redirectUri, state: 'goodbye');
var response = await testClient.client!.get(url);
2017-09-29 02:16:44 +00:00
print('Body: ${response.body}');
2018-07-09 15:38:29 +00:00
expect(json.decode(response.body)['state'], 'goodbye');
2017-09-29 02:16:44 +00:00
});
test('sends auth code', () async {
var grant = createGrant();
var url = grant.getAuthorizationUrl(redirectUri);
var response = await testClient.client!.get(url);
2017-09-29 02:16:44 +00:00
print('Body: ${response.body}');
expect(
2018-07-09 15:38:29 +00:00
json.decode(response.body),
2017-09-29 02:16:44 +00:00
allOf(
isMap,
predicate((Map m) => m.containsKey('code'), 'contains "code"'),
),
);
});
test('exchange code for token', () async {
var grant = createGrant();
var url = grant.getAuthorizationUrl(redirectUri);
var response = await testClient.client!.get(url);
2017-09-29 02:16:44 +00:00
print('Body: ${response.body}');
2018-11-08 15:32:36 +00:00
var authCode = json.decode(response.body)['code'].toString();
2017-09-29 02:16:44 +00:00
var client = await grant.handleAuthorizationCode(authCode);
expect(client.credentials.accessToken, authCode + '_access');
});
test('can send refresh token', () async {
var grant = createGrant();
var url = grant.getAuthorizationUrl(redirectUri, state: 'can_refresh');
var response = await testClient.client!.get(url);
2017-09-29 02:16:44 +00:00
print('Body: ${response.body}');
2018-11-08 15:32:36 +00:00
var authCode = json.decode(response.body)['code'].toString();
2017-09-29 02:16:44 +00:00
var client = await grant.handleAuthorizationCode(authCode);
expect(client.credentials.accessToken, authCode + '_access');
expect(client.credentials.canRefresh, isTrue);
expect(client.credentials.refreshToken, authCode + '_refresh');
});
});
}
2017-10-16 06:38:46 +00:00
class _Server extends AuthorizationServer<PseudoApplication, Map> {
2019-05-02 07:28:38 +00:00
final Uuid _uuid = Uuid();
2017-09-29 02:16:44 +00:00
@override
FutureOr<PseudoApplication>? findClient(String? clientId) {
2017-09-29 02:16:44 +00:00
return clientId == pseudoApplication.id ? pseudoApplication : null;
}
@override
Future<bool> verifyClient(
PseudoApplication client, String? clientSecret) async {
2017-09-29 02:16:44 +00:00
return client.secret == clientSecret;
}
@override
2017-10-16 06:38:46 +00:00
Future requestAuthorizationCode(
2017-09-29 02:16:44 +00:00
PseudoApplication client,
String? redirectUri,
2017-09-29 02:16:44 +00:00
Iterable<String> scopes,
String state,
RequestContext req,
2019-05-03 07:24:24 +00:00
ResponseContext res,
bool implicit) async {
if (implicit) {
// Throw the default error on an implicit grant attempt.
return super.requestAuthorizationCode(
client, redirectUri, scopes, state, req, res, implicit);
}
if (state == 'hello') {
2017-09-29 02:16:44 +00:00
return 'Hello ${pseudoApplication.id}:${pseudoApplication.secret}';
}
2017-09-29 02:16:44 +00:00
2019-05-02 07:31:02 +00:00
var authCode = _uuid.v4();
var authCodes = req.container!.make<AuthCodes>()!;
2017-09-29 02:16:44 +00:00
authCodes[authCode] = state;
res.headers['content-type'] = 'application/json';
var result = {'code': authCode};
if (state.isNotEmpty == true) result['state'] = state;
2017-09-29 02:16:44 +00:00
return result;
}
@override
2017-10-16 06:38:46 +00:00
Future<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(
PseudoApplication? client,
String? authCode,
String? redirectUri,
2017-09-29 02:16:44 +00:00
RequestContext req,
ResponseContext res) async {
var authCodes = req.container!.make<AuthCodes>()!;
var state = authCodes[authCode!];
2017-09-29 02:16:44 +00:00
var refreshToken = state == 'can_refresh' ? '${authCode}_refresh' : null;
2019-05-02 07:28:38 +00:00
return AuthorizationTokenResponse('${authCode}_access',
2017-09-29 02:16:44 +00:00
refreshToken: refreshToken);
}
}
2018-11-08 15:32:36 +00:00
class AuthCodes extends MapBase<String, String> with MapMixin<String, String> {
var inner = <String, String>{};
@override
String? operator [](Object? key) => inner[key as String];
2018-11-08 15:32:36 +00:00
@override
void operator []=(String key, String value) => inner[key] = value;
@override
void clear() => inner.clear();
@override
Iterable<String> get keys => inner.keys;
@override
String? remove(Object? key) => inner.remove(key);
2018-11-08 15:32:36 +00:00
}