diff --git a/.idea/runConfigurations/Callback_Tests.xml b/.idea/runConfigurations/Callback_Tests.xml new file mode 100644 index 00000000..d900f0b4 --- /dev/null +++ b/.idea/runConfigurations/Callback_Tests.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index db79da6e..a0f371ed 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_auth -![version 1.1.0-dev+12](https://img.shields.io/badge/version-1.1.0--dev+12-red.svg) +![version 1.1.0-dev+13](https://img.shields.io/badge/version-1.1.0--dev+13-red.svg) ![build status](https://travis-ci.org/angel-dart/auth.svg?branch=master) A complete authentication plugin for Angel. Inspired by Passport. diff --git a/lib/src/options.dart b/lib/src/options.dart index 13b66d9d..39a18778 100644 --- a/lib/src/options.dart +++ b/lib/src/options.dart @@ -1,10 +1,18 @@ +import 'package:angel_framework/angel_framework.dart'; +import 'auth_token.dart'; + +typedef AngelAuthCallback( + RequestContext req, ResponseContext res, AuthToken token); + class AngelAuthOptions { + AngelAuthCallback callback; bool canRespondWithJson; String successRedirect; String failureRedirect; AngelAuthOptions( - {this.canRespondWithJson: true, + {this.callback, + this.canRespondWithJson: true, this.successRedirect, String this.failureRedirect}); } diff --git a/lib/src/plugin.dart b/lib/src/plugin.dart index 1f4e9040..d3989678 100644 --- a/lib/src/plugin.dart +++ b/lib/src/plugin.dart @@ -237,6 +237,10 @@ class AngelAuth extends AngelPlugin { if (allowCookie) req.cookies.add(new Cookie("token", jwt)); + if (options?.callback != null) { + return await options.callback(req, res, jwt); + } + if (options?.canRespondWithJson != false && req.headers.value("accept") != null && (req.headers.value("accept").contains("application/json") || diff --git a/lib/src/strategies/oauth2.dart b/lib/src/strategies/oauth2.dart deleted file mode 100644 index 1a663bbd..00000000 --- a/lib/src/strategies/oauth2.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:oauth2/oauth2.dart' as Oauth2; -import '../options.dart'; -import '../strategy.dart'; -/// Logs a user in based on an incoming OAuth access and refresh token. -typedef Future OAuth2AuthVerifier(String accessToken, String refreshToken, - Map profile); - -class OAuth2AuthStrategy extends AuthStrategy { - @override - String name = "oauth2"; - OAuth2AuthVerifier verifier; - - Uri authEndPoint; - Uri tokenEndPoint; - String clientId; - String clientSecret; - Uri callbackUri; - List scopes; - - @override - Future authenticate(RequestContext req, ResponseContext res, - [AngelAuthOptions options_]) async { - Oauth2.Client client = await makeGrant().handleAuthorizationResponse(req.query); - // Remember: Do stuff - } - - @override - Future canLogout(RequestContext req, ResponseContext res) async { - return true; - } - - OAuth2AuthStrategy(String this.name, OAuth2AuthVerifier this.verifier, - {Uri this.authEndPoint, - Uri this.tokenEndPoint, - String this.clientId, - String this.clientSecret, - Uri this.callbackUri, - List this.scopes: const[]}) { - if (this.authEndPoint == null) - throw new ArgumentError.notNull('authEndPoint'); - if (this.tokenEndPoint == null) - throw new ArgumentError.notNull('tokenEndPoint'); - if (this.clientId == null || this.clientId.isEmpty) - throw new ArgumentError.notNull('clientId'); - } - - call(RequestContext req, ResponseContext res) async { - var grant = makeGrant(); - - Uri to = grant.getAuthorizationUrl(callbackUri, scopes: scopes); - return res.redirect(to.path); - } - - Oauth2.AuthorizationCodeGrant makeGrant() { - return new Oauth2.AuthorizationCodeGrant( - clientId, authEndPoint, tokenEndPoint, secret: clientSecret); - } -} - -class OAuth2AuthorizationError extends AngelHttpException { - OAuth2AuthorizationError({String message: "OAuth2 Authorization Error", - List errors: const []}) - : super.NotAuthenticated(message: message) { - this.errors = errors; - } -} diff --git a/lib/src/strategies/strategies.dart b/lib/src/strategies/strategies.dart index 3861d127..b86a4fdc 100644 --- a/lib/src/strategies/strategies.dart +++ b/lib/src/strategies/strategies.dart @@ -1,3 +1 @@ -export 'local.dart'; -export 'oauth2.dart'; -export 'token.dart'; \ No newline at end of file +export 'local.dart'; \ No newline at end of file diff --git a/lib/src/strategies/token.dart b/lib/src/strategies/token.dart deleted file mode 100644 index 145eda86..00000000 --- a/lib/src/strategies/token.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'dart:async'; -import 'package:angel_framework/angel_framework.dart'; -import '../options.dart'; -import '../strategy.dart'; - -class JwtAuthStrategy extends AuthStrategy { - - @override - Future authenticate(RequestContext req, ResponseContext res, - [AngelAuthOptions options]) async { - - } - - @override - Future canLogout(RequestContext req, ResponseContext res) async => false; -} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 65aaf892..67286d5c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,11 @@ name: angel_auth description: A complete authentication plugin for Angel. -version: 1.0.0-dev+12 +version: 1.0.0-dev+13 author: Tobe O homepage: https://github.com/angel-dart/angel_auth dependencies: angel_framework: ">=1.0.0-dev <2.0.0" crypto: ">=2.0.0 <3.0.0" - oauth2: ">= 1.0.2 < 2.0.0" dev_dependencies: http: ">= 0.11.3 < 0.12.0" test: ">= 0.12.13 < 0.13.0" diff --git a/test/all_tests.old.dart b/test/all_tests.old.dart deleted file mode 100644 index fbd6cb5f..00000000 --- a/test/all_tests.old.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; -import 'auth_token_test.dart' as authToken; -import 'local_test.dart' as local; - -wireAuth(Angel app) async { - -} - -main() async { - group('everything', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - group("JWT (de)serialization", authToken.main); - group("local", local.main); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow everything', () async { - - }); - - test('force everything', () async { - - }); - - test('logout', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/callback_test.dart b/test/callback_test.dart new file mode 100644 index 00000000..8dca3b6e --- /dev/null +++ b/test/callback_test.dart @@ -0,0 +1,70 @@ +import 'dart:io'; +import 'package:angel_auth/angel_auth.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:http/http.dart' as http; +import 'package:test/test.dart'; + +class User extends MemoryModel { + String username, password; + + User({this.username, this.password}); +} + +main() { + Angel app; + AngelAuth auth; + http.Client client; + HttpServer server; + String url; + + setUp(() async { + app = new Angel(); + app.use('/users', new MemoryService()); + + await app + .service('users') + .create({'username': 'jdoe1', 'password': 'password'}); + + await app.configure(auth = new AngelAuth()); + + auth.serializer = (User user) async => user.id; + auth.deserializer = app.service('users').read; + + auth.strategies.add(new LocalAuthStrategy((username, password) async { + final List users = await app.service('users').index(); + final found = users.firstWhere( + (user) => user.username == username && user.password == password, + orElse: () => null); + + return found != null ? found : false; + })); + + app.post( + '/login', + auth.authenticate('local', + new AngelAuthOptions(callback: (req, res, token) { + return res + ..write('Hello!') + ..end(); + }))); + + client = new http.Client(); + server = await app.startServer(); + url = 'http://${server.address.address}:${server.port}'; + }); + + tearDown(() async { + client.close(); + await server.close(force: true); + app = null; + client = null; + url = null; + }); + + test('login', () async { + final response = await client.post('$url/login', + body: {'username': 'jdoe1', 'password': 'password'}); + print('Response: ${response.body}'); + expect(response.body, equals('Hello!')); + }); +} diff --git a/test/digest.dart b/test/digest.dart deleted file mode 100644 index 00bf631b..00000000 --- a/test/digest.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('digest', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow digest', () async { - - }); - - test('force digest', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/local_test.dart b/test/local_test.dart index 3d0e1473..ffae76d7 100644 --- a/test/local_test.dart +++ b/test/local_test.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:angel_framework/angel_framework.dart'; import 'package:angel_auth/angel_auth.dart'; import 'package:http/http.dart' as http; -import 'package:merge_map/merge_map.dart'; import 'package:test/test.dart'; final AngelAuth Auth = new AngelAuth(); @@ -43,10 +42,6 @@ main() async { app.get('/success', "yep", middleware: ['auth']); app.get('/failure', "nope"); - app - ..normalize() - ..dumpTree(); - HttpServer server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 0); url = "http://${server.address.host}:${server.port}"; diff --git a/test/oauth1.dart b/test/oauth1.dart deleted file mode 100644 index 2655f2f5..00000000 --- a/test/oauth1.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('oauth1', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow oauth1', () async { - - }); - - test('force oauth1', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/oauth2.dart b/test/oauth2.dart deleted file mode 100644 index bf6ff35c..00000000 --- a/test/oauth2.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('oauth2', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow oauth2', () async { - - }); - - test('force oauth2', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/oauth_in_house.dart b/test/oauth_in_house.dart deleted file mode 100644 index 82a77767..00000000 --- a/test/oauth_in_house.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('oauth2 in-house', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow oauth2 in-house', () async { - - }); - - test('force oauth2 in-house', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/openid.dart b/test/openid.dart deleted file mode 100644 index 2bd4354a..00000000 --- a/test/openid.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('openid', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow openid', () async { - - }); - - test('force openid', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/token.dart b/test/token.dart deleted file mode 100644 index a36e1135..00000000 --- a/test/token.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('token', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow token', () async { - - }); - - test('force token', () async { - - }); - }); -} \ No newline at end of file diff --git a/test/websocket.dart b/test/websocket.dart deleted file mode 100644 index ff6482e1..00000000 --- a/test/websocket.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'dart:io'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:http/http.dart' as http; -import 'package:test/test.dart'; - -wireAuth(Angel app) async { - -} - -main() async { - group('websocket', () { - Angel app; - http.Client client; - String url; - - setUp(() async { - client = new http.Client(); - app = new Angel(); - await app.configure(wireAuth); - HttpServer server = await app.startServer( - InternetAddress.LOOPBACK_IP_V4, 0); - url = "http://${server.address.host}:${server.port}"; - }); - - tearDown(() async { - await app.httpServer.close(force: true); - client = null; - url = null; - }); - - test('can use login as middleware', () async { - - }); - - test('successRedirect', () async { - - }); - - test('failureRedirect', () async { - - }); - - test('allow websocket', () async { - - }); - - test('force websocket', () async { - - }); - }); -} \ No newline at end of file