Provides Shelf middleware for authenticating users (or systems) and establishing sessions, as well as authorising access to resources.
Note: For an alternative way to build authentication middleware, see the Authentication Builder section below.
var authMiddleware = authenticate([
new BasicAuthenticator(new TestLookup()),
new RandomAuthenticator()]));
Shelf Auth provides an authenticate
function that takes a list of Authenticator
s and an optional SessionHandler
(see below) and creates Shelf Middleware
.
You then add this Middleware
at the appropriate place in your shelf pipeline
var handler = const Pipeline()
.addMiddleware(exceptionHandler())
.addMiddleware(authMiddleware)
.addHandler((Request request) => new Response.ok("I'm in with "
"${getAuthenticatedContext(request).map((ac) => ac.principal.name)}\n"));
io.serve(handler, 'localhost', 8080);
When the authentication middleware is invoked it goes through the authenticators in order. Each Authenticator
does one of the following
The first Authenticator
that either returns a successful authentication or throws an exception wins. If an Authenticator
indicates it did not find relevant credentials, the next authenticator in the list is called.
If no exception is thrown, then the innerHandler
passed to the middleware will be called. If the authentication was successful then the request will contain authentication related data in the request context. This can be retrieved from the current request via the getAuthenticatedContext
function or from the current zone via authenticatedContext
.
Successful authentication results in a new zone created with the authenticated context set as a zone variable. This can be accessed with the authenticatedContext
function.
If none of the authenticators handle the request, then the innerHandler
is invoked without any authentication context. Downstream handlers should treat this is access by an unauthenticated (guest) user. You can deny anonymous access by invoking the authenticate
function with allowAnonymousAccess: false
.
If no SesionHandler
is provided to the authenticate
function then no session will be established. This means each request needs to be authenticated. This is suitable for system to system calls as well as authentication mechanisms like Basic Authentication.
To create sessions on successful login a SessionHandler
is included
var authMiddleware = authenticate([new RandomAuthenticator()],
new JwtSessionHandler('super app', 'shhh secret', testLookup));
The SessionHandler
will be invoked on successful authentication if the resulting AuthenticatedContext
supports sessions.
Note that in addition to indicating whether authentication succeeded, Authenticator
s also indicate whether session creation is allowed. For some authentication mechanisms (e.g. server to server calls) it may not be desirable to create a session.
SessionHandler
s provide an Authenticator
that will always be the first authenticator called for a request. The other authenticators will only be called if there is no active session.
Note that Shelf Auth does not cover the storage (adding / retrieving) of session attributes. This is out of scope. Only the authentication related parts of session handling are in scope. Any session storage libraries that support Shelf Auth headers or can be integrated with them will work with Shelf auth.
Shelf Auth provides the following authenticators out of the box:
Supports Basic Authentication (http://tools.ietf.org/html/rfc2617)
By default the BasicAuthenticator
does not support session creation. This can be overriden when creating the authenticator as follows
new BasicAuthenticator(new TestLookup(), sessionCreationAllowed: true)
An Authenticator
that is intended for use with a dedicated login route. It defaults to assuming a form based POST with form fields called username
and password
such as.
curl -i -H 'contentType: application/x-www-form-urlencoded' -X POST -d 'username=fred&password=blah' http://localhost:8080/login
This style of authentication is almost always associated with establishing a session.
var loginMiddleware = authenticate(
[new UsernamePasswordAuthenticator(lookupByUsernamePassword)],
sessionHandler: sessionHandler);
You can set up a login route (in this example using shelf_route) and pass in this middleware.
rootRouter.post('/login', (Request request) => new Response.ok(
"I'm now logged in as ${loggedInUsername(request)}\n"),
middleware: loginMiddleware);
Now you typically set up other routes which are accessed via the session that was established on log in.
var defaultAuthMiddleware = authenticate([],
sessionHandler: sessionHandler, allowHttp: true,
allowAnonymousAccess: false);
rootRouter.child('/authenticated', middleware: defaultAuthMiddleware)
..get('/foo', (Request request) => new Response.ok(
"Doing foo as ${loggedInUsername(request)}\n"));
In this example all routes starting with /authenticated
will require a valid session.
The list of authenticators is expected to grow over time.
In addition you can easily create your own custom authenticators.
Shelf Auth provides the following SessionHandler
s out of the box:
This uses JWT to create authentication tokens which are returned in the Authorization
header in the response. Subsequent requests must pass the token back in Authorization
header. This is a Bearer style token mechanism.
Note: as with all security credentials passed in HTTP messages, if someone is able to intercept the request or response then they can steal the token and impersonate the user. Make sure you use HTTPS.
Features
Other session handlers like a cookie based mechanism is likely to be added in the future
To make it simpler to create authentication middleware, particularly when you use the bundled authenticators and session handlers, a builder is provided.
For example
var authMiddleware = (builder()
.basic(userNamePasswordLookup,
sessionCreationAllowed: true)
.jwtSession('me', 'sshh', usernameLookup)
..allowHttp=true)
.build();
Note: this example is a bit convoluted as you don't typically want session creation with basic auth
var authorisationMiddleware = authorise([new SameOriginAuthoriser()]);
Shelf Auth provides an authorise
function that takes a list of Authoriser
s and creates Shelf Middleware
.
Additionally authorisationBuilder
provides a builder for creating authorisation middleware including the out of the box authorisers
var authorisationMiddleware = (authorisationBuilder()
.sameOrigin()
.principalWhitelist((Principal p) => p.name == 'fred'))
.build();
If any Authoriser
denies access then:
ForbiddenException
is thrownUnauthorizedException
is thrown.Shelf Auth provides the following authorisers out of the box:
Only allows access to authenticated users. If there is not current
AuthenticatedContext
in the request then access is denied.
Helps protect against XSRF attacks by denying access to requests where the referer is not from the same host as the request url.
An Authoriser
that allows access to any principal that is part of a given PrincipalWhiteList
.
PrincipalWhiteList
can be implemented in many ways. For example it check principal names against a static in memory list of names.
final whitelist = [ 'fredlintstone@stone.age' ];
final whitelistAuthoriser = new PrincipalWhitelistAuthoriser(
(Principal p) => whitelist.contains(p.name));
Or it might check the users group against a database for example.
crypto
dependency (dart 1.16.0 ships with all necessary functions in dart:convert
)builder
function to create a builderauthenticatedContext()
Note the AuthenticationMiddleware class is no longer exposed. If you depended on it then this change is backwards incompatible. Otherwise all good
example/example.dart
// Copyright (c) 2014, The Shelf Auth project authors.
// Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by
// a BSD 2-Clause License that can be found in the LICENSE file.
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_auth/shelf_auth.dart';
import 'package:shelf_exception_handler/shelf_exception_handler.dart';
import 'dart:async';
import 'package:option/option.dart';
import 'package:logging/logging.dart';
import 'package:http_exception/http_exception.dart';
void main() {
Logger.root.level = Level.FINER;
Logger.root.onRecord.listen((lr) {
print('${lr.time} ${lr.level} ${lr.message}');
});
var authMiddleware = authenticate(
[new BasicAuthenticator(testLookup), new RandomAuthenticator()],
// allow http for testing with curl. Don't do in production
allowHttp: true);
var handler = const Pipeline()
.addMiddleware(logRequests())
.addMiddleware(exceptionHandler())
.addMiddleware(authMiddleware)
.addHandler((Request request) => new Response.ok("I'm in with "
"${getAuthenticatedContext(request).map((ac) => ac.principal.name)}\n"));
io.serve(handler, 'localhost', 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}
class RandomAuthenticator extends Authenticator {
bool approve = true;
bool readsBody = false;
@override
Future<Option<AuthenticatedContext>> authenticate(Request request) {
approve = !approve;
return approve
? new Future.value(
new Some(new AuthenticatedContext(new Principal("fred"))))
: new Future.error(new UnauthorizedException());
}
}
Future<Option<Principal>> testLookup(String username, String password) {
final validUser = username == 'Aladdin' && password == 'open sesame';
final principalOpt =
validUser ? new Some(new Principal(username)) : const None();
return new Future.value(principalOpt);
}
Add this to your package's pubspec.yaml file:
dependencies:
shelf_auth: ^0.7.4
You can install packages from the command line:
with pub:
$ pub get
Alternatively, your editor might support pub get
.
Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:shelf_auth/shelf_auth.dart';
Version | Uploaded | Documentation | Archive |
---|---|---|---|
0.7.4 | Oct 21, 2016 |
|
|
0.7.3 | Oct 8, 2016 |
|
|
0.7.2 | Apr 10, 2016 |
|
|
0.7.1 | Jan 6, 2016 |
|
|
0.7.0 | Aug 12, 2015 |
|
|
0.6.8 | Jul 14, 2015 |
|
|
0.6.7 | Jul 12, 2015 |
|
|
0.6.6 | Jul 4, 2015 |
|
|
0.6.5 | Jul 1, 2015 |
|
|
0.6.3 | Jun 25, 2015 |
|
|
Popularity:
Describes how popular the package is relative to other packages.
[more]
|
31
|
Health:
Code health derived from static analysis.
[more]
|
--
|
Maintenance:
Reflects how tidy and up-to-date the package is.
[more]
|
--
|
Overall:
Weighted score of the above.
[more]
|
16
|
The package version is not analyzed, because it does not support Dart 2. Until this is resolved, the package will receive a health and maintenance score of 0.
Support Dart 2 in pubspec.yaml
.
The SDK constraint in pubspec.yaml
doesn't allow the Dart 2.0.0 release. For information about upgrading it to be Dart 2 compatible, please see https://www.dartlang.org/dart-2#migration.
Make sure dartdoc
successfully runs on your package's source files. (-10 points)
Dependencies were not resolved.
Package | Constraint | Resolved | Available |
---|---|---|---|
Direct dependencies | |||
Dart SDK | >=1.16.0 <2.0.0 |