17
This commit is contained in:
parent
ee72bf748c
commit
771a4e8a5c
8 changed files with 72 additions and 25 deletions
|
@ -1,6 +1,6 @@
|
|||
# angel_auth
|
||||
|
||||
[](https://pub.dartlang.org/packages/angel_auth)
|
||||
[](https://pub.dartlang.org/packages/angel_auth)
|
||||

|
||||
|
||||
A complete authentication plugin for Angel. Inspired by Passport.
|
||||
|
|
|
@ -11,27 +11,35 @@ class AuthToken {
|
|||
DateTime issuedAt;
|
||||
num lifeSpan;
|
||||
var userId;
|
||||
Map<String, dynamic> payload = {};
|
||||
|
||||
AuthToken(
|
||||
{this.ipAddress, this.lifeSpan: -1, this.userId, DateTime issuedAt}) {
|
||||
{this.ipAddress,
|
||||
this.lifeSpan: -1,
|
||||
this.userId,
|
||||
DateTime issuedAt,
|
||||
Map<String, dynamic> payload: const {}}) {
|
||||
this.issuedAt = issuedAt ?? new DateTime.now();
|
||||
this.payload.addAll(payload ?? {});
|
||||
}
|
||||
|
||||
factory AuthToken.fromJson(String json) => new AuthToken.fromMap(JSON.decode(json));
|
||||
factory AuthToken.fromJson(String json) =>
|
||||
new AuthToken.fromMap(JSON.decode(json));
|
||||
|
||||
factory AuthToken.fromMap(Map data) {
|
||||
return new AuthToken(
|
||||
ipAddress: data["aud"],
|
||||
lifeSpan: data["exp"],
|
||||
issuedAt: DateTime.parse(data["iat"]),
|
||||
userId: data["sub"]);
|
||||
userId: data["sub"],
|
||||
payload: data["pld"] ?? {});
|
||||
}
|
||||
|
||||
factory AuthToken.validate(String jwt, Hmac hmac) {
|
||||
var split = jwt.split(".");
|
||||
|
||||
if (split.length != 3)
|
||||
throw new AngelHttpException.NotAuthenticated(message: "Invalid JWT.");
|
||||
throw new AngelHttpException.notAuthenticated(message: "Invalid JWT.");
|
||||
|
||||
// var headerString = new String.fromCharCodes(BASE64URL.decode(split[0]));
|
||||
var payloadString = new String.fromCharCodes(BASE64URL.decode(split[1]));
|
||||
|
@ -39,7 +47,7 @@ class AuthToken {
|
|||
var signature = BASE64URL.encode(hmac.convert(data.codeUnits).bytes);
|
||||
|
||||
if (signature != split[2])
|
||||
throw new AngelHttpException.NotAuthenticated(
|
||||
throw new AngelHttpException.notAuthenticated(
|
||||
message: "JWT payload does not match hashed version.");
|
||||
|
||||
return new AuthToken.fromMap(JSON.decode(payloadString));
|
||||
|
@ -47,19 +55,37 @@ class AuthToken {
|
|||
|
||||
String serialize(Hmac hmac) {
|
||||
var headerString = BASE64URL.encode(JSON.encode(_header).codeUnits);
|
||||
var payloadString = BASE64URL.encode(JSON.encode(this).codeUnits);
|
||||
var payloadString = BASE64URL.encode(JSON.encode(toJson()).codeUnits);
|
||||
var data = headerString + "." + payloadString;
|
||||
var signature = hmac.convert(data.codeUnits).bytes;
|
||||
return data + "." + BASE64URL.encode(signature);
|
||||
}
|
||||
|
||||
Map toJson() {
|
||||
return new SplayTreeMap.from({
|
||||
return _splayify({
|
||||
"iss": "angel_auth",
|
||||
"aud": ipAddress,
|
||||
"exp": lifeSpan,
|
||||
"iat": issuedAt.toIso8601String(),
|
||||
"sub": userId
|
||||
"sub": userId,
|
||||
"pld": _splayify(payload)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SplayTreeMap _splayify(Map map) {
|
||||
var data = {};
|
||||
map.forEach((k, v) {
|
||||
data[k] = _splay(v);
|
||||
});
|
||||
return new SplayTreeMap.from(data);
|
||||
}
|
||||
|
||||
_splay(value) {
|
||||
if (value is Iterable) {
|
||||
return value.map(_splay).toList();
|
||||
} else if (value is Map)
|
||||
return _splayify(value);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class RequireAuthorizationMiddleware extends AngelMiddleware {
|
|||
bool _reject(ResponseContext res) {
|
||||
if (throwError) {
|
||||
res.statusCode = HttpStatus.FORBIDDEN;
|
||||
throw new AngelHttpException.Forbidden();
|
||||
throw new AngelHttpException.forbidden();
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ class AngelAuth extends AngelPlugin {
|
|||
}
|
||||
|
||||
if (req.ip != null && req.ip != token.ipAddress)
|
||||
throw new AngelHttpException.Forbidden(
|
||||
throw new AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ class AngelAuth extends AngelPlugin {
|
|||
token.issuedAt.add(new Duration(milliseconds: token.lifeSpan));
|
||||
|
||||
if (!token.issuedAt.isAfter(new DateTime.now()))
|
||||
throw new AngelHttpException.Forbidden(message: "Expired JWT.");
|
||||
throw new AngelHttpException.forbidden(message: "Expired JWT.");
|
||||
} else if (debug) {
|
||||
print('This token has an infinite life span.');
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class AngelAuth extends AngelPlugin {
|
|||
if (debug) print('Found JWT: $jwt');
|
||||
|
||||
if (jwt == null) {
|
||||
throw new AngelHttpException.Forbidden(message: "No JWT provided");
|
||||
throw new AngelHttpException.forbidden(message: "No JWT provided");
|
||||
} else {
|
||||
var token = new AuthToken.validate(jwt, _hs256);
|
||||
|
||||
|
@ -174,7 +174,7 @@ class AngelAuth extends AngelPlugin {
|
|||
.ip}');
|
||||
|
||||
if (req.ip != token.ipAddress)
|
||||
throw new AngelHttpException.Forbidden(
|
||||
throw new AngelHttpException.forbidden(
|
||||
message: "JWT cannot be accessed from this IP address.");
|
||||
}
|
||||
|
||||
|
@ -216,7 +216,7 @@ class AngelAuth extends AngelPlugin {
|
|||
}
|
||||
|
||||
if (e is AngelHttpException) rethrow;
|
||||
throw new AngelHttpException.BadRequest(message: "Malformed JWT");
|
||||
throw new AngelHttpException.badRequest(message: "Malformed JWT");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ class AngelAuth extends AngelPlugin {
|
|||
}
|
||||
|
||||
Future authenticationFailure(RequestContext req, ResponseContext res) async {
|
||||
throw new AngelHttpException.NotAuthenticated();
|
||||
throw new AngelHttpException.notAuthenticated();
|
||||
}
|
||||
|
||||
logout([AngelAuthOptions options]) {
|
||||
|
|
|
@ -56,7 +56,7 @@ class LocalAuthStrategy extends AuthStrategy {
|
|||
verificationResult =
|
||||
await verifier(usrPassMatch.group(1), usrPassMatch.group(2));
|
||||
} else
|
||||
throw new AngelHttpException.BadRequest(errors: [invalidMessage]);
|
||||
throw new AngelHttpException.badRequest(errors: [invalidMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ class LocalAuthStrategy extends AuthStrategy {
|
|||
} else if (verificationResult != null && verificationResult != false) {
|
||||
return verificationResult;
|
||||
} else {
|
||||
throw new AngelHttpException.NotAuthenticated();
|
||||
throw new AngelHttpException.notAuthenticated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
pubspec.yaml
12
pubspec.yaml
|
@ -1,11 +1,13 @@
|
|||
name: angel_auth
|
||||
description: A complete authentication plugin for Angel.
|
||||
version: 1.0.0-dev+16
|
||||
version: 1.0.0-dev+17
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_auth
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
angel_framework: ">=1.0.0-dev <2.0.0"
|
||||
crypto: ">=2.0.0 <3.0.0"
|
||||
angel_framework: ^1.0.0-dev
|
||||
crypto: ^2.0.0
|
||||
dev_dependencies:
|
||||
http: ">= 0.11.3 < 0.12.0"
|
||||
test: ">= 0.12.13 < 0.13.0"
|
||||
http: ^0.11.0
|
||||
test: ^0.12.0
|
||||
|
|
|
@ -12,5 +12,24 @@ main() async {
|
|||
|
||||
var parsed = new AuthToken.validate(jwt, hmac);
|
||||
print(parsed.toJson());
|
||||
expect(parsed.toJson()['aud'], equals(token.ipAddress));
|
||||
expect(parsed.toJson()['sub'], equals(token.userId));
|
||||
});
|
||||
}
|
||||
|
||||
test('custom payload', () {
|
||||
var token =
|
||||
new AuthToken(ipAddress: "localhost", userId: "thosakwe", payload: {
|
||||
"foo": "bar",
|
||||
"baz": {
|
||||
"one": 1,
|
||||
"franken": ["stein"]
|
||||
}
|
||||
});
|
||||
var jwt = token.serialize(hmac);
|
||||
print(jwt);
|
||||
|
||||
var parsed = new AuthToken.validate(jwt, hmac);
|
||||
print(parsed.toJson());
|
||||
expect(parsed.toJson()['pld'], equals(token.payload));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ main() {
|
|||
'/login',
|
||||
auth.authenticate('local',
|
||||
new AngelAuthOptions(callback: (req, res, token) {
|
||||
return res
|
||||
res
|
||||
..write('Hello!')
|
||||
..end();
|
||||
})));
|
||||
|
|
Loading…
Reference in a new issue