Coldbox and VueJS untangled

Author: Wil (Page 2 of 4)

Customize your resource routing in Coldbox

In this post I will show you how coldbox can help you creating resourceful routes, how cbswagger shows me that I don’t want the defaults resource() routing method, and how easy it is to create your own method!

If you want to create a REST API in coldbox, you often need to create a lot of routes for this API. So let’s say you want to create endpoints to list, view, update, create and delete a User resource. Following the coldbox manual, I need to implement the following routes:

Continue reading

Configuring SES URL’s on apache and the Coldbox router.

Often when we deploy a coldbox website we fire up commandbox, create a webserver and put NGINX in front to route our application requests to the correct lucee instance. We never experienced any problems when configuring search engine safe (SES) url’s. Just to remind you: instead of writing this

https://mysite.nl/index.cfm?event=myhandler.myaction

we can rewrite this to something more friendly such as

https://mysite4u.nl/myhandler/myaction

So we proxy our request via Nginx to a lucee coldbox application and use some rewrite rules as described in the coldbox manual. Recently we had a slightly different configuration: apache in front of an old-fashioned standard lucee standard install on Linux. Again, we followed instructions in the same coldbox manual but NO success…

Continue reading

Event.buildLink: query params vs path variables

I had some issues with how event.buildlink() in Coldbox is generating a URL. To understand what’s my problem let me introduce the old-fashioned way to hit a coldbox application

http://mysite.ext/index.cfm?event=user&age=30
or
http://mysite.ext/index.cfm?event=user.index&age=30

With some rewrite magic this last form can be written as

http://mysite.ext/user/index&age=30
or even
http://mysite.ext/user/index/age/30

So all four forms are behaving the same, if you apply the correct rewrite rules in your webserver and the default rules in the Coldbox router. The coldbox manual has some info on rewrites for several webservers. So in most cases the web is rewriting your url’s in such a format that coldbox will receive this:

http://mysite.ext/index.cfm?user/index/age/30

Coldbox itself is smart enough to hand this over to the router, which has some default rules enabled which translate the user/index/age/30 to:

  • event = user.index
  • age=30
Continue reading

Cfcookie or cookieStorage?

Coldbox has the cbstorages module which can be used as an API for accessing persistent storage such as cookie, session, application, cache and more. The question is: why should I use such storage if there is cfcookie or something simple as as session struct?

Let me explain with some code. I was working on updating the cbi18n module where we can use session, client, cookie or request scope to store the currently selected locale. In code this is getting ugly soon, with code like this

switch(instance.localeStorage){
  case "session" : { storage = session; break; }
  case "client"  : { storage = client; break;  }
  case "cookie"  : { storage = cookie; break;  }
  case "request" : { storage = request; break; }
}

and in other places in the code:

<!--- Storage of the Locale in the user storage --->
<cfif instance.localeStorage eq "session">
  <cfset session.DefaultLocale = arguments.locale>
<cfelseif instance.localeStorage eq "client">
  <cfset client.DefaultLocale = arguments.locale>
<cfelseif instance.localeStorage eq "request">
  <cfset request.DefaultLocale = arguments.locale>
<cfelse>
  <cfcookie name="DefaultLocale" value="#arguments.locale#" />
</cfif>
Continue reading

Using bCrypt in cbsecurity

In a previous post I explained why bCrypt is a good choice for hashing your passwords. In this post I will show were you can hash and check your passwords: in your handlers, in a service layer or in some entity model. When using cbsecurity I will show you why it fits best in your service layer or entity model.

But let’s start with some hashing and checking in a handler. Make sure bcrypt is installed by using commandbox and execute the command:

box install bcrypt

Let’s say you want to store your password in a db table. Bcrypt has two important methods for hashing and checking called hashPassword() and checkPassword() but you have to call them on a bcrypt instance, so you can inject bcrypt in a handler:

property name="bCrypt" inject="BCrypt@BCrypt";

or getting your instance directly by calling getInstance("BCrypt@BCrypt"). But you don’t have to do this, by installing the module some mixin helpers are created so you can just call bCryptHash() or bCryptCheck(). These handy functions will be available in all handlers, views, layouts or even interceptors. So let’s say we want to save a user in some handler it will look like this:

Continue reading

cbOrm: populating new objects

In the past I’ve been using cborm a lot, since it makes handling coldfusion (hibernate) ORM so much easier. But lucee support for ORM was less than optimal in a multi-datasource environment, so I decided to rewrite this application more or less according to the fluent API approach as demonstrated by Gavin Pickin at ITB 2020. In this coding style I have two quite efficient ways of populating a new object:

property name="UserService" inject;

//populate
var user = populateModel(
  model=UserService.new(), 
  memento=myUserData 
);

//vs a shorter method
var user = UserService.new( myUserData );

Both should return the same populated user object, but the second one does the population within the new() method, so I got used to using this handy method.

Continue reading

Arguments in arguments…

I have to admit. This is not the most useful post I ever wrote, but today I discovered something funny but interesting when I tried to fix some small bug. I was working with the bcrypt module. If you don’t know what this module is doing: it is a very secure way for hashing passwords, and since checking the validity of your password is relatively slow it is quite useful to prevent password cracking. Before diving into bugfixing let’s see what bcrypt is doing. It is a coldbox module and only has a few relevant functions:

  • hashPassword(password, [ workfactor], [salt]) which generates a password hash based on a salt and a workfactor. If you don’t supply a workFactor or salt, the coldbox module will generate a salt with a default workfactor of 12. The higher the workfactor, the longer it takes to check for a valid password. (on my system a workfactor means it takes 200 milliseconds to check for a valid password.
  • checkPassword( candidate, bCryptHash) will check a password candidate agains a (stored) bcryptHash.
  • generateSalt( workFactor ) will generate a salt, based on a workFactor. Increasing a workfactor by 1 will mean it takes double the time to check your bCryptHash. This way you can prevent password attacks, because generating and checking is relatively slow.
Continue reading

Protecting your passwords with bCrypt.

We all know. We should never ever store a plaintext password in a database. If a hacker gains access to your data you will be in serious trouble. There are many ways to protect your data, but at least you should make sure your passwords are not readable. In the past we did this by some simple hashing, but modern computers are so fast it is easy to do some password cracking. In time it even gets easier because processors are becoming faster and faster. Another disadvantage: simple hashing will reveal some records with the same passwords. These are often the easiest to guess or crack by brute force. So we need something better.

Coldbox has a nice little module called bcrypt, which is just a wrapper to the jBcrypt java library which is a Java™ implementation of OpenBSD’s Blowfish password hashing code. Wikipedia has a nice description of the bcrypt password hashing algoritme. Bcrypt hash some strong points:

  • generating a hash for the same string will always return different results
  • comparing a password candidate with the stored hash is relatively slow, which makes brute force attacks harder.
  • the hash can be generated with different workfactors. The higher the workfactor, the more time it takes to compare your hash with a password candidate. By increasing the workfactor in time you can account for faster processors, so brute-force attacks remain unattractive.
Continue reading

CbSecurity and JWT: when are you authenticated?

Some days ago I was polishing my login procedure for my shiny new JWT cbsecurity. When my users are providing a valid username and password I wanted to update their lastLoginDate property, so I can see from my user list when they used the system for the last time. This doesn’t sound to complicated, so I created this code for my login procedure:

try {
  // authenticate yourself
  var myToken = jwtAuth().attempt( rc.username, rc.password );
  // get the current user and update login date			 
  jwtAuth().getUser().setDateLastLogin(now()).save();
  prc.response
    .setStatusCode( STATUS.CREATED )
    .setData({ 'apiKey' = myToken })
}
catch (InvalidCredentials e) {
  prc.response
    .setStatusCode( STATUS.NOT_AUTHENTICATED )
    .setStatusText( "Invalid username or password" )
    .addMessage( "Invalid username or password" );
}

So what’s happening here? I try to get a token by using the jwtAuth().attempt() method. The method succeeds, so I get a token which I have to return to my user. Because of this success I want to update my user’s lastLogin date. Jwt has some handy method to retrieve the current user, so I called
jwtAuth().getUser(), update the DateLastLogin property and save the user. Unfortunately this fails: “General application error: Token not found in authorization header or the custom header or the request collection”. So although I just received a valid token, the system doesn’t know about the current user yet.

Is this a bug? It depends how you look at it. cbSecurity with jwt will assume you can be validated if

  • you provide a valid bearer authentication header, ie :
Authorization : Bearer hereComes.your.Jwt
  • or you provide a valid other header as defined in cbsecurity.customAuthHeader setting. The default is: x-auth-token
  • or you provide the token in the requestcollection. It has the same cbsecurity.customAuthHeader name, so if you have a rc[“x-auth-token”] variable (in the default setting) with a valid jwt token you will be fine.

According to this three conditions, I am still not logged in. I have received a token in my code (on line 3) which I returned in the response. That’s all. Most of the time it’s ok, but if you want to act immediately on the user object (or some other jwt related method which assumes the token is there) there’s an easy trick. Just put it in the rc directly after your login attempt like this:

var myToken = jwtAuth().attempt( rc.username, rc.password );
// x-auth-token OR the value as defined in cbsecurity.customAuthHeader
rc["x-auth-token"]= mytoken;
// get the current user and update login date			 
jwtAuth().getUser().setDateLastLogin(now()).save();

So that was easy! Happy now? Not really. I’ll explain why. It is trivial to add this line to the cbsecurity source, so it will immediately behave as if we just logged in, instead of waiting to a next request. I’ll create a pull request for that, and it is up to others to decide if it is a valid choice. Now I know this I don’t care. I can add this extra line.

But there’s something else which didn’t make me happy, and I found out when trying to debug my issue. When the jwtAuth().getUser() method was throwing exceptions I tried to make it conditional by using the jwtAuth().isLoggedIn() method. To my surprise it returned true, even when jwtAuth().getUser() was not able to return the user. That’s at least confusing. The jwtAuth().isLoggedIn() method is shows quite erratic behaviour. So I created the following code and executed it with several different conditions:

var resultA = jwtService.isLoggedIn(); 
var decodedToken = jwtService.parseToken();
var resultB = jwtService.isLoggedIn();

I logged in to the system, obtained a token and followed this login request with a second request with the above code in the following scenarios.

scenariojwtTokencbauth
session
Storage
cbsecurityresultAresultB
1nonerequestsecurelistfalseexception
2nonesessionsecurelisttrue or false1exception
3yesrequest or sessionsecurelisttruetrue
5yesrequestwhitelistedfalsetrue
6yessessionwhitelistedtrue or false1true
1 depending on session timeout

As you can see, the results of my isLoggedIn() function is quite different each time

  1. No token provided, so we are not logged in (see resultA column). If we try to parse the non-existing token we get. an exception, so this behaviour is normal
  2. In this case, we are logged in although we don’t have a token. This is incorrect, and this just happens because cbauth is storing a userId in a session. But all other token info is not there, so our second step fails.
  3. If we provide a token and our event is secured by cbsecurity, login information is correct
  4. if we provide a token, store cbauth userId in a requestStorage our first call to IsloggedIn is false. Only after parsing our token we are logged in in ResultB.
  5. this scenario is quite simular to 4. Only in this case IsLoggedIn is true most of the time, because it is depending on session storage.

I spent quite some time searching for an explanation for this results. IsLoggedIn is just a shortcut to the cbauth isLoggedIn function. I think this has to be changed. JWT is used in APIs most of the time where session storage is not very desirable. So isLoggedIn should check for a valid token and login to cbauth based on this token, and only return true if both conditions are true.
Other results can be explained by the fact that jwtAuth().parseToken() is not only parsing the token, but also calling the cbauth login, which is a good thing: If you have a valid token you should be logged in. If an event is secured cbsecurity will parse the token and you will be OK. If your event is on a whitelist however, there is no token parsing, even though there is a valid token, so isLoggedIn will return false.

So what can we conclude from this? Let me start by saying I still like the jwt handling in cbsecurity a lot. All encoding and decoding is fine, there is multiple mechanisms for token invalidation, there’s automatic logins on the presence of a token, and we don’t need session storage anymore. So a lot of the hard work already has been done. There’s just a few things to remember:

  • put your token in your rc immediately after your jwtAuth.attempt() call if you have more code in the same handler which depends on jwtAuth()
  • Don’t rely on the isLoggedIn function at the moment, unless you are sure jwtAuth().parseToken() has been called. I will create a pull request for this one.
  • When using JWT you should not rely on cfml sessions. Since cbsecurity JWT authentication is calling the cbauth module, make sure you modify the cbauth settings so it will NOT use cfml sessions by changing the cbauth module setting for sessionStorage, e.g:
modulesettings.cbauth.sessionStorage = "RequestStorage@cbstorages"

It may sound counter intuitive, but the only thing this setting does is deciding where your userId will be stored. Naming it userIdStorage instead of sessionStorage would be more appropriate. Since you send your userId in a JWT on every request you don’t have to store it in a cfml session.

CbSecurity: a custom validator for fine-grained permissions

CbSecurity has some fine mechanisms to work with user permissions (the CbAuth validator) or user roles (CFML Security validator). The cbauth validator is really flexible, but sometimes you still need more options. In one of our projects our users can have one or more fixed roles and we assigned several permissions to each role. Our rules looked like this:

{
  secureList  : "auth.Customers\.index",
  permissions : LIST_CUSTOMERS
},
{
  secureList  : "auth.Customers\.show",
  permissions : SHOW_CUSTOMERS
},
{
  secureList  : "auth.Customers\..*",
  permissions : MANAGE_CUSTOMERS
},
// ......
// block all rule
{
  secureList  : ".*",
  permissions : "JUST_A_PLACEHOLDER",
  whitelist   : whiteRules.toList()
}

This is only a very small part of our rules. We had to keep track of all kind of permission names (and we put them in variables to prevent typo’s) and still had to assign these permissions to a fixed set of roles. Quite some administration. We decided we had more wishes, in our case:

  1. each individual endpoint should have its own permission for very fine-grained control.
  2. any number of roles which we can create in an API
  3. assignment of permission to these roles in the API
  4. assignment of roles to users in the API
  5. and since we are using a VueJS frontend, we wanted a list of ALL available endpoints (based on permissions) for a user
Continue reading
« Older posts Newer posts »

© 2021 ShiftInsert.nl

Theme by Anders NorenUp ↑