Use async injection instead of decodeJwt
This commit is contained in:
parent
b6acfb1573
commit
53b249da8b
8 changed files with 91 additions and 16 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
# 2.1.4
|
||||||
|
* Deprecate `decodeJwt`, in favor of asynchronous injections.
|
||||||
|
|
||||||
# 2.1.3
|
# 2.1.3
|
||||||
* Use `await` on redirects, etc.
|
* Use `await` on redirects, etc.
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -17,7 +17,7 @@ Ensure you have read the [wiki](https://github.com/angel-dart/auth/wiki).
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
configureServer(Angel app) async {
|
configureServer(Angel app) async {
|
||||||
var auth = AngelAuth();
|
var auth = AngelAuth<User>();
|
||||||
auth.serializer = ...;
|
auth.serializer = ...;
|
||||||
auth.deserializer = ...;
|
auth.deserializer = ...;
|
||||||
auth.strategies['local'] = LocalAuthStrategy(...);
|
auth.strategies['local'] = LocalAuthStrategy(...);
|
||||||
|
@ -25,6 +25,12 @@ configureServer(Angel app) async {
|
||||||
// POST route to handle username+password
|
// POST route to handle username+password
|
||||||
app.post('/local', auth.authenticate('local'));
|
app.post('/local', auth.authenticate('local'));
|
||||||
|
|
||||||
|
// Using Angel's asynchronous injections, we can parse the JWT
|
||||||
|
// on demand. It won't be parsed until we check.
|
||||||
|
app.get('/profile', ioc((User user) {
|
||||||
|
print(user.description);
|
||||||
|
}));
|
||||||
|
|
||||||
// Use a comma to try multiple strategies!!!
|
// Use a comma to try multiple strategies!!!
|
||||||
//
|
//
|
||||||
// Each strategy is run sequentially. If one succeeds, the loop ends.
|
// Each strategy is run sequentially. If one succeeds, the loop ends.
|
||||||
|
@ -37,11 +43,8 @@ configureServer(Angel app) async {
|
||||||
authOptions
|
authOptions
|
||||||
);
|
);
|
||||||
|
|
||||||
// Apply angel_auth-specific configuration
|
// Apply angel_auth-specific configuration.
|
||||||
await app.configure(auth.configureServer);
|
await app.configure(auth.configureServer);
|
||||||
|
|
||||||
// Middleware to decode JWT's...
|
|
||||||
app.use(auth.decodeJwt);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ main() async {
|
||||||
auth.deserializer = (id) => fetchAUserByIdSomehow(id);
|
auth.deserializer = (id) => fetchAUserByIdSomehow(id);
|
||||||
|
|
||||||
// Middleware to decode JWT's and inject a user object...
|
// Middleware to decode JWT's and inject a user object...
|
||||||
app.fallback(auth.decodeJwt);
|
await app.configure(auth.configureServer);
|
||||||
|
|
||||||
auth.strategies['local'] = LocalAuthStrategy((username, password) {
|
auth.strategies['local'] = LocalAuthStrategy((username, password) {
|
||||||
// Retrieve a user somehow...
|
// Retrieve a user somehow...
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
|
||||||
/// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present.
|
/// Forces Basic authentication over the requested resource, with the given [realm] name, if no JWT is present.
|
||||||
|
@ -5,7 +6,13 @@ import 'package:angel_framework/angel_framework.dart';
|
||||||
/// [realm] defaults to `'angel_auth'`.
|
/// [realm] defaults to `'angel_auth'`.
|
||||||
RequestHandler forceBasicAuth<User>({String realm}) {
|
RequestHandler forceBasicAuth<User>({String realm}) {
|
||||||
return (RequestContext req, ResponseContext res) async {
|
return (RequestContext req, ResponseContext res) async {
|
||||||
if (req.container.has<User>()) return true;
|
if (req.container.has<User>())
|
||||||
|
return true;
|
||||||
|
else if (req.container.has<Future<User>>()) {
|
||||||
|
await req.container.makeAsync<User>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
res.headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"';
|
res.headers['www-authenticate'] = 'Basic realm="${realm ?? 'angel_auth'}"';
|
||||||
throw AngelHttpException.notAuthenticated();
|
throw AngelHttpException.notAuthenticated();
|
||||||
};
|
};
|
||||||
|
@ -25,7 +32,10 @@ RequestHandler requireAuthentication<User>() {
|
||||||
|
|
||||||
if (req.container.has<User>() || req.method == 'OPTIONS')
|
if (req.container.has<User>() || req.method == 'OPTIONS')
|
||||||
return true;
|
return true;
|
||||||
else
|
else if (req.container.has<Future<User>>()) {
|
||||||
|
await req.container.makeAsync<User>();
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
return _reject(res);
|
return _reject(res);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,9 @@ class AngelAuth<User> {
|
||||||
_jwtLifeSpan = jwtLifeSpan?.toInt() ?? -1;
|
_jwtLifeSpan = jwtLifeSpan?.toInt() ?? -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future configureServer(Angel app) async {
|
/// Configures an Angel server to decode and validate JSON Web tokens on demand,
|
||||||
|
/// whenever an instance of [User] is injected.
|
||||||
|
Future<void> configureServer(Angel app) async {
|
||||||
if (serializer == null)
|
if (serializer == null)
|
||||||
throw StateError(
|
throw StateError(
|
||||||
'An `AngelAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
|
'An `AngelAuth` plug-in was called without its `serializer` being set. All authentication will fail.');
|
||||||
|
@ -101,6 +103,30 @@ class AngelAuth<User> {
|
||||||
if (runtimeType != AngelAuth)
|
if (runtimeType != AngelAuth)
|
||||||
app.container.registerSingleton(this, as: AngelAuth);
|
app.container.registerSingleton(this, as: AngelAuth);
|
||||||
|
|
||||||
|
if (!app.container.has<_AuthResult<User>>()) {
|
||||||
|
app.container
|
||||||
|
.registerLazySingleton<Future<_AuthResult<User>>>((container) async {
|
||||||
|
var req = container.make<RequestContext>();
|
||||||
|
var res = container.make<ResponseContext>();
|
||||||
|
var result = await _decodeJwt(req, res);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw AngelHttpException.forbidden();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.container.registerLazySingleton<Future<User>>((container) async {
|
||||||
|
var result = await container.makeAsync<_AuthResult<User>>();
|
||||||
|
return result.user;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.container.registerLazySingleton<Future<AuthToken>>((container) async {
|
||||||
|
var result = await container.makeAsync<_AuthResult<User>>();
|
||||||
|
return result.token;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (reviveTokenEndpoint != null) {
|
if (reviveTokenEndpoint != null) {
|
||||||
app.post(reviveTokenEndpoint, reviveJwt);
|
app.post(reviveTokenEndpoint, reviveJwt);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +138,7 @@ class AngelAuth<User> {
|
||||||
|
|
||||||
void _apply(
|
void _apply(
|
||||||
RequestContext req, ResponseContext res, AuthToken token, User user) {
|
RequestContext req, ResponseContext res, AuthToken token, User user) {
|
||||||
if (!req.container.has<User>()) {
|
if (!req.container.has<User>() && !req.container.has<Future<User>>()) {
|
||||||
req.container
|
req.container
|
||||||
..registerSingleton<AuthToken>(token)
|
..registerSingleton<AuthToken>(token)
|
||||||
..registerSingleton<User>(user);
|
..registerSingleton<User>(user);
|
||||||
|
@ -123,12 +149,39 @@ class AngelAuth<User> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A middleware that decodes a JWT from a request, and injects a corresponding user.
|
/// DEPRECATED: A middleware that decodes a JWT from a request, and injects a corresponding user.
|
||||||
|
///
|
||||||
|
/// Now that `package:angel_framework` supports asynchronous injections, this middleware
|
||||||
|
/// is no longer directly necessary. Instead, call [configureServer]. You can then use
|
||||||
|
/// `makeAsync<User>`, or Angel's injections directly:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// var auth = AngelAuth<User>(...);
|
||||||
|
/// await app.configure(auth.configureServer);
|
||||||
|
///
|
||||||
|
/// app.get('/hmm', (User user) async {
|
||||||
|
/// // `package:angel_auth` decodes the JWT on demand.
|
||||||
|
/// print(user.name);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// @Expose('/my')
|
||||||
|
/// class MyController extends Controller {
|
||||||
|
/// @Expose('/hmm')
|
||||||
|
/// String getUsername(User user) => user.name
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
@deprecated
|
||||||
Future decodeJwt(RequestContext req, ResponseContext res) async {
|
Future decodeJwt(RequestContext req, ResponseContext res) async {
|
||||||
if (req.method == "POST" && req.path == reviveTokenEndpoint) {
|
if (req.method == "POST" && req.path == reviveTokenEndpoint) {
|
||||||
return await reviveJwt(req, res);
|
return await reviveJwt(req, res);
|
||||||
|
} else {
|
||||||
|
await _decodeJwt(req, res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<_AuthResult<User>> _decodeJwt(
|
||||||
|
RequestContext req, ResponseContext res) async {
|
||||||
String jwt = getJwt(req);
|
String jwt = getJwt(req);
|
||||||
|
|
||||||
if (jwt != null) {
|
if (jwt != null) {
|
||||||
|
@ -148,11 +201,12 @@ class AngelAuth<User> {
|
||||||
throw AngelHttpException.forbidden(message: "Expired JWT.");
|
throw AngelHttpException.forbidden(message: "Expired JWT.");
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = await deserializer(token.userId);
|
var user = await deserializer(token.userId);
|
||||||
_apply(req, res, token, user);
|
_apply(req, res, token, user);
|
||||||
|
return _AuthResult(user, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a JWT from a request, if any was sent at all.
|
/// Retrieves a JWT from a request, if any was sent at all.
|
||||||
|
@ -384,3 +438,10 @@ class AngelAuth<User> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _AuthResult<User> {
|
||||||
|
final User user;
|
||||||
|
final AuthToken token;
|
||||||
|
|
||||||
|
_AuthResult(this.user, this.token);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel_auth
|
name: angel_auth
|
||||||
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
|
description: A complete authentication plugin for Angel. Includes support for stateless JWT tokens, Basic Auth, and more.
|
||||||
version: 2.1.3
|
version: 2.1.4
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_auth
|
homepage: https://github.com/angel-dart/angel_auth
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -75,7 +75,6 @@ main() {
|
||||||
(id) async => await app.findService('users').read(id) as User;
|
(id) async => await app.findService('users').read(id) as User;
|
||||||
|
|
||||||
await app.configure(auth.configureServer);
|
await app.configure(auth.configureServer);
|
||||||
app.fallback(auth.decodeJwt);
|
|
||||||
|
|
||||||
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
|
auth.strategies['local'] = LocalAuthStrategy((username, password) async {
|
||||||
var users = await app
|
var users = await app
|
||||||
|
|
|
@ -27,7 +27,6 @@ Future wireAuth(Angel app) async {
|
||||||
|
|
||||||
auth.strategies['local'] = LocalAuthStrategy(verifier);
|
auth.strategies['local'] = LocalAuthStrategy(verifier);
|
||||||
await app.configure(auth.configureServer);
|
await app.configure(auth.configureServer);
|
||||||
app.fallback(auth.decodeJwt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
|
Loading…
Reference in a new issue