Bump to 2.0.0-alpha

This commit is contained in:
Tobe O 2018-08-26 19:11:37 -04:00
parent 10dd869095
commit a6b08ae7c4
8 changed files with 74 additions and 79 deletions

View file

@ -1,3 +1,9 @@
# 2.0.0-alpha
* Depend on Dart 2 and Angel 2.
* Remove `dart2_constant`.
* Remove `requireAuth`.
* Remove `userKey`, instead favoring generic parameters.
# 1.2.0 # 1.2.0
* Deprecate `requireAuth`, in favor of `requireAuthentication`. * Deprecate `requireAuth`, in favor of `requireAuthentication`.
* Allow configuring of the `userKey`. * Allow configuring of the `userKey`.

View file

@ -3,24 +3,19 @@ import 'package:angel_framework/angel_framework.dart';
/// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present. /// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present.
/// ///
/// [realm] defaults to `'angel_auth'`. /// [realm] defaults to `'angel_auth'`.
RequestHandler forceBasicAuth({String realm, String userKey: 'user'}) { RequestHandler forceBasicAuth<User>({String realm}) {
return (RequestContext req, ResponseContext res) async { return (RequestContext req, ResponseContext res) async {
if (req.properties.containsKey(userKey)) return true; if (req.container.has<User>()) return true;
res res
..statusCode = 401 ..statusCode = 401
..headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"' ..headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"'
..end(); ..close();
return false;
}; };
} }
/// Use [requireAuthentication] instead.
@deprecated
final RequestMiddleware requireAuth = requireAuthentication(userKey: 'user');
/// Restricts access to a resource via authentication. /// Restricts access to a resource via authentication.
RequestMiddleware requireAuthentication({String userKey: 'user'}) { RequestHandler requireAuthentication<User>() {
return (RequestContext req, ResponseContext res, return (RequestContext req, ResponseContext res,
{bool throwError: true}) async { {bool throwError: true}) async {
bool _reject(ResponseContext res) { bool _reject(ResponseContext res) {
@ -31,7 +26,7 @@ RequestMiddleware requireAuthentication({String userKey: 'user'}) {
return false; return false;
} }
if (req.properties.containsKey(userKey) || req.method == 'OPTIONS') if (req.container.has<User>() || req.method == 'OPTIONS')
return true; return true;
else else
return _reject(res); return _reject(res);

View file

@ -3,7 +3,6 @@ import 'dart:io';
import 'dart:math' as Math; import 'dart:math' as Math;
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'middleware/require_auth.dart';
import 'auth_token.dart'; import 'auth_token.dart';
import 'defs.dart'; import 'defs.dart';
import 'options.dart'; import 'options.dart';
@ -38,15 +37,6 @@ class AngelAuth<T> {
/// Only applies if [allowCookie] is `true`. /// Only applies if [allowCookie] is `true`.
final String cookiePath; final String cookiePath;
/// The name to register [requireAuthentication] as. Default: `auth`.
@deprecated
String middlewareName;
/// The name to inject authenticated users as.
///
/// Defaults to `'user'`.
final String userKey;
/// If `true` (default), then JWT's will be considered invalid if used from a different IP than the first user's it was issued to. /// If `true` (default), then JWT's will be considered invalid if used from a different IP than the first user's it was issued to.
/// ///
/// This is a security provision. Even if a user's JWT is stolen, a remote attacker will not be able to impersonate anyone. /// This is a security provision. Even if a user's JWT is stolen, a remote attacker will not be able to impersonate anyone.
@ -90,10 +80,8 @@ class AngelAuth<T> {
this.allowTokenInQuery: true, this.allowTokenInQuery: true,
this.enforceIp: true, this.enforceIp: true,
this.cookieDomain, this.cookieDomain,
this.userKey: 'user',
this.cookiePath: '/', this.cookiePath: '/',
this.secureCookies: true, this.secureCookies: true,
this.middlewareName: 'auth',
this.reviveTokenEndpoint: "/auth/token"}) this.reviveTokenEndpoint: "/auth/token"})
: super() { : super() {
_hs256 = new Hmac(sha256, (jwtKey ?? _randomString()).codeUnits); _hs256 = new Hmac(sha256, (jwtKey ?? _randomString()).codeUnits);
@ -108,11 +96,9 @@ class AngelAuth<T> {
throw new StateError( throw new StateError(
'An `AngelAuth` plug-in was called without its `deserializer` being set. All authentication will fail.'); 'An `AngelAuth` plug-in was called without its `deserializer` being set. All authentication will fail.');
app.container.singleton(this); app.container.registerSingleton(this);
if (runtimeType != AngelAuth) app.container.singleton(this, as: AngelAuth); if (runtimeType != AngelAuth)
app.container.registerSingleton(this, as: AngelAuth);
// ignore: deprecated_member_use
app.registerMiddleware(middlewareName, requireAuthentication());
if (reviveTokenEndpoint != null) { if (reviveTokenEndpoint != null) {
app.post(reviveTokenEndpoint, reviveJwt); app.post(reviveTokenEndpoint, reviveJwt);
@ -123,10 +109,11 @@ class AngelAuth<T> {
}); });
} }
void _apply(RequestContext req, ResponseContext res, AuthToken token, user) { void _apply(
req RequestContext req, ResponseContext res, AuthToken token, T user) {
..inject(AuthToken, req.properties['token'] = token) req.container
..inject(user.runtimeType, req.properties[userKey] = user); ..registerSingleton<AuthToken>(token)
..registerSingleton<T>(user);
if (allowCookie == true) { if (allowCookie == true) {
_addProtectedCookie(res, 'token', token.serialize(_hs256)); _addProtectedCookie(res, 'token', token.serialize(_hs256));
@ -176,8 +163,9 @@ class AngelAuth<T> {
} else if (allowCookie && } else if (allowCookie &&
req.cookies.any((cookie) => cookie.name == "token")) { req.cookies.any((cookie) => cookie.name == "token")) {
return req.cookies.firstWhere((cookie) => cookie.name == "token").value; return req.cookies.firstWhere((cookie) => cookie.name == "token").value;
} else if (allowTokenInQuery && req.query['token'] is String) { } else if (allowTokenInQuery &&
return req.query['token']?.toString(); req.uri.queryParameters['token'] is String) {
return req.uri.queryParameters['token']?.toString();
} }
return null; return null;
@ -214,7 +202,7 @@ class AngelAuth<T> {
var jwt = getJwt(req); var jwt = getJwt(req);
if (jwt == null) { if (jwt == null) {
var body = await req.lazyBody(); var body = await req.parseBody();
jwt = body['token']?.toString(); jwt = body['token']?.toString();
} }
if (jwt == null) { if (jwt == null) {
@ -282,14 +270,14 @@ class AngelAuth<T> {
orElse: () => orElse: () =>
throw new ArgumentError('No strategy "$name" found.')); throw new ArgumentError('No strategy "$name" found.'));
var hasExisting = req.properties.containsKey(userKey); var hasExisting = req.container.has<T>();
var result = hasExisting var result = hasExisting
? req.properties[userKey] ? req.container.make<T>()
: await strategy.authenticate(req, res, options); : await strategy.authenticate(req, res, options) as T;
if (result == true) if (result == true)
return result; return result;
else if (result != false) { else if (result != false) {
var userId = await serializer(result as T); var userId = await serializer(result);
// Create JWT // Create JWT
var token = new AuthToken( var token = new AuthToken(
@ -297,8 +285,8 @@ class AngelAuth<T> {
var jwt = token.serialize(_hs256); var jwt = token.serialize(_hs256);
if (options?.tokenCallback != null) { if (options?.tokenCallback != null) {
var r = await options.tokenCallback( req.container.registerSingleton<T>(result);
req, res, token, req.properties[userKey] = result); var r = await options.tokenCallback(req, res, token, result);
if (r != null) return r; if (r != null) return r;
jwt = token.serialize(_hs256); jwt = token.serialize(_hs256);
} }
@ -319,8 +307,8 @@ class AngelAuth<T> {
} else if (options?.canRespondWithJson != false && } else if (options?.canRespondWithJson != false &&
req.accepts('application/json')) { req.accepts('application/json')) {
var user = hasExisting var user = hasExisting
? result as T ? result
: await deserializer(await serializer(result as T)); : await deserializer(await serializer(result));
_onLogin.add(user); _onLogin.add(user);
return {"data": user, "token": jwt}; return {"data": user, "token": jwt};
} }
@ -365,7 +353,7 @@ class AngelAuth<T> {
} }
/// Log an authenticated user out. /// Log an authenticated user out.
RequestMiddleware logout([AngelAuthOptions options]) { RequestHandler logout([AngelAuthOptions options]) {
return (RequestContext req, ResponseContext res) async { return (RequestContext req, ResponseContext res) async {
for (AuthStrategy strategy in strategies) { for (AuthStrategy strategy in strategies) {
if (!(await strategy.canLogout(req, res))) { if (!(await strategy.canLogout(req, res))) {
@ -379,11 +367,10 @@ class AngelAuth<T> {
} }
} }
var user = req.grab(userKey); if (req.container.has<T>()) {
if (user != null) _onLogout.add(user as T); var user = req.container.make<T>();
_onLogout.add(user);
req.injections..remove(AuthToken)..remove(userKey); }
req.properties.remove(userKey);
if (allowCookie == true) { if (allowCookie == true) {
res.cookies.removeWhere((cookie) => cookie.name == "token"); res.cookies.removeWhere((cookie) => cookie.name == "token");

View file

@ -1,12 +1,12 @@
import 'dart:io';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:http_parser/http_parser.dart';
import 'options.dart'; import 'options.dart';
/// Displays a default callback page to confirm authentication via popups. /// Displays a default callback page to confirm authentication via popups.
AngelAuthCallback confirmPopupAuthentication({String eventName: 'token'}) { AngelAuthCallback confirmPopupAuthentication({String eventName: 'token'}) {
return (req, ResponseContext res, String jwt) async { return (req, ResponseContext res, String jwt) async {
res res
..contentType = new ContentType('text', 'html') ..contentType = new MediaType('text', 'html')
..write(''' ..write('''
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>

View file

@ -61,7 +61,7 @@ class LocalAuthStrategy extends AuthStrategy {
res res
..statusCode = 401 ..statusCode = 401
..headers['www-authenticate'] = 'Basic realm="$realm"' ..headers['www-authenticate'] = 'Basic realm="$realm"'
..end(); ..close();
return false; return false;
} }
@ -70,11 +70,11 @@ class LocalAuthStrategy extends AuthStrategy {
} }
if (verificationResult == null) { if (verificationResult == null) {
await req.parse(); var body = await req.parseBody();
if (_validateString(req.body[usernameField]?.toString()) && if (_validateString(body[usernameField]?.toString()) &&
_validateString(req.body[passwordField]?.toString())) { _validateString(body[passwordField]?.toString())) {
verificationResult = await verifier(req.body[usernameField]?.toString(), verificationResult = await verifier(
req.body[passwordField]?.toString()); body[usernameField]?.toString(), body[passwordField]?.toString());
} }
} }
@ -89,7 +89,7 @@ class LocalAuthStrategy extends AuthStrategy {
res res
..statusCode = 401 ..statusCode = 401
..headers['www-authenticate'] = 'Basic realm="$realm"' ..headers['www-authenticate'] = 'Basic realm="$realm"'
..end(); ..close();
} }
return false; return false;

View file

@ -1,16 +1,15 @@
name: angel_auth name: angel_auth
description: A complete authentication plugin for Angel. description: A complete authentication plugin for Angel.
version: 1.2.0 version: 2.0.0-alpha
author: Tobe O <thosakwe@gmail.com> author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_auth homepage: https://github.com/angel-dart/angel_auth
environment: environment:
sdk: ">=1.8.0 <3.0.0" sdk: ">=2.0.0-dev <3.0.0"
dependencies: dependencies:
angel_framework: ^1.1.0-alpha angel_framework: ^2.0.0-alpha
crypto: ^2.0.0 crypto: ^2.0.0
dart2_constant: ^1.0.0
dev_dependencies: dev_dependencies:
http: ^0.11.0 http: ^0.11.0
io: ^0.3.2 io: ^0.3.2
logging: ^0.11.0 logging: ^0.11.0
test: ^0.12.0 test: ^1.0.0

View file

@ -1,7 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:angel_auth/angel_auth.dart'; import 'package:angel_auth/angel_auth.dart';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/common.dart';
import 'package:dart2_constant/convert.dart'; import 'package:dart2_constant/convert.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:io/ansi.dart'; import 'package:io/ansi.dart';
@ -17,7 +16,7 @@ class User extends Model {
main() { main() {
Angel app; Angel app;
AngelHttp angelHttp; AngelHttp angelHttp;
AngelAuth auth; AngelAuth<User> auth;
http.Client client; http.Client client;
HttpServer server; HttpServer server;
String url; String url;
@ -26,7 +25,7 @@ main() {
hierarchicalLoggingEnabled = true; hierarchicalLoggingEnabled = true;
app = new Angel(); app = new Angel();
angelHttp = new AngelHttp(app); angelHttp = new AngelHttp(app);
app.use('/users', new TypedService<User>(new MapService())); app.use('/users', new MapService());
var oldErrorHandler = app.errorHandler; var oldErrorHandler = app.errorHandler;
app.errorHandler = (e, req, res) { app.errorHandler = (e, req, res) {
@ -58,7 +57,7 @@ main() {
(id) async => await app.service('users').read(id) as User; (id) async => await app.service('users').read(id) as User;
await app.configure(auth.configureServer); await app.configure(auth.configureServer);
app.use(auth.decodeJwt); app.fallback(auth.decodeJwt);
auth.strategies.add(new LocalAuthStrategy((username, password) async { auth.strategies.add(new LocalAuthStrategy((username, password) async {
final List<User> users = await app.service('users').index(); final List<User> users = await app.service('users').index();
@ -75,14 +74,19 @@ main() {
new AngelAuthOptions(callback: (req, res, token) { new AngelAuthOptions(callback: (req, res, token) {
res res
..write('Hello!') ..write('Hello!')
..end(); ..close();
}))); })));
app.chain((RequestContext req) { app.chain([
req.properties['user'] = (req, res) {
new User(username: req.params['name']?.toString()); req.container.registerSingleton<User>(
return true; new User(username: req.params['name']?.toString()));
}).post('/existing/:name', auth.authenticate('local')); return true;
}
]).post(
'/existing/:name',
auth.authenticate('local'),
);
client = new http.Client(); client = new http.Client();
server = await angelHttp.startServer(); server = await angelHttp.startServer();

View file

@ -6,11 +6,12 @@ import 'package:dart2_constant/convert.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:test/test.dart'; import 'package:test/test.dart';
final AngelAuth auth = new AngelAuth(); final AngelAuth<Map<String, String>> auth =
new AngelAuth<Map<String, String>>();
var headers = <String, String>{'accept': 'application/json'}; var headers = <String, String>{'accept': 'application/json'};
AngelAuthOptions localOpts = new AngelAuthOptions( AngelAuthOptions localOpts = new AngelAuthOptions(
failureRedirect: '/failure', successRedirect: '/success'); failureRedirect: '/failure', successRedirect: '/success');
Map sampleUser = {'hello': 'world'}; Map<String, String> sampleUser = {'hello': 'world'};
Future verifier(String username, String password) async { Future verifier(String username, String password) async {
if (username == 'username' && password == 'password') { if (username == 'username' && password == 'password') {
@ -25,7 +26,7 @@ Future wireAuth(Angel app) async {
auth.strategies.add(new LocalAuthStrategy(verifier)); auth.strategies.add(new LocalAuthStrategy(verifier));
await app.configure(auth.configureServer); await app.configure(auth.configureServer);
app.use(auth.decodeJwt); app.fallback(auth.decodeJwt);
} }
main() async { main() async {
@ -40,11 +41,14 @@ main() async {
app = new Angel(); app = new Angel();
angelHttp = new AngelHttp(app, useZone: false); angelHttp = new AngelHttp(app, useZone: false);
await app.configure(wireAuth); await app.configure(wireAuth);
app.get('/hello', 'Woo auth', middleware: [auth.authenticate('local')]); app.get('/hello', (req, res) => 'Woo auth',
app.post('/login', 'This should not be shown', middleware: [auth.authenticate('local')]);
app.post('/login', (req, res) => 'This should not be shown',
middleware: [auth.authenticate('local', localOpts)]); middleware: [auth.authenticate('local', localOpts)]);
app.get('/success', "yep", middleware: ['auth']); app.get('/success', (req, res) => "yep", middleware: [
app.get('/failure', "nope"); requireAuthentication<Map<String, String>>(),
]);
app.get('/failure', (req, res) => "nope");
HttpServer server = await angelHttp.startServer('127.0.0.1', 0); HttpServer server = await angelHttp.startServer('127.0.0.1', 0);
url = "http://${server.address.host}:${server.port}"; url = "http://${server.address.host}:${server.port}";