Access control - username and password authentication
This page describes the configuration options for authentication. Remember that if you want to override the default configuration of the auth-preferences.kts, you need to modify or create the following file: application-name-script-config/src/main/resources/scripts/auth-preferences.kts.
Session tokens and refresh tokens Session tokens and refresh tokens work in pairs together to enable you to control secure user sessions. These tokens always have an associated expiry date. This is in DATETIME format, and is typically a number of minutes in the future.
The expiry date of the refresh token is always further in the future than the expiry date of the session token, so that session tokens can be refreshed.
Once a session token expires, you can use its associated refresh token to create a new user session - assuming the refresh token has not expired yet.
The security function
All the configuration settings in the auth-preferences.kts file are wrapped within the security
function. From this top level, you can set the following variables:
sessionTimeoutMins
specifies a time out (in minutes) for the session. Sessions are timed out (logged out) after the value defined here. The front end of your application can monitor web movement, page changes, etc. and perform an automatic refresh - in which case, the user is not aware of the logout and the start of the new session. Default: 30 minutes.refreshTokenExpirationMins
specifies a time out (in minutes) for the refresh token value that was provided on successful login. One refresh token is associated with one user session on a 1-to-1 basis; the value of the refresh token can be used to create a new user session after the session token has expired. Once the refresh token has expired, it can't be used to create a new user session. Default: 7200 minutes (5 days).expiryCheckMins
specifies the time interval (in minutes) used to check for idle sessions in the system. Default: 5 minutes.maxSimultaneousUserLogins
specifies the maximum number of concurrent active sessions a user can maintain. Once this limit has been reached, the user cannot activate additional sessions until one or more of the active sessions has been logged out. So, a value of 1 means that only one session can be logged in at any time; a value of two allows two sessions to be logged in concurrently, and so on. If the value is zero, is not defined, or is not a positive integer, then any number of sessions is permitted. Default: 0.
security {
sessionTimeoutMins = 60 //60 minutes (not the default 30 minutes)
refreshTokenExpirationMins = 2880 // 2880 minutes (not the default 7200 minutes)
expiryCheckMins = 10 //5 minutes (not the default 5 minutes)
maxSimultaneousUserLogins = 5 //5 active sessions (not the default unlimited)
}
Within security
there is a further range of functions you can call in order to configure the username and password authentication. These are detailed below.
authentication
The authentication
function is used to define which authenticator implementations will be used.
LDAP
Within the scope of the authentication
function, you can insert an ldap
block in order to define connections to one or more LDAP servers.
- To define a connection to a single server, call the
connection
function and set the relevant details. - To define connections to more than one server, simply call the
connection
function multiple times.
When using multiple LDAP connections, the connections will be used in the order specified to authenticate a login request. Only one server need return a successful result for the login to be successful.
The following variables are used to configure an LDAP connection; these are only used when the type
is either AuthType.LDAP
or AuthType.HYBRID
.
url
specifies the LDAP server hostname. Default:localhost
.port
specifies the LDAP server port. Default: 389.searchBases
defines the location(s) in the directory in which the LDAP search begins. Default: an organisational unit oftemp
with a domain component oftemp
(ou=temp,dc=temp
).- This is set by first invoking the
searchBases
function, and repeatedly invokingsearchBase(location)
function(s) within it, wherelocation
is the exact name of the application on the LDAP server.
- This is set by first invoking the
userGroups
defines the group(s) that the user needs to belong to on the LDAP server in order to log in. Default: no groups.- This is set by first invoking the
userGroups
function, and repeatedly invokinguserGroup(group)
function(s) within it, wheregroup
is the specific name of a group.
- This is set by first invoking the
userPrefix
specifies a prefix added to every username when communicating with the LDAP server. Default: an empty string.bindDn
specifies the exact name of the application within the LDAP server. Normally, LDAP servers do not allow anonymous searches, so this name is essential. IfbindDn
is not specified, no bindings will be used. Default: nullbindPassword
specifies the password for thebindDn
account. IfbindDn
is not specified, this value is not used. Default: null.userIdType
defines the attribute to match in the directory search against the provided username. Default:cn
.- Amongst the most common LDAP implementations, you can find three main ways of configuring usernames:
- using the
uid
attribute (Userid) - using the
cn
attribute (Common Name) - using the
sAMAccountName
in Windows
- using the
- Amongst the most common LDAP implementations, you can find three main ways of configuring usernames:
bypassLoginInternalAuth
is a boolean flag that prevents internal authorisation checks on login.onFirstLogin
is a function that is called the first time a user who doesn't already exist in the database has been authenticated. Here you can define two things:- how the
User
and itsUserAttributes
will be created from the token after the user has been authenticated, using thecreateUser
function - which user permissions are allocated, using
createUserPermissions
- how the
onLoginSuccess
is a function that is invoked on a successful LDAP login; for example, it allows you to insert a user into the database when it exists in LDAP but not yet in your application's database.useTLS
is a boolean value indicating whether or not to use TLS encryption on the connection to the remote LDAP server.
For more information about the various authentication types, see the Authentication overview.
genesisPassword
The genesisPassword
authenticator defines the configuration for validating user accounts that are stored locally in the application database.
passwordRetry
The passwordRetry
function has been deprecated in favour of the retry
function within the genesisPassword
configuration.
validation
The validation
function enables password validation, and is used to set the variables relating to this validation.
The following variables can be used to configure the application's password validation; these can only be used when type
is either AuthType.INTERNAL
or AuthType.HYBRID
.
-
enabled
is the key configuration variable; you must set this totrue
in order for the rest of thevalidation
configuration to take effect. -
passwordSalt
defines a system-specific salt to be added to your password hashes. This is a security measure that ensures that the same combination of username and password on different applications built on the Genesis low-code platform are stored as different hashes. Default: empty string indicating no additional salting. -
passwordStrength
can be called to set a range of configuration variables. These enable you to specify in detail the mandatory characteristics for the password.minimumLength
specifies the minimum length of password. If null or undefined, this assumes there is no minimum limit. Default: null.maximumLength
specifies the maximum length of password. If null or undefined, this assumes there is no maximum limit. Default: null.minDigits
specifies the minimum number of numeric digits required in a password. If null or undefined, this assumes there is no minimum limit. Default: null.maxRepeatCharacters
specifies the maximum number of the same characters across an entire password. This does not just include consecutive repeat characters, which is controlled by therepeatCharacterRestrictSize
variable, below. If null or undefined, this assumes there is no maximum limit. Default: null.minUppercaseCharacters
specifies the minimum number of upper-case characters in a password. If null or undefined, this assumes there is no minimum limit. Default: null.minLowercaseCharacters
specifies the minimum number of lower-case characters in a password. If null or undefined, this assumes there is no minimum limit. Default: null.minNonAlphaNumericCharacters
specifies the minimum number of non-alphanumeric characters, such as punctuation and other special characters. If null or undefined, this assumes there is no minimum limit. Default: null.restrictWhitespace
specifies if whitespace characters are prevented from being used in passwords. Default: true.restrictAlphaSequences
specifies if alphabetical sequences in passwords (e.g. abcdefg) are restricted. Sequences greater than or equal to five characters won't be permitted if this is true. Default: false.restrictQWERTY
specifies if QWERTY sequences in passwords (e.g. qwertyuiop) are restricted. Sequences greater or equal to five characters won't be permitted if this is true. Default: true.restrictNumericalSequences
specifies if numeric sequences in passwords (e.g. 123456) are restricted. Sequences greater or equal to five numbers won't be allowed if active. Default: true.illegalCharacters
specifies which characters are not permitted in user passwords. Default: empty.historicalCheck
specifies how many previous passwords to check against, in order to prevent password re-use. If null or undefined, no historical check is performed. Default: null.restrictPassword
specifies if the password should differ from a list of the worst passwords stored within the application. Default: false.restrictDictionarySubstring
specifies if any dictionary word of four or more characters can be included in a password (either forwards or backwards). Default: false.restrictUserName
specifies if the user's username is restricted as part of their password. Default: false.repeatCharacterRestrictSize
specifies the number of consecutive repeated characters that make a password restricted. If null or undefined, this assumes there is no limit. Default: null.passwordExpiryDays
specifies how many days before a password expires. If null or undefined, this assumes there is no limit. Default: null.passwordExpiryNotificationDays
specifies how many days before password expiry a user is notified. If null or undefined, the user is not notified in advance of their password expiry. Default: null.
retry
The retry
function enables you to configure settings for limiting the rate at which a user can retry passwords. You can set the following variables:
maxAttempts
specifies the maximum number of attempts allowed if a user enters a wrong password. Default: 3 attempts.waitTimeMins
specifies the time to wait in minutes when the maximum number of incorrect attempts is reached before allowing a user to try again. Default: 5 minutes.
User login attempts are stored in the USER_LOGIN_ATTEMPT table. If a user exceeds the allowed limit of password entry attempts, the system updates the corresponding record in the USER_LOGIN_ATTEMPT table, and locks the user.
To assist users who have exceeded their limit of password retries, an administrator can delete or amend the relevant record(s) in the USER_LOGIN_ATTEMPT table. The user can then try to login again.
selfServiceReset
The selfServiceReset
function enables the self-service reset workflow for users. In this, users authenticated with the internal auth type can request an email to reset their password. For this workflow, you must have Genesis Notify configured with a working email gateway. When a user requests a reset, an email with a link to a password reset page is sent to their configured email address. This link is valid for a preconfigured timeout.
In the interest of security, this response will always receive an ACK, even if there is a problem in identifying the user or email address. In the case of a problem with the request, details will be provided in the auth manager log file.
The selfServiceReset
function has the following options:
timeoutInMinutes
- the time in minutes for which a reset link remains validcoolDownInMinutes
- the time in minutes before the next password reset can be madenotifyTopic
- the email topic in Genesis Notify to be usedredirectUrl
- the url that will direct the user to the web page containing the form used for them to input their new password using the token provided in the email notification.
This is normally set to https://$HOSTNAME/login/reset-password
acceptClientUrl
- boolean flag; if true, the reset will use the client-provided reset url
You can set acceptClientUrl
to true
in a development environment. For security, always set it to false
in all other environments. Always.
resetMessage
The resetMessage
function enables your application's users to configure the email sent when a reset is requested. It has the following options:
subject
the subject line of the emailbody
the body of the email
Both the subject and the body support templating. Values surrounded by double curly braces {{ }}
will be replaced when the email is sent. The following values are available:
RESET_URL
the reset urlTIMEOUT
the time the url is valid forUSER
the user record, properties on this record should be accessed using lowerCamelCase, e.g.{{ USER.firstName }}
- any system definition or environment variable available
example:
authentication {
genesisPassword {
selfServiceReset {
timeoutInMinutes = 20
notifyTopic = "smtpEmail"
redirectUrl = "https://genesis.global/login/password-reset"
}
}
}
mfa
The mfa
function enables you to configure Multi-factor Authentication (MFA). From within the mfa
function, you can choose between different implementations of MFA providers.
qrCode
This method of MFA generates a qrCode that can be imported into apps such as Google and Microsfoft authenticator; the code generates a one-time-only time-based password to use as multi-factor codes to login. This block exposes the following configuration items:
codePeriodSeconds
specifies how many seconds a Time-based One-time Password (TOTP) remains valid. Default: 30 seconds.codePeriodDiscrepancy
specifies the allowed discrepancy to the TOTP. 1 would mean a single block of eachcodePeriodSeconds
either side of the time window. Default: 1.codeDigits
specifies the number of digits used in the TOTP. Default: 6 digits.hashingAlgorithm
specifies the Hashing Algorithm to use. Available choices are:HashingFunction.SHA1
,HashingFunction.SHA256
orHashingFunction.SHA512
. Default:HashingFunction.SHA1
.issuer
specifies a reference to the organisation or entity issuing the MFA. Default: Genesis.label
specifies a label for the MFA. This is typically an email address of the issuing entity or organisation. Default: genesis.global.confirmWaitPeriodSecs
specifies the time period in seconds before a secret has to be confirmed. Default: 300 seconds.secretEncryptKey
specifies the key that is used to encrypt Secrets in the database. If this is null or undefined, Secrets will not be encrypted in the database. Default: null.usernameTableLookUpSalt
specifies the salt with which a username is hashed when stored in the database with the above Secret. If this is null or undefined, the username will not be hashed in the database. Default: null.
notify
This method of MFA generates a one-time login link that is sent via the Genesis Notify module.
Each time a login is unsuccessful, a new one-time link is generated, using a temporary code with a short-timed expiry.
When login is successful using a temporary code, an active code is generated with the configured expiry. This can either be stored or saved as a cookie, preventing the need for the user to perform a second-factor authentication again until it has expired.
This block exposes the following configuration items:
loginUrl
specifies the base URL to open in the one-time login link.tempCodeExpiryDuration
specifies a duration for temporary generated codes. Default is 15 minutes.activeCodeExpiryDuration
specifies a duration for active generated codes. Default is 30 days.notifyTopic
is the topic to publish to the notify module on. Default is 'MFA'.messageHeader
is the header of the resulting message.messageBody
is the body of the resulting message.
loginAck
The loginAck
function enables you to define additional values to be sent back to the client as part of the LOGIN_ACK
message. When you call the loginAck
function, you have to supply a table or view as a parameter.
The following functions will be invoked on this table or view:
- The
loadRecord
within theloginAck
function loads a single record from the previously supplied table or view. - The
fields
function within theloginAck
function specifies which additional fields should be sent back to the client as part of the LOGIN_ACK message. - The
customLoginAck
function enables you to modify the list of permissions, profiles and user preferences returned to the client as part of theLOGIN_ACK
message. For this purpose, theUser
entity is provided as a parameter, as well as three properties:- permissions - a mutable list containing all the right codes associated with the user. Given its mutability, codes can be added or removed.
- profiles - a mutable list containing all the profiles associated with the user. Given its mutability, profiles can be added or removed.
- userPreferences - a GenesisSet object containing additional fields provided as part of the loginAck function. This
GenesisSet
can be modified to provide additional fields or remove existing ones.
Here is an example configuration:
import global.genesis.auth.manager.AuthType
import global.genesis.auth.manager.data.HashingFunction
import global.genesis.auth.manager.security.sso.jwt.NewUserMode
import global.genesis.db.entity.DbEntity
import global.genesis.dictionary.pal.view.IndexedDataStructure
import global.genesis.gen.config.tables.USER_ATTRIBUTES.ACCESS_TYPE
import global.genesis.gen.config.tables.USER_ATTRIBUTES.ADDRESS_LINE1
import global.genesis.gen.config.tables.USER_ATTRIBUTES.USER_TYPE
security {
sessionTimeoutMins = 30
expiryCheckMins = 5
maxSimultaneousUserLogins = 0
authentication {
type = AuthType.LDAP
ldap {
connection {
url = "localhost"
port = 389
searchBase {
searchBase("ou=temp,dc=temp")
}
userGroups {
}
userPrefix = ""
bindDn = null
bindPassword = null
userIdType = "cn"
}
}
genesisPassword {
validation {
enabled = true
passwordSalt = ""
passwordStrength {
minimumLength = null
maximumLength = null
minDigits = null
maxRepeatCharacters = null
minUppercaseCharacters = null
minLowercaseCharacters = null
minNonAlphaNumericCharacters = null
restrictWhiteSpace = true
restrictAlphaSequences = false
restrictQWERTY = true
restrictNumericalSequences = true
illegalCharacters = "\$£^"
historicalCheck = 0
restrictDictionarySubstring = false
restrictUserName = false
repeatCharacterRestrictSize = null
passwordExpiryDays = null
passwordExpiryNotificationDays = null
}
}
retry {
maxAttempts = 3
waitTimeMins = 5
}
}
}
sso {
enabled = false
}
mfa {
codePeriodSeconds = 30
codePeriodDiscrepency = 1
codeDigits = 6
usernameTableLookUpSalt = null
secretEncryptKey = null
hashingAlgorithm = HashingFunction.SHA1
issuer = "Genesis"
label = "genesis.global"
confirmWaitPeriodSecs = 300
}
loginAck(USER_ATTRIBUTES) {
loadRecord { UserAttributes.byUserName(userName) }
fields {
USER_TYPE
ACCESS_TYPE withPrefix "USER"
ADDRESS_LINE1
}
}
customLoginAck { user ->
if(user.userName == "TestUser"){
permissions += listOf("TEST_USER_INSERT", "TEST_USER_AMEND", "TEST_USER_DELETE")
profiles += listOf("TEST_ADMIN")
userPreferences = userPreferences.apply {
setString("TEST_VALUE", "TEST")
}
}
}
}
Message flows
Security messages can be split into three categories.
- Pre-authentication
- Authentication
- Post-authentication
All requests below are capable of returning an error with a code of INTERNAL_ERROR, which will be used as a last resort.
Pre-authentication
Pre-authentication messages can be sent by a client without the user being logged in. There are three messages that can be used without being logged in.
Self-service password reset
It is possible to initiate a self-service password message workflow, assuming it is configured appropriately.
The message to request a self-service password reset looks like this:
MESSAGE_TYPE = EVENT_SELF_SERVICE_PASSWORD_RESET DETAILS.USER_NAME = JohnWolf
It is also possible to provide a RETURN_URL field as part of the details block if the URL used by the back-end configuration is not suitable.
Once this event has been triggered, the password reset link will be sent through the appropriate channels (e.g. e-mail) and the password reset action can be triggered.
MESSAGE_TYPE = EVENT_PASSWORD_RESET_ACTION DETAILS.USER_NAME = JohnWolf DETAILS.RESET_TOKEN = 1329048120a0sdf DETAILS.NEW_PASSWORD = ******* DETAILS.INVALIDATE_ACTIVE_SESSIONS = true
In the example message above, you can see that:
- the RESET_TOKEN field has the code provided to the user when the reset operation was requested
- the NEW_PASSWORD field has the new password to be used for the user
- the optional INVALIDATE_ACTIVE_SESSIONS field has been set to true, to log out any currently logged in sessions
Change password
The change password message is available whether the user is currently logged in or not. This is important, because the first time a user logs in, their newly created one-time password will be in a PASSWORD_EXPIRED state; this forces a "Change password" workflow before they can actually log in for the first time.
The message structure for a change password message looks like this:
MESSAGE_TYPE = EVENT_CHANGE_USER_PASSWORD DETAILS.USER_NAME = JohnWolf DETAILS.OLD_PASSWORD = ******* DETAILS.NEW_PASSWORD = *******
The naming convention for the fields is also self-explanatory.
Logout
A logout request can be triggered before a user has logged in, if limits have been set for a maximum number of user sessions. This message workflow enables you to terminate an existing active session so that a new session can be created.
A sample message would look like this:
MESSAGE_TYPE = EVENT_LOGOUT DETAILS.USER_NAME = JohnWolf DETAILS.SESSION_ID = *******
Authentication
The password is provided in plain text, as it is expected that you will secure the connection using TLS.
Login request
MESSAGE_TYPE = EVENT_LOGIN_AUTH DETAILS.USER_NAME = JohnWolf DETAILS.PASSWORD = ******
Login response
If successful, a message could look like this:
MESSAGE_TYPE = EVENT_LOGIN_AUTH_ACK
SESSION_AUTH_TOKEN = ********
REFRESH_AUTH_TOKEN = ********
SESSION_ID = c4eb5f62-2d11-4028-98cb-018ff45d7035
USER_NAME = JohnWolf
DETAILS.HEARTBEAT_INTERVAL_SECONDS = 30
DETAILS.SESSION_TIMEOUT_MINS = 20
DETAILS.REFRESH_TOKEN_EXPIRATION_MINS = 7200
DETAILS.FAILED_LOGIN_ATTEMPTS = 0
DETAILS.REJECTED_LOGIN_ATTEMPTS = 0
DETAILS.LAST_LOGIN_DATE_TIME = 2024-01-25 15:48:33.413 (1706197713413)
DETAILS.DAYS_TO_PASSWORD_EXPIRY = 730
DETAILS.NOTIFY_EXPIRY = 8
DETAILS.MFA_CODE = null
DETAILS.MFA_CODE_EXPIRY_MINS = null
DETAILS.SYSTEM.DATE = Thu Jan 25 15:48:33 UTC 2024
USER_DETAILS.FIRST_NAME = John
USER_DETAILS.LAST_NAME = Wolf
PERMISSION[0] = AMEND_PROFILE
PERMISSION[1] = AMEND_USER
PERMISSION[2] = CHANGE_PWD
PERMISSION[3] = DELETE_PROFILE
PERMISSION[4] = DELETE_USER
PERMISSION[5] = DISABLE_USER
PERMISSION[6] = ENABLE_USER
PERMISSION[7] = EXPIRE_PWD
PERMISSION[8] = INSERT_PROFILE
PERMISSION[9] = INSERT_USER
PERMISSION[10] = MFA_CONFIRM
PERMISSION[11] = MFA_CREATE
PERMISSION[12] = MFA_DISABLE
PERMISSION[13] = MFA_ENABLE
PROFILE[0] = ADMIN
PROFILE[1] = USER_ADMIN
If there is a problem, the server returns the standard error set with CODE/TEXT details and the error code LOGIN_AUTH_NACK
. The following error codes can be provided:
UNKNOWN_ACCOUNT
- User is unknownMAX_ACTIVE_SESSIONS_REACHED
- Maximum number of sessions reached.INCORRECT_CREDENTIALS
- User/password combination is invalidLOCKED_ACCOUNT
- Account is locked and needs to be re-activated by administratorPASSWORD_EXPIRED
- Password must be changedLOGIN_FAIL
- Generic error code
Password change
As explained in the previous section, if the response for the login attempt is PASSWORD_EXPIRED
, then the GUI can allow the user to change the password - provided they know their existing password.
Change request
MESSAGE_TYPE = EVENT_CHANGE_USER_PASSWORD DETAILS.USER_NAME = JohnWolf DETAILS.OLD_PASSWORD = ****** DETAILS.NEW_PASSWORD = ******
Change response
If successful:
MESSAGE_TYPE = EVENT_CHANGE_USER_PASSWORD_ACK
If there's a problem, you will receive a standard error set with type CHANGE_USER_PASSWORD_NACK
.
The error codes that can be returned are currently:
TOO_SHORT
- password length too short.TOO_LONG
- password length too long.INSUFFICIENT_CHARACTERS
- covers a few cases, so text field may be required, used for things like no digits provided when 1 digit is required.ILLEGAL_MATCH
- covers a few cases so text field may be required, used for things like repeating characters in password.ILLEGAL_WHITESPACE
- if password contains white space.INSUFFICIENT_CHARACTERISTICS
- can be provided if you have configured passwords to be successful only if a predetermined set of strength checks pass. Should be provided alongside "real" error codes.ILLEGAL_SEQUENCE
- Numeric/alphabetic sequence detected.
Logout
As explained in the previous section, if the response for the login attempt is MAX_ACTIVE_SESSIONS_REACHED
, the server will reply with a list of active sessions; this gives the client the option to terminate one of them. In this scenario, the client would need to send a LOGOUT message with the specific SESSION_ID value of the session to terminate.
The response message for this scenario would look like this:
MESSAGE_TYPE = EVENT_LOGIN_AUTH_NACK
ERROR[0].@type = LoginError
ERROR[0].CODE = MAX_ACTIVE_SESSIONS_REACHED
ERROR[0].TEXT = Problem logging in
ERROR[0].STATUS_CODE = 403 Forbidden
ERROR[0].DETAILS.SESSION[0].SESSION_ID = abb8f6be-8009-4370-a474-3a0ded4dc2cf
ERROR[0].DETAILS.SESSION[0].HOST = host1
ERROR[0].DETAILS.SESSION[0].LAST_ACCESS_TIME = 2024-01-25 14:27:54.925 (1706192874925)
ERROR[0].DETAILS.SESSION[1].SESSION_ID = c8c04ba5-d450-48f7-a863-2a500fe0a4e7
ERROR[0].DETAILS.SESSION[1].HOST = host2
ERROR[0].DETAILS.SESSION[1].LAST_ACCESS_TIME = 2024-01-25 14:27:55.037 (1706192875037)
ERROR[0].DETAILS.SESSION[2].SESSION_ID = f6255056-952f-4985-b984-6e48c822c3a4
ERROR[0].DETAILS.SESSION[2].HOST = host3
ERROR[0].DETAILS.SESSION[2].LAST_ACCESS_TIME = 2024-01-25 14:27:55.235 (1706192875235)
ERROR[0].DETAILS.SESSION[3].SESSION_ID = 29220a26-0753-4408-9c28-9f00457e98ac
ERROR[0].DETAILS.SESSION[3].HOST = host4
ERROR[0].DETAILS.SESSION[3].LAST_ACCESS_TIME = 2024-01-25 14:27:55.438 (1706192875438)
A logout message to terminate the first session would look like this:
MESSAGE_TYPE = EVENT_LOGOUT
DETAILS.USER_NAME = JohnWolf
DETAILS.SESSION_ID = abb8f6be-8009-4370-a474-3a0ded4dc2cf
Post-authentication
Expire password
A password can expire in three different ways:
- time-based
- user-based
- admin-based
The end result is always the same, the user will need to change the password on the next login, as their previous one has now expired.
Expire password request
The password expiry mechanism can be triggered by sending a message like this:
MESSAGE_TYPE = EVENT_EXPIRE_USER_PASSWORD DETAILS.USER_NAME = JohnWolf
It is common for administrators to help users recover their account credentials by expiring the current User record STATUS
with a new one-time password; this action forces users to change it on their first login. In this case, the message can optionally receive a one-time password, as shown below:
MESSAGE_TYPE = EVENT_EXPIRE_USER_PASSWORD DETAILS.USER_NAME = JohnWolf DETAILS.PASSWORD = ******
Login details
If the client needs to re-receive the information provided by the login response for some reason (such as re-reading user preferences), it is possible to send an EVENT_LOGIN_DETAILS message to the server. The response will be equivalent to the response received by the login message, without actively logging in the system for a second time.
Login details request
MESSAGE_TYPE = EVENT_LOGIN_DETAILS DETAILS.SESSION_AUTH_TOKEN = *******
The SESSION_AUTH_TOKEN value is returned as part of the first login operation, so it is only possible to call EVENT_LOGIN_DETAILS after a successful login.
Login details reply
MESSAGE_TYPE = EVENT_LOGIN_DETAILS_ACK SESSION_AUTH_TOKEN = ******** REFRESH_AUTH_TOKEN = ******** SESSION_ID = c4eb5f62-2d11-4028-98cb-018ff45d7035 USER_NAME = JohnWolf DETAILS.HEARTBEAT_INTERVAL_SECONDS = 30 DETAILS.SESSION_TIMEOUT_MINS = 20 DETAILS.REFRESH_TOKEN_EXPIRATION_MINS = 7200 DETAILS.FAILED_LOGIN_ATTEMPTS = 0 DETAILS.REJECTED_LOGIN_ATTEMPTS = 0 DETAILS.LAST_LOGIN_DATE_TIME = 2024-01-25 15:48:33.413 (1706197713413) DETAILS.DAYS_TO_PASSWORD_EXPIRY = 730 DETAILS.NOTIFY_EXPIRY = 8 DETAILS.MFA_CODE = null DETAILS.MFA_CODE_EXPIRY_MINS = null DETAILS.SYSTEM.DATE = Thu Jan 25 15:48:33 UTC 2024 USER_DETAILS.FIRST_NAME = John USER_DETAILS.LAST_NAME = Wolf PERMISSION[0] = AMEND_PROFILE PERMISSION[1] = AMEND_USER PERMISSION[2] = CHANGE_PWD PERMISSION[3] = DELETE_PROFILE PERMISSION[4] = DELETE_USER PERMISSION[5] = DISABLE_USER PERMISSION[6] = ENABLE_USER PERMISSION[7] = EXPIRE_PWD PERMISSION[8] = INSERT_PROFILE PERMISSION[9] = INSERT_USER PERMISSION[10] = MFA_CONFIRM PERMISSION[11] = MFA_CREATE PERMISSION[12] = MFA_DISABLE PERMISSION[13] = MFA_ENABLE PROFILE[0] = ADMIN PROFILE[1] = USER_ADMIN
Rights polling
The GUI can receive rights from a process called GENESIS_AUTH_DATASERVER
. The view ALL_USER_RIGHTS
displays all users and codes. A logged-in user should automatically set the Filter expression to be USER_NAME
=='xxx' to receive push updates to user privileges.
Working with heartbeats (non-web applications only)
If you are building a desktop application that does not connect to a web host, the server can be set up to receive heartbeat messages after the user has been authenticated (as defined in the interval setting on the ACK message).
In response to a heartbeat, the GUI receives a list of available services to connect to, along with their details; if the environment is configured not to use Consul as the cluster mode, this can include multiple hostnames.
If the back end of your non-web application is not using the Consul cluster mode, a client can use the services in this list in the order that they are defined. Existing connections can simply ignore the order changes, but in the event of failover or reconnection, the order must be adhered to.
Heartbeat request
MESSAGE_TYPE = EVENT_HEARTBEAT USER_NAME = JohnWolf
Heartbeat response
MESSAGE_TYPE = EVENT_HEARTBEAT_ACK DETAILS.SERVICE[0].NAME = SBL_EVENT_HANDLER DETAILS.SERVICE[0].ENCRYPTED = false DETAILS.SERVICE[0].HOST[0].NAME = genesisserv1 DETAILS.SERVICE[0].HOST[0].PORT = 9001 DETAILS.SERVICE[0].HOST[1].NAME = genesisserv2 DETAILS.SERVICE[0].HOST[1].PORT = 9001 DETAILS.SERVICE[1].NAME = SBL_DATA_SERVER DETAILS.SERVICE[1].ENCRYPTED = false DETAILS.SERVICE[1].HOST[0].NAME = genesisserv1 DETAILS.SERVICE[1].HOST[0].PORT = 9002 DETAILS.SERVICE[1].HOST[1].NAME = genesisserv2 DETAILS.SERVICE[1].HOST[1].PORT = 9002
Entity management
In the Genesis low-code platform, there are profiles, users and rights. A profile is a group of users, which can be permissioned. For example, you could have a SALES_TRADER group in which all users have the same permissions. In all cases where you specify either a right for a user/profile, or a user in a profile, the event represents what you want the entity to look like; i.e. if you amend a profile and don't supply a user that previously was part of that profile, then that user will be removed from that profile on the server.
Note the following:
- The User
STATUS
field can be set toENABLED
,DISABLED
orPASSWORD_EXPIRED
. A User set up withPASSWORD_EXPIRED
should prompt the user to enter a new password on next login. - The Profile
STATUS
field can be set toENABLED
orDISABLED
.
Insert profile
Insert request
MESSAGE_TYPE = EVENT_INSERT_PROFILE USER_NAME = JohnWolf DETAILS.NAME = SALES_TRADERS DETAILS.DESCRIPTION = Sales Traders DETAILS.STATUS = ENABLED DETAILS.RIGHT_CODES[0].CODE = ORDEN DETAILS.RIGHT_CODES[1].CODE = ORDAM DETAILS.USER_NAMES[0].USER_NAME = JohnWolf DETAILS.USER_NAMES[1].USER_NAME = james
Insert response
MESSAGE_TYPE = EVENT_INSERT_PROFILE_ACK
Amend profile
Amend request
This amend request supplies a new set of details that changes the SALES_TRADERS profile (inserted in the previous example) in two ways:
- There is no
ORDAM
right code. - There is no "james" user name.
MESSAGE_TYPE = EVENT_AMEND_PROFILE
USER_NAME = JohnWolf
DETAILS.NAME = SALES_TRADERS
DETAILS.DESCRIPTION = Sales Traders
DETAILS.STATUS = ENABLED
DETAILS.RIGHT_CODES[0].CODE = ORDEN
DETAILS.USER_NAMES[0].USER_NAME = JohnWolf
Amend response
MESSAGE_TYPE = EVENT_ACK
Delete profile
Delete request
MESSAGE_TYPE = EVENT_DELETE_PROFILE USER_NAME = JohnWolf DETAILS.NAME = SALES_TRADERS
Delete response
MESSAGE_TYPE = EVENT_ACK
Insert User
Insert request
MESSAGE_TYPE = EVENT_INSERT_USER USER_NAME = JohnDoe DETAILS.USER_NAME = JohnWolf DETAILS.FIRST_NAME = John DETAILS.LAST_NAME = Wolf DETAILS.EMAIL_ADDRESS = john.wolf@genesis.global DETAILS.STATUS = ENABLED DETAILS.USER_PROFILES[0] = USER_ADMIN
Insert response
MESSAGE_TYPE = EVENT_ACK
Amend user
In the example below, the logged-in user (in the second line) is JohnDoe, who is modifying the profile of JohnWolf to give him the last name: Smith.
Amend request
MESSAGE_TYPE = EVENT_AMEND_USER USER_NAME = JohnDoe DETAILS.USER_NAME = JohnWolf DETAILS.FIRST_NAME = John DETAILS.LAST_NAME = Smith DETAILS.EMAIL_ADDRESS = john.wolf@genesis.global DETAILS.STATUS = ENABLED DETAILS.USER_PROFILES[0] = USER_ADMIN
Amend response
MESSAGE_TYPE = EVENT_ACK
Delete user
Delete request
MESSAGE_TYPE = EVENT_DELETE_USER USER_NAME = JohnDoe DETAILS.USER_NAME = JohnWolf
Delete response
MESSAGE_TYPE = EVENT_ACK
Full permissions list
Most of the message workflows described on this page are permissioned on the basis of a set of default rights. These are provided in the form of a CSV file as part of the platform authentication distribution. Most permissions and their relationship to events have been explained in their own sections. For reference, here is the whole table:
Code | Description |
---|---|
INSERT_PROFILE | Add a new Profile |
INSERT_USER | Add a new User |
AMEND_PROFILE | Amend an existing Profile |
AMEND_USER | Amend an existing User |
CHANGE_PWD | Change another User's password |
DELETE_PROFILE | Delete a Profile |
DELETE_USER | Delete a User |
DISABLE_USER | Disable a User |
ENABLE_USER | Enable a User |
EXPIRE_PWD | Expire another User's password |
A default Profile record named USER_ADMIN is also provided. This contains all the previous rights.
Users can change their own password as well as expire their own password (assuming they are logged in). However, it is only possible to change/expire another user's password if you have administrator rights.
Event auditing
Each authentication event is audited in one way or another, either using the automatic mechanism provided at the table definition level (e.g. PROFILE, PROFILE_RIGHT, PROFILE_USER, PASSWORD_RESET, USER and USER_ATTRIBUTES), or by providing custom tables with the audit information.
In the first case scenario, auditing works as it would do for any other Genesis table: AUDIT_EVENT_TYPE reflects the event message type (i.e. EVENT_INSERT_USER), AUDIT_EVENT_TEXT may contain a free text field provided by the user calling the event, AUDIT_EVENT_DATETIME is autogenerated with the current date and time the change happened and AUDIT_EVENT_USER corresponds to the user who triggered the event in question.
In the second case scenario, we have automatic events to log changes in USER_AUDIT and USER_ATTRIBUTES when a password expires. And we also have specific handling for USER_LOGIN audits. The USER_LOGIN_AUDIT table will contain entries for the following events: LOGIN, LOGOUT, SESSION_EXPIRED, REJECTED (only available if a maximum number of user sessions has been configured), FAILED_LOGIN, and FAILED_LOGOUT (if an incorrect session ID or user name has been provided).
Additionally, all USER_LOGIN audit events are logged to a file at INFO level in the following format:
[25 Jan 2024 16:31:18.823 12729 [dbCoroutinesContext-4 @coroutine#994] INFO global.genesis.auth.manager.controller.LoginAuditController - AuditLoginEvent: UserLoginAudit{serialVersionUID='1',userLoginAuditId={not-set}, userName=JohnDoe, authAction=LOGIN, ipAddress=/127.0.0.1, reason=, recordId={not-set}, timestamp={not-set}}]