3. Tokens¶
If you decide to implement new token classes, you need to change the parameter linotpTokenModules
in
/etc/linotp2/linotp.ini
in the DEFAULT
section. This parameter contains a list of token modules
LinOTP should use.
Note
The module linotp.lib.tokenclass
will always be used.
Starting with LinOTP 2.5.2 it is even simpler to add new token types to LinOTP. The complete token type handling is now done dynamically. This means you can
add a new token module
new policies that are necessary for this token
new management GUI (enrollment and token settings) and
new selfservice enrollment GUI
for this new token without having to adapt anything in the LinOTP core.
This is done by creating two files
new_token.py
This file contains all the program logic, the calculation and verification of the OTP values and the handling of the policies.
new_token.mako
This file contains the GUI parts for management and selfservice.
The existing tokens are located in the module linotp.lib.tokens
.
Both files describe the new token and need to be located in the same directory i.e. the same python module.
3.1. The GUI file¶
The new_token.mako file is written in the MAKO templating language. More information on how MAKO works can be found on the project website 1.
Note
Please take a look at the example file dyntoken.mako
.
To make the file handling easier there is only this single file providing the GUI for
selfservice
token configurations
token enrollment.
Therefore there is a top level switch in this file that evaluates the variable c.scope
.
3.1.1. The scope config¶
config
The scope
config
is used for the generic token configuration information. E.g. for a time based HMAC Token (TOTP) you can use this scope to configure the default time window and the default OTP length. The scopeconfig
contains the complete HTML code of this config tab.
config.title
This is the header text of the config tab.
To read and write the token configuration to the LinOTP configuration database you need to define two javascript functions:
function dyn_get_config_val(){
var ret = {};
ret['DynTokenTimeOut'] = 'dyn_challenge_timeout';
ret['DynTokenMaxChallemges'] = 'dyn_max_challenge';
return ret;
}
function dyn_get_config_params(){
var ret ={}
ret['DynTokenTimeOut'] = $('#dyn_challenge_timeout').val();
ret['DynTokenMaxChallemges'] = $('#dyn_max_challenge').val();
return ret
}
The name of the function starts with the token type name of the token.
You already know tokens like hmac
, totp
, spass
.
So the function names concatenate like <token type>_get_config_val
and <token type>_get_config_params()
.
The example above are the functions of the token type dyn
.
In the config tab for the token, you might have several HTML object. The function maps the identifier of the HTML objects to the configuration keys stored in the LinOTP configuration database.
The function _get_config_val
is called, when the config tab is opened. It tells LinOTP to get the config entry with the key
DynTokenTimeOut
from the LinOTP configuration and put it into the HTML object with the id=dyn_challenge_timeout
.
The function _get_config_params
is called, when the config tab is saved. The values of the HTML objects are written to the
corresponding LinOTP config database entries.
Warning
All config entries are stored in one config table. So you need to review third party token modules and assure, that a foreign token modules does not interfere with your other tokens or break your LinOTP. Best practice is to let your token config key start with the token type as prefix.
3.1.2. The scope enroll¶
enroll
This is the HTML GUI for the enrollment of a token by the administrator. Here all necessary data input is displayed.
enroll.title
This is the header text for the enrollment.
In the enrollment process an administrator needs to add additional information, like the RADIUS server in case of a RADIUS Token,
the secret key or whatever.
These data need to be passed to LinOTP, therefore you need to define a javascript function <token type>_get_enroll_params
.
The function has to return a hash of all the parameters, that will be passed to the admin/init
function:
function dyn_get_enroll_params(){
var url = {}
url['type'] = 'dyn';
url['otppin'] = $('#dyn_pin1').val();
url['hashlib'] = $('#dyn_algorith').val();
url['otplen'] = $('#dyn_otplen').val();
url['serial'] = create_serial('DYN');
if ( $('#dyn_key_cb').attr('checked') ) {
url['genkey']='1';}
else {
url['otpkey'] = $('#dyn_key').val();}
return url;
}
3.1.3. The scope selfservice¶
selfservice.enroll
This is the HTML GUI for the user self enrollment in the Selfservice Portal.
selfservice.title.enroll
This is the header text of the selfservice tab.
In the self enrollment process several information needs to be entered by the user and needs to be passed to LinOTP.
Three javascript functions are used to read the data from the HTML GUI and pass it to LinOTP. The first function
self_<token type>_get_param
function is used to retrieve the hashed parameters that needs to be passed to
the selfservice/userinit
function:
function self_dyn_get_param()
{
var urlparam = {};
urlparam['type'] = 'dyn';
urlparam['description'] = "self enrolled" ;
urlparam['otpkey'] = $('#motp_secret').val();
urlparam['otppin'] = $('#motp_s_pin1').val();
urlparam['serial'] = generate_serial("LSMO");
return urlparam;
}
Note
The generate_serial()
function generates a new serial number with the passed prefix.
The function self_<token type>_clear
is used to clear the input fields:
function self_dyn_clear()
{
$('#motp_s_pin1').val('');
$('#motp_secret').val('');
$('#motp_s_pin2').val('');
}
Finally there is a function self_<token type>_submit
that is called when the token is enrolled.
This function can also be used to do a input validation:
function self_dyn_submit(){
var ret = false
if ($('#form_registermotp').valid()) {
var params = self_motp_get_param();
enroll_token( params );
//self_motp_clear();
ret = true;
} else {
alert("Form data not valid.");
}
return ret;
}
Note
When doing input validation the corresponding jQuery.validator
needs to be defined.
3.2. The token file¶
The token file dyntoken.py
itself contains the token definition as a python class that inherits from
TokenClass
which is defined in linotp.lib.tokenclass
.
The token class needs to provide a class method getClassInfo
, that tells LinOTP, which scopes to use in which situation.
Therefore it returns a dictionary of the following kind:
{
'type' : 'dyn',
'title' : 'my dyn Token',
'description' : ('my own dynamic otp token'),
'init' : {'page' : {'html' : 'dyntoken.mako',
'scope' : 'enroll',},
'title' : {'html' : 'dyntoken.mako',
'scope' : 'enroll.tab',},
},
'config' : { 'page' : {'html' : 'dyntoken.mako',
'scope' : 'config',},
'title' : {'html' : 'dyntoken.mako',
'scope' : 'config.tab',},
},
'selfservice' : { 'enroll' : {'page' : {'html' : 'dyntoken.mako',
'scope' : 'selfservice.enroll',},
'title' : { 'html' : 'dyntoken.mako',
'scope' : 'selfservice.tab.enroll',},
},
},
}
3.2.1. Challenge Response¶
Starting with LinOTP 2.6 you can easily create tokens capable of doing challenge response.
Usually a challenge will be triggered when sending an authentication request to /validate/check
with
the additional parameter data
or challenge
.
The method is_challenge_request()
determines,
if the given request is a challenge or not.
The response will also be sent to the /validate/check
interface. Usually the response has to contain
the parameter state
or transactionid
so that the request will be identified as a response
to a previous challenge. The method is_challenge_response()
determines, if the given request is
a response for a previous challenge.
Each token type can define it’s own challenge handling by overwriting the corresponding base class method.
So the methods is_challenge_request
and is_challenge_response
determine in which of the following
three branches the request will be verified:
authenticate
This is the normal single shot authentication e.g. for push button OTP tokens. No challenge and no response is involved.
is_challenge_request
andis_challenge_response
both return false. The authentication workflow will call theauthenticate()
method.
challenge
The method
is_challenge_request
returns true andis_challenge_response
returns false. A new challenge will be created. The authentication workflow will call the methodinitChallenge()
andcreateChallenge()
.
response
The method
is_challenge_request
returns false andis_challenge_response
returns true. The authentication workflow will call the methodcheckResponse4Challenge()
.
Please see the base class definition in Base TokenClass.
3.2.2. Base TokenClass¶
See documentation for TokenClass class: linotp.lib.tokenclass.TokenClass