14.2.1.2.21. U2F Token¶
U2F is the “Universal 2nd Factor” specified by the FIDO Alliance. The register and authentication process is described here:
But you do not need to be aware of this. eduMFA wraps all FIDO specific communication, which should make it easier for you, to integrate the U2F tokens managed by eduMFA into your application.
U2F Tokens can be either
registered by administrators for users or
registered by the users themselves.
14.2.1.2.21.1. Enrollment¶
The enrollment/registering can be completely performed within eduMFA.
But if you want to enroll the U2F token via the REST API you need to do it in two steps:
14.2.1.2.21.1.1. 1. Step¶
POST /token/init HTTP/1.1
Host: example.com
Accept: application/json
type=u2f
This step returns a serial number.
14.2.1.2.21.1.2. 2. Step¶
POST /token/init HTTP/1.1
Host: example.com
Accept: application/json
type=u2f
serial=U2F1234578
clientdata=<clientdata>
regdata=<regdata>
clientdata and regdata are the values returned by the U2F device.
You need to call the javascript function
u2f.register([registerRequest], [], function(u2fData) {} );
and the responseHandler needs to send the clientdata and regdata back to eduMFA (2. step).
14.2.1.2.21.2. Authentication¶
The U2F token is a challenge response token. I.e. you need to trigger a challenge e.g. by sending the OTP PIN/Password for this token.
14.2.1.2.21.2.1. Get the challenge¶
POST /validate/check HTTP/1.1
Host: example.com
Accept: application/json
user=cornelius
pass=tokenpin
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"detail": {
"attributes": {
"hideResponseInput": true,
"img": ...imageUrl...
"u2fSignRequest": {
"challenge": "...",
"appId": "...",
"keyHandle": "...",
"version": "U2F_V2"
}
},
"message": "Please confirm with your U2F token (Yubico U2F EE ...)"
"transaction_id": "02235076952647019161"
},
"id": 1,
"jsonrpc": "2.0",
"result": {
"status": true,
"value": false,
},
"version": "eduMFA unknown"
}
14.2.1.2.21.2.2. Send the Response¶
The application now needs to call the javascript function u2f.sign with the u2fSignRequest from the response.
var signRequests = [ error.detail.attributes.u2fSignRequest ]; u2f.sign(signRequests, function(u2fResult) {} );
The response handler function needs to call the /validate/check API again with the signatureData and clientData returned by the U2F device in the u2fResult:
POST /validate/check HTTP/1.1
Host: example.com
Accept: application/json
user=cornelius
pass=
transaction_id=<transaction_id>
signaturedata=signatureData
clientdata=clientData
14.2.1.2.21.3. Implementation¶
- class edumfa.lib.tokens.u2ftoken.U2fTokenClass(db_token)[source]¶
The U2F Token implementation.
Create a new U2F Token object from a database object
- Parameters:
db_token (DB object) – instance of the orm db object
- classmethod api_endpoint(request, g)[source]¶
This provides a function to be plugged into the API endpoint /ttype/u2f
The u2f token can return the facet list at this URL.
- Parameters:
request – The Flask request
g – The Flask global object g
- Returns:
Flask Response or text
- check_otp(otpval, counter=None, window=None, options=None)[source]¶
This checks the response of a previous challenge.
- Parameters:
otpval – N/A
counter – The authentication counter
window – N/A
options – contains “clientdata”, “signaturedata” and “transaction_id”
- Returns:
A value > 0 in case of success
- client_mode = 'u2f'¶
- create_challenge(transactionid=None, options=None)[source]¶
This method creates a challenge, which is submitted to the user. The submitted challenge will be preserved in the challenge database.
If no transaction id is given, the system will create a transaction id and return it, so that the response can refer to this transaction.
- Parameters:
transactionid – the id of this challenge
options (dict) – the request context parameters / data
- Returns:
tuple of (bool, message, transactionid, attributes)
- Return type:
tuple
The return tuple builds up like this:
bool
if submit was successful;message
which is displayed in the JSON response; additional challengereply_dict
, which are displayed in the JSON challenges response.
- static get_class_info(key=None, ret='all')[source]¶
returns a subtree of the token definition
- Parameters:
key (string) – subsection identifier
ret (user defined) – default return value, if nothing is found
- Returns:
subsection if key exists or user defined
- Return type:
dict or scalar
- static get_class_prefix()[source]¶
Return the prefix, that is used as a prefix for the serial numbers. :return: U2F :rtype: basestring
- static get_class_type()[source]¶
Returns the internal token type identifier :return: u2f :rtype: basestring
- get_init_detail(params=None, user=None)[source]¶
At the end of the initialization we ask the user to press the button
- is_challenge_request(passw, user=None, options=None)[source]¶
check, if the request would start a challenge In fact every Request that is not a response needs to start a challenge request.
At the moment we do not think of other ways to trigger a challenge.
This function is not decorated with
@challenge_response_allowed
as the U2F token is always a challenge response token!- Parameters:
passw – The PIN of the token.
options – dictionary of additional request parameters
- Returns:
returns true or false