15.2.1.2.20. U2F Token

U2F is the “Universal 2nd Factor” specified by the FIDO Alliance. The register and authentication process is described here:

https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html

But you do not need to be aware of this. privacyIDEA wraps all FIDO specific communication, which should make it easier for you, to integrate the U2F tokens managed by privacyIDEA into your application.

U2F Tokens can be either

  • registered by administrators for users or

  • registered by the users themselves.

15.2.1.2.20.1. Enrollment

The enrollment/registering can be completely performed within privacyIDEA.

But if you want to enroll the U2F token via the REST API you need to do it in two steps:

15.2.1.2.20.1.1. 1. Step

POST /token/init HTTP/1.1
Host: example.com
Accept: application/json

type=u2f

This step returns a serial number.

15.2.1.2.20.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 privacyIDEA (2. step).

15.2.1.2.20.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.

15.2.1.2.20.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": "privacyIDEA unknown"
 }

15.2.1.2.20.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

15.2.1.2.20.3. Implementation

class privacyidea.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 challenge reply_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

update(param, reset_failcount=True)[source]

This method is called during the initialization process.

Parameters

param (dict) – parameters from the token init

Returns

None