Use async injection instead of decodeJwt

This commit is contained in:
Tobe O 2019-04-19 05:08:06 -04:00
parent b6acfb1573
commit 53b249da8b
8 changed files with 91 additions and 16 deletions

View file

@ -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.

View file

@ -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);
} }
``` ```

View file

@ -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...

View file

@ -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);
}; };
} }

View file

@ -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);
}

View file

@ -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:

View file

@ -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

View file

@ -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 {