Skip to content

Commit

Permalink
#172 Fix 'Page Expired' errors keep occurring after 30 minutes due to…
Browse files Browse the repository at this point in the history
… default timeout by CBCSRF module
  • Loading branch information
grantcopley committed Jan 3, 2025
1 parent be3105e commit 2fa5a70
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 6 deletions.
6 changes: 5 additions & 1 deletion ModuleConfig.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ component {
/**
* Trims string properties if set to true
*/
"trimStringValues" : false
"trimStringValues" : false,
/**
* The wirebox mapping to use for the CSRF storage.
*/
"csrfStorage": "SessionStorage@cbstorages"
};

routes = [
Expand Down
19 changes: 15 additions & 4 deletions models/CBWIREController.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ component singleton {
// Injected RequestService so that we can access the current ColdBox RequestContext.
property name="requestService" inject="coldbox:requestService";

// Inject CBCSRF for CSRF token generation and verification
property name="cbcsrf" inject="provider:@cbcsrf";
// Inject CSRF token generation and verification
property name="csrf" inject="CSRF@cbwire";

// Inject module settings
property name="moduleSettings" inject="coldbox:modulesettings:cbwire";
Expand Down Expand Up @@ -66,7 +66,8 @@ component singleton {
// Set the CSRF token for the request
local.csrfToken = local.payload._token;
// Validate the CSRF token
local.csrfTokenVerified = variables.wirebox.getInstance( dsl="@cbcsrf" ).verify( local.csrfToken );
local.csrfTokenVerified = verifyCSRFToken( local.csrfToken );

// Check the CSRF token, throw 403 if invalid
if( !local.csrfTokenVerified ){
throw( type="CBWIREException", message="Page expired." );
Expand Down Expand Up @@ -424,7 +425,17 @@ component singleton {
*/
function generateCSRFToken() {
// Generate the CSRF token using the cbcsrf library
return variables.cbcsrf.generate();
return variables.csrf.generate();
}
/**
* Verifies a CSRF token.
*
* @token string | The CSRF token to verify.
*
* @return boolean
*/
function verifyCSRFToken( token ) {
return variables.csrf.verify( token );
}

/**
Expand Down
39 changes: 39 additions & 0 deletions models/CSRF.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
component singleton {

property name="wirebox" inject="wirebox";
property name="settings" inject="coldbox:moduleSettings:cbwire";

/**
* Generate a CSRF token.
*
* @return string
*/
function generate() {
var csrf = hash( createUUID(), "SHA-256" );
getCSRFStorage().set( "CBWIRE_CSRF", csrf );
return csrf;
}

/**
* Verify a CSRF token.
*
* @token string
*
* @return boolean
*/
function verify( token ) {
if ( !getCSRFStorage().exists( "CBWIRE_CSRF" ) ) {
throw( type="CSRF Expired", message="Page expired" );
}
return true;
}

/**
* Get the CSRF storage object.
*
* @return any
*/
function getCSRFStorage() {
return wirebox.getInstance( settings.csrfStorage );
}
}
9 changes: 8 additions & 1 deletion test-harness/tests/specs/CBWIRESpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,11 @@ component extends="coldbox.system.testing.BaseTestCase" {
// and prepareMock() is a custom method to mock any dependencies, if necessary.
setup();
cbwireController = getInstance("CBWIREController@cbwire");
event = getRequestContext();
prepareMock( cbwireController );
event = getRequestContext();
csrf = prepareMock( getInstance( "CSRF@cbwire" ) );
csrf.$( "verify", true );
cbwireController.$property( "csrf", "variables", csrf );
});

it( "should trim string values if global setting enabled on coldbox.cfc", () => {
Expand Down Expand Up @@ -572,6 +575,8 @@ component extends="coldbox.system.testing.BaseTestCase" {
} );

it( "should throw a 419 Page Expired error if the CSRF token doesn't match", function() {
csrf.$( "verify", false );

var payload = incomingRequest(
memo = {
"name": "TestComponent",
Expand All @@ -586,6 +591,8 @@ component extends="coldbox.system.testing.BaseTestCase" {
expect( function() {
cbwireController.handleRequest( payload, event );
} ).toThrow( type="CBWIREException", message="Page expired." );

csrf.$( "verify", true );
} );

it( "should provide a handleRequest() method that returns subsequent payloads", function() {
Expand Down

0 comments on commit 2fa5a70

Please sign in to comment.