platform/lib/src/plugin.dart

141 lines
4.4 KiB
Dart
Raw Normal View History

2016-09-21 23:09:23 +00:00
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:math' as Math;
2016-09-21 06:19:52 +00:00
import 'package:angel_framework/angel_framework.dart';
2016-09-21 23:09:23 +00:00
import 'package:crypto/crypto.dart';
2016-09-21 06:19:52 +00:00
import 'middleware/require_auth.dart';
2016-09-21 23:09:23 +00:00
import 'auth_token.dart';
2016-09-21 06:19:52 +00:00
import 'defs.dart';
import 'options.dart';
import 'strategy.dart';
class AngelAuth extends AngelPlugin {
2016-09-21 23:09:23 +00:00
Hmac _hs256;
num _jwtLifeSpan;
Math.Random _random = new Math.Random.secure();
final RegExp _rgxBearer = new RegExp(r"^Bearer");
RequireAuthorizationMiddleware _requireAuth =
new RequireAuthorizationMiddleware();
bool enforceIp;
2016-09-21 06:19:52 +00:00
List<AuthStrategy> strategies = [];
UserSerializer serializer;
UserDeserializer deserializer;
2016-09-21 23:09:23 +00:00
String _randomString({int length: 32, String validChars: "ABCDEFHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"}) {
var chars = <int>[];
while (chars.length < length) chars.add(_random.nextInt(validChars.length));
return new String.fromCharCodes(chars);
}
AngelAuth({String jwtKey, num jwtLifeSpan, this.enforceIp}) : super() {
_hs256 = new Hmac(sha256, (jwtKey ?? _randomString()).codeUnits);
_jwtLifeSpan = jwtLifeSpan ?? -1;
}
2016-09-21 06:19:52 +00:00
@override
call(Angel app) async {
app.container.singleton(this);
2016-09-21 23:09:23 +00:00
if (runtimeType != AngelAuth) app.container.singleton(this, as: AngelAuth);
2016-09-21 06:19:52 +00:00
2016-09-21 23:09:23 +00:00
app.before.add(_decodeJwt);
2016-09-21 06:19:52 +00:00
app.registerMiddleware('auth', _requireAuth);
}
2016-09-21 23:09:23 +00:00
_decodeJwt(RequestContext req, ResponseContext res) async {
String jwt = null;
if (req.headers.value("Authorization") != null) {
var jwt =
req.headers.value("Authorization").replaceAll(_rgxBearer, "").trim();
} else if (req.cookies.any((cookie) => cookie.name == "token")) {
jwt = req.cookies.firstWhere((cookie) => cookie.name == "token").value;
}
if (jwt != null) {
var token = new AuthToken.validate(jwt, _hs256);
if (enforceIp) {
if (req.ip != token.ipAddress)
throw new AngelHttpException.Forbidden(
message: "JWT cannot be accessed from this IP address.");
}
if (token.lifeSpan > -1) {
token.issuedAt.add(new Duration(milliseconds: token.lifeSpan));
if (!token.issuedAt.isAfter(new DateTime.now()))
throw new AngelHttpException.Forbidden(message: "Expired JWT.");
}
req.properties["user"] = await deserializer(token.userId);
2016-09-21 06:19:52 +00:00
}
return true;
}
authenticate(String type, [AngelAuthOptions options]) {
return (RequestContext req, ResponseContext res) async {
AuthStrategy strategy =
2016-09-21 23:09:23 +00:00
strategies.firstWhere((AuthStrategy x) => x.name == type);
2016-09-21 06:19:52 +00:00
var result = await strategy.authenticate(req, res, options);
if (result == true)
return result;
else if (result != false) {
2016-09-21 23:09:23 +00:00
var userId = await serializer(result);
// Create JWT
var jwt = new AuthToken(userId: userId, lifeSpan: _jwtLifeSpan)
.serialize(_hs256);
req.cookies.add(new Cookie("token", jwt));
if (req.headers.value("accept") != null &&
(req.headers.value("accept").contains("application/json") ||
req.headers.value("accept").contains("*/*") ||
req.headers.value("accept").contains("application/*"))) {
return {"data": result, "token": jwt};
} else if (options != null && options.successRedirect != null &&
options.successRedirect.isNotEmpty) {
return res.redirect(options.successRedirect, code: HttpStatus.OK);
}
2016-09-21 06:19:52 +00:00
return true;
} else {
2016-09-21 23:09:23 +00:00
await authenticationFailure(req, res);
2016-09-21 06:19:52 +00:00
}
};
}
2016-09-21 23:09:23 +00:00
Future authenticationFailure(RequestContext req, ResponseContext res) async {
throw new AngelHttpException.NotAuthenticated();
}
2016-09-21 06:19:52 +00:00
logout([AngelAuthOptions options]) {
return (RequestContext req, ResponseContext res) async {
for (AuthStrategy strategy in strategies) {
if (!(await strategy.canLogout(req, res))) {
if (options != null &&
options.failureRedirect != null &&
options.failureRedirect.isNotEmpty) {
return res.redirect(options.failureRedirect);
}
return false;
}
}
2016-09-21 23:09:23 +00:00
req.cookies.removeWhere((cookie) => cookie.name == "token");
2016-09-21 06:19:52 +00:00
if (options != null &&
options.successRedirect != null &&
options.successRedirect.isNotEmpty) {
return res.redirect(options.successRedirect);
}
return true;
};
}
2016-09-21 23:09:23 +00:00
}