shelf_auth 0.7.4

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 66

Authentication and Authorisation Middleware for Dart Shelf

Build Status Pub Version

Introduction

Provides Shelf middleware for authenticating users (or systems) and establishing sessions, as well as authorising access to resources.

Usage

Authentication

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

  • return a result (with a context) indicating that the authentication succeeded
  • return a result indicating that the authenticator did not find any credentials relevant to it
  • throw an exception indicating that the authenticator did find relevant credentials but deemed that the user should not be logged in

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.

Establishing a Session on Login

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, Authenticators 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.

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

Authenticators

Shelf Auth provides the following authenticators out of the box:

BasicAuthenticator

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)
UsernamePasswordAuthenticator

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.

Session Handlers

Shelf Auth provides the following SessionHandlers out of the box:

JwtSessionHandler

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

  • Does not require anything to be stored on the server to support a session. Any server processes that have access to the secret used to create the token can validate it.
  • Supports both an inactivity timeout and a total session timeout

Other session handlers like a cookie based mechanism is likely to be added in the future

Authentication Builder

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

Authorisation

var authorisationMiddleware = authorise([new SameOriginAuthoriser()]);

Shelf Auth provides an authorise function that takes a list of Authorisers 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:

  • if there is an authenticated user, a ForbiddenException is thrown
  • otherwise a UnauthorizedException is thrown.

Authorisers

Shelf Auth provides the following authorisers out of the box:

AuthenticatedOnlyAuthoriser

Only allows access to authenticated users. If there is not current AuthenticatedContext in the request then access is denied.

SameOriginAuthoriser

Helps protect against XSRF attacks by denying access to requests where the referer is not from the same host as the request url.

PrincipalWhitelistAuthoriser

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.

0.7.3

  • Require dart-sdk version 1.16.0
  • Remove crypto dependency (dart 1.16.0 ships with all necessary functions in dart:convert)

0.7.2

  • Updated crypto dependency with required code changes

0.7.1

  • Update dependency versions on dart_jwt, shelf_path and shelf_route

0.7.0

  • SessionHandler returns a Future
  • Support for custom session claimsets

0.6.0+1

  • Upgrade to shelf 0.6.0
  • Use test package
  • Upgrade option

0.5.2

  • Port to option >1.0.0
  • unittest to test replacement
  • Added explicit dependency from matcher 0.12.0

0.5.1

  • Exposed a models.dart with just things like Principal, AuthenticatedContext etc

0.5.0

  • Now uses http_exception package rather than shelf_exception_response

0.4.0

  • Upgraded dart-jwt and shelf versions

0.3.0

  • BREAKING CHANGE: Made sessionIdentifier mandatory for jwt sessions.
    • If you are using jwt sessions in production then release a version with 0.2.6 first. Otherwise you will get errors from any existing sessions as they won't have sessionIdentifiers.

0.2.6

  • Added an optional sessionIdentifier

0.2.5

  • Added support for excluding some requests from authorisation checks

0.2.4

  • Added authenticated only authoriser

0.2.3+1

  • Added some logging

0.2.3

  • Added authorisation support

0.2.2+1

  • Some doco

0.2.2

  • Exposed zone functionality via an spi so other libs can manually set auth context

0.2.1

  • Added builder to simplify creating authentication middleware. Use the new builder function to create a builder

0.2.0

  • Added AuthenticatedContext as a Zone variable. Available via a new function authenticatedContext()

Note the AuthenticationMiddleware class is no longer exposed. If you depended on it then this change is backwards incompatible. Otherwise all good

0.1.0+1

  • Added missing dependency on shelf_path

0.1.0

  • Added Jwt Session Mechanism
  • SessionHandlers now have an associated Authenticator

0.0.3

  • Added Basic Auth

0.0.1

  • First version

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

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  shelf_auth: "^0.7.4"

2. Install it

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.

3. Import it

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 Go to the documentation of shelf_auth 0.7.4 Download shelf_auth 0.7.4 archive
0.7.3 Oct 8, 2016 Go to the documentation of shelf_auth 0.7.3 Download shelf_auth 0.7.3 archive
0.7.2 Apr 10, 2016 Go to the documentation of shelf_auth 0.7.2 Download shelf_auth 0.7.2 archive
0.7.1 Jan 6, 2016 Go to the documentation of shelf_auth 0.7.1 Download shelf_auth 0.7.1 archive
0.7.0 Aug 12, 2015 Go to the documentation of shelf_auth 0.7.0 Download shelf_auth 0.7.0 archive
0.6.8 Jul 14, 2015 Go to the documentation of shelf_auth 0.6.8 Download shelf_auth 0.6.8 archive
0.6.7 Jul 12, 2015 Go to the documentation of shelf_auth 0.6.7 Download shelf_auth 0.6.7 archive
0.6.6 Jul 4, 2015 Go to the documentation of shelf_auth 0.6.6 Download shelf_auth 0.6.6 archive
0.6.5 Jul 1, 2015 Go to the documentation of shelf_auth 0.6.5 Download shelf_auth 0.6.5 archive
0.6.3 Jun 25, 2015 Go to the documentation of shelf_auth 0.6.3 Download shelf_auth 0.6.3 archive

All 36 versions...

Analysis

This feature is new.
We welcome feedback.

We analyzed this package, and provided a score, details, and suggestions below.

  • tool failures on Dec 6, 2017
  • Dart: 2.0.0-dev.8.0
  • pana: 0.7.3+1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
82
Health:
Code health derived from static analysis. [more]
54
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
44
Overall score:
Weighted score of the above. [more]
66

Platforms

Detected platforms:

Error(s) prevent platform classification.

Suggestions

  • Fix lib/src/authenticators/username_password_auth.dart.

    Strong-mode analysis of lib/src/authenticators/username_password_auth.dart failed with the following error:

    line: 76 col: 33
    The argument type '(Option<P>) → Option<dynamic>' can't be assigned to the parameter type '(Option<P>) → FutureOr<Option<AuthenticatedContext<P>>>'.

  • Fix lib/src/session/jwt/jwt_session_auth.dart.

    Strong-mode analysis of lib/src/session/jwt/jwt_session_auth.dart failed with the following error:

    line: 41 col: 26
    The function 'decodeSessionToken' has type '(String, {validationContext: JwsValidationContext}) → JsonWebToken<SessionClaimSet>' that isn't of expected type '(String, {validationContext: JwsValidationContext}) → JsonWebToken<CS>'. This means its parameter or return type does not match what is expected.

  • Fix further 3 Dart files.

    Similar analysis of the following files failed:

    • lib/src/session/jwt/jwt_session_handler.dart
    • lib/src/context.dart
    • lib/src/util.dart

Dependencies

Package Constraint Resolved Available
Direct dependencies
dart_jwt ^0.5.2 0.5.2 0.6.0
http_exception ^0.1.0 0.1.0
matcher ^0.12.0 0.12.1+4
option ^1.1.0 1.2.0
shelf ^0.6.2 0.6.8 0.7.1
shelf_path ^0.1.8 0.1.8
uuid >=0.4.0 <0.6.0 0.5.3
Transitive dependencies
async 1.13.3 2.0.1
bignum 0.0.7 0.1.0
charcode 1.1.1
cipher 0.7.1
collection 1.14.3
concepts 0.2.0
convert 2.0.1
crypto 2.0.2+1
either 0.1.8
fixnum 0.9.1+2 0.10.6
http_parser 3.1.1
logging 0.11.3+1
path 1.5.1
source_span 1.4.0
stack_trace 1.9.1
stream_channel 1.6.2
string_scanner 1.0.2
typed_data 1.1.5
Dev dependencies
mock >=0.11.0+1 <0.13.0
mockito >=0.11.0 <0.12.0
shelf_exception_handler >=0.1.0
shelf_route ^0.14.3
test ^0.12.0