mirror of
https://github.com/jonasroussel/dart_jsonwebtoken.git
synced 2025-07-30 23:42:11 +08:00
v1.0.0
This commit is contained in:
@ -1,3 +1,10 @@
|
||||
## 1.0.0
|
||||
|
||||
- New RSA Algorithm (RS256)
|
||||
- Keys a now using an abstract class 'Key' instead of a string
|
||||
- SecretKey: for HMAC (HS256)
|
||||
- PrivateKey & PublicKey: for RSA (RS256)
|
||||
|
||||
## 0.2.1
|
||||
|
||||
- Formatting
|
||||
|
17
README.md
17
README.md
@ -1,8 +1,11 @@
|
||||
# JsonWebToken
|
||||
# JSON Web Token
|
||||
[](https://pub.dev/packages/dart_jsonwebtoken)
|
||||
|
||||
A dart implementation of the famous javascript library `jsonwebtoken`.
|
||||
|
||||
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
|
||||
https://jwt.io allows you to decode, verify and generate JWT.
|
||||
|
||||
## Usage
|
||||
|
||||
### Import
|
||||
@ -25,8 +28,8 @@ final jwt = JWT(
|
||||
issuer: 'https://github.com/jonasroussel/jsonwebtoken',
|
||||
);
|
||||
|
||||
// Sign it
|
||||
token = jwt.sign('secret-key');
|
||||
// Sign it (default with HS256 algorithm)
|
||||
token = jwt.sign(SecretKey('secret passphrase'));
|
||||
|
||||
print('Signed token: $token\n');
|
||||
```
|
||||
@ -36,7 +39,7 @@ print('Signed token: $token\n');
|
||||
```dart
|
||||
try {
|
||||
// Verify a token
|
||||
final jwt = JWT.verify(token, 'secret-key');
|
||||
final jwt = JWT.verify(token, SecretKey('secret passphrase'));
|
||||
|
||||
print('Payload: ${jwt.payload}');
|
||||
} on JWTExpiredError {
|
||||
@ -45,3 +48,9 @@ try {
|
||||
print(ex.message); // ex: invalid signature
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Supported Algorithms
|
||||
|
||||
- HS256 (HMAC / SHA256)
|
||||
- RS256 (RSA / SHA256)
|
||||
|
@ -17,7 +17,7 @@ main() {
|
||||
);
|
||||
|
||||
// Sign it
|
||||
token = jwt.sign('secret-key');
|
||||
token = jwt.sign(SecretKey('secret passphrase'));
|
||||
|
||||
print('Signed token: $token\n');
|
||||
}
|
||||
@ -25,7 +25,7 @@ main() {
|
||||
/* Verify */ {
|
||||
try {
|
||||
// Verify a token
|
||||
final jwt = JWT.verify(token, 'secret-key');
|
||||
final jwt = JWT.verify(token, SecretKey('secret passphrase'));
|
||||
|
||||
print('Payload: ${jwt.payload}');
|
||||
} on JWTExpiredError {
|
||||
|
@ -3,3 +3,4 @@ library jsonwebtoken;
|
||||
export 'src/jwt.dart';
|
||||
export 'src/errors.dart';
|
||||
export 'src/algorithms.dart';
|
||||
export 'src/keys.dart';
|
||||
|
@ -1,15 +1,21 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import 'package:pointycastle/pointycastle.dart' hide PrivateKey, PublicKey;
|
||||
import 'package:rsa_pkcs/rsa_pkcs.dart' hide RSAPrivateKey, RSAPublicKey;
|
||||
|
||||
abstract class JWTAlgorithm {
|
||||
static const HS256 = HS256Algorithm();
|
||||
static const RS256 = RS256Algorithm();
|
||||
|
||||
static JWTAlgorithm fromName(String name) {
|
||||
switch (name) {
|
||||
case 'HS256':
|
||||
return JWTAlgorithm.HS256;
|
||||
case 'RS256':
|
||||
return JWTAlgorithm.RS256;
|
||||
default:
|
||||
throw JWTInvalidError('unknown algorithm');
|
||||
}
|
||||
@ -18,8 +24,8 @@ abstract class JWTAlgorithm {
|
||||
const JWTAlgorithm();
|
||||
|
||||
String get name;
|
||||
List<int> sign(String key, List<int> body);
|
||||
bool verify(String key, List<int> body, List<int> signature);
|
||||
List<int> sign(Key key, List<int> body);
|
||||
bool verify(Key key, List<int> body, List<int> signature);
|
||||
}
|
||||
|
||||
class HS256Algorithm extends JWTAlgorithm {
|
||||
@ -29,13 +35,18 @@ class HS256Algorithm extends JWTAlgorithm {
|
||||
String get name => 'HS256';
|
||||
|
||||
@override
|
||||
List<int> sign(String key, List<int> body) {
|
||||
final hmac = Hmac(sha256, utf8.encode(key));
|
||||
List<int> sign(Key key, List<int> body) {
|
||||
assert(key is SecretKey, 'key must be a SecretKey');
|
||||
final secretKey = key as SecretKey;
|
||||
|
||||
final hmac = Hmac(sha256, utf8.encode(secretKey.key));
|
||||
return hmac.convert(body).bytes;
|
||||
}
|
||||
|
||||
@override
|
||||
bool verify(String key, List<int> body, List<int> signature) {
|
||||
bool verify(Key key, List<int> body, List<int> signature) {
|
||||
assert(key is SecretKey, 'key must be a SecretKey');
|
||||
|
||||
final actual = sign(key, body);
|
||||
|
||||
if (actual.length != signature.length) return false;
|
||||
@ -47,3 +58,80 @@ class HS256Algorithm extends JWTAlgorithm {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class RS256Algorithm extends JWTAlgorithm {
|
||||
const RS256Algorithm();
|
||||
|
||||
@override
|
||||
String get name => 'RS256';
|
||||
|
||||
@override
|
||||
List<int> sign(Key key, List<int> body) {
|
||||
assert(key is PrivateKey, 'key must be a PrivateKey');
|
||||
final privateKey = key as PrivateKey;
|
||||
|
||||
final parser = RSAPKCSParser();
|
||||
RSAKeyPair pair;
|
||||
|
||||
try {
|
||||
pair = parser.parsePEM(privateKey.key, password: privateKey.passphrase);
|
||||
assert(pair.private != null);
|
||||
} catch (ex) {
|
||||
throw JWTInvalidError('invalid private RSA key');
|
||||
}
|
||||
|
||||
final signer = Signer('SHA-256/RSA');
|
||||
final params = ParametersWithRandom(
|
||||
PrivateKeyParameter<RSAPrivateKey>(
|
||||
RSAPrivateKey(
|
||||
pair.private.modulus,
|
||||
pair.private.privateExponent,
|
||||
pair.private.prime1,
|
||||
pair.private.prime2,
|
||||
),
|
||||
),
|
||||
SecureRandom('AES/CTR/PRNG'),
|
||||
);
|
||||
|
||||
signer.init(true, params);
|
||||
|
||||
RSASignature signature = signer.generateSignature(Uint8List.fromList(body));
|
||||
|
||||
return signature.bytes.toList(growable: false);
|
||||
}
|
||||
|
||||
@override
|
||||
bool verify(Key key, List<int> body, List<int> signature) {
|
||||
assert(key is PublicKey, 'key must be a PublicKey');
|
||||
final publicKey = key as PublicKey;
|
||||
|
||||
final parser = RSAPKCSParser();
|
||||
RSAKeyPair pair;
|
||||
|
||||
try {
|
||||
pair = parser.parsePEM(publicKey.key, password: publicKey.passphrase);
|
||||
assert(pair.public != null);
|
||||
} catch (ex) {
|
||||
throw JWTInvalidError('invalid public RSA key');
|
||||
}
|
||||
|
||||
try {
|
||||
final signer = Signer('SHA-256/RSA');
|
||||
final params = ParametersWithRandom(
|
||||
PublicKeyParameter<RSAPublicKey>(
|
||||
RSAPublicKey(
|
||||
pair.public.modulus,
|
||||
BigInt.from(pair.public.publicExponent),
|
||||
),
|
||||
),
|
||||
SecureRandom('AES/CTR/PRNG'),
|
||||
);
|
||||
|
||||
signer.init(false, params);
|
||||
|
||||
return signer.verifySignature(Uint8List.fromList(body), RSASignature(Uint8List.fromList(signature)));
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,45 +5,39 @@ import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import './utils.dart';
|
||||
|
||||
class JWT {
|
||||
static JWT verify(String token, String key) {
|
||||
try {
|
||||
final parts = token.split('.');
|
||||
static JWT verify(String token, Key key) {
|
||||
final parts = token.split('.');
|
||||
|
||||
final header = Map<String, dynamic>.from(jsonBase64.decode(base64Padded(parts[0])));
|
||||
final header = Map<String, dynamic>.from(jsonBase64.decode(base64Padded(parts[0])));
|
||||
|
||||
if (header['typ'] != 'JWT') throw JWTInvalidError('not a jwt');
|
||||
if (header['typ'] != 'JWT') throw JWTInvalidError('not a jwt');
|
||||
|
||||
final algorithm = JWTAlgorithm.fromName(header['alg']);
|
||||
final algorithm = JWTAlgorithm.fromName(header['alg']);
|
||||
|
||||
if (parts.length < 3) throw JWTInvalidError('jwt malformated');
|
||||
if (parts.length < 3) throw JWTInvalidError('jwt malformated');
|
||||
|
||||
final body = utf8.encode(parts[0] + '.' + parts[1]);
|
||||
final signature = base64Url.decode(base64Padded(parts[2]));
|
||||
final body = utf8.encode(parts[0] + '.' + parts[1]);
|
||||
final signature = base64Url.decode(base64Padded(parts[2]));
|
||||
|
||||
if (!algorithm.verify(key, body, signature)) {
|
||||
throw JWTInvalidError('invalid signature');
|
||||
}
|
||||
|
||||
final payload = Map<String, dynamic>.from(jsonBase64.decode(base64Padded(parts[1])));
|
||||
|
||||
if (payload.containsKey('exp')) {
|
||||
final exp = DateTime.fromMillisecondsSinceEpoch(payload['exp'] * 1000);
|
||||
if (exp.isBefore(DateTime.now())) {
|
||||
throw JWTExpiredError();
|
||||
}
|
||||
}
|
||||
|
||||
return JWT(
|
||||
payload: payload,
|
||||
audience: payload.remove('aud'),
|
||||
issuer: payload.remove('iss'),
|
||||
subject: payload.remove('sub'),
|
||||
);
|
||||
} on JWTError {
|
||||
rethrow;
|
||||
} catch (ex) {
|
||||
throw JWTInvalidError('jwt invalid');
|
||||
if (!algorithm.verify(key, body, signature)) {
|
||||
throw JWTInvalidError('invalid signature');
|
||||
}
|
||||
|
||||
final payload = Map<String, dynamic>.from(jsonBase64.decode(base64Padded(parts[1])));
|
||||
|
||||
if (payload.containsKey('exp')) {
|
||||
final exp = DateTime.fromMillisecondsSinceEpoch(payload['exp'] * 1000);
|
||||
if (exp.isBefore(DateTime.now())) {
|
||||
throw JWTExpiredError();
|
||||
}
|
||||
}
|
||||
|
||||
return JWT(
|
||||
payload: payload,
|
||||
audience: payload.remove('aud'),
|
||||
issuer: payload.remove('iss'),
|
||||
subject: payload.remove('sub'),
|
||||
);
|
||||
}
|
||||
|
||||
JWT({
|
||||
@ -59,7 +53,7 @@ class JWT {
|
||||
final String issuer;
|
||||
|
||||
String sign(
|
||||
String key, {
|
||||
Key key, {
|
||||
JWTAlgorithm algorithm = JWTAlgorithm.HS256,
|
||||
Duration expiresIn,
|
||||
bool noTimestamp = false,
|
||||
|
21
lib/src/keys.dart
Normal file
21
lib/src/keys.dart
Normal file
@ -0,0 +1,21 @@
|
||||
abstract class Key {}
|
||||
|
||||
class SecretKey extends Key {
|
||||
String key;
|
||||
|
||||
SecretKey(this.key);
|
||||
}
|
||||
|
||||
class PrivateKey extends Key {
|
||||
String key;
|
||||
String passphrase;
|
||||
|
||||
PrivateKey(this.key, [this.passphrase = '']);
|
||||
}
|
||||
|
||||
class PublicKey extends Key {
|
||||
String key;
|
||||
String passphrase;
|
||||
|
||||
PublicKey(this.key, [this.passphrase = '']);
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
name: dart_jsonwebtoken
|
||||
description: A dart implementation of the famous javascript library 'jsonwebtoken'.
|
||||
version: 0.2.1
|
||||
version: 1.0.0
|
||||
repository: https://github.com/jonasroussel/jsonwebtoken
|
||||
homepage: https://github.com/jonasroussel/jsonwebtoken#readme
|
||||
|
||||
environment:
|
||||
sdk: '>=2.7.0 <3.0.0'
|
||||
sdk: '>=2.3.0 <3.0.0'
|
||||
|
||||
dependencies:
|
||||
crypto: ^2.1.5
|
||||
rsa_pkcs: ^1.1.3
|
||||
pointycastle: ^1.0.2
|
||||
|
Reference in New Issue
Block a user