Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
In this section you will find the release notes for each version we release under this major version. If you are looking for the release notes of previous major versions use the version switcher at the top left of this documentation book. Here is a breakdown of our major version releases.
Version 2 is a major release of our security module. We completely refactored the engine to make it more modern and to adhere to our new coding standards. We then proceeded to enhance it to tap into our HMVC approach and allow rules to be contributed from modules themselves. We also added annotation driven security to complete the ability to secure not only incoming requests by rules but also by easy annotations.
We have made great strides in this release to make it a one-stop-shop for security concerns within ColdBox applications.
Our first release as a module decoupled from the ColdBox 2 days!
2021-MAR-29
2021-SEP-
Adobe 2021 Support
Migration to GitHub Actions from Travis CI
Refresh tokens support
Refresh token endpoint /cbsecurity/refreshToken
for secure refresh token generation
Manual refresh token method on the JwtService
: refreshToken( token )
Auto refresh token header interceptions for JWT validators
Detect on authenticate()
if the payload is empty and throw the appropriate exceptions
Added ability for the authenticate( payload )
to receive a payload to authenticate
Added ability to recreate the token storage using a force
argument getTokenStorage( force = false )
Ability for the parseToken()
to choose to store and authenticate or just parse
Unique jti
could have collisions if tokens created at the same time, add randomness to it
TokenExpirationException
not relayed from the base jwt library
If variables.settings.jwt.tokenStorage.enabled
is disabled all invalidations failed, make sure if the storage is disabled to not throw storage exceptions.
2021-DEC-10
Pass custom claims from refreshToken()
method when refreshing tokens
In the JWTService the refreshToken( struct customClaims = {} )
now has a customClaims
argument which you can use to seed the refresh token with custom claims.
Pass in the current JWT payload in to getJWTCustomClaims( payload )
method
This was done to help authors have the exact payload that was used for the execution call. This affects the IJwtSubject
interface.
The auto refresh token features now will auto refresh not only on expired tokens, but on invalid and missing tokens as well. Thanks to @elpete
Timeout in token storage is now the token timeout
The ColdBox cbsecurity
module is a collection of modules to help secure your applications.
The major areas of concern are:
A security authentication/authorization firewall ( cbsecurity
) which can secure your application based on:
Security rules and a rule engine for validation incoming events or URL's
Handler annotations
A security service for explicit authorizations ( cbsecurity
) to provide you with functional approaches to security context authorization in any layer of your application.
A JWT generator, decoder and authentication services ( jwtcfml
)
Cross Site Request Forgery (CSRF) Protection ( cbcsrf
)
An authentication manager ( cbauth
)
Ability to have global security rules
Ability for modules to add their own security rules and action overrides
Ability to distinguish between authentication and authorization issues
Annotation driven cascading security for handlers and actions
A functional security service that can be injected anywhere to provide you with authorizations
Security rules can exist in:
XML File
JSON File
Database
Models
The rules can be configured to use regular expressions or simple snippets
Can use ColdFusion authentication security
Can leverage any custom authentication provider
Plug any Authentication service or can leverage cbauth by default
Capability to distinguish between invalid authentication and invalid authorization and determine an outcome of the process.
Ability to load/unload security rules from contributing modules.
Ability for each module to define it's own validator
JWT Access and Refresh Tokens Native support
The ColdBox Security Module is maintained under the Semantic Versioning guidelines as much as possible. Releases will be numbered with the following format:
And constructed with the following guidelines:
Breaking backward compatibility bumps the major (and resets the minor and patch)
New additions without breaking backward compatibility bumps the minor (and resets the patch)
Bug fixes and misc changes bumps the patch
Apache 2 License: http://www.apache.org/licenses/LICENSE-2.0
The ColdBox Security Module is a professional open source software backed by Ortus Solutions, Corp offering services like:
Custom Development
Professional Support & Mentoring
Training
Server Tuning
Security Hardening
Code Reviews
The Box products and modules community for discussion and help can be found here:
https://community.ortussolutions.com/c/box-modules/cbsecurity/
Because of His grace, this project exists. If you don't like this, then don't read it, it's not for you.
"Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5
2021-MAR-10
Add a secureSameUser
method to throw when passed a different user #29 (https://github.com/coldbox-modules/cbsecurity/pull/29)
Fix getRealIP()
to only return originating user's source IP, if the forwarded ip is a list
2020-NOV-09
parseToken( token )
now accepts a token of your choice to work with in the request or it will continue to discover it if not passed.
Added new JWT Service method: invalidateAll()
which invalidates all tokens in the token storage
Added the new event: cbSecurity_onJWTInvalidateAllTokens
that fires once all tokens in the storage are cleared
Added storage of the authenticated user into the prc
scope when using attempt()
to be consistent with API calls
Spelling corrections on the readme
Added full var scoping for cbsecurity
in JWTService calls
2020-SEP-14
Contributed module rules are now pre-pended instead of appended. (@wpdebruin)
Not loading rules by source file detection due to invalid setting check
Don't trigger ColdBox's invalid event looping protection. It also auto-senses between ColdBox 6 and 5 (@homestar9)
Fixed token scopes according to JWT spec, it is called scope
and it is a space separated list. This doesn't change the User interface for it. (@wpdebruin)
Update token storages so no token rejection anymore when storage is not enabled. (@wpdebruin)
2020-DEC-11
Fixes a typo in the cbSecurity_onInvalidAuthorization
interception point declaration. Previously, the typo would prevent ColdBox from allowing the correctly-typed interception point from ever triggering an interception listener.
The userValidator()
method has been changed to roleValidator()
, but the error message was forgotten! So the developer is told they need a userValidator()
method... because the userValidator
method is no longer supported. :/
The isLoggedIn()
method now makes sure that a jwt is in place and valid, before determining if you are logged in or not.
Migrated all automated tests to focal
and mysql8
in preparation for latest updates
Add support for JSON/XML/model rules source when loading rules from modules. Each module can now load rules not only inline but from the documented external sources.
Ensure non-configured rules
default to empty array
2020-APR-03
In this release we have updated our internal authentication library cbauth
to version 5.x. Which brings the following changes to cbauth
:
Added preLogin, postLogin, preLogout, postLogout interception points
authentication()
now returns the user if valid
Just run update cbsecurity
and you are done!
2020-APR-02
This release adds the inclusion of the Cross Site Request Forgery module into cbsecurity: cbcsrf
. You can find all the details about this module here: https://github.com/coldbox-modules/cbcsrf. Below are the major features of this module:
Ability to generate security tokens based on your session
Automatic token rotation when leveraging cbauth
login and logout operations
Ability to on-demand rotate all security tokens for specific users
Leverages cbStorages
to store your tokens in CacheBox, which can be easily distributed and clustered
Ability to create multiple tokens via unique reference keys
Auto-verification interceptor that will verify all non-GET operations to ensure a security token is passed via rc
or headers
Auto-sensing of integration testing so the verifier can allow testing calls
Token automatic rotation on specific time periods for enhance security
Helpers to automatically generate hidden fields for the token
Automatic generation endpoint that can be used for Ajax applications to request tokens for users
2020-FEB-12
Feature
: Migrated from the jwt to the jwtcfml
(https://forgebox.io/view/jwt-cfml) library to expand encoding/decoding capabilities to support RS
and ES
algorithms:
HS256
HS384
HS512
RS256
RS384
RS512
ES256
ES384
ES512
Feature
: Added a new convenience method on the JWT Service: isTokenInStorage( token )
to verify if a token still exists in the token storage
Feature
: If no jwt secret is given in the settings, we will dynamically generate one that will last for the duration of the application scope.
Feature
: New setting for jwt
struct: issuer
, you can now set the issuer of tokens string or if not set, then cbSecurity will use the home page URI as the issuer of authority string.
Feature
: All tokens will be validated that the same iss
(Issuer) has granted the token
Improve
: Ability to have defaults for all JWT settings instead of always typing them in the configs
Improve
: More formattting goodness!
Bug
: Invalidation of tokens was not happening due to not using the actual key for the storage
2020-MAR-30
This release focuses on bringing a strong focus on protecting every development layer of a ColdBox application. It introduces the cbSecurity
model that can be used by any layer and provides you with a great functional API to secure your code.
There will be times where you will need authorization checks outside of the incoming request rules or the handler annotations. This can be from within interceptors, models, layouts or even views. For this, we have provided the cbSecurity
model so you can do explicit authorization checks anywhere you like.
cbSecurity
ModelYou can inject our model or you can use our handy cbsecure()
mixin (interceptors/handlers/layouts/views) and then call the appropriate security functions:
All security methods will call the application's configured Authentication Service to retrieve the currently logged in user. If the user is not logged in an immediate NoUserLoggedIn
exception will be thrown by all methods.
secure()
MethodsNow that you have access to the model, you can use the following method to verify explicit permissions and authorize access. This method will throw an exception if the user does not validate the incoming permissions context (NotAuthorized
).
The permission
can be an array, string or list of the permissions to validate.
The message
is a custom error message to be used in the message
string of the exception thrown.
You also have two more authorization methods that will verify certain permission conditions for you:
when()
There are also cases where you want to execute a piece of code by determining if the user has access to do so. For example, only a USER_ADMIN
can change people's roles or you want to filter some data for certain users. For this, we have created the when()
method with the following signature:
The permissions
is a permission array or list that will be Or'ed
The success
is a closure/lambda or UDF that will execute if the permissions validate.
The fail
is a closure/lambda or UDF that will execute if the permissions DID not validate, much like an else statement
Both closures/functions takes in a user
which is the currently authenticated user, the called in permissions
and can return anything.
You can also chain the when()
calls if needed, to create beautiful security contexts. So if we go back to our admin examples, we can do something like this:
We have also added the following whenX()
methods to serve your needs when evaluating the permissions:
If you just want to validate if a user has certain permissions or maybe no permissions at all or if a passed user is the same as the logged in user, then you can use the following boolean methods that only do verification.
Please note that you could potentially do these type of methods by leveraging the currently logged in user and it's hasPermission()
method. However, these methods provide abstraction and can easily be mocked!
These are great to have a unified and abstracted way to verifying permissions or if the passed user is the same as the logged in user. Here are some examples:
View Layer
Other Layers:
Please note that we do user equality by calling the getId()
method of the authenticated user and the incoming user. This is part of our IAuthUser
interface requirements.
There are also times where you need to validate custom conditions and block access to certain areas. This way, you can implement your own custom security logic and leverage cbSecurity for blockage. You will accomplish this via the secureWhen()
method:
The context
can be a closure/lambda/udf or a boolean evaluation:
The closure/udf will receive the currently authenticated user as the first argument.
You can also use our handy event.secureView()
method in the request context to pivot between views according to user permissions.
cbSecurity injects the secureView()
method into the request context via the preProcess
interception point.
This will allow you to set the successView
if the user has the permissions or the failView
if they don't.
cbSecurity
Method SummaryWhen certain permission context is met, if not throws NotAuthorized
secure( permissions, [message] )
secureAll( permissions, [message] )
secureNone( permissions, [message] )
secureWhen( context, [message] )
guard() alias to secure()
When certain permission context is met, execute the success function/closure, else if a fail
closure is defined, execute that instead.
when( permissions, success, fail )
whenAll( permissions, success, fail )
whenNone( permissions, success, fail )
Verify permissions or user equality
has( permissions ):boolean
all( permissions ):boolean
none( permissions ):boolean
sameUser( user ):boolean
secureView( permissions, successView, failView )
2019-SEP-25
Adobe 2016,2018 Support
Settings transferred to ColdBox 4/5 moduleSettings
approach instead of root approach (See compat section)
The rulesModelMethod
now defaults to getSecurityRules()
ColdFusion security validator has an identity now CFValidator@cbsecurity
instead of always being inline.
You can now add an overrideEvent
element to a rule. If that is set, then we will override the incoming event via event.overrideEvent()
instead of doing a relocation using the redirect
rule element.
You can now declare your rules inline in the configuration settings using the rules
key. This will allow you to build the rules in your config instead of a rule source.
We now can distinguish between invalid auth and invalid authorizations
New interception block points cbSecurity_onInvalidAuthentication
, cbSecurity_onInvalidAuhtorization
You now have a defaultAuthorizationAction
setting which defaults to redirect
You now have a invalidAuthenticationEvent
setting that can be used
You now have a defaultAuthenticationAction
setting which defaults to redirect
You now have a invalidAuthorizationEvent
setting that can be used
If a rule is matched, we will store it in the prc
as cbSecurity_matchedRule
so you can see which security rule was used for processing invalid access actions.
If a rule is matched we will store the validator results in prc
as cbSecurity_validatorResults
Ability for modules to register cbSecurity rules and setting overrides by registering a settings.cbSecurity
key.
New security rule visualizer for graphically seeing you rules and configuration. Can be locked down via the enableSecurityVisualizer
setting. Disabled by default.
Annotation based security for handlers and actions using the secured
annotation. Which can be boolean or a list of permissions, roles or whatever you like.
You can disable annotation based security by using the handlerAnnotationSecurity
boolean setting.
JWT Token Security Support
SSL Enforcement now cascades according to the following lookup: Global, rule, request
Interfaces documented for easier extension interfaces.*
Migration to script and code modernization
New Module Layout
Secured rules are now logged as warn()
with the offending Ip address.
New setting to turn on/off the loading of the security firewall: autoLoadFirewall
. The interceptor will auto load and be registered as cbsecurity@global
in WireBox.
Adobe 11 Dropped
Lucee 4.5 Dropped
Migrate your root cbSecurity
settings in your config/ColdBox.cfc
to inside the moduleSettings
IOC rules support dropped
OCM rules support dropped
validatorModel
dropped in favor of just validator
to be a WireBox Id
Removed preEventSecurity
it was too chatty and almost never used
The function userValidator
has been renamed to ruleValidator
and also added the annotationValidator
as well.
rulesSource
removed you can now use the rules
setting
The rules
can be: array, db, model, filepath
If the filepath
has json
or xml
in it, we will use that as the source style
rulesFile
removed you can now use the rules
setting.
The source code for this book is hosted in GitHub: . You can freely contribute to it and submit pull requests. The contents of this book is copyright by and cannot be altered or reproduced without author's consent. All content is provided "As-Is" and can be freely distributed.
The majority of code examples in this book are done in cfscript
.
The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL -
Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.
The information in this book is distributed “as is”, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity with respect to loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software and resources described in it.
We highly encourage contribution to this book and our open source software. The source code for this book can be found in our where you can submit pull requests.
10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - . So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.
Shalom Children’s Home is one of the ministries that is dear to our hearts located in El Salvador. During the 12 year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.
Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education and life skills training in a Christian environment. The home is supported by a child sponsorship program.
We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador that either have no families or have been abandoned. This is good earth to seed and plant.
Luis Majano is a Computer Engineer that has been developing and designing software systems since the year 2000. He was born in in the late 70’s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelors of Science in Computer Engineering at . Luis resides in Houston, Texas with his beautiful wife Veronica, baby girl Alexia and baby boy Lucas!
Luis has a passion for Jesus, tennis, golf, volleyball and anything electronic. Random Author Facts:
He played volleyball in the Salvadorean National Team at the tender age of 17
The Lord of the Rings and The Hobbit is something he reads every 5 years. (Geek!)
His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)
He has a geek love for circuits, microcontrollers and overall embedded systems.
He has of late (during old age) become a fan of organic gardening.
Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!
“Trust in the LORD with all your heart, and do not lean on your own understanding.” Proverbs 3:5
He is the CEO of , a consulting firm specializing in web development, ColdFusion (CFML), Java development and all open source professional services under the ColdBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, MockBox, LogBox and anything “BOX”, and contributes to many open source ColdFusion/Java projects. You can read his blog at
If you are using cbauth as your authenticationService
(the default), you also need to
The security firewall can be configured with rules that can come from many different sources:
Declared inline in your config/Coldbox.cfc
A JSON file
An XML file
From a model object via a method call
From a database
Declared inline in ANY module's ModuleConfig.cfc
When defining your rules source, you ALWAYS have to define the rules
property. You specify an array of rules for inline, or
rules = "(db|json|xml|model)"
if you define your rules externally.
If you have external rules you probably have to specify additional properties as explained in the next pages.
Let's start exploring these sources.
Inline rules will be used by declaring them in your configuration for cbsecurity in the config/ColdBox.cfc.
This is done by making the rules
key an array of rule structures.
By Default, the security module will register itself for you using the module configuration settings you define in theconfig/ColdBox.cfc.
Below you can find all the settings with their default value and description.
The invalidAuthenticationEvent
and invalidAuthorizationEvent
keys can be used to provide default events when Authentication or Authorization failed. The defaultAuthenticationAction and defaultAuthorizationAction determine whether there will be a redirection or override. The default action is redirect
, but especially for API's an override
will be more appropriate. When using rule-based security you can override these keys for any individual rule.
You can place a global validator in the configuration settings, but you can also override the validator on a module by module basis as well. The default validator is using the CBAuth Validator.
cbsecurity ships with the cbauth module that can provide you with a nice interface for authentication services. If you use the default authenticationService
authenticationService@cbauth, you have to define the UserServiceClass in the cbauth module.
However, you can plug in any WireBox ID and select your own authentication services.
If you are using cbauth as your authenticationService
(the default), you also need to configure cbauth.
cbsecurity will also require a user service if you will be dealing with any JWT security tokens. Just add your WireBox ID to the user service of your choice. If you are using cbauth, you have to define the UserServiceClass in the cbauth module.
Please note that by default, the security firewall will be auto-registered for you. If you do NOT want the firewall to be automatically registered for you, then use the autoLoadFirewall
setting and make it false. Then you can use the Custom Firewall approach below to register the firewall manually in the order of the interceptors that you would like.
By default, annotation security is enabled. This will inspect ALL incoming event executions for the security annotations. If you do not want to use annotation security we recommend you turn it off to avoid the inspection of events.
ColdBox security comes with a nice graphical visualizer for all the registered security rules and settings in your global firewall. You can enable it by using the enableSecurityVisualizer setting.
You can then visit the /cbsecurity
URL and you will be presented with this magical tool:
Important The visualizer is disabled by default and if it detects an environment of production, it will disable itself.
Each module can override some settings for cbsecurity according to its needs. You will create a cbsecurity
struct within the module's settings
struct in the ModuleConfig.cfc
The settings you see above are the only ones that module's support as of now.
You can also register multiple instances of the cbsecurity
module using different configurations by just adding them to your app's config or even your module's configuration. This will register a NEW firewall apart from the global security firewall registered using the global settings as defined above.
If you have already a JSON file with your rules, then all you need to do is add the path (relative or absolute) to that file in the rules
configuration key. However, the path MUST include the keyword json
in it.
Then your file can be something like this:
Every module in ColdBox has the capability to contribute their own rules to cbsecurity
by registering them in the ModuleConfig.cfc
within the settings
struct. Just create another struct called cbsecurity
with the following allowed keys:
As you can see each module can have it's own overrides for authentication and authorization events as well as their own rules.
Please note that these security rules will be PREPENDED to the global rules
As with the global rules defined in config/Coldbox.cfc
, the module cbsecurity.rules
setting supports multiple rule sources:
For example, you can load security rules specific to a module from a JSON file stored in your module:
Also note that if modules are loaded dynamically, it will still inspect them and register them if cbsecurity settings are found. The same goes for unloading, the entire security rules for that module will cease to exist.
If you prefer to store your rules your way, then that's perfectly fine. Just make your rules
setting point to model
and then provide us with the object to get the rules from.
Property
Type
Required
Default
Description
rulesModel
string
true
---
The WireBox ID of the object that we will use to retrieve the rules from
rulesModelMethod
string
false
getSecurityRules
The name of the method to call on the object.
2019-OCT-02
Small but big release!
Feature
: cbauth upgraded to version 4
ColdBox security can work with ANY authentication service provider.
You can register ANY authentication provider with cbsecurity by using the authenticationService
setting. The value must be a valid WireBox Id and the object must adhere to the following interface. The authentication services can be used in conjunction with our JWT services and more features coming in the future.
Please note that cbauth already implements this interface and is included with cbsecurity as a dependency.
If you are using cbauth as your authenticationService
(the default), you also need to configure cbauth.
This interface has been provided by convenience and it is not mandatory at runtime (cbsecurity.interfaces.IAuthService
)
You can find the information for cbauth in its own book:
If you are using cbauth as your authenticationService
(the default), you also need to configure cbauth.
As you can see from above, the authentication services all expect a User
object to model your user in the system. So your User
object must also adhere to the following methods modeled by the cbsecurity.interfaces.IAuthUser
interface. This will allow the validators and JWT services to get the appropriate data it needs.
If you will be using cbauth or any of our JWT features, then we will also require you register a user service class that can provide us with the right data to encapsulate security using the userService
setting. We have provided this interface for your usage:
If using cbauth
, you also have to specify the UserServiceClass
key in the cbauth module settings.
Remember that the User Service setting is only required if you will be using JWT token security
If you have already an XML file with your rules, then all you need to do is add the path (relative or absolute) to that file in the rules
configuration key. However, the path MUST include the keyword XML
in it.
Then your xml file can look like this:
We have seen how the module works on the concept of rules and how to declare them. Before we dig deeper and decompose the rules let's have a look at the processing of the rules first.
Each rule is modeled by a struct with keys in it:
The only required key is the secureList
which is what you are trying to secure. The rest are optional and described below. Please note that you can add as many keys as you like to your security rules, which can contain much more context and information for the validators to use for validation.
Please remember that by default the secure and white lists are evaluated as regular expressions. You can turn that off in your configuration settings.
When processing rules, it is important to realize these rules come as an array which will be processed in order, so make sure your more specific rules will be processed before the more generic ones.
As we saw from the overview and our configuration sections. We can declare the default actions for authorizations and authentication issues and to which events/URLs to go if that happens. There can be a time where you can override those global/module settings directly within a rule. Let's explore those overrides:
If you add a redirect
element, then you will be explicitly overriding the global/module setting and if a match is made a redirect will occur.
If you add an overrideEvent
element, then you will be explicitly overriding the global/module setting and an event override will occur.
If you add a action
element, then you will be explicitly overriding the global/module setting and the action will be based on this value (override
or event
)
If a rule has a white list, then it means that you can declare what are the exceptions to ALLOW if the incoming URL/event was matched against the securedList
. This is a great way to say, hey secure all but allow the following events:
Please note: if a rule has a whiteList, it only applies to the current rule. So if the whitelist matches, it the current rule is skipped and the process continues to the next rule.
Sometimes you want to make sure ALL events are secured, except for the ones specified, such as login events. If you add new functionality to your app it is easy to forget a new rule. To prevent unwanted access you could specify a LAST rule, which matches ALL event but NO permission at all. In that case you have to add a whitelist for all events which should still pass, for example:
The security module also allows you to secure your events via annotations instead of using security rules. The setting that controls this security feature is the handlerAnnotationSecurity
which can see in the
The security module has a tiered approach to annotation security as it will check the handler component first and then the requested action method second. You can apply different security contexts to each level as you see fit.
Please note that the security rules will be inspected first, annotations second.
See the diagram below for inspecting security based on annotations:
Secure
AnnotationThe firewall will inspect handlers for a secured
annotation. This annotation can be added to the entire handler or to an action method or both. The default value of the secured
annotation is a Boolean true
. Which means, we need a user to be authenticated in order to access it.
You can also give the annotation a value, which can be anything you like: A list of roles, a role, a list of permissions, metadata, JSON, etc. Whatever it is, this is called the authorization context and the user validator must be able to not only authenticate but authorize the context or an invalid authorization will occur.
The secured value will be passed to the validator's for authorization.
By having the ability to annotate the handler and also the action you create a cascading security model where they need to be able to access the handler first and only then will the action be evaluated for access as well.
_securedURL
The security module has the concept of a secured URL which is the actual URL that got intercepted and relocated because of a security exception. If the module detects an invalid authentication or authorization and an action must be issued, then the firewall will store this URL in the RC
scope and flash it so it can be available in the next request (if a relocation occurs).
The flash RAM variable is called: _securedURL
. This key will be persisted in the flash memory of the framework and when the user gets relocated to the redirect
element, this key will be populated in the request collection automatically for you.
So always remember to use this key if you want to provide a seamless login experience to your users. You can easily place it in the login form:
Property
Type
Description
match
event or URL
Determines if it needs to match the incoming URL or the incoming event. By default it matches the incoming event.
whitelist
varchar
A comma delimited list of events or regex patterns to whitelist or to bypass security on if a match is made on the secureList
securelist
varchar
A comma delimited list of events or regex patterns to secure
roles
varchar
A comma delimited list of roles that can access these secure events
permissions
varchar
A comma delimited list of permissions that can access these secure events
redirect
varchar
An event or route to redirect if the user is not authenticated or authorized
overrideEvent
varchar
The event to override using ColdBox's event.overrideEvent()
if the user if not authenticated or authorized
useSSL
Boolean
If true, force SSL, else use whatever the request protocol is
action
string
The action to use (redirect or override) when no explicit overrideEvent or redirect elements are defined. If not set, then we use the global settings.
This object is used to provide you with human, fluent and explicit security authorizations and contexts.
The cbSecurity
model is a specialized service that will allow you to do explicit authorizations in any layer of your ColdBox application.
There will be times where you will need authorization checks outside of the incoming request rules or the handler annotations. This can be from within interceptors, models, layouts or even views. For this, we have provided the cbSecurity
model so you can do explicit authorization checks anywhere you like.
cbSecurity
ModelYou can inject our model or you can use our handy cbsecure()
mixin (handlers/layouts/views) and then call the appropriate security functions:
All security methods will call the application's configured Authentication Service to retrieve the currently logged in user. If the user is not logged in an immediate NoUserLoggedIn
exception will be thrown by all methods.
You can now discover our sections for securing using cbSecurity
cbSecurity
Method SummaryWhen certain permission context is met, if not throws NotAuthorized
secure( permissions, [message] )
secureAll( permissions, [message] )
secureNone( permissions, [message] )
secureWhen( context, [message] )
guard() alias to secure()
When certain permission context is met, execute the success function/closure, else if a fail
closure is defined, execute that instead.
when( permissions, success, fail )
whenAll( permissions, success, fail )
whenNone( permissions, success, fail )
Verify permissions or user equality
has( permissions ):boolean
all( permissions ):boolean
none( permissions ):boolean
sameUser( user ):boolean
secureView( permissions, successView, failView )
The security firewall will announce some interception events when invalid access or authorizations occur within the system:
cbSecurity_onInvalidAuthentication
cbSecurity_onInvalidAuthorization
You will receive the following data in the interceptData
struct in each interception call:
ip
: The offending IP address
rule
: The security rule intercepted or empty if annotations
settings
: The firewall settings
validatorResults
: The validator results
annotationType
: The annotation type intercepted, handler
or action
or empty if rule driven
processActions
: A Boolean indicator that defaults to true. If you change this to false, then the interceptor won't fire the invalid actions. Usually this means, you manually will do them.
With these interceptions you can build a nice auditing system, login tracking and much more.
The intercept data has a key called processActions
which defaults to true. This Boolean indicator tells the firewall to process the invalid authentication/authorization procedures. If you change this value to false, then the firewall will do NOTHING because it is expecting for YOU to have done the actions.
If you are using our JWT facilities, then we will announce the following interceptions during JWT usage:
cbSecurity_onJWTCreation
cbSecurity_onJWTInvalidation
cbSecurity_onJWTValidAuthentication
cbSecurity_onJWTInvalidUser
cbSecurity_onJWTInvalidClaims
cbSecurity_onJWTExpiration
cbSecurity_onJWTStorageRejection
cbSecurity_onJWTValidParsing
cbSecurity_onJWTInvalidateAllTokens
Check them all out in our JWT Interceptions Page.
You can always find the latest interception points here:
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
.
If you have your security rules in a database, then cbsecurity can read the rules from the database for you. Just make the rules
key equal to db
and fill out the extra configuration keys shown below:
If you just want to validate if a user has certain permissions or maybe no permissions at all or if a passed user is the same as the logged in user, then you can use the following boolean methods that only do verification.
Please note that you could potentially do these type of methods by leveraging the currently logged in user and it's hasPermission()
method. However, these methods provide abstraction and can easily be mocked!
These are great to have a unified and abstracted way to verifying permissions or if the passed user is the same as the logged in user. Here are some examples:
View Layer
Other Layers:
Please note that we do user equality by calling the getId()
method of the authenticated user and the incoming user. This is part of our IAuthUser
interface requirements.
secure()
MethodsNow that you have access to the model, you can use the following method to verify explicit permissions and authorize access. This method will throw an exception if the user does not validate the incoming permissions context (NotAuthorized
).
The permission
can be an array, string or list of the permissions to validate. The user must have at least one of the permissions specified.
The message
is a custom error message to be used in the message
string of the exception thrown.
You also have two more authorization methods that will verify certain permission conditions for you:
when()
There are also cases where you want to execute a piece of code by determining if the user has access to do so. For example, only a USER_ADMIN
can change people's roles or you want to filter some data for certain users. For this, we have created the when()
method with the following signature:
The permissions
is a permission array or list that will be Or'ed
The success
is a closure/lambda or UDF that will execute if the permissions validate.
The fail
is a closure/lambda or UDF that will execute if the permissions DID not validate, much like an else statement
Both closures/functions takes in a user
which is the currently authenticated user, the called in permissions
and can return anything.
You can also chain the when()
calls if needed, to create beautiful security contexts. So if we go back to our admin examples, we can do something like this:
We have also added the following whenX()
methods to serve your needs when evaluating the permissions:
Property | Type | Required | Default | Description |
| string | true | --- | The dsn to use if the rules are coming from a database |
| string | true | --- | The table where the rules are |
| string | false |
| The custom SQL statement to use to retrieve the rules according to the rulesTable property. If not set, the default of select* from rulesTable will be used. |
| string | false | --- | The column to order the rules by. If not chosen, the interceptor will not order the query, just select it. |
You can also use our handy event.secureView()
method in the request context to pivot between views according to user permissions.
cbSecurity injects the secureView()
method into the request context via the preProcess
interception point.
This will allow you to set the successView
if the user has the permissions or the failView
if they don't.
There are also times where you need to validate custom conditions and block access to certain areas. This way, you can implement your own custom security logic and leverage cbSecurity for blockage. You will accomplish this via the secureWhen()
method:
The context
can be a closure/lambda/udf or a boolean evaluation:
The closure/udf will receive the currently authenticated user as the first argument.
ColdBox security ships with the cbauth validator that knows how to talk to the authentication service and validate authentication and authorization via permissions. All you need to do is use the WireBox ID of CBAuthValidator@cbsecurity
in your validator
setting:
CBAuthValidator is the default validator for ColdBox Security
Just make sure your User object adheres to our interface of IAuthUser.
This feature set is provided by the cbcsrf module.
Since version 2.4.x we have added the cbcsrf
module as a dependency of cbSecurity. Below is how you can use it:
Below are the settings you can use for this module. Remember you must create the cbcsrf
struct in your ColdBox.cfc
under the moduleSettings
structure:
This module will add the following UDF mixins to handlers, interceptors, layouts and views:
csrfToken()
: To generate a token, using the default
or a custom key
csrfVerify()
: Verify a valid token or not
csrf()
: To generate a hidden field (csrf
) with the token
csrfField()
: Generate a random token in a hidden form element and javascript that will refresh the page automatically when the token expires
csrfRotate()
: To wipe and rotate the tokens for the user
Here are the method signatures:
The module also registers the following mapping in WireBox: @cbcsrf
so you can call our service model directly.
By default, the module is configured to rotate all user csrf tokens every 30 minutes. This means that every token that gets created has a maximum life-span of {rotationTimeout}
minutes. If you do NOT want the tokens to EVER expire during the user's logged in session, then use the value of 0
zero.
It is recommended to rotate your keys often, in case your token get's compromised.
We have provided several methods to rotate or clear out all of a user's tokens. If you are using cbAuth
as your module of choice for authentication, then we will listen to logins and logouts and rotate the keys for you if you have enabled the enableAuthTokenRotator
setting.
If you are NOT using cbAuth
then we recommend you leverage the csrfRotate()
mixin or the cbsrf.rotate()
method on the @cbsrf
model and do the manual rotation yourself.
Below is a simple example of manually verifying tokens in your handlers:
We have included an interceptor that if loaded will verify all incoming requests to make sure the token has been passed or it will throw an exception.
The settings for this feature are:
You can also register an array of regular expressions that will be tested against the incoming event and if matched, it will allow the request through with no verification.
The verification process is as follows:
If we are doing an integration test, then skip verification
If the incoming HTTP Method is a get,options or head
skip verification
If the incoming event matches any of the verifyExcludes
setting, then skip verification
If the action is marked with a skipCsrf
annotation, then skip verification
If no rc.csrf
exists and no x-csrf-token
header exists, throw a
TokenNotFoundException
exception
If the token is invalid then throw a TokenMismatchException
exception
Please note that this verifier will check the following locations for the token:
The request collection (rc
) via the cbcsrf
key
The request HTTP header (x-csrf-token
) key
skipCsrf
AnnotationYou can also annotate your event handler actions with a skipCsrf
annotation and the verifier will also skip the verification process for those actions.
/cbcsrf/generate
EndpointThis module also allows you to turn on the generation HTTP endpoint via the enableEndpoint
boolean setting. When turned on the module will register the following route: GET /cbcsrf/generate/:key?
. You can use this endpoint to generate tokens for your users via AJAX or UI only applications. Please note that you can pass an optional /:key
URL parameter that will generate the token for that specific key.
This endpoint should be secured, so we have annotated it with a secured
annotation so if you are using cbSecurity
or cbGuard
this endpoint will only be available to logged in users.
In order to register your own custom security validator just open the config/Coldbox.cfc
and add the validator
key with the value being a WireBox ID that points to your object that will provide the validation.
A security validator object is a simple CFC that implements the following functions
Each validator must return a struct with the following keys:
allow:boolean
A Boolean indicator if authentication or authorization was violated
type:stringOf(authentication|authorization)
A string that indicates the type of violation: authentication or authorization.
Here is a sample validator using permission based security in both rules and annotation context
That's it! Go validate!
In this page you will find a thorough overview of the capabilities of the ColdBox Security module.
For any security system you need to know who is authenticated (authentication) and what (authorization) this user is allowed to do. cbsecurity
is no different, so it provides an:
Authentication system which performs the following functions:
Validates user credentials
Logs them in and out
Tracks their security in sessions or any custom storage
Authorization system which:
validates permissions or roles or both
With the ColdBox security module you will be able to secure all your incoming ColdBox events from execution either through security rules or discrete annotations within your code. You will also be able to leverage our CBSecurity
service model to secure any code context anywhere.
The module wraps itself around the preProcess
interception point (The first execution of a ColdBox request) and will try to validate if the request has been authenticated and authorized to execute. This is done via security rules and/or annotations on the requested handler actions through a CBSecurity Validator
. The job of the validator is to make sure user requests have been authenticated and authorized:
CBAuth Validator: this is the default (and recommended) validator, which makes use of the cbauth module. It provides authentication and permission based security.
CFML Security Validator: Coldbox security has had this validator since version 1, and it will talk to the ColdFusion engine's security methods (cflogin,cflogout
). It provides authentication and roles based security.
JWT Validator: If you want to use Json Web Tokens the JWT Validator provides authorization and authentication by validating incoming access/refresh tokens for RESTFul APIs.
Custom Validator: You can define your own authentication and authorization engines and plug them in to the cbsecurity framework.
How does the interceptor know a user doesn't or does have access? Well, here is where you register a Validator CFC (validator
setting) with the interceptor that implements two validation functions: ruleValidator()
and annotationValidator()
that will allow the module to know if the user is logged in and has the right authorizations to continue with the execution.
You can find an interface for these methods in cbsecurity.interfaces.ISecurityValidator
The validator has two options to determine if the user will be allowed access:
The ruleValidator
() function will evaluate configured security rules
The annotationValidator()
function will look at security annotations in your handler and handler actions.
You can use rules, annotations or even both. Rules are much more flexible, but more complex. Rules will be evaluated before annotations.
The validators' job is to tell back to the firewall if they are allowed access and if they don't, what type of validation they broke: authentication or authorization.
Authentication
is when a user is NOT logged in
Authorization
is when a user does not have the right permissions to access an event/handler or action.
Once the firewall has the results and the user is NOT allowed access, the following will occur:
The request that was blocked will be logged via LogBox with the offending IP and extra metadata
The current requested URL will be flashed as _securedURL
so it can be used in relocations
If using a rule, the rule will be stored in prc
as cbsecurity_matchedRule
The validator results will be stored in prc
as cbsecurity_validatorResults
If the type of invalidation is authentication
the cbSecurity_onInvalidAuthentication
interception will be announced
If the type of invalidation is authorization
the cbSecurity_onInvalidAuthorization
interception will be announced
If the type is authentication
the default action (defaultAuthenticationAction
) for that type will be executed (An override or a relocation) will occur against the setting invalidAuthenticationEvent
which can be an event or a destination URL.
If the type is authorization
the default action (defaultAuthorizationAction
) for that type will be executed (An override or a relocation) invalidAuthorizationEvent
which can be an event or a destination URL.
Your application can be secured with security rules or handler and method annotations. Before making your choice, you should take the following arguments into consideration:
annotations are directly visible in your code, but very static.
annotations can protect events. Rules can protect events and incoming Url's.
rules allow you to change your action (override or redirect) and target on each rule. With annotations you can only use your configured default action and target.
when stored in a file or database, rules can be edited by admins at runtime.
Global Rules can be declared in your config/ColdBox.cfc
in plain CFML or in any module's ModuleConfig.cfc
or they can come from the following global sources:
A json file
An xml file
The database by adding the configuration settings for it
A model by executing a getSecurityRules()
method from it
A rule is a struct that can be composed of the following elements. All of them are optional except the secureList
.
Rules can be declared globally in your config/ColdBox.cfc
or they can also be place in any custom module in your application:
The firewall will inspect handlers for the secured
annotation. This annotation can be added to the entire handler or to an action or both. The default value of the secured
annotation is a Boolean true
. Which means, we need a user to be authenticated in order to access it.
You can also give the annotation some value, which can be anything you like: A list of roles, a role, a list of permissions, metadata, etc. Whatever it is, this is the authorization context and the user validator must be able to not only authenticate but authorize the context or an invalid authorization will occur.
By having the ability to annotate the handler and also the action you create a cascading security model where they need to be able to access the handler first and only then will the action be evaluated for access as well.
As we mentioned at the beginning of this overview, the security module will use a Validator object in order to determine if the user has authentication/authorization or not. This setting is the validator
setting and will point to the WireBox ID that implements the following methods: ruleValidator() and annotationValidator().
Each validator must return a struct
with the following keys:
allow:boolean
A Boolean indicator if authentication or authorization was violated
type:stringOf(authentication|authorization)
A string that indicates the type of violation: authentication or authorization.
messages:string
Info or debugging messages
ColdBox security ships with the CBAuthValidator@cbsecurity
which is the default validator in the configuration setting validator
setting.
When using the default CBAuthValidator@cbsecurity
you also have to configure the cbauth module.
ColdBox security ships also with a CFML authentication and authorization validator called CFSecurity
which has the following WireBox ID: CFValidator@cbsecurity
and can be found at cbsecurity.models.CFSecurity
You basically use cfloginuser
to log in a user and set their appropriate roles in the system. The module can then match to these roles via the security rules you have created.
The second method of authentication is based on your custom security logic. You will be able to register a validation object with the module. Once a rule is matched, the module will call your validation object, send in the rule/annotation value and ask if the user can access it or not. It will be up to your logic to determine if the rule is satisfied or not. Below is a sample permission based security validator:
The security module can distinguish between authentication issues and authorization issues. Once these actions are identified, the security module can act upon the result of these actions. These actions are based on the following 4 settings, but they all come down to two outcomes:
a relocation to another event or URL
an event override
When invalid authentication or authorizations occur the interceptor will announce the following events:
cbSecurity_onInvalidAuthentication
cbSecurity_onInvalidAuthorization
You will receive the following data in the interceptData
struct:
ip
: The offending IP address
rule
: The security rule intercepted or empty if annotations
settings
: The firewall settings
validatorResults
: The validator results
annotationType
: The annotation type intercepted, handler
or action
or empty if rule driven
processActions
: A Boolean indicator that defaults to true. If you change this to false, then the interceptor won't fire the invalid actions. Usually this means, you manually will do them.
You can use these security listeners to do auditing, logging, or even override the result of the operation.
There are many more interception points available to you, check them out in our Interceptions page.
The CBSecurity
model was introduced in version 2.3.0 and it provides you with a way to provide authorization checks and contexts anywhere you like: handlers, layouts, views, interceptors and even models.
Getting access to the model is easy via our cbSecure()
mixin (handlers/layouts/views/interceptors) or injecting it via WireBox:
Once injected you can leverage it using our awesome methods listed below:
When certain permission context is met, if not throws NotAuthorized
secure( permissions, [message] )
secureAll( permissions, [message] )
secureNone( permissions, [message] )
secureWhen( context, [message] )
When certain permission context is met, execute the success function/closure, else if a fail
closure is defined, execute that instead.
when( permissions, success, fail )
whenAll( permissions, success, fail )
whenNone( permissions, success, fail )
Verify permissions or user equality
has( permissions ):boolean
all( permissions ):boolean
none( permissions ):boolean
sameUser( user ):boolean
secureView( permissions, successView, failView )
This module also ships with a security visualizer that will document all your security rules and your settings in a nice panel. In order to activate it you must add the enableSecurityVisualizer
setting to your config and mark it as true
. Once enabled you can navigate to: /cbsecurity
and you will be presented with the visualizer.
Important The visualizer is disabled by default and if it detects an environment of production, it will disable itself.
ColdBox Security offers a comprehensive feature set for RESTFul applications that require JSON web tokens. We offer both access and refresh token capabilities. Check out our JWT Section for an in-depth overview.
ColdBox security has had this security validator since version 1, in which it will talk to the ColdFusion engine's security methods to authenticate and authorize users. With it you will be able to authenticate users and also do role base authorization.
All you need to do is use the WireBox ID of CFValidator@cbsecurity
in your validator
setting:
The default value is of CFValidator@cbsecurity
which is the WireBox ID for the object.
The code for this validator can be found at cbsecurity.models.CFValidator
For more information about cflogin, cfloginuser and cflogout
, please visit the docs http://cfdocs.org/security-functions
Now that we have all the pieces in place for JWT, we can now register the JWT validator as our validator of choice for requests which in our case it is the same JWT Service that will take care of the validation: JWTService@cbsecurity
.
The validator will inspect the incoming requests for valid jwt authorization headers. It will be in charge of verifying their expiration, their required claims, and the user it represents. Once that is done, it goes in the same rule/annotation security flow that cbsecurity leverages.
Each module can also override their validator via it's configuration setting cbsecurity.validator
. So if the global validator is something other than jwt but your module REQUIRES JWT validation, then just add it in your ModuleConfig.cfc
The JWT validator will discover the incoming JWT token from 3 sources:
authorization
header using the bearer token approach
Custom header configured in your settings: cbsecurity.customAuthHeader
Incoming rc
variable with the same name as cbsecurity.customAuthHeader
If your rules have the permissions
element or your secure
annotations have context, then we will treat those as the scopes/permissions to check the user/token must have at validation.
The validator will have the following validation process:
Verify the jwt token exists via the authorization
header or custom header x-auth-token
or incoming rc[ 'x-auth-token' ]
Verify we can decode it
Verify if it has not expired from the token itself
Verify it has the required claims
If token storage is enabled, verify the token in the permanent storage
Verify the subject (sub
) claim and try to retrieve the user it represents
Try to authenticate the user for the request
Verify the subject has the right permissions or the token has the right scopes attached to it.
If all is valid then place the token in prc.jwt_token
and the payload in prc.jwt_payload
If all is valid then place the user object in prc.oCurrentUser
or the variable of your choice via the cbsecurity.prcUserVariable
setting.
Continue or block
That's it! You can create your rules and annotations just like your used to, but now the validator will make sure valid JWT tokens are passed for those requests.
You can enable token storage in cbsecurity via the tokenStorage
setting. By default it is enabled and leverages CacheBox's default
cache using a key prefix of cbjwt_
+ the token's unique identifier claim of jti
.
We recommend that you create a separate provider for the cache.
The storage of keys are great in order to visualize in your application all the registered keys in the system. You can also invalidate keys, as by default if the token does not exist in the storage, it is considered invalid.
You can retrieve the token storage by injection or the helper method:
We ship with two drivers:
cachebox
: Leverages any cache registered in CacheBox
db
: Leverages a database table to store the keys
cacheName
: The cache to use
table
: The table to use for storage
schema
: A schema to use if the database supports it, else empty
dns
: The datasource to use, defaults to the one set in Application.cfc
autoCreate:true
: Autocreate the table if not found
rotationDays:7
: How many days should the expiration be before removal
rotationFrequency:60
: How many minutes should pass before issuing a rotation check
The columns it will create are:
id
- identifier
cacheKey
- The unique cacke key, indexed
token
- The encrypted token
expiration
- The expiration
issued
- The issue date
subject
- The subject identifier
If you would like to create your own token storage, just add your own WireBox ID to the driver
, properties
and implement the following interface: cbsecurity.interfaces.jwt.IJwtStorage
ColdBox Security supports the concept of refresh tokens alongside the normal JWT access tokens. Let's start exploring this feature in detail.
A refresh token is a credential artifact that lets a client application get new access tokens without having to ask the user to log in again. Access tokens may be valid for a short amount of time. Once they expire, client applications can use a refresh token to "refresh" the access token.
The client application can get a new access token as long as the refresh token is valid and unexpired. Consequently, a refresh token that has a very long lifespan could theoretically give infinite power to the token bearer to get a new access token to access protected resources anytime. The bearer of the refresh token could be a legitimate user or a malicious user.
In the jwt
section of the cbsecurity
configuration you will have the following settings dealing with refresh tokens (Please note that the other jwt configurations are also mandatory)
This setting is used to turn on the refresh capabilities of the JWT Service. If this remains false, then exceptions will be thrown when trying to use refresh capabilities.
The default time refresh tokens expire in. The default is 7 days or 10080 minutes
The header to inspect for refresh tokens for automatic refreshment or our refresh endpoints. The default is x-refresh-token
If you enable the auto refresh validator setting, then cbsecurity will try to auto-refresh expired access tokens via the Validator security events. These events fire when a rule is detected or a secured annotation is detected.
If enabled, the REST cbsecurity/refreshToken
endpoint will be available for the application, so users can refresh their tokens.
You must enable the setting (enableRefreshTokens
) in order for the following methods to return a struct
of tokens. If not, only the access token will be returned as a string
.
attempt( username, password ):struct
fromUser( user, customClaims ):struct
The returned struct will contain the access and refresh tokens:
This is the same procedure for creating access tokens, but now you get a struct of tokens instead of a single access token.
You can refresh tokens manually by using the refreshToken( token, customClaims )
method on the JwtService
object. You can pass a valid refresh token to be used for refreshment or pass none and the token will be inspected from the headers or incoming rc using the x-refresh-token
value or whatever you setup as your customRefreshHeader
setting.
Here is the signature for the refresh method:
customClaims
where added in v2.15.0
Important:
Please note that the currently used refresh token will be invalidated and rotated for you automatically. This is a security feature called refresh token rotation, where the refreshed token is automatically rotated for you upon refresh usage.
If you have enabled the refresh token setting (enableRefreshEndpoint
) then you will also have access to the POST > /cbsecurity/refreshtoken
API endpoint.
This endpoint is used for applications to refresh their access tokens using the refresh token received when authenticating in the application.
This endpoint must be executed with a POST
and you will need to pass in your refresh token via the following header: x-refresh-token
or rc form variable of the same name. If valid, the response data
will contain two new tokens for you:
Important:
Please note that the currently used refresh token will be invalidated and rotated for you automatically. This is a security feature called refresh token rotation, where the refreshed token is automatically rotated for you upon refresh usage.
CBSecurity by default provides you with refresh token rotation every time you want to refresh your access token. This guarantees that every time an application exchanges a refresh token for an access token, a NEW refresh token is returned as well. The old refresh token is invalidated and can no longer be used.
Therefore, you no longer have a long-lived refresh token that could provide illegitimate access to resources if it ever becomes compromised or leaked. The threat of unauthorized access is reduced as refresh tokens are continually exchanged and invalidated.
CBSecurity has the ability to refresh access tokens automatically for you when calling any secure resource that is protected by the JWT Validator. All you have to do is send in both tokens via the appropriate headers and enable the autoRefreshValidator
setting:
access token
bearer token or
x-auth-token
refresh token
x-refresh-token
If the access token has expired or is invalid or missing and the x-refresh-token
was passed and is valid, then the access token will be re-generated, the refresh token will be rotated, the request will continue as normal and two new response headers will be sent back to the calling application.
x-auth-token
: refreshed access token
x-refresh-token
: new refresh token
The calling application can monitor if those two response headers are sent and save them appropriately.
The JWT Services will announce some key events for you to listen to
cbSecurity_onJWTCreation
- Whenever a new token is generated for a user
cbSecurity_onJWTInvalidation
- Whenever an invalidation occurs for a token
cbSecurity_onJWTValidAuthentication
- Whenever a valid JWT token is parsed, tested and authenticated with the authentication services
cbSecurity_onJWTInvalidUser
- When trying to find the token's subject and the user service returns null or not a valid user
cbSecurity_onJWTInvalidClaims
- When the parsed token does not adhere to the required claims
cbSecurity_onJWTExpiration
- When the parsed token has expired
cbSecurity_onJWTStorageRejection
- When the parsed token is valid but cannot be found in the permanent storage
cbSecurity_onJWTValidParsing
- When the parsed token has passed all validation procedures but has NOT been authenticated yet.
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
This event has the following data in the interceptData
struct
CBSecurity also provides you with a JWT (Json Web Tokens) authentication and authorization system.
JSON Web Token (JWT) is an open standard () that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.
JSON Web Tokens have become the standard for authenticating and authorizing API requests. They can be used on their own or with an oauth/single sign-on server as well.
Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.
Information Exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.
The ColdBox Security module will assist you with all the generation, decoding, encoding and security aspects of JWT. All you need to do is, configure it, create a few standard files and off you go.
The tokens created by the JWT services will have the mandatory headers, but also will have a standardizes payload structure. This payload structure can also be customized as you see fit.
A JSON Web Token encodes a series of claims in a JSON object. Some of these claims have specific meaning, while others are left to be interpreted by the users. You can consider claims to be the keys of the payload structure and it can contain, well, pretty much anything you like.
Here are the base claims that the ColdBox Security JWT token creates for you automatically:
Issuer (iss
) - The issuer of the token (defaults to the application's base URL)
Issued At (iat
) - When the token was issued (unix timestamp)
Subject (sub
) - This holds the identifier for the token (defaults to user id)
Expiration time (exp
) - The token expiry date (unix timestamp)
Unique ID (jti
) - A unique identifier for the token (md5 of the sub and iat claims)
Scopes (scope)
- A space delimited string of scopes attached to the token
Refresh Token (cbsecurity_refresh
) - If you are using refresh tokens, this custom claim will be added to the payload.
You can add much more to this payload via the JWT service methods or via the User that models the token.
The service can be found here cbsecurity.models.JWTService
and can be retrieved by either injecting the service (JwtService@cbsecurity
) or using our helper method (jwtAuth()
).
In order to begin exploring the JWT capabilities, let's explore how to configure it first.
Our JWT services have several configuration settings, let's explore them:
The default variable name in the prc
scope that will be used to store an authenticated user object if the JWT request is valid. The default is prc.oCurrentUser
The issuer authority for the tokens, placed in the iss
claim of the token. If empty, we will use the event.buildLink()
to create the issuer. By default, our validators also check that tokens are created by the same issuer.
The secret key is used to sign the JWT tokens. By default it will try to load an environment variable called JWT_SECRET
, if that setting is also empty, then we will auto-generate a secret token that will last as long as the ColdFusion application scope lasts. So technically, your secret will rotate only if a secret is not specified.
Also, this key is ignored in modules. To specify a fixed key to be used in your modules, you will have to configure it by adding a cbsecurity key settings in the moduleSettings structure within the config/Coldbox.cfc.
Your secret key will auto-rotate every application scope rotation. Please note that all tokens used after that scope rotation will automatically become invalid.
By default, our jwt services will look into the authorization
header for a bearer token. However, it can also look in a custom header by this name, which defaults to x-auth-token
. Finally, if not found, it will also look into the rc
scope for a rc[ 'x-auth-token' ]
as well.
The default expiration in minutes for the JWT tokens. Defaults to 60 minutes
The encryption algorithm to use for the tokens. The default is HS512, but the available ones for are:
HS256
HS384
HS512
RS256
RS384
RS512
ES256
ES384
ES512
In the case of the RS
and ES
algorithms, asymmetric keys are expected to be provided in unencrypted PEM or JWK format (in the latter case first deserialize the JWK to a CFML struct). When using PEM, private keys need to be encoded in PKCS#8 format.
If your private key is not currently in this format, conversion should be straightforward:
When decoding tokens, either a public key or certificate can be provided. (If a certificate is provided, the public key will be extracted from it.)
This is an array of claim names that each token MUST have in order to be authenticated. If a token comes in but does not have these claims in the payload structure, it will be deemed invalid.
By default, our JWT services will store tokens in CacheBox for you in order to be able to invalidate them. We ship with two providers for token storage: db and cachebox.
By default the token storage is enabled.
The key prefix to use when storing the keys in the permanent storage. Defaults to cbjwt_
The driver to use. Can be either db or cachebox or your own WireBox Id for using a custom storage.
A struct of properties to configure each storage with.
The next step is to make sure that our JWT services can handle the construction of the JWT tokens as per YOUR requirements. So your User
object must implement our JWTSubject
interface with the following functions:
Basically, it's two functions:
getJwtCustomClaims( payload )
- This is a struct of custom claims to incorporate into the token payload at construction time. This can be ANYTHING you like.
getJwtScopes()
- We will also call this at construction time in order to incorporate the right permission scopes into the token according to your user. This must be an array of scopes/permissions.
Since also the authentication services will be used with JWT, your user object might end up looking like this:
Ok, now we can focus on all the wonderful methods the JWT service offers:
attempt( username, password, [ customClaims:struct ] ):token
- Attempt to authenticate a user with the authentication service and if successful, return the token using the identifier and custom claims. Exception if invalid authentication
fromUser( user, [ customClaims:struct ] ):token
- Generate a token according to the passed user object and custom claims.
encode( struct payload ):token
- Generate a raw jwt token from a native payload struct.
verify( required token ):boolean
- Verify a token string or throws exception
decode( required token ):struct
- Decode and retrieve the passed in token to CFML struct
parseToken( token, storeInContext, authenticate ):struct
- Get the decoded token using the headers strategy and store it in the prc.jwt_token
and the decoded data as prc.jwt_payload
if it verifies correctly. Throws: TokenExpiredException
if the token is expired, TokenInvalidException
if the token doesn't verify decoding, TokenNotFoundException
if not found
getToken():string
- Get the stored token from prc.jwt_token
, if it doesn't exist, it tries to parse it via parseToken()
, if not token is set this will be an empty string.
getPayload():struct
- Get the stored token from prc.jwt_payload
, if it doesn't exist, it tries to parse it via parseToken()
, if not token is set this will be an empty struct.
setToken( token ):JWTService
- Store the token in prc.jwt_token
, and store the decoded version in prc.jwt_payload
authenticate( [payload] ):User
- Authenticates a passed or detected token payload and return the user it represents
getuser()
- Get the authenticated user according to the access token detected
logout()
- Logout a user and invalidate their token
invalidateAll( async:false )
- Invalidate all access and refresh tokens in permanent storage
invalidate( token )
- Invalidates the incoming token by removing it from the permanent storage.
isTokenInStorage( token )
- Checks if the passed token exists in permanent storage.
getTokenStorage( force:false )
- Get the current token storage implementation. You can also force create it again if needed.
attempt( username, password, [ customClaims:struct ] ):struct
- Attempt to authenticate a user with the authentication service and if successful, return a struct containing an access and refresh token.
fromUser( user, [ customClaims:struct ] ):struct
- Generate a struct of refresh and access token according to the passed user object and custom claims.
That's it, we are ready to put it all together. Now cbsecurity knows about your authentication/user services, can talk to your user to create tokens and can guard the incoming requests via the JWT Validator. Here is a sample controller for login, logout and user registration:
Let's configure some routes first:
Then build out the Auth
controller
Make sure you add validation!
In order to implement JWT authentication in your application, you may need to modify some web server settings. Most web servers have default content length restrictions on the size of an individual header. If your web server platform has such default enabled, you will need to increase the buffer size to accommodate the presence of JTW tokens in both the request and response headers. The size of a JWT token header, encrypted via the default cbSecurity HMAC512 algorithm, is around 44 kilobytes. As such you will need to allow for at least that size. Below are some examples for common web server configurations
The following configuration may be applied to the main NGINX http
configuration block to allow for the presence of tokens in both the request and response headers:
You will need to modify two registry keys:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters\MaxFieldLength
- Sets an upper limit, in bytes, for each header. The default value is 65534 bytes and the maximum value is 65534 bytes ( 64kb )
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters\MaxRequestBytes
- Sets the upper limit for the request line and the headers, combined. As such 128K should allow for both long URLs, as well as JWT tokens in the headers. The default value is 16384 bytes and the maximum value is 16777216 bytes ( 16 MB )
You will need to add a LimitRequestFieldSize
setting in each <VirtualHost...>
entry in order increase the default header size from the default 8 kilobytes. Example, with a setting of 128 kilobytes:
If you have enabled auto refresh tokens, check out the .
You can find much more information about JWT at .
The WireBox Id of the service to provide our authentication. cbauth is our default provider, but you can use any authentication service that .
The WireBox Id of the service to provide our user retrieval and validation functions. You can use any service that .
Please note that we use the jwt-cfml library for encoding/decoding tokens. Please in order to leverage RS and ES algorithms with certificates.
Refresh tokens have several configuration items, check them out in our .
Please note that the JWT validators must talk to the authentication and user services. Please refer to the page to configure and create them.
That's it, we now can login a user, give them a token, register a new user and give them their token, and also log them out. The next step is for you to build your rules and/or security annotations and make sure the is configured for your global app or module.
Setting
Default
Description
invalidAuthenticationEvent
---
The global invalid authentication event or URI or URL to go if an invalid authentication occurs
defaultAuthenticationAction
redirect
Default Authentication Action: override or redirect when a user has not logged in
invalidAuthorizationEvent
---
The global invalid authorization event or URI or URL to go if an invalid authorization occurs
defaultAuthorizationAction
redirect
Default Authorization Action: override or redirect when a user does not have enough permissions to access something
A container for user authentication and login code. The body of the tag runs only if the user is not logged in. When using application-based security, you place code in the body of the cflogin tag to check the user-provided ID and password against a data source, LDAP directory, or other repository of login identification. The body of the tag includes a cfloginuser tag (or a ColdFusion page that contains a cfloginuser tag) to establish the authenticated user's identity in ColdFusion.
Identifies (logs in) a user to ColdFusion. Specifies the user's ID, password, and roles. This tag is typically used inside a cflogin tag. The cfloginuser tag requires three attributes, name, password, and roles, and does not have a body. The roles attribute is a comma-delimited list of role identifiers to which the logged-in user belongs. All spaces in the list are treated as part of the role names, so you should not follow commas with spaces.While the user is logged-in to ColdFusion, security functions access the user ID and role information.
Logs out the current user. Removes knowledge of the user ID and roles from the server. If you do not use this tag, the user is automatically logged out as described in Logging out users in Using ColdFusion security tags and functions.The cflogout tag does not take any attributes, and does not have a body.
Authenticates a user name and password against the NT domain on which ColdFusion server is running, and optionally retrieves the user's groups.
If you include a roles attribute, the function executes only when there is a logged-in user who belongs to one of the specified roles.
Returns True if the current user is a member of the specified role.
Returns the ID of the currently logged-in user.This tag first checks for a login made with cfloginuser tag. If none exists, it checks for a web server login (cgi.remote_user.
Key | Description |
| The JWT token |
| The payload that was used to create it |
| The user it belongs to |
Key | Description |
| The JWT token that was invalidated |
Key | Description |
| The JWT token that was parsed |
| The payload that was decoded |
| The authenticated user |
Key | Description |
| The JWT token that was parsed |
| The JWT payload that was parsed |
Key | Description |
| The JWT token that was parsed |
| The JWT payload that was parsed |
Key | Description |
| The JWT token that was parsed |
| The JWT payload that was parsed |
Key | Description |
| The JWT token that was parsed |
| The JWT payload that was parsed |
Key | Description |
| The JWT token that was parsed |
| The JWT payload that was parsed |