Bump to 2.0.0-alpha
This commit is contained in:
parent
10dd869095
commit
a6b08ae7c4
8 changed files with 74 additions and 79 deletions
|
@ -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`.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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}";
|
||||||
|
|
Loading…
Reference in a new issue