mirror of
https://github.com/jonasroussel/dart_jsonwebtoken.git
synced 2025-08-01 00:16:26 +08:00
v0.1.0
This commit is contained in:
@ -1,3 +1,3 @@
|
|||||||
## 1.0.0
|
## 0.1.0
|
||||||
|
|
||||||
- Initial version, created by Stagehand
|
- First version with every based features
|
||||||
|
24
README.md
24
README.md
@ -1,22 +1,2 @@
|
|||||||
A library for Dart developers.
|
# JsonWebToken
|
||||||
|
[](https://pub.dev/packages/jsonwebtoken)
|
||||||
Created from templates made available by Stagehand under a BSD-style
|
|
||||||
[license](https://github.com/dart-lang/stagehand/blob/master/LICENSE).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
A simple usage example:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
import 'package:jsonwebtoken/jsonwebtoken.dart';
|
|
||||||
|
|
||||||
main() {
|
|
||||||
var awesome = new Awesome();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features and bugs
|
|
||||||
|
|
||||||
Please file feature requests and bugs at the [issue tracker][tracker].
|
|
||||||
|
|
||||||
[tracker]: http://example.com/issues/replaceme
|
|
||||||
|
@ -1,11 +1,37 @@
|
|||||||
import 'package:jsonwebtoken/jsonwebtoken.dart';
|
import 'package:jsonwebtoken/jsonwebtoken.dart';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
final token = JWT(
|
String token;
|
||||||
payload: {
|
|
||||||
'hello': 'world',
|
|
||||||
},
|
|
||||||
).sign(key: 'test');
|
|
||||||
|
|
||||||
print(token);
|
/* Sign */ {
|
||||||
|
// Create a json web token
|
||||||
|
final jwt = JWT(
|
||||||
|
payload: {
|
||||||
|
'id': 123,
|
||||||
|
'server': {
|
||||||
|
'id': '3e4fc296',
|
||||||
|
'loc': 'euw-2',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
issuer: 'https://github.com/jonasroussel/jsonwebtoken',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sign it
|
||||||
|
token = jwt.sign('secret-key');
|
||||||
|
|
||||||
|
print('Signed token: $token\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify */ {
|
||||||
|
try {
|
||||||
|
// Verify a token
|
||||||
|
final jwt = JWT.verify(token, 'secret-key');
|
||||||
|
|
||||||
|
print('Payload: ${jwt.payload}');
|
||||||
|
} on JWTExpiredError {
|
||||||
|
print('jwt expired');
|
||||||
|
} on JWTError catch (ex) {
|
||||||
|
print(ex.message); // ex: invalid signature
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
library jsonwebtoken;
|
library jsonwebtoken;
|
||||||
|
|
||||||
export 'src/jsonwebtoken.dart';
|
export 'src/jwt.dart';
|
||||||
|
export 'src/errors.dart';
|
||||||
|
export 'src/algorithms.dart';
|
||||||
|
49
lib/src/algorithms.dart
Normal file
49
lib/src/algorithms.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:jsonwebtoken/jsonwebtoken.dart';
|
||||||
|
|
||||||
|
abstract class JWTAlgorithm {
|
||||||
|
static const HS256 = HS256Algorithm();
|
||||||
|
|
||||||
|
static JWTAlgorithm fromName(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case 'HS256':
|
||||||
|
return JWTAlgorithm.HS256;
|
||||||
|
default:
|
||||||
|
throw JWTInvalidError('unknown algorithm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const JWTAlgorithm();
|
||||||
|
|
||||||
|
String get name;
|
||||||
|
List<int> sign(String key, List<int> body);
|
||||||
|
bool verify(String key, List<int> body, List<int> signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HS256Algorithm extends JWTAlgorithm {
|
||||||
|
const HS256Algorithm();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'HS256';
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<int> sign(String key, List<int> body) {
|
||||||
|
final hmac = Hmac(sha256, utf8.encode(key));
|
||||||
|
return hmac.convert(body).bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool verify(String key, List<int> body, List<int> signature) {
|
||||||
|
final actual = sign(key, body);
|
||||||
|
|
||||||
|
if (actual.length != signature.length) return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < actual.length; i++) {
|
||||||
|
if (actual[i] != signature[i]) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
13
lib/src/errors.dart
Normal file
13
lib/src/errors.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
class JWTError extends Error {
|
||||||
|
JWTError(this.message);
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
}
|
||||||
|
|
||||||
|
class JWTInvalidError extends JWTError {
|
||||||
|
JWTInvalidError(String message) : super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
class JWTExpiredError extends JWTError {
|
||||||
|
JWTExpiredError() : super('jwt expired');
|
||||||
|
}
|
@ -1,90 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:crypto/crypto.dart';
|
|
||||||
|
|
||||||
class JWT {
|
|
||||||
JWT({
|
|
||||||
this.payload = const {},
|
|
||||||
this.audience,
|
|
||||||
this.subject,
|
|
||||||
this.issuer,
|
|
||||||
});
|
|
||||||
|
|
||||||
static String secretKey = null;
|
|
||||||
|
|
||||||
final _jsonToBase64Url = json.fuse(utf8.fuse(base64Url));
|
|
||||||
|
|
||||||
final Map<String, dynamic> payload;
|
|
||||||
final String audience;
|
|
||||||
final String subject;
|
|
||||||
final String issuer;
|
|
||||||
|
|
||||||
String signedToken;
|
|
||||||
|
|
||||||
String sign({
|
|
||||||
String key,
|
|
||||||
Duration expiresIn = null,
|
|
||||||
bool noTimestamp = false,
|
|
||||||
}) {
|
|
||||||
if (key == null && JWT.secretKey != null) key = JWT.secretKey;
|
|
||||||
assert(key != null);
|
|
||||||
|
|
||||||
final header = {'alg': 'HS256', 'typ': 'JWT'};
|
|
||||||
final algorithm = HS256Algorithm(key);
|
|
||||||
|
|
||||||
// Creation timestamp
|
|
||||||
if (!noTimestamp)
|
|
||||||
payload['iat'] = DateTime.now();
|
|
||||||
else
|
|
||||||
payload.remove('iat');
|
|
||||||
|
|
||||||
// Expiration timestamp
|
|
||||||
if (expiresIn != null)
|
|
||||||
payload['exp'] = DateTime.now().add(expiresIn);
|
|
||||||
else
|
|
||||||
payload.remove('exp');
|
|
||||||
|
|
||||||
final body = _jsonToBase64Url.encode(header) + '.' + _jsonToBase64Url.encode(payload);
|
|
||||||
final signature = base64Url.encode(algorithm.sign(utf8.encode(body)));
|
|
||||||
|
|
||||||
return (signedToken = (body + '.' + signature));
|
|
||||||
}
|
|
||||||
|
|
||||||
static JWT verify(String token) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class JWTAlgorithm {
|
|
||||||
const JWTAlgorithm();
|
|
||||||
|
|
||||||
String get name;
|
|
||||||
List<int> sign(List<int> body);
|
|
||||||
bool verify(List<int> body, List<int> signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
class HS256Algorithm extends JWTAlgorithm {
|
|
||||||
const HS256Algorithm(this.secretKey);
|
|
||||||
|
|
||||||
final String secretKey;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get name => 'HS256';
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<int> sign(List<int> body) {
|
|
||||||
final hmac = Hmac(sha256, utf8.encode(secretKey));
|
|
||||||
return hmac.convert(body).bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool verify(List<int> body, List<int> signature) {
|
|
||||||
final actual = sign(body);
|
|
||||||
|
|
||||||
if (actual.length != signature.length) return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < actual.length; i++) {
|
|
||||||
if (actual[i] != signature[i]) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
80
lib/src/jwt.dart
Normal file
80
lib/src/jwt.dart
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:jsonwebtoken/jsonwebtoken.dart';
|
||||||
|
|
||||||
|
import './utils.dart';
|
||||||
|
|
||||||
|
class JWT {
|
||||||
|
static JWT verify(String token, String key) {
|
||||||
|
try {
|
||||||
|
final parts = token.split('.');
|
||||||
|
|
||||||
|
final header = Map<String, dynamic>.from(jsonBase64.decode(base64Padded(parts[0])));
|
||||||
|
|
||||||
|
if (header['typ'] != 'JWT') throw JWTInvalidError('not a jwt');
|
||||||
|
|
||||||
|
final algorithm = JWTAlgorithm.fromName(header['alg']);
|
||||||
|
|
||||||
|
if (parts.length < 3) throw JWTInvalidError('jwt malformated');
|
||||||
|
|
||||||
|
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 catch (ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (ex) {
|
||||||
|
throw JWTInvalidError('jwt invalid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JWT({
|
||||||
|
this.payload = const {},
|
||||||
|
this.audience,
|
||||||
|
this.subject,
|
||||||
|
this.issuer,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Map<String, dynamic> payload;
|
||||||
|
final String audience;
|
||||||
|
final String subject;
|
||||||
|
final String issuer;
|
||||||
|
|
||||||
|
String sign(
|
||||||
|
String key, {
|
||||||
|
JWTAlgorithm algorithm = JWTAlgorithm.HS256,
|
||||||
|
Duration expiresIn = null,
|
||||||
|
bool noTimestamp = false,
|
||||||
|
}) {
|
||||||
|
final header = {'alg': algorithm.name, 'typ': 'JWT'};
|
||||||
|
|
||||||
|
if (!noTimestamp) payload['iat'] = secondsSinceEpoch(DateTime.now());
|
||||||
|
if (expiresIn != null) payload['exp'] = secondsSinceEpoch(DateTime.now().add(expiresIn));
|
||||||
|
if (audience != null) payload['aud'] = audience;
|
||||||
|
if (subject != null) payload['sub'] = subject;
|
||||||
|
if (issuer != null) payload['iss'] = issuer;
|
||||||
|
|
||||||
|
final body = base64Unpadded(jsonBase64.encode(header)) + '.' + base64Unpadded(jsonBase64.encode(payload));
|
||||||
|
final signature = base64Unpadded(base64Url.encode(algorithm.sign(key, utf8.encode(body))));
|
||||||
|
|
||||||
|
return body + '.' + signature;
|
||||||
|
}
|
||||||
|
}
|
26
lib/src/utils.dart
Normal file
26
lib/src/utils.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
final jsonBase64 = json.fuse(utf8.fuse(base64Url));
|
||||||
|
|
||||||
|
String base64Unpadded(String value) {
|
||||||
|
if (value.endsWith('==')) return value.substring(0, value.length - 2);
|
||||||
|
if (value.endsWith('=')) return value.substring(0, value.length - 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String base64Padded(String value) {
|
||||||
|
final lenght = value.length;
|
||||||
|
|
||||||
|
switch (lenght % 4) {
|
||||||
|
case 2:
|
||||||
|
return value.padRight(lenght + 2, '=');
|
||||||
|
case 3:
|
||||||
|
return value.padRight(lenght + 1, '=');
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int secondsSinceEpoch(DateTime time) {
|
||||||
|
return time.millisecondsSinceEpoch ~/ 1000;
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
name: jsonwebtoken
|
name: jsonwebtoken
|
||||||
description: A starting point for Dart libraries or applications.
|
description: A dart implementation of JSON Web Tokens.
|
||||||
version: 1.0.0
|
version: 0.1.0
|
||||||
homepage: https://www.example.com
|
repository: https://github.com/jonasroussel/jsonwebtoken
|
||||||
|
homepage: https://github.com/jonasroussel/jsonwebtoken#readme
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.7.0 <3.0.0'
|
sdk: '>=2.7.0 <3.0.0'
|
||||||
|
Reference in New Issue
Block a user