In this post, we’ll examine and explain how the Access application that demonstrates a possible, real life use case, of the device detection feature of the AxDeviceDetector ActiveX server.
See the downloads section below to download a copy of the database.
Scenario
We want to allow, let’s say, restaurant waiters or employees, to register their shifts by typing in their password, clicking a (start or end) button, and then plugging a USB key into the computer, to confirm they are who they say to be. This is an instance of a multi factor authentication, or MFA, system.
The DeviceDetectorAuthDemo.accdb demo database is essentially a finite state machine implementation.
States and transitions
The frmMain form can be in one of these 4 states:
- Select employee
- Enter password
- Wait for action
- Wait for device detection
It’s a cycle, when the wait for the device detection ends, we return on the first state (Select employee).
There is a constant definition for each of these states:
1 2 3 4 5 |
Private Const STEP_SELECT_USER As Integer = 1 Private Const STEP_ENTER_PASSWORD As Integer = 2 Private Const STEP_CHOOSE_ACTION As Integer = 3 Private Const STEP_PLUG_DEVICE As Integer = 4 |
When placed in one of these states, the form needs to update its UI, so only the next possible state(s) can be triggered. This is done in the EnableStepControls() method. This method also updates the UI to show in which state number it is, by moving a green square (it’s a label control) under the correct state number label.
State transition triggers
There are two classes of events that provoke the transition between these states:
-
UI elements actions
- Double-click on the employee listbox
- Button clicks
-
Timer event
Each event code that provokes a state transition has the responsibility to do so only if the UI holds valid values for every possible event parameters. In this simple demo, cmdOK_Click() is a good example:
1 2 3 4 5 6 7 8 9 10 |
Private Sub cmdOK_Click() If Len(Me.txtPassword) = 0 Then MsgBox "Please provide a password", vbCritical Exit Sub End If msPassword = Me.txtPassword DisableCurrentStep ActivateStep STEP_CHOOSE_ACTION End Sub |
To change state, DisableCurrentStep() and ActivateStep() are called in sequence.
DisableCurrentStep() executes whatever code is needed, before the state transition occurs:
1 2 3 4 5 6 7 8 9 10 11 12 |
Private Sub DisableCurrentStep() EnableStepControls miStepNumber, False Select Case miStepNumber Case STEP_SELECT_USER Case STEP_ENTER_PASSWORD Me.txtPassword = "" 'clear the password Case STEP_CHOOSE_ACTION Case STEP_PLUG_DEVICE StopWaitOnUSBTimer End Select End Sub |
ActivateStep() executes whatever code needed, before the new state is reached:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Private Sub ActivateStep(ByVal piStepNumber As Integer) miStepNumber = piStepNumber EnableStepControls miStepNumber, True Select Case miStepNumber Case STEP_SELECT_USER Case STEP_ENTER_PASSWORD Case STEP_CHOOSE_ACTION If Len(msAuthDeviceHash) = 0 Then Me.cmdBeginShift.Enabled = False Me.cmdEndShift.Enabled = False End If Case STEP_PLUG_DEVICE StartWaitOnUSBTimer End Select End Sub |
By design, there is a parameter for the transition to state 4 (Wait for device detection): the action code, which is one of these possible values defined as constants:
1 2 3 4 |
Private Const ACTION_REG_DEVICE As Integer = 0 Private Const ACTION_BEGIN_SHIFT As Integer = 1 Private Const ACTION_END_SHIFT As Integer = 2 |
This way, when the device arrival/removal event sent by the AxDeviceDetector ActiveX server arrives, we know what action to take. The action code is stored in the miAction form scoped variable. Here an example, in the code executed when we click on the cmdRegisterDevice() button:
1 2 3 4 5 6 |
Private Sub cmdRegisterDevice_Click() miAction = ACTION_REG_DEVICE DisableCurrentStep ActivateStep STEP_PLUG_DEVICE End Sub |
In the moDetector_OnDeviceArrival() there is a “Select Case miAction” statement that can now execute the correct action and transition back to step (ie state) #1 (select employee).
A class of state transitions is missing
Guessed which one ? – If not, then try to select another employee while you’re in state #3. You can’t.
There are at least two “cancel” actions that are missing:
- A “cancel” button is missing, along the “OK” button
- Another “cancel” button is missing if we want to cancel the wait for the device arrival.
Both of the action could for instance simply return us to state #1. I’ll let that for you.
About hashing
To link the USB device to an Employee, we generate a simple hash with the password, the device product id and the device serial number. That is discriminating enough to make it reasonably difficult to reverse engineer:
1 2 3 4 |
Private Function MakeHash(ByVal psUserPassword As String, ByVal psDeviceProductID As String, ByVal psDeviceSerial As String) As String MakeHash = MD5Hex(psUserPassword & psDeviceProductID & psDeviceSerial) End Function |
I’ve used here one of the first MD5 functions for VBA I googled for. You can replace that with a SHA256 to make it stronger, and add your additional parameters to generate the hash.
You should however avoid displaying and/or logging too much information, as I do for the sake of the demo, like the device product id, in a production application.
Avoiding blocking UI while handling ActiveX server events
We should avoid displaying any blocking UI elements like message boxes, input boxes or modal dialog boxes. This would prevent the server from notifying us of further events while those UI elements block the application’s VBA thread. The ActiveX server itself runs on another process/thread, so it will not block or crash. It has room for buffering 50 messages, that’s its event queue default size. When the server event queue becomes full, it’s emptied and the cleared events are lost.
About the Employee table
- We have to store the device serial number (field AuthDeviceSerial), if we want to avoid registering an already registered device to another user;
- The EventLog table is just here for the demo. You may want to (re)use your own logging system.
- You should of course split your database, ie do not keep the data tables in the same database as the application, but rather link them.
That’s it !
Downloads
All the material including the DeviceDetectorAuthDemo.accdb database, is available in my AxDeviceDetector github project
Credits: Post photo by Kentaro Toma on Unsplash
Recent Comments