Alle Beiträge von Mario Enrico Ragucci

Sozial angepasster Nerd mit einem Faible für Technologie. Ich beschäftige mich auf der Arbeit und im privaten Bereich mit Anwendungen, die unser Umfeld beeinflussen, und der Infrastruktur, die solche Anwendungen erst ermöglichen.

go-tiny-mfa: a multifactor solution written in go


a tinymfa implementation written in go. See for more information.

Our repository on github:

Find a docker repository at

Attention This is a hobby project to get more used to go-programming. It is not intended to be used in a production environment without making further security related steps.

How it works

  1. tinymfa connects to a postgres database and creates the required table structures. Then, it generates a root encryption key and access token. The encryption key is stored on the filesystem.
  2. when creating an issuer, a new encryption key is generated, encrypted with the root encryption key and then stored to the database. Also, an access token unique to this issuer is generated as well.
  3. when creating a user below an issuer, a new secret key is generated and encrypted with the issuer encryption key.
  4. The api offers an endpoint to generate a QRCode for a user. Use this to let the user register their secret key in an Authenticator App
  5. The api offers an endpoint to validate a token. Send the token using a http post request to the api interface. The resulting json object contains the boolean result of the validation.

Access tokens

tinymfa can be configured to validate access to its resources. Once activated, tinymfa checks for presence of the http header key ‚tiny-mfa-access-token‘. This must be either the root token created on installation, or the issuer token presented upon issuer creation.

API Endpoints

System Configuration and Audit

/api/v1/system/auditGETReturn audit entries
/api/v1/system/configurationGETReturn current system configuration
/api/v1/system/configurationPOSTUpdates the system configuration

payload: Update system configuration

http_portintegerthe port to run on. Requires a restart!
deny_limitintegerhow many times is a user allowed to input a wrong token before we don’t allow validation for the given message. This is to defeat brute force attacks
veriy_tokenbooleanwhether to verify if the tiny-mfa-access-token is set and contains a valid token
    "http_port" : 57687,
    "deny_limit": 3,
    "verify_tokens": true

Issuer handling

/api/v1/issuerGETReturn all registered issuers
/api/v1/issuerPOSTCreate a new issuer using a POST request
/api/v1/issuer/{issuer}GETReturn a distinct issuer by its name
/api/v1/issuer/{issuer}POSTUpdates a distinct issuer using a POST request
/api/v1/issuer/{issuer}DELETEDeletes a distinct issuer using a DELETE request

payload: create a new issuer

namestringthe name of this issuer
contactstringa mail adress of the responsible person
enabledbooleanwhether this issuer is active
    "name": "issuer.local",
    "contact": "demo@issuer.local",
    "enabled": true

payload: update a new issuer

contactstringa mail adress of the responsible person
enabledbooleanwhether this issuer is active
    "contact": "demo@issuer.local",
    "enabled": true

Access token handling

/api/v1/issuer/{issuer}/tokenGETReturn all registered access tokens for a given issuer
/api/v1/issuer/{issuer}/tokenPOSTCreates a new access token for the given issuer using a PUT request
/api/v1/issuer/{issuer}/token/{tokenid}DELETEDeletes a distinct access token in the scope of a distinct issuer

payload: create a new issuer access token

descriptionstringa description for the new token
    "description" : "my access token"

User handling

/api/v1/issuer/{issuer}/usersGETReturn all users belonging to the scope of a distinct issuer
/api/v1/issuer/{issuer}/usersPOSTCreate a new user in the scope of a distinct issuer
/api/v1/issuer/{issuer}/users/{user}GETReturn a distinct user in the scope of a distinct issuer
/api/v1/issuer/{issuer}/users/{user}POSTUpdate a distinct user in the scope of a distinct issuer
/api/v1/issuer/{issuer}/users/{user}DELETEDeletes a distinct user in the scope of a distinct issuer

payload: create a new user

namestringthe name this user
emailstringa mail adress of the user
enabledbooleanwhether this user is active
    "name" : "demo",
    "email": "demo@issuer.local",
    "enabled": true

payload: update an existing user

emailstringa mail adress of the user
enabledbooleanwhether this user is active
    "email": "demo.address@issuer.local",
    "enabled": true

User token handling

/api/v1/issuer/{issuer}/users/{user}/totpGETGenerates and returns a PNG image of a QRCode in the scope of a distinct user and issuer
/api/v1/issuer/{issuer}/users/{user}/totpPOSTValidates a given token in the scope of a distinct user and issuer

payload: validate a totp token

tokenstringthe token to validate
    "token": "123456"


This will result in a working tiny-mfa instance:

version: "3"
        image: postgres:latest
            - tiny-mfa-net
            - data:/var/lib/postgresql/data
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=postgres
            - POSTGRES_DB=tinymfa
        image: tinymfa/go-tiny-mfa
            - tiny-mfa-net
            - "57687:57687"
            - tinysecret:/opt/go-tiny-mfa/secrets
            - POSTGRES_HOST=database
            - POSTGRES_USER=postgres
            - POSTGRES_PASSWORD=postgres
            - POSTGRES_DB=tinymfa
        restart: unless-stopped



  • create a tiny-mfa instance using the docker-compose script from above
  • create an issuer:
curl --location --request POST 'http://localhost:57687/api/v1/issuer' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "issuer.local",
    "contact": "contact@issuer.local",
    "enabled": true
  • create a user:
curl --location --request POST 'http://localhost:57687/api/v1/issuer/issuer.local/users' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name" : "demo",
    "email": "demo@issuer.local",
    "enabled": true
  • get the QRCode for the Authenticator App:
curl --location --request GET 'http://localhost:57687/api/v1/issuer/issuer.local/users/demo/totp'
  • validate a token
curl --location --request POST 'http://localhost:57687/api/v1/issuer/issuer.local/users/demo/totp' \
--header 'Content-Type: application/json' \
--data-raw '{
    "token" : "123456"

Already working

  • v1 api to CRUD issuers and users
  • validate tokens
  • limit validation attempts to defeat brute force attacks
  • generate QRCode png images
  • basic authorization via http header


  • authorization model
  • administrative UI

TinyMFA: MultifactOr Authentication for IdentityIQ


IdentityIQ 7.2 introduced a new functionality for multifactor authentication. To leverage this functionality without having the need of owning a RSA- or DUO account, the TinyMFA Plugin was created.

This plugin implements the RFC for TOTP based tokens, so (in theory) it is compatible with any app that can register QR-Codes and calculate TOTP tokens (tested with google-authenticator, DUO mobile and FreeOTP). To maximize security, each user gets a unique 128bit key assigned that will be used to calculate the token.

Also, it ships with a MFA configuration, a DynamicScope and assignable Capabilities to grant users access to their personalized token (via QR-Code) as well as forcing users to authenticate with a token.



This plugin makes usage of the new multifactor features introduces with IdentityIQ 7.2. Therefore, its usage is limited to this version (or its successors).


As an administrator, go to the plugin administration page. Click on the “new” button on the topright corner of the administration page. Drag & Drop the on the page.

This concludes the installation.


Plugin Settings

configuration settings of the plugin

Figure 1: Plugin Settings

There are several settings on the Plugin Settings page that can be changed according to your needs.

Issuer Domain

This will define how the authenticator app will present your account. The format will be %identity.getName()%@%issuerDomain%

Example: spadmin@sailpoint.labs

Defaults to sailpoint.lab

Maximum validation attempts

How many validation-attempts per 30 seconds to accept until we don’t allow this certain token to authenticate. This will defeat brute-force attacks. Defaults to 15

iOS Appstore Link

The link to a totp authentication app in the Apple Appstore. Default points to Google Authenticator.

Android Appstore Link

The link to a totp authentication app in the Google Play Store. Default points to Google Authenticator.

QRCode background color

The color of the background color of the QRCode that is generated. Defaults to white

QRCode foreground color

The color of the foreground color of the QRCode that is generated. Defaults to SailPoint blue.

Send enrollment notification

If this is checked, a mail is sent out upon user enrollment.

Enrollment notification template

The template to use when notifying users

Assigning Capabilities

After installing the plugin, a new Capability “TinyMFA Plugin Access” is introduced. This grants access to the plugin page, where you can see your personalized QR Code as well as checking whether your authenticator was registered successfully.

Another Capability “TinyMFA activated Identity” can be assigned that results in every user having this capability assigned being part of the Dynamic Scope “TinyMFA Authenticated”.

Also, each identity having this capability assigned are forced to authenticate via a multifactor token.

Finally, the Capability “TinyMFA Administrator” grants access to a simple admin page, allowing the administrator to review login attempts and disable/enable accounts.

Administrative Setup

To review the changes made to the login Login Configuration, go to Global Settings –> Login Configuration –> MFA Configuration

You should find an activated Configuration “MFA TinyMFA” with a Population “TinyMFA Authenticated”.

MFA Configuration settings

Figure 2: Login Configuration


Once entitled to plugin usage, you will find a new icon resembling a mobile phone on your menubar.

By clicking on this icon, you will be transferred to the TinyMFA Plugin page, where you will have several options:

  • Generate a QR-Code
  • Test your token
  • Activate your token
  • Optional: If you are an administrator, you will see two further options:
    • Account Administration
    • Audit

Generate QR-Code

Screenshot of a generated QR Code

Figure 3: Generated QR-Code

A QR-Code is generated for your identity that contains all information needed by a multifactor app like google authenticator or DUO mobile.

Test your token

screenshot of token validation page

Figure 4: Test token

On this page, you can test your token generator. Enter the token that your device/app generated and click on validate. You will receive either success- or errormessage depending on the outcome.

Activate your token

screenshot of token activation page

Figure 5: Activate token

On this page, you can activate your token generator. Enter the token that your device/app generated and click on activate. You will receive either success- or errormessage depending on the outcome.

After successful activation, you have to enter a token on your next login!

Account Administration

The account administration page offers you Enrollment and Management functions.

User Enrollment

Enter the identity name in the search field and click on search. If an identity can be found, it will present several basic information like firstname, lastname and email address.

For an ordinary enrollment, just click on “enroll”.

enroll user account

Figure 6: User Enrollment with standard capability

If you want to enroll the account with administrative access, click on the checkbox “provision with admin capability”.

enroll user account with admin capability

Figure 7: User Enrollment with admin capability

User Management

The User Management functionality lets you disable or enable an enrolled account.

user management

Figure 8: User management


The Audit page offers a quick and convenient way to have a look at the latest authentication results.

audit information

Figure 9: Audit Table

Plugin Uninstall Instructions

After uninstalling the plugin via the plugin administration page, there are several leftovers that need to be uninstalled manually. You can do this via the debug pages.

  • SPRight: TinyMfaPluginIcon
  • SPRight: TinyMfaPluginActivatedIdentity
  • SPRight: TinyMfaPluginAdministrator
  • Capability: TinyMFAActivatedIdentity
  • Capability: TinyMFAPluginAccess
  • Capability: TinyMFAAdministrator
  • DynamicScope: TinyMFA Authenticated
  • Form: TinyMFA Token Form
  • Workflow: TinyMFA Authentication Workflow
  • Workflow: TinyMFA Enroll User Workflow
  • EmailTemplate: TinyMFA Enroll User Notification Template
  • Configuration: MFAConfiguration

Let’s Encrypt: What is wrong with you?

June, 2016: An initiative wants to bring SSL to the people. Let’s Encrypt is out of beta status, already serving 1,3 million certificates (according to them) and is eager to tell everyone that they happily deliver SSL to everyone, without exception.
Well, except to those that use IPv6! Really, what is wrong with you people at Let’s Encrypt? Do you realize that you will get SSL certificates cheaper than an IPv4 address these days? It is because of people like you that we still need to talk about a change to IPv6. This has been necessary for 10 years at this time of writing.

This really gets me angry. Not because I urgently need their SSL certificates, but because I had to realize this after installing 350 Megabytes of Phython dependencies for their certbot tool.

Also it doesn’t help that they are making good progress on this since end of January. Honestly, Let’s Encrypt, what’s the matter? Are you confused by those long addresses?

Thanks for nothing, Let’s Encrypt. Please do your homework, your 1,3 million certificates don’t impress me. Get the basics straight!

Why CSV is a bad idea and should not be considered

Yes, I can already hear your shoutings.

„What is he talking about? CSV is a great format, and the easiest to use by far! You just String.split() by ‚;‘ and are done!!!“

Well, I’ll try to be honest: You are very wrong.

First of all: CSV is not a format. It is a bunch of values, separated by a character (this is what CSV stands for: Character Separated Values). There might be two of them. Or twenty. Or twohundred. We don’t know until we parse it. But even then we cannot say for sure that the column we are currently processing actually contains the data we are expecting. We rely on hope. Therefore we write down some expectations on the CSV file and call this format. Sometimes this works.

Second: If you are talking about the easyness of handling CSV, you are talking about that colleague that exports her Excel sheets to a .csv file. Yeah, that’s pretty easy. So, let’s be honest on this one, too: We decide to use CSV, because it is the easiest thing to do for the customer. She does not need a specialized application to create structured data with a strict format. She just needs to open Excel, fill in a few columns and then click on „Save as“.

As nice as it is to our beloved customer: This is where every implementer’s nightmare starts. Because the customer does not care how this CSV file looks like. What she does care is what her pretty Excel Sheet looks like, because this is what will (probably) be seen by someone (important). So she will be using all her Excel Skills and every aestethic sense to create a great looking Workbook, including carriage returns and almost every obscure UTF-8 character the codepage has to offer. And I cannot even be mad at her: If the CSV shall include, let’s say, a column for a description of something, I do not want to put that 2000 character description in one single line. If you have ever tried to do that with Excel will will know that you almost are forced to use carriage returns!

But this leads us to

Third: You cannot just String.split() by ‚;‘ and are done. You need to check if the columns are, by chance, also surrounded by quotes, because if so, you need to be aware of the fact that each column might include at least one ocurrence of your separator ‚;‘, which belongs to the value and shall not be considered as your separator. You also have to consider carriage returns, therefore you need to implement readahead of your CSV file. You also can never be sure if the customer did not move a column to another or splits one into two because „it looks more pretty“.

The list goes on and on, and over the time you will be implementing a monster in form of your very own CSV parser. By this time you will have also created your very own „format“ of a CSV file, because after several months of bugfixing „that crappy parser thing“ (that, by the way, has destroyed some of your reputation) you will have implemented at least some basic boundary checking on the supplied file. You will also be very tired at this point. Even if you were clever enough to use one of the several CSV parsers out there, you will still have a hard time.

  • Because you are fighting human creativity. You won’t believe what people did to their Excel Sheet just to make it look „perfect“.
  • Because the customer is not aware that her creativity is causing problems. She will be even mad at you because you force her to make her Excel Sheet look ugly.
  • Because you are forgetting one of the most important rules: Fix the sender, not the receiver.

Here we are, looking at Excel. And Excel does not do ANY checks for you. It just exports CSV. And most of the time it is capable of importing its own CSV export. Most of the time…

So, what to do? Well, I think it depends on the complexity of the data. Of course a CSV processor that processes data created by another machine that also follows strict patterns will be implemented very fast. But as soon as Excel Sheets are involved that transfer more than, let’s say, 10 Columns and 20 lines, you better think about doing something else. From my personal experience, implementing a specialized GUI will help you check the boundaries as your customer starts to input data. Of course it will take more time to deliver. But I bet you will save several weeks of work fixing another issue that came up with the latest CSV file. Also, you have absolute control over the to-be-used export format. By using annotations on Java classes you are able to marshall data to XML and unmarshall it back to objects without even thinking about how to parse this format.

I mean, how cool is that?!?

Creating SailPoint IdentityIQ WAR file with Maven

Using SailPoint’s Service Standard Build is okay if you need a quick and easy to setup buildenvironment. However, when you enter projects at bigger companies, you will realize that those companies rely on build systems around git/mercurial and Maven. This is a way of setting up a build using maven.
Creating SailPoint IdentityIQ WAR file with Maven weiterlesen