{"id":20284866,"url":"https://github.com/allanoricil/esp32-mfa-authenticator","last_synced_at":"2025-07-23T06:37:31.051Z","repository":{"id":224286165,"uuid":"761894953","full_name":"AllanOricil/esp32-mfa-authenticator","owner":"AllanOricil","description":"ESP32 MFA Authenticator ","archived":false,"fork":false,"pushed_at":"2025-05-03T21:08:34.000Z","size":3428,"stargazers_count":83,"open_issues_count":8,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-03T22:18:25.931Z","etag":null,"topics":["2fa","authenticator","esp32","mfa","totp"],"latest_commit_sha":null,"homepage":"https://allanoricil.github.io/esp32-mfa-authenticator/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AllanOricil.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-22T17:28:04.000Z","updated_at":"2025-05-03T21:08:36.000Z","dependencies_parsed_at":"2024-03-08T01:25:07.128Z","dependency_job_id":"6119a797-9106-45c6-b5d2-1a65c11d8caa","html_url":"https://github.com/AllanOricil/esp32-mfa-authenticator","commit_stats":null,"previous_names":["allanoricil/esp32-mfa-display","allanoricil/esp32-mfa-totp-generator","allanoricil/esp32-mfa-authenticator"],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/AllanOricil/esp32-mfa-authenticator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllanOricil%2Fesp32-mfa-authenticator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllanOricil%2Fesp32-mfa-authenticator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllanOricil%2Fesp32-mfa-authenticator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllanOricil%2Fesp32-mfa-authenticator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AllanOricil","download_url":"https://codeload.github.com/AllanOricil/esp32-mfa-authenticator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AllanOricil%2Fesp32-mfa-authenticator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266631702,"owners_count":23959422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2fa","authenticator","esp32","mfa","totp"],"created_at":"2024-11-14T14:22:35.163Z","updated_at":"2025-07-23T06:37:31.032Z","avatar_url":"https://github.com/AllanOricil.png","language":"C","readme":"![Build with PlatformIO](https://img.shields.io/badge/build%20with-PlatformIO-orange?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMjUwMCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTEyOCAwQzkzLjgxIDAgNjEuNjY2IDEzLjMxNCAzNy40OSAzNy40OSAxMy4zMTQgNjEuNjY2IDAgOTMuODEgMCAxMjhjMCAzNC4xOSAxMy4zMTQgNjYuMzM0IDM3LjQ5IDkwLjUxQzYxLjY2NiAyNDIuNjg2IDkzLjgxIDI1NiAxMjggMjU2YzM0LjE5IDAgNjYuMzM0LTEzLjMxNCA5MC41MS0zNy40OUMyNDIuNjg2IDE5NC4zMzQgMjU2IDE2Mi4xOSAyNTYgMTI4YzAtMzQuMTktMTMuMzE0LTY2LjMzNC0zNy40OS05MC41MUMxOTQuMzM0IDEzLjMxNCAxNjIuMTkgMCAxMjggMCIgZmlsbD0iI0ZGN0YwMCIvPjxwYXRoIGQ9Ik0yNDkuMzg2IDEyOGMwIDY3LjA0LTU0LjM0NyAxMjEuMzg2LTEyMS4zODYgMTIxLjM4NkM2MC45NiAyNDkuMzg2IDYuNjEzIDE5NS4wNCA2LjYxMyAxMjggNi42MTMgNjAuOTYgNjAuOTYgNi42MTQgMTI4IDYuNjE0YzY3LjA0IDAgMTIxLjM4NiA1NC4zNDYgMTIxLjM4NiAxMjEuMzg2IiBmaWxsPSIjRkZGIi8+PHBhdGggZD0iTTE2MC44NjkgNzQuMDYybDUuMTQ1LTE4LjUzN2M1LjI2NC0uNDcgOS4zOTItNC44ODYgOS4zOTItMTAuMjczIDAtNS43LTQuNjItMTAuMzItMTAuMzItMTAuMzJzLTEwLjMyIDQuNjItMTAuMzIgMTAuMzJjMCAzLjc1NSAyLjAxMyA3LjAzIDUuMDEgOC44MzdsLTUuMDUgMTguMTk1Yy0xNC40MzctMy42Ny0yNi42MjUtMy4zOS0yNi42MjUtMy4zOWwtMi4yNTggMS4wMXYxNDAuODcybDIuMjU4Ljc1M2MxMy42MTQgMCA3My4xNzctNDEuMTMzIDczLjMyMy04NS4yNyAwLTMxLjYyNC0yMS4wMjMtNDUuODI1LTQwLjU1NS01Mi4xOTd6TTE0Ni41MyAxNjQuOGMtMTEuNjE3LTE4LjU1Ny02LjcwNi02MS43NTEgMjMuNjQzLTY3LjkyNSA4LjMyLTEuMzMzIDE4LjUwOSA0LjEzNCAyMS41MSAxNi4yNzkgNy41ODIgMjUuNzY2LTM3LjAxNSA2MS44NDUtNDUuMTUzIDUxLjY0NnptMTguMjE2LTM5Ljc1MmE5LjM5OSA5LjM5OSAwIDAgMC05LjM5OSA5LjM5OSA5LjM5OSA5LjM5OSAwIDAgMCA5LjQgOS4zOTkgOS4zOTkgOS4zOTkgMCAwIDAgOS4zOTgtOS40IDkuMzk5IDkuMzk5IDAgMCAwLTkuMzk5LTkuMzk4em0yLjgxIDguNjcyYTIuMzc0IDIuMzc0IDAgMSAxIDAtNC43NDkgMi4zNzQgMi4zNzQgMCAwIDEgMCA0Ljc0OXoiIGZpbGw9IiNFNTcyMDAiLz48cGF0aCBkPSJNMTAxLjM3MSA3Mi43MDlsLTUuMDIzLTE4LjkwMWMyLjg3NC0xLjgzMiA0Ljc4Ni01LjA0IDQuNzg2LTguNzAxIDAtNS43LTQuNjItMTAuMzItMTAuMzItMTAuMzItNS42OTkgMC0xMC4zMTkgNC42Mi0xMC4zMTkgMTAuMzIgMCA1LjY4MiA0LjU5MiAxMC4yODkgMTAuMjY3IDEwLjMxN0w5NS44IDc0LjM3OGMtMTkuNjA5IDYuNTEtNDAuODg1IDIwLjc0Mi00MC44ODUgNTEuODguNDM2IDQ1LjAxIDU5LjU3MiA4NS4yNjcgNzMuMTg2IDg1LjI2N1Y2OC44OTJzLTEyLjI1Mi0uMDYyLTI2LjcyOSAzLjgxN3ptMTAuMzk1IDkyLjA5Yy04LjEzOCAxMC4yLTUyLjczNS0yNS44OC00NS4xNTQtNTEuNjQ1IDMuMDAyLTEyLjE0NSAxMy4xOS0xNy42MTIgMjEuNTExLTE2LjI4IDMwLjM1IDYuMTc1IDM1LjI2IDQ5LjM2OSAyMy42NDMgNjcuOTI2em0tMTguODItMzkuNDZhOS4zOTkgOS4zOTkgMCAwIDAtOS4zOTkgOS4zOTggOS4zOTkgOS4zOTkgMCAwIDAgOS40IDkuNCA5LjM5OSA5LjM5OSAwIDAgMCA5LjM5OC05LjQgOS4zOTkgOS4zOTkgMCAwIDAtOS4zOTktOS4zOTl6bS0yLjgxIDguNjcxYTIuMzc0IDIuMzc0IDAgMSAxIDAtNC43NDggMi4zNzQgMi4zNzQgMCAwIDEgMCA0Ljc0OHoiIGZpbGw9IiNGRjdGMDAiLz48L3N2Zz4=) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md) [![CI](https://github.com/AllanOricil/esp32-mfa-totp-generator/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/AllanOricil/esp32-mfa-totp-generator/actions/workflows/ci.yaml)\n\n# ESP32 MFA Authenticator\n\n\u003cimg src=\"https://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/bb904daf-c194-4e9f-8948-a0503e94bca1\" height=\"400\"\u003e\n\nThis is a personal project that I created to help me to get MFA TOTPs without using my phone.\n\n## 🌐 Install\n\nYou can flash your ESP32-CYD board with the latest build using this [site](https://allanoricil.github.io/esp32-mfa-authenticator).\n\n\u003e [!IMPORTANT]\n\u003e Read the site and the github workflows source codes to verify that the build artifact is, in fact, the one from the latest release published in this repository.\n\n\u003e [!NOTE]\n\u003e This site was based on https://esphome.github.io/esp-web-tools/\n\n## 🎬 Demos\n\n\u003chttps://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/166f6ea7-1046-4117-ae22-67991c8e6d8c\u003e\n\n\u003chttps://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/6e240518-a35b-4bf0-8a41-ece0dad9efb9\u003e\n\n\u003chttps://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/a398b55b-a415-4d21-8f28-91df153bac9f\u003e\n\n\u003chttps://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/b610d1de-1bf9-47fe-9148-8973cb30205d\u003e\n\n## ⚙️ Parts\n\n- `ESP32-2432S028`\n- 3D printed black case\n- Acrillic case\n\n\u003e [!TIP]\n\u003e You can buy this board from Aliexpress clicking on any of these affiliate links: [USD](https://s.click.aliexpress.com/e/_mNCBRAA) [BRL](https://s.click.aliexpress.com/e/_mOtZaxM)\n\n\u003e [!TIP]\n\u003e You can buy this acrillic case from Aliexpress clicking on any of these affiliate links: [USD](https://s.click.aliexpress.com/e/_mLYVthc) [BRL](https://s.click.aliexpress.com/e/_m0Tt9wq)\n\n\u003e [!TIP]\n\u003e The 3D model for the black case was taking from this [link](https://makerworld.com/en/models/137424#profileId-149549)\n\n## 💵 Total Project Cost\n\n| Part                  | Cost     |\n| --------------------- | -------- |\n| ESP32-2432S028        | 9.25 USD |\n| 3D printed black case | 12.7 USD |\n| Acrillic case         | 2.5 USD  |\n\n\u003e [!NOTE]\n\u003e The above list doesn't consider expenses with taxes and shipping.\n\n\u003e [!NOTE]\n\u003e Prices were taking in February 2024.\n\n## 💻 Dev Environment Requirements\n\n| dependency                       | version   |\n| -------------------------------- | --------- |\n| python                           | \u003e= v3.9   |\n| node                             | \u003e= v18.18 |\n| npm                              | \u003e= v10.2  |\n| vscode                           | \u003e= v1.87  |\n| platform.io ide vscode extension | \u003e= v3.3   |\n| docker                           | \u003e= v25.0  |\n\n\u003e [!IMPORTANT]\n\u003e Don't forget to install a [driver to allow your OS to recognize esp32](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers)\n\n\u003e [!IMPORTANT]\n\u003e Node and npm, its package manager, are required because several development tools are used in this project. Among these tools are those that enforce the \"conventional commits\" standard. This standard is a lightweight convention on top of commit messages, offering an easy set of rules for creating an explicit commit history.\n\n\u003e [!TIP]\n\u003e If platform.io extension does not recognize your board after clicking on `Upload`, `Upload and Monitor` or `Monitor` buttons, it means the driver was not properly setup. In MacOS, after installing the driver from Sillicon Labs, I had to restart the system before mac could identify the board.\n\n## 🔌 Boot and Reset Requirements\n\n- 2.4Ghz WiFi signal with internet connection, in order to sync the board's clock with the [NTP server](https://ntp.org/).\n- SD card with `config.yml` and `services.yml` files in the root, as shown below:\n\n### ⚙️ config.yml\n\n```yml\n# [REQUIRED] configure the credentials used to connect to a wifi network\nwifi:\n  # [REQUIRED] (text) wifi connection password\n  password: test\n  # [REQUIRED] (text) wifi id\n  ssid: test\n\n# [REQUIRED] configure authentication for the board\nauthentication:\n  # [OPTIONAL] (number) [default 3] board is locked and requires a hard reset, after N wrong unlock attempts\n  unlock_attempts: 3\n  pin:\n    # [OPTIONAL] (text) pin code composed of numbers only and HMAC-SHA256 hashed\n    hash: 7dbd45736c57090dd62a7e1c8db1a08c353b4a836f2c6b43fd1dd3f1e747ea59\n    # [OPTIONAL] (text) key used to hash pin code\n    key: TUwNzIxF5lJncAJVMkmb4EiSP9vm0OyF\n\n# [OPTIONAL] configure display settings\ndisplay:\n  # [OPTIONAL] (number) [default 10] if provided, the display will turn off after n seconds have passed\n  sleep_timeout: 10\n\n# [OPTIONAL] configure touch settings\ntouch:\n  # [OPTIONAL] (bool=false|0) calibrate touch sensor if true or 1\n  calibrate: 0\n\n# [OPTIONAL] configure the management app\nmanager:\n  # [OPTIONAL] configure authentication for the management app. The management app is enabled only if username, password, key are set.\n  authentication:\n    # [REQUIRED] (text) username to start a session\n    username: admin\n    # [REQUIRED] (text) HMAC-SHA256 hashed password to start a session\n    password: 7dbd45736c57090dd62a7e1c8db1a08c353b4a836f2c6b43fd1dd3f1e747ea59\n    # [REQUIRED] (text) 32 characters key used to hash the password\n    key: TUwNzIxF5lJncAJVMkmb4EiSP9vm0OyF\n    # [OPTIONAL] (number) [default 5] amount of minutes for the session duration\n    session_length: 5\n```\n\n\u003e [!IMPORTANT]\n\u003e Upon the initial boot, it is imperative to undergo the calibration process at least once, as outlined in the `How to build` section below.\n\n\u003e [!TIP]\n\u003e Once the boot process is finished, remove the SD card from the board, and store it somewhere safe. Before rebooting, or if you want to add new secrets, remember to put it back in the board.\n\n\u003e [!NOTE]\n\u003e A pink screen appears to indicate that both `config.yml` and `services.yml` have been parsed and loaded into memory.\n\n### 🔒 services.yml\n\n```yml\n# [REQUIRED] (list) stores a list of services\nservices:\n  # [REQUIRED] (text) unique name for a service in a group. It must not exceed 60 characters.\n  - name: abc\n    # [REQUIRED] (text) Base32 encoded secret for the service.\n    secret: abc\n    # [OPTIONAL] (number) [default 0] it must be an integer between 0 and 255. It is used to group services and determine their priority. Services with smaller group numbers are rendered first. For instance, services assigned to group 100 will be rendered before those in group 255. In a services file, you can have up to 100 services.\n    group: 0\n```\n\n## 📖 Guides\n\n### 📚 How to navigate and control the board\n\n- \u003cb\u003eChange groups:\u003c/b\u003e Swipe left or right to navigate between different groups.\n- \u003cb\u003eLock:\u003c/b\u003e Tap twice on the screen to lock the board.\n- \u003cb\u003eWake up:\u003c/b\u003e Tap once to wake up the board.\n\n### 📚 How to build using PlatformIO CLI\n\nInstall PlatformIO's official CLI using this [tutorial](https://platformio.org/install/cli), and then follow the next steps:\n\n1. Run `platformio device list` and annotate the device port of your board.\n\n\u003e [!TIP]\n\u003e You can discover which port belongs to your board by comparing the outputs of this command when your board is connected and when it is not.\n\n2. Run `./scripts/dev.sh --port ${DEVICE_PORT} --env ${ENV}` to build and flash the code into your board\n\n\u003e [!IMPORTANT]\n\u003e Remember to substitue `${DEVICE_PORT}` with the value you got in step 1.\n\n\u003e [!IMPORTANT]\n\u003e Remember to substitute `${ENV}` by `prod` or `dev`. The only difference between both environments is the log level. In `prod` logs are disabled, while in `dev` all logs are visible.\n\n### 📚 How to listen to the serial port using PlatformIO CLI\n\nTo listen to the serial port using PlatformIO CLI you can use the following commnad:\n\n```bash\nplatformio device monitor\n```\n\n### 📚 How to register a Service\n\nServices are registered in a file called `services.yml` that must be located in the root of an SD card. For example:\n\n```yml\nservices:\n  - name: aws:root:allanoricil@company-1.com\n    secret: encoded-secret\n    group: 0\n  - name: aws:staging:allanoricil@company-1.com\n    secret: encoded-secret\n    group: 0\n  - name: aws:production:allanoricil@company-1.com\n    secret: encoded-secret\n    group: 0\n  - name: aws:1234565:allanoricil@company-2.com\n    secret: encoded-secret\n    group: 1\n  - name: aws:6785910:allanoricil@company-2.com\n    secret: encoded-secret\n    group: 1\n  - name: aws:7815795:allanoricil@company-2.com\n    secret: encoded-secret\n    group: 1\n  - name: github\n    secret: encoded-secret\n    group: 2\n  - name: docker\n    secret: encoded-secret\n    group: 2\n  - name: npm\n    secret: encoded-secret\n    group: 2\n```\n\n\u003e [!IMPORTANT]\n\u003e At present, you can register up to 100 services divided across 10 groups.\n\n\u003e [!IMPORTANT]\n\u003e The service name must not exceed 60 characters.\n\n\u003e [!IMPORTANT]\n\u003e The service group must be between 0 and 255. If you don't set a value, it will default to 0.\n\n\u003e [!IMPORTANT]\n\u003e Secrets must be stored unencrypted and encoded using Base32. All MFA services I tried already provide secrets in Base32 encoding. If you find one that does not, ensure the secret is Base32 encoded before adding it to the file.\n\n\u003e [!IMPORTANT]\n\u003e The service name acts as a unique key within a group. If two services share the same key within the same group, the last one listed in the file will be the one used because it was the last service to be added in the db.\n\n### 📚 How to verify if TOTP codes are correct\n\n1. Go to \u003chttps://totp.danhersam.com/\u003e\n2. Paste/type your encoded base 32 secret in the secret field, and then compare the TOTP code shown with the one you are seeing on the ESP32's screen.\n\n### 📚 How to recalibrate the touch sensor\n\n1. Open your `config.yml` file.\n2. Add the following property at the root level:\n\n```yml\ntouch:\n  calibrate: true\n```\n\n3. Insert the SD card with the updated `config.yml` into your board.\n4. Press the `RST` button on the board to reboot it.\n5. Wait for the calibration screen to appear, as shown below:\n\n\u003cimg src=\"./images/touch-calibration-flow-1.png\" width=\"250px\"\u003e\n\n6. Follow the on-screen instructions to complete the calibration process.\n7. Once the calibration is finished, update `config.yml` again:\n\n```yml\ntouch:\n  calibrate: false\n```\n\n8. Save the file, insert the SD card back into the board, and reboot by pressing the `RST` button.\n9. Confirm that the calibration screen no longer appears.\n10. Your touchscreen is now calibrated and ready to use! 🎉\n\n\u003e [!NOTE]\n\u003e The calibration state will initiate upon the initial boot of the board, regardless of the content stored in `config.yml`, if no calibration is found in [SPIFFS](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/spiffs.html).\n\n\u003e [!IMPORTANT]\n\u003e The pin screen won't work if you did not calibrate the touch sensor.\n\n### 📚 How to update `config.yml` from a browser\n\nWhen the board is connected to your local network, a settings page, similarly to the one found in routers, can be used to update the `config.yml` in the SD card without the need of inserting it on a different computer. You can access this settings page at `http://${LOCAL_NETWORK_DEVICE_IP}/esp32/settings`.\n\n\u003cimg width=\"700\" alt=\"image\" src=\"https://github.com/user-attachments/assets/9da1271e-d3a6-41fa-bef5-bef930022761\" /\u003e\n\n\u003e [!IMPORTANT]\n\u003e At the moment, all secrets in this form must be set before submiting it. If you fail to do it, all secrets in your `config.yml` will be overwritten by `*****`.\n\n\u003e [!IMPORTANT]\n\u003e For security purposes, none of the secrets are exposed by the board's webserver. If you inspect the page using your browser dev tools, you will noticed that all secrets are fetched as `*****`. In the future, after implementing HTTPS, you will be to manage those secrets from the browser, but only after providing a PIN number of using your fingerprint.\n\n### 📚 How to setup PIN number\n\n1. Open a terminal and run the following command to create a strong secret that is exactly 32 characters long\n\n```bash\nopenssl rand -base64 24 | head -c 32; echo\n```\n\u003cimg width=\"500\" alt=\"image\" src=\"https://github.com/user-attachments/assets/5f653506-aaa3-452b-bd1e-c7acc95f8bb4\"\u003e\n\n\n2. Then run the following comand to hash your pin number. Don't forget to substitute `\"YOUR_PIN_NUMBER\"` and `\"YOUR_32_CHARACTERS_LONG_SECRET\"`. The PIN must consist only of digits and must be between 6 and 20 digits in length.\n\n```bash\necho -n \"YOUR_PIN_NUMBER\" | openssl dgst -sha256 -hmac \"YOUR_32_CHARACTERS_LONG_SECRET\" | awk '{print $2}'\n```\n\n\u003cimg width=\"988\" alt=\"image\" src=\"https://github.com/user-attachments/assets/503a01de-0a4f-4bf8-95a3-d5ed660bfcee\"\u003e\n\n3. Copy the generated hash. It must be 64 characters long.\n\n4. In your config.yml\n\n- set `hash` with the generated hash you got in step 3\n- set `key` with the secret you got in step 1\n\n```yml\nauthentication:\n  pin:\n    hash: 7dbd45736c57090dd62a7e1c8db1a08c353b4a836f2c6b43fd1dd3f1e747ea59\n    key: TUwNzIxF5lJncAJVMkmb4EiSP9vm0OyF\n  unlock_attempts: 3\n```\n\n### 📚 How to setup the Password for the Management App\n\nThe steps to generate the password for the management app are the same as those used to set up the PIN. Once the password is created, add the following properties to your `config.yml`:\n\n```yml\nmanager:\n  authentication:\n    username: admin\n    password: 7dbd45736c57090dd62a7e1c8db1a08c353b4a836f2c6b43fd1dd3f1e747ea59\n    key: TUwNzIxF5lJncAJVMkmb4EiSP9vm0OyF\n```\n\n## 🎯 Roadmap\n\n### ✅ Display multiple TOTPs\n\n### ✅ Unlock with PIN Code\n\n### ✅ Manage the board settings via Web App\n\n### ✅ Organize Services into Groups\n\n### ✅ Lock the board after N seconds of inactivity\n\n### ✅ Lock the board after N failed unlock attempts\n\n### 🔜 Encryption\n\n### 🔜 Unlock with fingerprint\n\n## 💖 Become a Sponsor\n\nIf this device has made your life easier, consider supporting its development by clicking the button below.\n\n\u003ca href=\"https://www.buymeacoffee.com/allanoricil\" target=\"_blank\"\u003e\n  \u003cimg\n      src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\"\n      alt=\"Buy Me A Coffee\"\n      style=\"width: 217px;\" /\u003e\n\u003c/a\u003e\n","funding_links":["https://www.buymeacoffee.com/allanoricil"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallanoricil%2Fesp32-mfa-authenticator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallanoricil%2Fesp32-mfa-authenticator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallanoricil%2Fesp32-mfa-authenticator/lists"}