Only this pageAll pages
Powered by GitBook
1 of 9

cbAuth

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

External links

Introduction

Authentication is tedious. Many projects need some sort of authentication system. And in many of these projects you will basically start from scratch. That's why cbauth was created.

cbauth is a library that handles creating user sessions for your app including authenticating users, logging users in and out, and retrieving the currently authenticated user. It is customizable to plug in to your existing authentication method and session storage.

cbauth is designed to be customized for existing applications. As such, it requires some work to configure correctly. If you are starting a new project and would like this configuration done for you check out the pre-configured application templates on ForgeBox like quick-with-auth and quick-tailwind-inertia.

These are some of the methods you can use in your project:

auth.authenticate( username, password );
auth.isLoggedIn();
auth.getUser();
auth.logout();

While cbauth manages user sessions, it doesn't protect handlers or actions from being accessed by logged out our unauthorized users. This part of the job is left to libraries such as cbsecurity or cbguard.

Installation and Usage

Requirements

  • Lucee 5+

  • Adobe ColdFusion 2016+

  • Coldbox 4.3 +

Installation

cbauth requires ColdBox 4.3 or higher for module parent settings.

cbauth can be installed with

box install cbauth

Configuration

cbauth needs a userServiceClass which has to be specified in the module settings. It should be a WireBox mapping that resolves to a component that implements the IUserService interface (though this implementation can be implicit.)

By default, cbauth uses sessionStorage@cbstorages and RequestStorage@cbstorages for the sessionStorage and requestStorage, respectively.

Your config settings will look like this:

	moduleSettings = {
			cbauth: {
				userServiceClass: "UserService",
				// optional, override when needed
				sessionStorage: "SessionStorage@cbstorages",
				requestStorage: "RequestStorage@cbstorages"
			},
			//..... other module settings 
	}

You can specify other requestStorage or sessionStorage (e.g distributed cache) as long as your new storages follow the Interface definitions as defined:

interface name="SessionStorageInterface" { // or "RequestStorageInterface"
	  public any function getVar( required string name, any defaultValue );
	  public void function setVar( required string name, required any value );
	  public boolean function deleteVar( required string name );
	  public boolean function exists( required string name );
}

You don't have to formally implement the interface in your code.

Usage

Before you can use cbauth you have to create a UserService which implement the IUserService.

interface {

	/**
	 * Verify if the incoming username/password are valid credentials.
	 *
	 * @username The username
	 * @password The password
	 */
	boolean function isValidCredentials( required username, required password );

	/**
	 * Retrieve a user by username
	 *
	 * @return User that implements IAuthUser
	 */
	function retrieveUserByUsername( required username );

	/**
	 * Retrieve a user by unique identifier
	 *
	 * @id The unique identifier
	 *
	 * @return User that implements IAuthUser
	 */
	function retrieveUserById( required id );
	
}

You also have a User class which implements the IauthUser interface.

interface {

    /**
     * Return the unique identifier for the user
     */
    function getId();

}

If you use cbauth combined with cbsecurity or cbguard there are some additional method requirements for the User. Please check the corresponding docs for details.

When you have create your User and UserService and configured the moduleSettings you are ready to use the AuthenticationService. You can inject the authentication service using WireBox:

property name="auth" inject="authenticationService@cbauth";

// OR

var auth = wirebox.getInstance( "authenticationService@cbauth" );

Or, the quick way, you can just use the auth() helper method (which is actually just a shortcut to a wirebox injection). The auth() helper is very useful in views. And since Wirebox handles singleton management, you don't have to worry about calling auth()too many times.

The auth() helper is available in handlers, layouts, and views. You will need to use the injection if you need cbauth in other models.

The Authentication service provides you with all methods for handling user sessions, e.g:

auth.authenticate( username, password );
auth.isLoggedIn();
auth.getUser();
auth.logout();

After login your userID is stored in sessionstorage. Your user object will be cached in the requeststorage, so you only have to hit your database once per request to access user information.

The full list of methods is described in the Authentication Service section.

IUserService

You have to create a UserService and specify a userServiceClass in your moduleSettings. This UserService needs to have three methods, according to cbauth.interfaces.IUserService

interface {

	/**
	 * Verify if the incoming username/password are valid credentials.
	 *
	 * @username The username
	 * @password The password
	 */
	boolean function isValidCredentials( required username, required password );

	/**
	 * Retrieve a user by username
	 *
	 * @return User that implements IAuthUser
	 */
	function retrieveUserByUsername( required username );

	/**
	 * Retrieve a user by unique identifier
	 *
	 * @id The unique identifier
	 *
	 * @return User that implements IAuthUser
	 */
	function retrieveUserById( required id );
}

If you want to implement a UserService for cbauth combined with cbsecurity you will find your interface specification in cbsecurity.interfaces.IUserService. Methods in this spec are equal.

The user component returned by both retrieve... methods needs to respond to getId() as specified by the IAuthUser interface.

Combined with cbsecurity or cbguard you might have to specify additional methods for checking roles or permissions.

IAuthUser

You have to create a User component which responds to the getId() method. This user will be retrieved by the retrieve methods from your IUserService

interface {

    /**
     * Return the unique identifier for the user
     */
    function getId();

    /**
     * Verify if the user has one or more of the passed in permissions
     *
     * @permission One or a list of permissions to check for access
     *
     */
    boolean function hasPermission( required permission );

    /**
     * Shortcut to verify it the user is logged in or not.
     */
    boolean function isLoggedIn();

}

Combined with cbsecurity or cbguard you might have to specify additional methods for checking roles or permissions.

Interception points

Interceptions

cbauth announces several custom interception points. You can use these interception points to change request data or add additional values to session or request scopes. The preAuthentication and postAuthentication events fire during the standard authenticate() method call with a username and password. The preLogin and postLogin events fire during the login() method call. The preLogout and postLogout events fire during the logout() method call.

the preLogin and postLogin interception points will be called during the course of authenticate(). The order of the calls then are preAuthentication -> preLogin -> postLogin -> postAuthentication.

preAuthentication

InterceptData

Name

Description

username

The username passed in to cbauth

password

The password passed in to cbauth

Modifying the values in the interceptData will change what is passed to isValidCredentials and retrieveUserByUsername. This is the prime time to ignore certain requests or remove or pad usernames.

postAuthentication

InterceptData

Name

Description

user

The user component to be logged in

sessionStorage

The sessionStorage object to store additional values if needed

requestStorage

The requestStorage object to store additional values if needed

This is the prime time to store additional values based on the user returned.

preLogin

InterceptData

Name

Description

user

The user component to be logged in

postLogin

InterceptData

Name

Description

user

The user component to be logged in.

sessionStorage

The sessionStorage object to store additional values if needed

requestStorage

The requestStorage object to store additional values if needed

This is a good opportunity to store additional data if your application logged the user in manually without authenticating via a username/password like a "remember me" system.

preLogout

InterceptData

Name

Description

user

The user component to be logged in.

postLogout

InterceptData

Name

Description

no additional data

Authentication Service

The authentication service can be used by injecting it using WireBox:

property name="auth" inject="authenticationService@cbauth";

// OR

var auth = wirebox.getInstance( "authenticationService@cbauth" );

Or, the quick way, you can just use the auth() helper method (which is actually just a shortcut to a wirebox injection). The auth() helper is very useful in views. And since Wirebox handles singleton management, you don't have to worry about calling auth()too many times.

The auth() helper is available in handlers, layouts, and views. You will need to use the injection if you need cbauth in other models.

Methods

login

argument

type

required

default

description

user

any

true

The user component to log in. The component must respond to the getId() method.

Logs a user in to the system. The returned user component must respond to the getId() method (as defined in the IAuthUser interface). Additionally, the user is cached in the request scope. If a user is already in the session, this will replace it with the given user. This method returns the passed in user object.

logout

argument

type

required

default

description

No arguments

Logs a user out of system. This method can be called regardless of if there is currently a logged in user.

authenticate

argument

type

required

default

description

username

string

true

The username to attempt to log in.

password

string

true

The password to attempt to log in.

Attempts to log a user by calling the retrieveUserByUsername and isValidCredentials on the provided userServiceClass. If isValidCredentials returns false, it throws a InvalidCredentials exception.

If it succeeds, it returns the logged in user object. If it succeeds, it also sets the user id (obtained by calling getId() on the returned user component) in the configured sessionStorage and the returned user component in the configured requestStorage.

isLoggedIn

argument

type

required

default

description

no arguments

Returns boolean whether a user is logged in to the system.

check

argument

type

required

default

description

no arguments

Alias for isLoggedIn

guest

argument

type

required

default

description

no arguments

Returns whether a user is logged out of the system. Opposite of check() and isLoggedIn().

getUser

argument

type

required

default

description

no arguments

Returns the currently logged in user component.

If there is no logged in user, it throws a NoUserLoggedIn exception.

If there is a user object in the configured requestStorage, it is returned.

If the user object has not been fetched this request, it uses the id set in the configured sessionStorage to fetch the user (using retrieveUserById). It then sets the user in the configured requestStorage so subsequent calls to getUser don't re-fetch the user.

user

argument

type

required

default

description

no arguments

Alias for getUser

getUserId

argument

type

required

default

description

Returns the currently logged in user id.

If there is no logged in user, it throws a NoUserLoggedIn exception.

Customization Options

cbauth is quite flexible. In your IUserService implementation you can define how you want to retrieve your IAuthUser implementation: from a database, LDAP or any other authentication provider. You can define any properties you want to store, permissions or roles, and additional data for your authenticated user.

The current user will be retrieved and cached in a configured requestStorage. The userID will be stored in a configured sessionStorage. Both sessionStorage and requestStorage can be anything you want, as long as they implement the required methods. A common scenario, for example, is when you don't want to use ColdFusion or Lucee sessions for API applications, but instead use a distributed cache.

interface name="SessionStorageInterface" { // or "RequestStorageInterface"
	  public any function getVar( required string name, any defaultValue );
    public void function setVar( required string name, required any value );
	  public boolean function deleteVar( required string name );
	  public boolean function exists( required string name );
}

Additional information for your user can be stored after authentication in your sessio- or request storage by using the interception points announced by cbauth.

What's New

v5.0.1

24 Apr 2020 — 03:47:00 UTC

other

  • *: fix: Allow any dsl to be the user service (7cfb533)

v5.0.0

02 Apr 2020 — 22:10:08 UTC

BREAKING

  • *: feat: Added interception points and return user from authorize (778cd73)

v4.1.2

18 Feb 2020 — 17:27:02 UTC

other

  • *: Adjust gitignore file for better directory matching (a2d0ba3)

v4.1.1

13 Feb 2020 — 17:36:32 UTC

other

  • *: chore: Use forgeboxStorage (7ac6965)

v4.1.0

23 Dec 2019 — 18:06:36 UTC

feat

  • Login: Add new preLogin and postLogin interception points (495d516)

v4.0.0

02 Oct 2019 — 05:29:36 UTC

BREAKING

  • cbstorages: Upgrade cbstorages to 2.0.0 (10b3156)

v3.0.3

24 Sep 2019 — 16:40:21 UTC

other

  • *: chore: Transfer cbauth to coldbox-modules namespace (2616896)

v3.0.2

22 Sep 2019 — 19:08:45 UTC

other

  • *: docs: Fixed Coldbox 4.3 docs link (d4252ce)

v3.0.1

22 Sep 2019 — 18:58:14 UTC

chore

  • build: Use openjdk8 on Travis (0ba288b)

v3.0.0

12 Jul 2019 — 20:14:17 UTC

BREAKING

  • Storages: Allow customizing of storages (b97a8ad)

v2.0.0

25 Oct 2018 — 06:56:50 UTC

BREAKING

  • build: Trigger major release for prior commit (fca4bc5)

chore

  • ci: Add commandbox-semantic-release (d9a0411)

  • formatting: Clean up spacing at end of lines (ba8f1d3)

  • server.json: Default to adobe@11 servers (a73da29)

  • tests: Remove Gulpfile and npm dependencies (031a697)

fix

  • build: Update box.json references to elpete (76e416e)

  • build: Remove incompatible scripts for commandbox-semantic-release (252db7e)

  • tests: Fix MockBox expectation to match struct pattern (2e5fe23)

other

  • *: Merge pull request #3 from elpete/add_csr (74c4f63)

  • *: Merge pull request #2 from jclausen/master (8c71db8)

v2.0.0

25 Oct 2018 — 06:40:06 UTC

BREAKING

  • build: Trigger major release for prior commit (fca4bc5)

chore

  • ci: Add commandbox-semantic-release (d9a0411)

  • formatting: Clean up spacing at end of lines (ba8f1d3)

  • server.json: Default to adobe@11 servers (a73da29)

  • tests: Remove Gulpfile and npm dependencies (031a697)

fix

  • build: Remove incompatible scripts for commandbox-semantic-release (252db7e)

  • tests: Fix MockBox expectation to match struct pattern (2e5fe23)

other

  • *: Merge pull request #3 from elpete/add_csr (74c4f63)

  • *: Merge pull request #2 from jclausen/master (8c71db8)