2016-09-21 23:09:23 +00:00
|
|
|
import 'dart:collection';
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:angel_framework/angel_framework.dart';
|
|
|
|
import 'package:crypto/crypto.dart';
|
|
|
|
|
|
|
|
class AuthToken {
|
|
|
|
final SplayTreeMap<String, String> _header =
|
|
|
|
new SplayTreeMap.from({"alg": "HS256", "typ": "JWT"});
|
|
|
|
|
|
|
|
String ipAddress;
|
|
|
|
DateTime issuedAt;
|
|
|
|
num lifeSpan;
|
|
|
|
var userId;
|
2017-01-13 15:50:38 +00:00
|
|
|
Map<String, dynamic> payload = {};
|
2016-09-21 23:09:23 +00:00
|
|
|
|
|
|
|
AuthToken(
|
2017-01-13 15:50:38 +00:00
|
|
|
{this.ipAddress,
|
|
|
|
this.lifeSpan: -1,
|
|
|
|
this.userId,
|
|
|
|
DateTime issuedAt,
|
|
|
|
Map<String, dynamic> payload: const {}}) {
|
2016-09-21 23:09:23 +00:00
|
|
|
this.issuedAt = issuedAt ?? new DateTime.now();
|
2017-01-13 15:50:38 +00:00
|
|
|
this.payload.addAll(payload ?? {});
|
2016-09-21 23:09:23 +00:00
|
|
|
}
|
|
|
|
|
2017-01-13 15:50:38 +00:00
|
|
|
factory AuthToken.fromJson(String json) =>
|
|
|
|
new AuthToken.fromMap(JSON.decode(json));
|
2016-09-21 23:09:23 +00:00
|
|
|
|
|
|
|
factory AuthToken.fromMap(Map data) {
|
|
|
|
return new AuthToken(
|
|
|
|
ipAddress: data["aud"],
|
|
|
|
lifeSpan: data["exp"],
|
|
|
|
issuedAt: DateTime.parse(data["iat"]),
|
2017-01-13 15:50:38 +00:00
|
|
|
userId: data["sub"],
|
|
|
|
payload: data["pld"] ?? {});
|
2016-09-21 23:09:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
factory AuthToken.validate(String jwt, Hmac hmac) {
|
|
|
|
var split = jwt.split(".");
|
|
|
|
|
|
|
|
if (split.length != 3)
|
2017-01-13 15:50:38 +00:00
|
|
|
throw new AngelHttpException.notAuthenticated(message: "Invalid JWT.");
|
2016-09-21 23:09:23 +00:00
|
|
|
|
2016-12-21 18:28:51 +00:00
|
|
|
// var headerString = new String.fromCharCodes(BASE64URL.decode(split[0]));
|
2016-09-21 23:09:23 +00:00
|
|
|
var payloadString = new String.fromCharCodes(BASE64URL.decode(split[1]));
|
|
|
|
var data = split[0] + "." + split[1];
|
|
|
|
var signature = BASE64URL.encode(hmac.convert(data.codeUnits).bytes);
|
|
|
|
|
|
|
|
if (signature != split[2])
|
2017-01-13 15:50:38 +00:00
|
|
|
throw new AngelHttpException.notAuthenticated(
|
2016-09-21 23:09:23 +00:00
|
|
|
message: "JWT payload does not match hashed version.");
|
|
|
|
|
|
|
|
return new AuthToken.fromMap(JSON.decode(payloadString));
|
|
|
|
}
|
|
|
|
|
|
|
|
String serialize(Hmac hmac) {
|
|
|
|
var headerString = BASE64URL.encode(JSON.encode(_header).codeUnits);
|
2017-01-13 15:50:38 +00:00
|
|
|
var payloadString = BASE64URL.encode(JSON.encode(toJson()).codeUnits);
|
2016-09-21 23:09:23 +00:00
|
|
|
var data = headerString + "." + payloadString;
|
|
|
|
var signature = hmac.convert(data.codeUnits).bytes;
|
|
|
|
return data + "." + BASE64URL.encode(signature);
|
|
|
|
}
|
|
|
|
|
|
|
|
Map toJson() {
|
2017-01-13 15:50:38 +00:00
|
|
|
return _splayify({
|
2016-09-21 23:09:23 +00:00
|
|
|
"iss": "angel_auth",
|
|
|
|
"aud": ipAddress,
|
|
|
|
"exp": lifeSpan,
|
|
|
|
"iat": issuedAt.toIso8601String(),
|
2017-01-13 15:50:38 +00:00
|
|
|
"sub": userId,
|
|
|
|
"pld": _splayify(payload)
|
2016-09-21 23:09:23 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2017-01-13 15:50:38 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|