Duo Auth
This commit is contained in:
60
README.md
60
README.md
@@ -54,12 +54,14 @@ See the "[Connecting via Ethernet](#connecting-via-ethernet-optional)" section f
|
||||
## Recommended ESP32 devices
|
||||
|
||||
We don't recommend using single-core ESP32 devices (ESP32-C3, ESP32-C6, ESP32-H2, ESP32-Solo1).<br>
|
||||
Although NukiHub supports single-core devices, NukiHub uses both CPU cores (if available) to process tasks (e.g. HTTP server/MQTT client/BLE scanner/BLE client) and thus runs much better on dual-core devices.<br>
|
||||
Although Nuki Hub supports single-core devices, Nuki Hub uses both CPU cores (if available) to process tasks (e.g. HTTP server/MQTT client/BLE scanner/BLE client) and thus runs much better on dual-core devices.<br>
|
||||
|
||||
When buying a new device in 2025 we can only recommend the ESP32-S3 with PSRAM (look for an ESP32-S3 with the designation N>=4 and R>=2 such as an ESP32-S3 N16R8).<br>
|
||||
The ESP32-S3 is a dual-core CPU with many GPIO's, ability to enlarge RAM using PSRAM, ability to connect Ethernet modules over SPI and optionally power the device with a PoE splitter.<br>
|
||||
The only functions missing from the ESP32-S3 as compared to other ESP devices is the ability to use some Ethernet modules only supported by the original ESP32 (and ESP32-P4) and the ability to connect over WIFI6 (C6 or ESP32-P4 with C6 module)
|
||||
|
||||
If/When the ESP32-P4 with ESP32-C6-MINI-1 module for BLE/WiFi is supported this will probably become the preferred device for Nuki Hub.
|
||||
|
||||
Other considerations:
|
||||
- If Ethernet/PoE is required: An ESP32-S3 with PSRAM in combination with a SPI Ethernet module ([W5500](https://www.aliexpress.com/w/wholesale-w5500.html)) and [PoE to Ethernet and USB type B/C splitter](https://aliexpress.com/w/wholesale-poe-splitter-usb-c.html) or the LilyGO-T-ETH ELite, LilyGO-T-ETH-Lite-ESP32S3 or M5stack Atom S3R with the M5stack AtomPoe W5500 module
|
||||
- If WIFI6 is absolutely required (it probably isn't): ESP32-C6
|
||||
@@ -70,12 +72,12 @@ Devices ranked best-to-worst:
|
||||
- ......
|
||||
- ESP32 with PSRAM
|
||||
- ......<br>
|
||||
(Devices below will not support some NukiHub functions)
|
||||
(Devices below will not support some Nuki Hub functions)
|
||||
- ......
|
||||
- ESP32-S3 without PSRAM
|
||||
- ESP32 without PSRAM
|
||||
- ...... <br>
|
||||
(Devices below will not support more NukiHub functions)
|
||||
(Devices below will not support more Nuki Hub functions)
|
||||
- ......
|
||||
- ESP32-C6
|
||||
- ESP32-solo1
|
||||
@@ -153,7 +155,7 @@ See the "[MQTT Encryption](#mqtt-encryption-optional)" section of this README.
|
||||
Make sure "Bluetooth pairing" is enabled for the Nuki device by enabling this setting in the official Nuki App in "Settings" > "Features & Configuration" > "Button and LED".
|
||||
After enabling the setting press the button on the Nuki device for a few seconds.<br>
|
||||
Pairing should be automatic whenever the ESP32 is powered on and no lock is paired.<br>
|
||||
To pair with an opener you need to first enable the opener on the "Nuki configuration" page of NukiHub.<br>
|
||||
To pair with an opener you need to first enable the opener on the "Nuki configuration" page of Nuki Hub.<br>
|
||||
<br>
|
||||
When pairing is successful, the web interface should show "Paired: Yes".<br>
|
||||
MQTT nodes like lock state and battery level should now reflect the reported values from the lock.<br>
|
||||
@@ -166,18 +168,18 @@ Enable "Register as app" before pairing to allow this. Otherwise the Bridge will
|
||||
|
||||
Make sure "Bluetooth pairing" is enabled for the Nuki device by enabling this setting in the official Nuki App in "Settings" > "Features & Configuration" > "Button and LED".
|
||||
|
||||
Before enabling pairing mode using the button on the Lock Ultra first setup NukiHub as follows:
|
||||
Before enabling pairing mode using the button on the Lock Ultra first setup Nuki Hub as follows:
|
||||
- Enable both "Nuki Smartlock enabled" and "Nuki Smartlock Ultra enabled" settings on the "Basic Nuki Configuration" page and Save. Setting the "Nuki Smartlock Ultra enabled" will change multiple other NukiHub settings.
|
||||
- Input your 6-digit Nuki Lock Ultra PIN on the "Credentials" page and Save
|
||||
- Press the button on the Nuki device for a few seconds
|
||||
- It is **strongly** recommended to setup and enable Hybrid mode over Thread/WiFi + official MQTT as NukiHub works best in Hybrid or Bridge mode and the Ultra does not support Bridge mode
|
||||
- It is **strongly** recommended to setup and enable Hybrid mode over Thread/WiFi + official MQTT as Nuki Hub works best in Hybrid or Bridge mode and the Ultra does not support Bridge mode
|
||||
|
||||
Pairing should be automatic if no lock is paired.<br>
|
||||
When pairing is successful, the web interface should show "Paired: Yes".<br>
|
||||
|
||||
## Hybrid mode
|
||||
|
||||
Hybrid mode allows you to use the official Nuki MQTT implemenation on a Nuki Lock 3.0 Pro, Nuki Lock 4.0, Nuki Lock 4.0 Pro or Nuki Lock Ultra in conjunction with NukiHub.<br>
|
||||
Hybrid mode allows you to use the official Nuki MQTT implemenation on a Nuki Lock 3.0 Pro, Nuki Lock 4.0, Nuki Lock 4.0 Pro or Nuki Lock Ultra in conjunction with Nuki Hub.<br>
|
||||
See [hybrid mode](/HYBRID.md) for more information.
|
||||
|
||||
## Memory constraints
|
||||
@@ -206,7 +208,9 @@ You can check on the info page of the Web configurator if PSRAM is available.
|
||||
|
||||
Note that there are two builds of Nuki Hub for the ESP32-S3 available.<br>
|
||||
One for devices with no or Quad SPI PSRAM and one for devices with Octal SPI PSRAM.<br>
|
||||
If your ESP32-S3 device has PSRAM but it is not detected please flash the other S3 binary.
|
||||
Webflash will automatically flash the no/Quad SPI PSRAM build when an ESP32-S3 is connected.<br>
|
||||
If your ESP32-S3 device has PSRAM but it is not detected please switch to the other S3 binary.<br>
|
||||
You can do this by flashing the correct binaries manually or by selecting the option to switch S3 binary build from the Firmware Update page of the Web Configurator.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -241,8 +245,8 @@ In a browser navigate to the IP address assigned to the ESP32.
|
||||
- MQTT Broker port: Set to the Port of the MQTT broker (usually 1883)
|
||||
- MQTT User: If using authentication on the MQTT broker set to a username with read/write rights on the MQTT broker, set to # to clear
|
||||
- MQTT Password : If using authentication on the MQTT broker set to the password belonging to a username with read/write rights on the MQTT broker, set to # to clear
|
||||
- MQTT NukiHub Path: Set to the preferred MQTT root topic for NukiHub, defaults to "nukihub". Make sure this topic is unique when using multiple ESP32 NukiHub devices
|
||||
- Enable Home Assistant auto discovery: Enable Home Assistant MQTT auto discovery. Will automatically create entities in Home Assistant for NukiHub and connected Nuki Lock and/or Opener when enabled.
|
||||
- MQTT Nuki Hub Path: Set to the preferred MQTT root topic for Nuki Hub, defaults to "nukihub". Make sure this topic is unique when using multiple ESP32 Nuki Hub devices
|
||||
- Enable Home Assistant auto discovery: Enable Home Assistant MQTT auto discovery. Will automatically create entities in Home Assistant for Nuki Hub and connected Nuki Lock and/or Opener when enabled.
|
||||
|
||||
#### Advanced MQTT Configuration
|
||||
|
||||
@@ -258,7 +262,7 @@ In a browser navigate to the IP address assigned to the ESP32.
|
||||
- Enable hybrid official MQTT and Nuki Hub setup: Enable to combine the official MQTT over Thread/Wi-Fi with BLE. Improves speed of state changes. Needs the official MQTT to be setup first. Also requires Nuki Hub to be paired as app and unregistered as a bridge using the Nuki app. See [hybrid mode](/HYBRID.md)
|
||||
- Enable sending actions through official MQTT: Enable to sent lock actions through the official MQTT topics (e.g. over Thread/Wi-Fi) instead of using BLE. Needs "Enable hybrid official MQTT and Nuki Hub setup" to be enabled. See [hybrid mode](/HYBRID.md)
|
||||
- Time between status updates when official MQTT is offline (seconds): Set to a positive integer to set the maximum amount of seconds between actively querying the Nuki lock for the current lock state when the official MQTT is offline, default 600.
|
||||
- Retry command sent using official MQTT over BLE if failed: Enable to publish lock commands over BLE if NukiHub fails to use Hybrid mode to execute the command over MQTT
|
||||
- Retry command sent using official MQTT over BLE if failed: Enable to publish lock commands over BLE if Nuki Hub fails to use Hybrid mode to execute the command over MQTT
|
||||
- Reboot Nuki lock on official MQTT failure: Enable to reboot the Nuki Lock if official MQTT over WiFi/Thread is offline for more than 180 seconds
|
||||
|
||||
### Nuki Configuration
|
||||
@@ -333,9 +337,9 @@ In a browser navigate to the IP address assigned to the ESP32.
|
||||
|
||||
### Import/Export Configuration
|
||||
|
||||
The "Import/Export Configuration" menu option allows the importing and exporting of the NukiHub settings in JSON format.<br>
|
||||
The "Import/Export Configuration" menu option allows the importing and exporting of the Nuki Hub settings in JSON format.<br>
|
||||
<br>
|
||||
Create a (partial) backup of the current NukiHub settings by selecting any of the following:<br>
|
||||
Create a (partial) backup of the current Nuki Hub settings by selecting any of the following:<br>
|
||||
- Basic export: Will backup all settings that are not considered confidential (as such passwords and pincodes are not included in this export).
|
||||
- Export with redacted settings: Will backup basic settings and redacted settings such as passwords and pincodes.
|
||||
|
||||
@@ -351,10 +355,10 @@ After importing the device will reboot.
|
||||
The advanced configuration menu is not reachable from the main menu of the web configurator by default.<br>
|
||||
You can reach the menu directly by browsing to http://NUKIHUBIP/get?page=advanced or enable showing it in the main menu by browsing to http://NUKIHUBIP/get?page=debugon once (http://NUKIHUBIP/get?page=debugoff to disable).
|
||||
|
||||
Note that the following options can break NukiHub and cause bootloops that will require you to erase your ESP and reflash following the instructions for first-time flashing.
|
||||
Note that the following options can break Nuki Hub and cause bootloops that will require you to erase your ESP and reflash following the instructions for first-time flashing.
|
||||
|
||||
- Disable Network if not connected within 60s: Enable to allow NukiHub to function without a network connection (for example when only using NukiHub with GPIO)
|
||||
- Enable Bootloop prevention: Enable to reset the following stack size and max entry settings to default if NukiHub detects a bootloop.
|
||||
- Disable Network if not connected within 60s: Enable to allow Nuki Hub to function without a network connection (for example when only using Nuki Hub with GPIO)
|
||||
- Enable Bootloop prevention: Enable to reset the following stack size and max entry settings to default if Nuki Hub detects a bootloop.
|
||||
- Char buffer size (min 4096, max 65536): Set the character buffer size, needs to be enlarged to support large amounts of auth/keypad/timecontrol/authorization entries. Default 4096.
|
||||
- Task size Network (min 12288, max 65536): Set the Network task stack size, needs to be enlarged to support large amounts of auth/keypad/timecontrol/authorization entries. Default 12288.
|
||||
- Task size Nuki (min 8192, max 65536): Set the Nuki task stack size. Default 8192.
|
||||
@@ -365,13 +369,13 @@ Note that the following options can break NukiHub and cause bootloops that will
|
||||
- Show Pairing secrets on Info page: Enable to show the pairing secrets on the info page. Will be disabled on reboot.
|
||||
- Manually set lock pairing data: Enable to save the pairing data fields and manually set pairing info for the lock.
|
||||
- Manually set opener pairing data: Enable to save the pairing data fields and manually set pairing info for the opener.
|
||||
- Custom URL to update Nuki Hub updater: Set to a HTTPS address to update to a custom NukiHub updater binary on next boot of the NukiHub partition.
|
||||
- Custom URL to update Nuki Hub: Set to a HTTPS address to update to a custom NukiHub binary on next boot of the NukiHub updater partition.
|
||||
- Custom URL to update Nuki Hub updater: Set to a HTTPS address to update to a custom Nuki Hub updater binary on next boot of the Nuki Hub partition.
|
||||
- Custom URL to update Nuki Hub: Set to a HTTPS address to update to a custom Nuki Hub binary on next boot of the Nuki Hub updater partition.
|
||||
- Force Lock ID to current ID: Enable to force the current Lock ID, irrespective of the config received from the lock.
|
||||
- Force Lock Keypad connected: Enable to force NukiHub to function as if a keypad was connected, irrespective of the config received from the lock.
|
||||
- Force Lock Doorsensor connected: Enable to force NukiHub to function as if a doorsensor was connected, irrespective of the config received from the lock.
|
||||
- Force Lock Keypad connected: Enable to force Nuki Hub to function as if a keypad was connected, irrespective of the config received from the lock.
|
||||
- Force Lock Doorsensor connected: Enable to force Nuki Hub to function as if a doorsensor was connected, irrespective of the config received from the lock.
|
||||
- Force Opener ID to current ID: Enable to force the current Opener ID, irrespective of the config received from the opener.
|
||||
- Force Opener Keypad: Enable to force NukiHub to function as if a keypad was connected, irrespective of the config received from the opener.
|
||||
- Force Opener Keypad: Enable to force Nuki Hub to function as if a keypad was connected, irrespective of the config received from the opener.
|
||||
- Enable Nuki connect debug logging: Enable to log debug information regarding Nuki BLE connection to MQTT and/or Serial.
|
||||
- Enable Nuki communication debug logging: Enable to log debug information regarding Nuki BLE communication to MQTT and/or Serial.
|
||||
- Enable Nuki readable data debug logging: Enable to log human readable debug information regarding Nuki BLE to MQTT and/or Serial.
|
||||
@@ -651,18 +655,18 @@ openssl req -new -key server.key -out server.csr -subj "/C=US/ST=YourState/L=You
|
||||
## HTTPS Server (optional, PSRAM enabled devices only)
|
||||
|
||||
The Webconfigurator can use/force HTTPS on PSRAM enabled devices.<br>
|
||||
To enable SSL encryption, supply the certificate and key in the Network configuration page and reboot NukiHub.<br>
|
||||
To enable SSL encryption, supply the certificate and key in the Network configuration page and reboot Nuki Hub.<br>
|
||||
|
||||
Example self-signed certificate creation for your HTTPS server:
|
||||
```console
|
||||
|
||||
# make a Certificate and key pair, MAKE SURE THE CN MATCHES THE DOMAIN AT WHICH NUKIHUB IS AVAILABLE
|
||||
# make a Certificate and key pair, MAKE SURE THE CN MATCHES THE DOMAIN AT WHICH NUKI HUB IS AVAILABLE
|
||||
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout nukihub.key -out nukihub.crt -subj "/C=US/ST=YourState/L=YourCity/O=YourOrganization/OU=YourUnit/CN=YourCAName"
|
||||
|
||||
```
|
||||
|
||||
Although you can use the HTTPS server in this way your client device will not trust the certificate by default.
|
||||
An option would be to configure a proxy SSL server (such as Caddy, Traefik, nginx) with a non-self signed (e.g. let's encrypt) SSL certificate and have this proxy server connect to NukiHub over SSL and trust the self-signed NukiHub certificate for this connection.
|
||||
An option would be to configure a proxy SSL server (such as Caddy, Traefik, nginx) with a non-self signed (e.g. let's encrypt) SSL certificate and have this proxy server connect to Nuki Hub over SSL and trust the self-signed NukiHub certificate for this connection.
|
||||
|
||||
## Home Assistant Discovery (optional)
|
||||
|
||||
@@ -680,7 +684,7 @@ The following mapping between Home Assistant services and Nuki commands is setup
|
||||
NOTE: MQTT Discovery uses retained MQTT messages to store devices configurations.<br>
|
||||
In order to avoid orphan configurations on your broker please disable autodiscovery first if you no longer want to use this software.<br>
|
||||
Retained messages are automatically cleared when unpairing and when changing/disabling autodiscovery topic in MQTT Configuration page.<br>
|
||||
If you experience "ghost" entities/devices related to NukiHub you can completely purge NukiHub related Home Assistant discovery topics from your MQTT broker by following the instructions [here](#purging-home-assistant-discovery-mqtt-topics)
|
||||
If you experience "ghost" entities/devices related to Nuki Hub you can completely purge Nuki Hub related Home Assistant discovery topics from your MQTT broker by following the instructions [here](#purging-home-assistant-discovery-mqtt-topics)
|
||||
|
||||
## Keypad control using JSON (optional)
|
||||
|
||||
@@ -883,7 +887,7 @@ This unfortunately means that older versions of Home Assistant are not supported
|
||||
- Click on the root `homeassistant` topic
|
||||
- Press delete on your keyboard
|
||||
- Click Yes to confirm deletion
|
||||
- Reboot all NukiHub devices connected to the MQTT broker to recreate the correct topics for Home Assistant
|
||||
- Reboot all Nuki Hub devices connected to the MQTT broker to recreate the correct topics for Home Assistant
|
||||
|
||||
### Nuki Hub in bridge mode doesn't work when Thread or Wi-Fi on a Nuki Smartlock (3.0 Pro / 4.0 / 4.0 Pro) is turned on.
|
||||
|
||||
@@ -898,7 +902,7 @@ When first setting up the lock (or opener), you have to set a PIN in the Nuki sm
|
||||
Navigate to the Nuki Hub Credentials page, enter this PIN, click save and reboot.<br>
|
||||
Check the main page of the configurator to see if the entered PIN is valid.
|
||||
|
||||
Also make sure that the you have enabled the specific functionality on the "Access Level Configuration" page of NukiHub.
|
||||
Also make sure that the you have enabled the specific functionality on the "Access Level Configuration" page of Nuki Hub.
|
||||
|
||||
### Authorization data isn't published
|
||||
|
||||
@@ -916,7 +920,7 @@ This button is disabled by default, but can be enabled in the Home Assistant UI.
|
||||
### When controlling two locks (or openers) connected to two ESPs, both devices react to the same command. When using Home Asistant, the same status is display for both locks.
|
||||
|
||||
When using multiple Nuki devices, different paths for each device have to be configured.<br>
|
||||
Navigate to "MQTT Configuration" and change the "MQTT NukiHub Path" under "Basic MQTT Configuration" for at least one of the devices.<br>
|
||||
Navigate to "MQTT Configuration" and change the "MQTT Nuki Hub Path" under "Basic MQTT Configuration" for at least one of the devices.<br>
|
||||
|
||||
### The Nuki battery is draining quickly.
|
||||
|
||||
|
||||
4
lib/DuoAuthLibrary/AUTHORS
Normal file
4
lib/DuoAuthLibrary/AUTHORS
Normal file
@@ -0,0 +1,4 @@
|
||||
# Duo Authentication Library for ESP32 Micro Controllers
|
||||
# List of Authors
|
||||
|
||||
Gary Oppel <gaoppel@cisco.com>
|
||||
7
lib/DuoAuthLibrary/CHANGELOG.md
Normal file
7
lib/DuoAuthLibrary/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Duo Authentication Library for ESP32 Micro Controllers - Change Log
|
||||
=======================
|
||||
|
||||
v1.0.0 (08/26/2020)
|
||||
------
|
||||
|
||||
* Initial Library Release
|
||||
24
lib/DuoAuthLibrary/CONTRIBUTING.md
Normal file
24
lib/DuoAuthLibrary/CONTRIBUTING.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Guidance on how to contribute
|
||||
|
||||
Contributions to this code are welcome and appreciated.
|
||||
|
||||
> All contributions to this code will be released under the terms of the [LICENSE](./LICENSE) of this code. By submitting a pull request or filing a bug, issue, or feature request, you are agreeing to comply with this waiver of copyright interest. Details can be found in our [LICENSE](./LICENSE).
|
||||
|
||||
There are two primary ways to contribute:
|
||||
|
||||
1. Using the issue tracker
|
||||
2. Changing the codebase
|
||||
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
Use the issue tracker to suggest feature requests, report bugs, and ask questions. This is also a great way to connect with the developers of the project as well as others who are interested in this solution.
|
||||
|
||||
Use the issue tracker to find ways to contribute. Find a bug or a feature, mention in the issue that you will take on that effort, then follow the _Changing the codebase_ guidance below.
|
||||
|
||||
|
||||
## Changing the codebase
|
||||
|
||||
Generally speaking, you should fork this repository, make changes in your own fork, and then submit a pull request. All new code should have associated unit tests (if applicable) that validate implemented features and the presence or lack of defects.
|
||||
|
||||
Additionally, the code should follow any stylistic and architectural guidelines prescribed by the project. In the absence of such guidelines, mimic the styles and patterns in the existing codebase.
|
||||
176
lib/DuoAuthLibrary/LICENSE
Normal file
176
lib/DuoAuthLibrary/LICENSE
Normal file
@@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
5
lib/DuoAuthLibrary/NOTICE
Normal file
5
lib/DuoAuthLibrary/NOTICE
Normal file
@@ -0,0 +1,5 @@
|
||||
Duo Authentication Library for ESP32 Micro Controllers
|
||||
|
||||
Copyright (c) 2020 Cisco Systems, Inc. or its affiliates
|
||||
|
||||
This project includes software developed at Cisco Systems, Inc. or its affiliates.
|
||||
217
lib/DuoAuthLibrary/README.md
Normal file
217
lib/DuoAuthLibrary/README.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# Duo Authentication Library for ESP32 Micro Controllers
|
||||
|
||||
*An Arduino Library to simplify the operations of performing Duo Multi-Factor Authentication within your ESP32 Micro Controller project*
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Duo Authentication Library was created to abstract the [Duo Auth API Authentication Requirements](https://duo.com/docs/authapi#authentication). These requirements, which utilizes the HMAC_SHA1 algorithm, NTP Time Source, and Duo Application details (API Host, Application/Integration Key, and Secret Key) to sign the properly formatted requests before being sent to Duo's Auth API. The library has been targeted for the Espressif ESP32 Micro Controllers, due to the built-in crypto hardware support for both the HTTPS and HMAC Signature requirements.
|
||||
|
||||
Duo Security extends various API Endpoints for use. In particular, this Library's focus is on Duo's [Authentication API ](https://duo.com/docs/authapi).
|
||||
|
||||
## Features
|
||||
|
||||
The Duo Authentication Library enables the following features/functionality:
|
||||
|
||||
- Duo Mobile Push Authentication
|
||||
- Duo Asynchronous Mobile Push Authentication
|
||||
- Duo Passcode Authentication
|
||||
- Ability to set device's IP Address for Duo Auth API Requests
|
||||
- Ability to set Duo's Mobile Push Notification Type (This string is displayed before the word "Request")
|
||||
- Various Duo Authentication API Response fields exposed with easy to use functions
|
||||
- **Includes 4 example sketches** to demonstrate the use of the library
|
||||
|
||||
## Key Design Requirements
|
||||
|
||||
The library was created with the following Key Duo API Requirements in Mind:
|
||||
|
||||
- Accurate Time Source for Signing the Authorization Signature
|
||||
- *Note: This variable can be declared as Global, Private 'setup();' or 'loop();', or within your own Method/Function that will interface with the Duo Auth Library! It is strongly suggested that you leverage a Global variable if you are using a primary authentication source as well!*
|
||||
|
||||
- Simple to use functions for anyone to get up and quickly as well as incorporating into your existing projects
|
||||
- Built-in URL Encoding as required for certain usernames or other API properties
|
||||
- Functions that enable granular 'Authentication' decision making based on Duo's Auth API Responses
|
||||
- Initial Library scope primarily targeted mainly Duo Push and Passcode functionality
|
||||
|
||||
## Technologies & Frameworks Used
|
||||
The library was built to provide a simple and easy to use interface for the Duo Auth API on Espressif's ESP32 Micro Controllers. The library is written for the use within the Arduino IDE.
|
||||
|
||||
**Cisco Products & Services:**
|
||||
|
||||
- Duo Security
|
||||
|
||||
**Duo Authentication API Endpoints in use:**
|
||||
|
||||
This library abstracts most of the [Duo Auth API](https://duo.com/docs/authapi) functionality. The specific API Endpoints leveraged by this library are as follows:
|
||||
|
||||
- GET ```/auth/v2/ping```
|
||||
- GET ```/auth/v2/check```
|
||||
- POST ```/auth/v2/auth```
|
||||
- GET ```/auth/v2/auth_status```
|
||||
|
||||
**Tools & Frameworks:**
|
||||
|
||||
- Arduino IDE (*Tested on v1.8.12*) or PlatformIO for VSCode IDE
|
||||
- [Arduino Core for the ESP32](https://github.com/espressif/arduino-esp32)
|
||||
- [ArduinoJson Library for Arduino and IoT Devices](https://github.com/bblanchon/ArduinoJson)
|
||||
|
||||
**I would like to thank all the authors & contributers on the previously mentioned Tools & Frameworks!**
|
||||
|
||||
The Documentation and Guides available from these tools, contained the details needed to bring this Library to life. Below are links to references used for the above Tools & Frameworks:
|
||||
|
||||
- Arduino - Writing a Library for Arduino [https://www.arduino.cc/en/Hacking/LibraryTutorial](https://www.arduino.cc/en/Hacking/LibraryTutorial)
|
||||
- ArduinoJson - Deserialization Tutorial [https://arduinojson.org/v6/doc/deserialization/](https://arduinojson.org/v6/doc/deserialization/)
|
||||
|
||||
**Hardware Required:**
|
||||
|
||||
- Espressif ESP32 WiFi Micro-controller
|
||||
- User Input Device
|
||||
- 10 Digit Keypad
|
||||
- LCD Touch Screen
|
||||
- Serial/RS-232 Device
|
||||
- RFID Reader/NFC Reader
|
||||
- Etc..
|
||||
|
||||
## Installation
|
||||
|
||||
#### Arduino IDE
|
||||
*Note: This document assumes that your Arduino IDE is already setup and configured for Espressif's ESP32 Micro Controllers (Arduino Core for the ESP32)*
|
||||
|
||||
- To install the Duo Authentication Library in your Arduino IDE, follow the instructions below:
|
||||
- Browse to the Duo Authentication Library Releases GitHub Page
|
||||
- Download Latest Duo Authentication Library package version (Choose ZIP Package)
|
||||
- Follow the **"Installing Additional Arduino Libraries"** documentation at [Arduino's Library Documentation Site](https://www.arduino.cc/en/guide/libraries)
|
||||
|
||||
#### PlatformIO for VSCode IDE
|
||||
- To install the Duo Authentication Library in your PlatformIO for VSCode IDE, follow the instructions below:
|
||||
- Open VSCode PlatformIO Core CLI
|
||||
- Execute the following installation command
|
||||
- `pio lib install https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32/archive/v1.0.0.zip`
|
||||
|
||||
## Library Usage
|
||||
|
||||
### Example Sketches
|
||||
|
||||
Included as part of the Duo Authentication Library are 4 Arduino example sketches, which outline and demonstrate how to use the library. The examples included are:
|
||||
- **Push Authentication** - Provides example on a Duo Library Push Authentication
|
||||
- **Passcode Authentication** - Provides example on a Duo Library Passcode Authentication
|
||||
- **Asynchronous Push Authentication** - Provides example on an Asynchronous Duo Push Authentication
|
||||
- **Common Functions** - Provides examples on the Libraries Common Functions
|
||||
|
||||
### Configuration Requirements
|
||||
|
||||
The following parameters are required inputs into the Duo Authentication Library:
|
||||
|
||||
- NTP Server (Intranet or Internet) and other Time Variables
|
||||
|
||||
```cpp
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
|
||||
//Adjusting these values from '0' are not necessary
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
```
|
||||
|
||||
- WiFi SSID & Pre-Shared Key
|
||||
```cpp
|
||||
const char* ssid = "MyWirelessAP";
|
||||
const char* wirelessPsk = "MySecretWiFiPassword";
|
||||
```
|
||||
|
||||
- Time Info Variable Declaration & Configuration
|
||||
|
||||
The Time Info structure provides the necessary variable for the NTP server to update the time, which is used by the Duo Auth Library.
|
||||
|
||||
```cpp
|
||||
//Create timeinfo variable. Note: This can be declared globally or within the function using the Duo Auth Library
|
||||
struct tm timeinfo;
|
||||
|
||||
//Configure the Time settings
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
```
|
||||
|
||||
- Root Certificate Authority (CA) Public Certificate for the Duo API Host
|
||||
|
||||
See [Root Certificate Authority Section](/README.md#root-certificate-authority)
|
||||
|
||||
- Duo API Hostname
|
||||
```cpp
|
||||
const char* duo_host = "";
|
||||
```
|
||||
|
||||
- Duo Application/Integration Key
|
||||
```cpp
|
||||
const char* duo_akey = "";
|
||||
```
|
||||
|
||||
- Duo Secret Key
|
||||
```cpp
|
||||
const char* duo_skey = "";
|
||||
```
|
||||
|
||||
- Username
|
||||
|
||||
- Passcode of User, if using Duo Passcode Authentication
|
||||
|
||||
*For a more comprehensive view into the Configuration Requirements and Library use, please view the included example sketches.*
|
||||
|
||||
### Root Certificate Authority
|
||||
Duo Authentication API calls are performed over HTTPS, and as such we require a trustpoint to know that we are communicating with our trusted Duo API Host.
|
||||
|
||||
You will need to export the Base64 Encoded X.509 Public Key of the Root Certificate Authority for your Duo API Host (```duo_host```).
|
||||
|
||||
Once exported, you will need to format the Base64 Output similar to the example below:
|
||||
|
||||
```cpp
|
||||
const char* root_ca= \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx\n" \
|
||||
"XxXxXxXxXxXx\n" \
|
||||
"-----END CERTIFICATE-----\n";
|
||||
```
|
||||
|
||||
## Authors & Maintainers
|
||||
|
||||
The following people are responsible for the creation and/or maintenance of this project:
|
||||
|
||||
- Gary Oppel <gaoppel@cisco.com>
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed to you under the terms of the [Apache License, Version 2.0](./LICENSE).
|
||||
|
||||
---
|
||||
|
||||
Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
*@license
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Duo Authentication Library (Common Library Functions Example)
|
||||
//GitHub: https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Include WiFi Header File to enable Network Connectivity
|
||||
#include <WiFi.h>
|
||||
|
||||
//Include DuoAuthLibrary Header File enabling the functions for Duo Authentication Requests
|
||||
#include <DuoAuthLib.h>
|
||||
|
||||
//UPDATE: Update the below values with your Wireless SSID and Pre-Shared Key settings
|
||||
const char* ssid = "MyWirelessAP";
|
||||
const char* wirelessPsk = "MySecretWiFiPassword";
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//START: Duo Authentication Library Required Variables
|
||||
|
||||
//Initialize the Time Variable Globally since it is needed for Duo Auth and potentially the
|
||||
//primary Authentication Provider
|
||||
struct tm timeinfo;
|
||||
|
||||
//UPDATE: Provide an accurate Local or Internet Based Time-source (NTP) (IP or FQDN)
|
||||
//If using a FQDN, please ensure that DNS Servers are configured or provided through DHCP
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
|
||||
//Duo Auth API uses UTC time, which is queried against the above NTP Server.
|
||||
//Adjusting these values from '0' are not necessary
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
|
||||
//UPDATE: Update these variables with the appropriate Duo Application API values
|
||||
const char* duo_host = "";
|
||||
const char* duo_akey = "";
|
||||
const char* duo_skey = "";
|
||||
|
||||
//END: Duo Authentication Library Required Variables
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
void setup(){
|
||||
//Start Serial Connection at 115200 Baud
|
||||
Serial.begin(115200);
|
||||
|
||||
delay(1000);
|
||||
|
||||
//Start WiFi with the provided SSID and PSK
|
||||
WiFi.begin(ssid, wirelessPsk);
|
||||
|
||||
//Wait for a valid WiFi Connection
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
delay(1000);
|
||||
Serial.println("Connecting to WiFi..");
|
||||
}
|
||||
|
||||
Serial.println("Connected to the WiFi network");
|
||||
|
||||
Serial.println("Configuring NTP Client");
|
||||
//Configure SNTP Settings for an Accurate Time-source
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
|
||||
Serial.println("Duo Authentication Library (Common Functions Example)");
|
||||
|
||||
//Declare Duo Authentication Instance
|
||||
DuoAuthLib duoAuth;
|
||||
|
||||
//Call the begin(...) function to initialize the Duo Auth Library
|
||||
duoAuth.begin(duo_host, duo_akey, duo_skey, &timeinfo);
|
||||
|
||||
//This function allows you to check if the Duo API AKey, SKey, and Host are correct and Valid.
|
||||
//This is good for validating or testing that the Duo variables correctly are correctly set
|
||||
bool checkAPIKey = duoAuth.checkApiKey();
|
||||
|
||||
//Let's see the response from the above command
|
||||
//If it is 'true' we have successfully authenticated against the Duo Auth API
|
||||
if(checkAPIKey){
|
||||
Serial.println("Duo API Credentials are VALID");
|
||||
}else{
|
||||
//The Credential validation has failed for the Duo Auth API, please check the AKey, SKey, and Duo Hostname
|
||||
Serial.println("Duo API Credentials are INVALID");
|
||||
}
|
||||
|
||||
//Example providing the HTTP Response code from DuoAuthLib's last API request
|
||||
Serial.print("DuoAuthLib:HTTP Code : ");
|
||||
Serial.println(String(duoAuth.getHttpCode()));
|
||||
|
||||
//Example providing the Duo Auth API Status from the DuoAuthLib's last API request
|
||||
Serial.print("DuoAuthLib:getApiStat : ");
|
||||
Serial.println(duoAuth.getApiStat());
|
||||
|
||||
//Example providing the Duo Authentication Result from the DuoAuthLib's last API request
|
||||
//NOTE: This value is the decision on the Authentication Request. This must match "allow"
|
||||
//for a successful Auth Request. https://duo.com/docs/authapi#/auth under Response Formats
|
||||
//
|
||||
//To simplify the ease of use, leverage the 'authSuccessful(...)' function instead of the
|
||||
//below function, unless advanced control is required.
|
||||
Serial.print("DuoAuthLib:getAuthResponseResult : ");
|
||||
Serial.println(duoAuth.getAuthResponseResult());
|
||||
|
||||
//Example providing Duo's Authentication Status from DuoAuthLib's last API request
|
||||
Serial.print("DuoAuthLib:getAuthResponseStatus : ");
|
||||
Serial.println(duoAuth.getAuthResponseStatus());
|
||||
|
||||
//Example providing the HTTP Response code from DuoAuthLib's last API request
|
||||
Serial.print("DuoAuthLib:getAuthResponseStatusMessage : ");
|
||||
Serial.println(duoAuth.getAuthResponseStatusMessage());
|
||||
|
||||
//Example providing Duo's API Failure code from DuoAuthLib's last API request
|
||||
//See https://help.duo.com/s/article/1338 for more details
|
||||
Serial.print("DuoAuthLib:getApiFailureCode : ");
|
||||
Serial.println(duoAuth.getApiFailureCode());
|
||||
|
||||
//Example providing Duo's API Failure Message from DuoAuthLib's last API request
|
||||
//See https://help.duo.com/s/article/1338 for more details
|
||||
Serial.print("DuoAuthLib:getApiFailureMessage : ");
|
||||
Serial.println(duoAuth.getApiFailureMessage());
|
||||
|
||||
//Example providing the RAW HTTP Response from DuoAuthLib's last API request
|
||||
Serial.print("DuoAuthLib:HTTP Raw Response : \n");
|
||||
Serial.println(duoAuth.getHttpResponse());
|
||||
}
|
||||
|
||||
void loop(){
|
||||
|
||||
if ((WiFi.status() == WL_CONNECTED)){
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Loop Function Code Block - Intentionally Left Blank (Example in 'setup()' function above)
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
*@license
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Duo Authentication Library (Passcode Authentication Example)
|
||||
//GitHub: https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Include WiFi Header File to enable Network Connectivity
|
||||
#include <WiFi.h>
|
||||
|
||||
//Include DuoAuthLibrary Header File enabling the functions for Duo Authentication Requests
|
||||
#include <DuoAuthLib.h>
|
||||
|
||||
//UPDATE: Update the below values with your Wireless SSID and Pre-Shared Key settings
|
||||
const char* ssid = "MyWirelessAP";
|
||||
const char* wirelessPsk = "MySecretWiFiPassword";
|
||||
|
||||
//Global variables used for example
|
||||
char duoUsername[25];
|
||||
char duoPasscode[7];
|
||||
bool duoUser;
|
||||
const byte numChars = 25;
|
||||
char userInputChars[numChars];
|
||||
bool newUserInput = false;
|
||||
bool readyToAuth = false;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//START: Duo Authentication Library Required Variables
|
||||
|
||||
//Initialize the Time Variable Globally since it is needed for Duo Auth and potentially the
|
||||
//primary Authentication Provider
|
||||
struct tm timeinfo;
|
||||
|
||||
//UPDATE: Provide an accurate Local or Internet Based Time-source (NTP) (IP or FQDN)
|
||||
//If using a FQDN, please ensure that DNS Servers are configured or provided through DHCP
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
|
||||
//Duo Auth API uses UTC time, which is queried against the above NTP Server.
|
||||
//Adjusting these values from '0' are not necessary
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
|
||||
//UPDATE: Update these variables with the appropriate Duo Application API values
|
||||
const char* duo_host = "";
|
||||
const char* duo_akey = "";
|
||||
const char* duo_skey = "";
|
||||
|
||||
//END: Duo Authentication Library Required Variables
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
void setup(){
|
||||
//Start Serial Connection at 115200 Baud
|
||||
Serial.begin(115200);
|
||||
|
||||
delay(1000);
|
||||
|
||||
//Start WiFi with the provided SSID and PSK
|
||||
WiFi.begin(ssid, wirelessPsk);
|
||||
|
||||
//Wait for a valid WiFi Connection
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
delay(1000);
|
||||
Serial.println("Connecting to WiFi..");
|
||||
}
|
||||
|
||||
Serial.println("Connected to the WiFi network");
|
||||
|
||||
Serial.println("Configuring NTP Client");
|
||||
//Configure SNTP Settings for an Accurate Time-source
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
|
||||
Serial.println("Duo Authentication Library (Passcode Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
//Check if Wifi is connected before proceeding
|
||||
if ((WiFi.status() == WL_CONNECTED)){
|
||||
|
||||
//Declare Duo Authentication Instance
|
||||
DuoAuthLib duoAuth;
|
||||
|
||||
//Check for New user input from Serial Interface
|
||||
//NOTE: See checkSerialInput(...) function below for more details
|
||||
checkSerialInput();
|
||||
|
||||
//Check if new data is available to be processed by the application
|
||||
if (newUserInput == true){
|
||||
newUserInput = false;
|
||||
|
||||
Serial.println(userInputChars);
|
||||
|
||||
if(!duoUser){
|
||||
//New Username being entered, empty the existing contents of both variables
|
||||
duoUsername[0] = '\0';
|
||||
duoPasscode[0] = '\0';
|
||||
|
||||
//Copy the user input into the duoUsername Variable
|
||||
strcat(duoUsername, userInputChars);
|
||||
userInputChars[0] = '\0';
|
||||
|
||||
Serial.print("Please enter your Duo Passcode :");
|
||||
|
||||
duoUser = true;
|
||||
}else{
|
||||
//Copy the user input into the duoUsername Variable
|
||||
strcat(duoPasscode, userInputChars);
|
||||
userInputChars[0] = '\0';
|
||||
|
||||
duoUser = false;
|
||||
readyToAuth = true;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Primary Identity Authentication will need to be inserted where appropriate
|
||||
//If we have received both a Username and Passcode, we can proceed to requesting
|
||||
//Duo Passcode Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
if(readyToAuth){
|
||||
readyToAuth = false;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//BEGIN : Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Declare Variable to hold our Duo Push Request Status
|
||||
bool duoRequestResult;
|
||||
|
||||
//Reset the new data flag as its being processed
|
||||
newUserInput = false;
|
||||
|
||||
//Call the begin(...) function to initialize the Duo Auth Library
|
||||
duoAuth.begin(duo_host, duo_akey, duo_skey, &timeinfo);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Optional configuration functions available to control some intermediate features
|
||||
//
|
||||
//--------
|
||||
//Set HTTP Timeout for the Duo Auth Library HTTP Request. Adjust to your requirements
|
||||
// Default Setting - 30000ms (30 Seconds)
|
||||
//
|
||||
//duoAuth.setHttpTimeout(10000);
|
||||
//--------
|
||||
//
|
||||
//--------
|
||||
//Sets the IP Address of the device for Duo API Auth Requests
|
||||
// Default: 0.0.0.0
|
||||
//
|
||||
//duoAuth.setIPAddress("255.255.255.255");
|
||||
//--------
|
||||
//
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Call passcodeAuth(...) function for user entered
|
||||
//This is a stopping function and will wait until a response is received, an error occurs,
|
||||
//or the session times out. NOTE: The default HTTP Timeout for the Duo Library is 30 Seconds
|
||||
//See 'Asynchronous Push Authentication' example for an alternative Push Method
|
||||
//-------------------------------------------------------------------------------------------
|
||||
duoRequestResult = duoAuth.passcodeAuth(duoUsername, duoPasscode);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check if Duo Auth Request was Successful
|
||||
//This returns the True/False if the Duo Auth Library API
|
||||
//Request was successful and received the appropriate "OK" response
|
||||
//If the result returned is 'true'
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//-------------------------------------------------------------------------------------------
|
||||
if(duoRequestResult == true){
|
||||
//Check if Duo Authentication for the user was Allowed
|
||||
if(duoAuth.authSuccessful()){
|
||||
//Duo Authentication was Successful as the "allow" response was received from the API
|
||||
Serial.println("Successful Auth!");
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your PROTECTED Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
}else{
|
||||
Serial.println("Failed Auth!");
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Authentication FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
}else{
|
||||
Serial.println("Failed Auth!");
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Duo API Request FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//END : Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Reset our Variables for the next User Input/Authentication Request
|
||||
//-------------------------------------------------------------------------------------------
|
||||
userInputChars[0] = '\0';
|
||||
Serial.println("Duo Authentication Library (Passcode Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check Serial for Input and wait for the "Enter" key to be pressed
|
||||
//-------------------------------------------------------------------------------------------
|
||||
void checkSerialInput(){
|
||||
static byte index = 0;
|
||||
//Define Terminating Character
|
||||
char endingCharacter = '\n';
|
||||
char receivedChar;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Only check for new Serial Data when new data is available from the Serial Interface and we
|
||||
//have not seen the "Enter" key press yet.
|
||||
//-------------------------------------------------------------------------------------------
|
||||
while (Serial.available() > 0 && newUserInput == false){
|
||||
//We have new input
|
||||
receivedChar = Serial.read();
|
||||
|
||||
if(receivedChar != endingCharacter){
|
||||
//Detected that other characters were entered
|
||||
userInputChars[index] = receivedChar;
|
||||
index++;
|
||||
if (index >= numChars) {
|
||||
index = numChars - 1;
|
||||
}
|
||||
}else{
|
||||
//Detected that the 'Enter' button was pressed
|
||||
userInputChars[index] = '\0';
|
||||
index = 0;
|
||||
|
||||
//Update the flag telling the main loop we now have User Input for processing
|
||||
newUserInput = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
*@license
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Duo Authentication Library (Asynchronous Push Authentication Example)
|
||||
//GitHub: https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Include WiFi Header File to enable Network Connectivity
|
||||
#include <WiFi.h>
|
||||
|
||||
//Include DuoAuthLibrary Header File enabling the functions for Duo Authentication Requests
|
||||
#include <DuoAuthLib.h>
|
||||
|
||||
//UPDATE: Update the below values with your Wireless SSID and Pre-Shared Key settings
|
||||
const char* ssid = "MyWirelessAP";
|
||||
const char* wirelessPsk = "MySecretWiFiPassword";
|
||||
|
||||
//Global variables used for example
|
||||
const byte numChars = 25;
|
||||
char userInputChars[numChars];
|
||||
boolean newUserInput = false;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//START: Duo Authentication Library Example Dependant Variables
|
||||
String transactionId;
|
||||
bool activeRequest;
|
||||
int asyncPollTimeout = 10000;
|
||||
unsigned long asyncBeginMarker;
|
||||
//END: Duo Authentication Library Example Dependant Variables
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//START: Duo Authentication Library Required Variables
|
||||
|
||||
//Initialize the Time Variable Globally since it is needed for Duo Auth and potentially the
|
||||
//primary Authentication Provider
|
||||
struct tm timeinfo;
|
||||
|
||||
//UPDATE: Provide an accurate Local or Internet Based Time-source (NTP) (IP or FQDN)
|
||||
//If using a FQDN, please ensure that DNS Servers are configured or provided through DHCP
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
|
||||
//Duo Auth API uses UTC time, which is queried against the above NTP Server.
|
||||
//Adjusting these values from '0' are not necessary
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
|
||||
//UPDATE: Update these variables with the appropriate Duo Application API values
|
||||
const char* duo_host = "";
|
||||
const char* duo_akey = "";
|
||||
const char* duo_skey = "";
|
||||
|
||||
//END: Duo Authentication Library Required Variables
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
void setup(){
|
||||
//Start Serial Connection at 115200 Baud
|
||||
Serial.begin(115200);
|
||||
|
||||
delay(1000);
|
||||
|
||||
//Start WiFi with the provided SSID and PSK
|
||||
WiFi.begin(ssid, wirelessPsk);
|
||||
|
||||
//Wait for a valid WiFi Connection
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
delay(1000);
|
||||
Serial.println("Connecting to WiFi..");
|
||||
}
|
||||
|
||||
Serial.println("Connected to the WiFi network");
|
||||
|
||||
Serial.println("Configuring NTP Client");
|
||||
//Configure SNTP Settings for an Accurate Time-source
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
|
||||
Serial.println("Duo Authentication Library (Asynchronous Push Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
//Check if Wifi is connected before proceeding
|
||||
if ((WiFi.status() == WL_CONNECTED)){
|
||||
|
||||
//Declare Duo Authentication Instance
|
||||
DuoAuthLib duoAuth;
|
||||
|
||||
//Check for New user input from Serial Interface
|
||||
//NOTE: See checkSerialInput(...) function below for more details
|
||||
checkSerialInput();
|
||||
|
||||
//Check if new data is available to be processed by the application
|
||||
if (newUserInput == true){
|
||||
Serial.println(userInputChars);
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//PERFORM Primary Identity Authentication Here before proceeding to Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Begin Duo Authentication
|
||||
//Declare Variable to hold our Duo Push Request Status
|
||||
bool duoRequestResult;
|
||||
|
||||
//Reset the new data flag as its being processed
|
||||
newUserInput = false;
|
||||
|
||||
//Call the 'begin(...)' function to initialize the Duo Auth Library
|
||||
duoAuth.begin(duo_host, duo_akey, duo_skey, &timeinfo);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Optional configuration functions available to control some intermediate features
|
||||
//
|
||||
//--------
|
||||
//Set HTTP Timeout for the Duo Auth Library HTTP Request. Adjust to your requirements
|
||||
// Default Setting - 30000ms (30 Seconds)
|
||||
//
|
||||
//duoAuth.setHttpTimeout(10000);
|
||||
//--------
|
||||
//
|
||||
//--------
|
||||
//Sets the IP Address of the device for Duo API Auth Requests
|
||||
// Default: 0.0.0.0
|
||||
//
|
||||
//duoAuth.setIPAddress("255.255.255.255");
|
||||
//--------
|
||||
//
|
||||
//--------
|
||||
//Sets the Push Type Notification Text that is displayed to the Enduser's Mobile Device.
|
||||
// Note: Only supported with Duo Push functionality
|
||||
//
|
||||
//duoAuth.setPushType(10000);
|
||||
//--------
|
||||
//
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Call 'pushAuth(...)' function for user defined in 'userInputChars'
|
||||
//This is a stopping function and will wait until a response is received, an error occurs,
|
||||
//or the session times out. NOTE: The default HTTP Timeout for the Duo Library is 30 Seconds
|
||||
//See 'Asynchronous Push Authentication' example for an alternative Push Method
|
||||
//-------------------------------------------------------------------------------------------
|
||||
duoRequestResult = duoAuth.pushAuth(userInputChars, true);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check if Duo Auth Request was Successful
|
||||
//This returns the True/False if the Duo Auth Library API
|
||||
//Request was successful and received the approriate "OK" response
|
||||
//If the result returned is 'true'
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//-------------------------------------------------------------------------------------------
|
||||
if(duoRequestResult == true){
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//We have a successful Asynchronous Request, Let's Grab the Transaction ID and save it so we
|
||||
//can continue processing code within our main loop() function. We can check the status of
|
||||
//Duo Push Authentication Request with the 'authStatus(...)' function
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
transactionId = duoAuth.getAuthTxId();
|
||||
|
||||
activeRequest = true;
|
||||
|
||||
asyncBeginMarker = millis();
|
||||
Serial.println(String("Processing other tasks for ") + String(asyncPollTimeout/1000) + String(" seconds"));
|
||||
}else{
|
||||
Serial.println("Failed Auth!");
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Duo API Request FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Run the Duo Prompt Loop Again
|
||||
Serial.println("Duo Authentication Library (Asynchronous Push Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
//Reset Variables
|
||||
userInputChars[0] = '\0';
|
||||
}
|
||||
|
||||
//Note: According to the Duo API, the 'auth_status' endpoint will perform a long Poll
|
||||
if(activeRequest && (millis() > (asyncBeginMarker + asyncPollTimeout))){
|
||||
//Call the 'begin(...)' function to initialize the Duo Auth Library
|
||||
duoAuth.begin(duo_host, duo_akey, duo_skey, &timeinfo);
|
||||
|
||||
Serial.println("Checking Duo Push Status...");
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Call 'authStatus(...)' function to check on the status of our Asynchronous Push Request
|
||||
//-------------------------------------------------------------------------------------------
|
||||
duoAuth.authStatus(transactionId);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Call 'authStatus(...)' function to check on the status of our Asynchronous Push Request
|
||||
//-------------------------------------------------------------------------------------------
|
||||
if(duoAuth.pushWaiting()){
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Duo Authentication was sucessfully pushed to the user, we are still waiting for user response
|
||||
//-------------------------------------------------------------------------------------------
|
||||
Serial.println("Duo Push Waiting...");
|
||||
|
||||
//Reset the asyncBeginMarker to check for auth again
|
||||
asyncBeginMarker = millis();
|
||||
Serial.println(String("Processing other tasks for ") + String(asyncPollTimeout/1000) + String(" seconds"));
|
||||
}else{
|
||||
if(duoAuth.authSuccessful()){
|
||||
//Duo Authentication was Successful as the "allow" response was received from the API
|
||||
Serial.println("Successful Auth!");
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your PROTECTED Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
activeRequest = false;
|
||||
transactionId = "";
|
||||
|
||||
//Run the Duo Prompt Loop Again
|
||||
Serial.println("Duo Authentication Library (Asynchronous Push Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}else{
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
Serial.println("Failed Auth!");
|
||||
|
||||
activeRequest = false;
|
||||
transactionId = "";
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Authentication FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Run the Duo Prompt Loop Again
|
||||
Serial.println("Duo Authentication Library (Asynchronous Push Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Insert your normal process code here to process while we wait to check on the Auth Status
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Insert your normal process code here to process while we wait to check on the Auth Status
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check Serial for Input and wait for the "Enter" key to be pressed
|
||||
//-------------------------------------------------------------------------------------------
|
||||
void checkSerialInput(){
|
||||
static byte index = 0;
|
||||
//Define Terminating Character
|
||||
char endingCharacter = '\n';
|
||||
char receivedChar;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Only check for new Serial Data when new data is available from the Serial Interface and we
|
||||
//have not seen the "Enter" key press yet.
|
||||
//-------------------------------------------------------------------------------------------
|
||||
while (Serial.available() > 0 && newUserInput == false){
|
||||
//We have new input
|
||||
receivedChar = Serial.read();
|
||||
|
||||
if(receivedChar != endingCharacter){
|
||||
//Detected that other characters were entered
|
||||
userInputChars[index] = receivedChar;
|
||||
index++;
|
||||
if (index >= numChars) {
|
||||
index = numChars - 1;
|
||||
}
|
||||
}else{
|
||||
//Detected that the 'Enter' button was pressed
|
||||
userInputChars[index] = '\0';
|
||||
index = 0;
|
||||
|
||||
//Update the flag telling the main loop we now have User Input for processing
|
||||
newUserInput = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
*@license
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Duo Authentication Library (Push Authentication Example)
|
||||
//GitHub: https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//Include WiFi Header File to enable Network Connectivity
|
||||
#include <WiFi.h>
|
||||
|
||||
//Include DuoAuthLibrary Header File enabling the functions for Duo Authentication Requests
|
||||
#include <DuoAuthLib.h>
|
||||
|
||||
//UPDATE: Update the below values with your Wireless SSID and Pre-Shared Key settings
|
||||
const char* ssid = "MyWirelessAP";
|
||||
const char* wirelessPsk = "MySecretWiFiPassword";
|
||||
|
||||
//Global variables used for example
|
||||
const byte numChars = 25;
|
||||
char userInputChars[numChars];
|
||||
bool newUserInput = false;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//START: Duo Authentication Library Required Variables
|
||||
|
||||
//Initialize the Time Variable Globally since it is needed for Duo Auth and potentially the
|
||||
//primary Authentication Provider
|
||||
struct tm timeinfo;
|
||||
|
||||
//UPDATE: Provide an accurate Local or Internet Based Time-source (NTP) (IP or FQDN)
|
||||
//If using a FQDN, please ensure that DNS Servers are configured or provided through DHCP
|
||||
const char* ntpServer = "pool.ntp.org";
|
||||
|
||||
//Duo Auth API uses UTC time, which is queried against the above NTP Server.
|
||||
//Adjusting these values from '0' are not necessary
|
||||
const long gmtOffset_sec = 0;
|
||||
const int daylightOffset_sec = 0;
|
||||
|
||||
//UPDATE: Update these variables with the appropriate Duo Application API values
|
||||
const char* duo_host = "";
|
||||
const char* duo_akey = "";
|
||||
const char* duo_skey = "";
|
||||
|
||||
//END: Duo Authentication Library Required Variables
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
void setup(){
|
||||
//Start Serial Connection at 115200 Baud
|
||||
Serial.begin(115200);
|
||||
|
||||
delay(1000);
|
||||
|
||||
//Start WiFi with the provided SSID and PSK
|
||||
WiFi.begin(ssid, wirelessPsk);
|
||||
|
||||
//Wait for a valid WiFi Connection
|
||||
while (WiFi.status() != WL_CONNECTED){
|
||||
delay(1000);
|
||||
Serial.println("Connecting to WiFi..");
|
||||
}
|
||||
|
||||
Serial.println("Connected to the WiFi network");
|
||||
|
||||
Serial.println("Configuring NTP Client");
|
||||
//Configure SNTP Settings for an Accurate Time-source
|
||||
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||
|
||||
Serial.println("Duo Authentication Library (Push Example)");
|
||||
Serial.print("Please enter your Username :");
|
||||
}
|
||||
|
||||
void loop(){
|
||||
//Check if Wifi is connected before proceeding
|
||||
if ((WiFi.status() == WL_CONNECTED)){
|
||||
|
||||
//Declare Duo Authentication Instance
|
||||
DuoAuthLib duoAuth;
|
||||
|
||||
//Check for New user input from Serial Interface
|
||||
//NOTE: See checkSerialInput(...) function below for more details
|
||||
checkSerialInput();
|
||||
|
||||
//Check if new data is available to be processed by the application
|
||||
if (newUserInput == true){
|
||||
Serial.println(userInputChars);
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//PERFORM Primary Identity Authentication Here before proceeding to Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//BEGIN : Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Declare Variable to hold our Duo Push Request Status
|
||||
bool duoRequestResult;
|
||||
|
||||
//Reset the new data flag as its being processed
|
||||
newUserInput = false;
|
||||
|
||||
//Call the begin(...) function to initialize the Duo Auth Library
|
||||
duoAuth.begin(duo_host, duo_akey, duo_skey, &timeinfo);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Optional configuration functions available to control some intermediate features
|
||||
//
|
||||
//--------
|
||||
//Set HTTP Timeout for the Duo Auth Library HTTP Request. Adjust to your requirements
|
||||
// Default Setting - 30000ms (30 Seconds)
|
||||
//
|
||||
//duoAuth.setHttpTimeout(10000);
|
||||
//--------
|
||||
//
|
||||
//--------
|
||||
//Sets the IP Address of the device for Duo API Auth Requests
|
||||
// Default: 0.0.0.0
|
||||
//
|
||||
//duoAuth.setIPAddress("255.255.255.255");
|
||||
//--------
|
||||
//
|
||||
//--------
|
||||
//Sets the Push Type Notification Text that is displayed to the Enduser's Mobile Device.
|
||||
// Note: Only supported with Duo Push functionality
|
||||
//
|
||||
//duoAuth.setPushType(10000);
|
||||
//--------
|
||||
//
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Call pushAuth(...) function for user defined in 'userInputChars'
|
||||
//This is a stopping function and will wait until a response is received, an error occurs,
|
||||
//or the session times out. NOTE: The default HTTP Timeout for the Duo Library is 30 Seconds
|
||||
//See 'Asynchronous Push Authentication' example for an alternative Push Method
|
||||
//-------------------------------------------------------------------------------------------
|
||||
duoRequestResult = duoAuth.pushAuth(userInputChars);
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check if Duo Auth Request was Successful
|
||||
//This returns the True/False if the Duo Auth Library API
|
||||
//Request was successful and received the approriate "OK" response
|
||||
//If the result returned is 'true'
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//-------------------------------------------------------------------------------------------
|
||||
if(duoRequestResult == true){
|
||||
//Check if Duo Authentication for the user was Allowed
|
||||
if(duoAuth.authSuccessful()){
|
||||
//Duo Authentication was Successful as the "allow" response was received from the API
|
||||
Serial.println("Successful Auth!");
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your PROTECTED Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
}else{
|
||||
Serial.println("Failed Auth!");
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Authentication FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
}else{
|
||||
Serial.println("Failed Auth!");
|
||||
//Duo Authentication Failed as the "allow" response was NOT received from the API
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//INSERT Your Duo API Request FAILURE Code HERE
|
||||
//-------------------------------------------------------------------------------------------
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//END : Duo Authentication
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Reset our Variables for the next User Input/Authentication Request
|
||||
//-------------------------------------------------------------------------------------------
|
||||
userInputChars[0] = '\0';
|
||||
Serial.println("Duo Authentication Library (Push Example)");
|
||||
Serial.print("Please enter your [Username] :");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Check Serial for Input and wait for the "Enter" key to be pressed
|
||||
//-------------------------------------------------------------------------------------------
|
||||
void checkSerialInput(){
|
||||
static byte index = 0;
|
||||
//Define Terminating Character
|
||||
char endingCharacter = '\n';
|
||||
char receivedChar;
|
||||
|
||||
//-------------------------------------------------------------------------------------------
|
||||
//Only check for new Serial Data when new data is available from the Serial Interface and we
|
||||
//have not seen the "Enter" key press yet.
|
||||
//-------------------------------------------------------------------------------------------
|
||||
while (Serial.available() > 0 && newUserInput == false){
|
||||
//We have new input
|
||||
receivedChar = Serial.read();
|
||||
|
||||
if(receivedChar != endingCharacter){
|
||||
//Detected that other characters were entered
|
||||
userInputChars[index] = receivedChar;
|
||||
index++;
|
||||
if (index >= numChars) {
|
||||
index = numChars - 1;
|
||||
}
|
||||
}else{
|
||||
//Detected that the 'Enter' button was pressed
|
||||
userInputChars[index] = '\0';
|
||||
index = 0;
|
||||
|
||||
//Update the flag telling the main loop we now have User Input for processing
|
||||
newUserInput = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
lib/DuoAuthLibrary/keywords.txt
Normal file
50
lib/DuoAuthLibrary/keywords.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Test
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
DuoAuthLib KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
|
||||
ping KEYWORD2
|
||||
checkApiKey KEYWORD2
|
||||
|
||||
authStatus KEYWORD2
|
||||
pushAuth KEYWORD2
|
||||
passcodeAuth KEYWORD2
|
||||
|
||||
authSuccessful KEYWORD2
|
||||
pushWaiting KEYWORD2
|
||||
|
||||
setHttpTimeout KEYWORD2
|
||||
setIPAddress KEYWORD2
|
||||
setPushType KEYWORD2
|
||||
|
||||
getHttpCode KEYWORD2
|
||||
getHttpResponse KEYWORD2
|
||||
getApiStat KEYWORD2
|
||||
getAuthResponseResult KEYWORD2
|
||||
getAuthResponseStatus KEYWORD2
|
||||
getAuthResponseStatusMessage KEYWORD2
|
||||
|
||||
getAuthTxId KEYWORD2
|
||||
|
||||
getApiFailureCode KEYWORD2
|
||||
getApiFailureMessage KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
||||
9
lib/DuoAuthLibrary/library.properties
Normal file
9
lib/DuoAuthLibrary/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=Duo Auth Library
|
||||
version=1.0.0
|
||||
author=Gary Oppel
|
||||
maintainer=Gary Oppel
|
||||
sentence=Enables Duo Authentication within your ESP32 Wi-Fi Projects
|
||||
paragraph=Extends Duo Authentication API's for Push, Passcode, and Asynchronous Push Authentication requests.
|
||||
category=Other
|
||||
url=https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
architectures=esp32
|
||||
719
lib/DuoAuthLibrary/src/DuoAuthLib.cpp
Normal file
719
lib/DuoAuthLibrary/src/DuoAuthLib.cpp
Normal file
@@ -0,0 +1,719 @@
|
||||
/**
|
||||
*@license
|
||||
*
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @file DuoAuthLib.cpp
|
||||
* @brief An Arduino Library to simplify the operations of performing Duo Multi-Factor Authentication within your ESP32 Micro Controller project.
|
||||
*
|
||||
* @url https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
* @version 1.0.0
|
||||
* @author Gary Oppel <gaoppel@cisco.com>
|
||||
*/
|
||||
|
||||
//Include DuoAuthLib Library Header
|
||||
#include "DuoAuthLib.h"
|
||||
|
||||
// Include ESP32 MDEDTLS Library
|
||||
#include "mbedtls/md.h"
|
||||
|
||||
// Load CA CRT BUNDLE
|
||||
extern const uint8_t x509_crt_imported_bundle_bin_start[] asm("_binary_x509_crt_bundle_start");
|
||||
extern const uint8_t x509_crt_imported_bundle_bin_end[] asm("_binary_x509_crt_bundle_end");
|
||||
|
||||
// Include ESP NetworkClientSecure Library
|
||||
#include <NetworkClientSecure.h>
|
||||
|
||||
// Include ESP HTTPClient Library
|
||||
#include <HTTPClient.h>
|
||||
|
||||
// Include ArduinoJSON Library
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//DuoAuthLib Constructor
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//Function that handles the creation and setup of instance
|
||||
DuoAuthLib::DuoAuthLib()
|
||||
{
|
||||
//Initialize Return Variables
|
||||
_duoApiStat = "";
|
||||
_duoAuthTxId = "";
|
||||
_duoApiAuthResponseResult = "";
|
||||
_duoApiAuthResponseStatus = "";
|
||||
|
||||
_duoApiAuthResponseStatusMessage = "";
|
||||
|
||||
_duoApiFailureCode = 0;
|
||||
_duoApiFailureMessage = "";
|
||||
_duoPushType[0] = '\0';
|
||||
_init = false;
|
||||
_async = false;
|
||||
}
|
||||
|
||||
//Function that handles the deletion/removals of instance
|
||||
DuoAuthLib::~DuoAuthLib()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//'begin(...)' - used to initialize and prepare for an Auth request.
|
||||
void DuoAuthLib::begin(const char* duoApiHost, const char* duoApiIKey, const char* duoApiSKey, struct tm* timeInfo)
|
||||
{
|
||||
_timeinfo = timeInfo;
|
||||
|
||||
_duoHost = (char *)duoApiHost;
|
||||
_duoIkey = (char *)duoApiIKey;
|
||||
_duoSkey = (char *)duoApiSKey;
|
||||
|
||||
//Set Default HTTP Timeout to 30 Seconds
|
||||
_httpTimeout = _defaultTimeout;
|
||||
|
||||
//Set Default IP Address to 0.0.0.0
|
||||
strcpy(_ipAddress, "0.0.0.0");
|
||||
|
||||
//Set the library initialized to True
|
||||
_init = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//DuoAuthLib Public Methods
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Duo Ping API
|
||||
// https://duo.com/docs/authapi#/ping
|
||||
bool DuoAuthLib::ping()
|
||||
{
|
||||
if(!_init){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pingSuccess = false;
|
||||
|
||||
bool apiResponse = submitApiRequest(0, (char *)DUO_PING_API_PATH, "", (char *)"", (char *)"");
|
||||
|
||||
if(apiResponse == true){
|
||||
if(_httpCode == 200){
|
||||
pingSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
return pingSuccess;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Duo Check API
|
||||
// https://duo.com/docs/authapi#/check
|
||||
bool DuoAuthLib::checkApiKey()
|
||||
{
|
||||
if(!_init){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duoAuthRequestResult = false;
|
||||
|
||||
if(getLocalTime(_timeinfo)){
|
||||
char hmac_password[41];
|
||||
char timeStringBuffer[TIME_BUF_STR_SIZE];
|
||||
|
||||
//Get Current time from timeinfo
|
||||
strftime(timeStringBuffer, sizeof(timeStringBuffer), "%a, %d %b %Y %T %z", _timeinfo);
|
||||
|
||||
// Create empty char array to hold our string
|
||||
char hmacPayload[SIGNATURE_PAYLOAD_BUFFER_SIZE + CHECK_AUTH_BUFFER_SIZE];
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
generateHmacPayload(hmacPayload, timeStringBuffer, (char *)"GET", _duoHost, DUO_CHECK_API_PATH, (char *)"");
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
calculateHmacSha1((char *)_duoSkey, hmacPayload, hmac_password);
|
||||
|
||||
bool apiResponse = submitApiRequest(0, timeStringBuffer, DUO_CHECK_API_PATH, hmac_password, (char *)"");
|
||||
|
||||
if(apiResponse == true){
|
||||
bool processResult = processResponse(&_lastHttpResponse);
|
||||
|
||||
if(processResult == true){
|
||||
duoAuthRequestResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return duoAuthRequestResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Duo Auth Status API
|
||||
// https://duo.com/docs/authapi#/auth_status
|
||||
bool DuoAuthLib::authStatus(String transactionId)
|
||||
{
|
||||
if(!_init){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool duoAuthRequestResult = false;
|
||||
|
||||
if(transactionId.length() == 36){
|
||||
char txId[37];
|
||||
|
||||
//Convert String to Character Array
|
||||
transactionId.toCharArray(txId, 37);
|
||||
|
||||
if(getLocalTime(_timeinfo)){
|
||||
char hmac_password[41];
|
||||
char timeStringBuffer[TIME_BUF_STR_SIZE];
|
||||
|
||||
char authRequestContents[CHECK_AUTH_BUFFER_SIZE];
|
||||
authRequestContents[0] = '\0';
|
||||
|
||||
//Get Current time from timeinfo
|
||||
strftime(timeStringBuffer, sizeof(timeStringBuffer), "%a, %d %b %Y %T %z", _timeinfo);
|
||||
|
||||
addRequestParameter(authRequestContents, (char *)TRANSACTION_PARAM, txId, true);
|
||||
|
||||
// Create empty char array to hold our string
|
||||
char hmacPayload[SIGNATURE_PAYLOAD_BUFFER_SIZE + CHECK_AUTH_BUFFER_SIZE];
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
generateHmacPayload(hmacPayload, timeStringBuffer, (char *)"GET", _duoHost, DUO_AUTHSTATUS_API_PATH, authRequestContents);
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
calculateHmacSha1((char *)_duoSkey, hmacPayload, hmac_password);
|
||||
|
||||
bool apiResponse = submitApiRequest(0, timeStringBuffer, DUO_AUTHSTATUS_API_PATH, hmac_password, authRequestContents);
|
||||
|
||||
if(apiResponse == true){
|
||||
bool processResult = processResponse(&_lastHttpResponse);
|
||||
|
||||
if(processResult == true){
|
||||
duoAuthRequestResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duoAuthRequestResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Duo Auth API
|
||||
// https://duo.com/docs/authapi#/auth
|
||||
// https://duo.com/docs/authapi#authentication
|
||||
bool DuoAuthLib::pushAuth(char* userName, bool async)
|
||||
{
|
||||
if(!_init){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool authSuccess = false;
|
||||
_async = async;
|
||||
|
||||
//Create new Buffer using the method below
|
||||
int userStrLen = encodeUrlBufferSize(userName);
|
||||
|
||||
char encodedUsername[userStrLen];
|
||||
|
||||
encodeUrl(encodedUsername, userName);
|
||||
|
||||
return performAuth(encodedUsername, (char*)"", PUSH);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Duo Auth API
|
||||
// https://duo.com/docs/authapi#/auth
|
||||
// https://duo.com/docs/authapi#authentication
|
||||
bool DuoAuthLib::passcodeAuth(char* userName, char* userPasscode)
|
||||
{
|
||||
if(!_init){
|
||||
return false;
|
||||
}
|
||||
|
||||
//Create new Buffer using the method below
|
||||
int userStrLen = encodeUrlBufferSize(userName);
|
||||
|
||||
char encodedUsername[userStrLen];
|
||||
|
||||
encodeUrl(encodedUsername, userName);
|
||||
|
||||
return performAuth(encodedUsername, userPasscode, PASSCODE);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Duo - Set Public IP Address of Device for Auth API Request
|
||||
void DuoAuthLib::setIPAddress(char* ipAddress)
|
||||
{
|
||||
if(strlen(ipAddress) < MAX_IP_LENGTH){
|
||||
strcpy(_ipAddress, ipAddress);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Duo - Set the Push Type String that is displayed to the end user
|
||||
//See the Parameter 'Type' within the 'Duo Push' Section of the
|
||||
//Duo API Documentation: https://duo.com/docs/authapi#/auth
|
||||
void DuoAuthLib::setPushType(char* pushType)
|
||||
{
|
||||
int typeLength = strlen(pushType);
|
||||
|
||||
if(typeLength > 0 && typeLength < MAX_TYPE_LENGTH){
|
||||
//Create new Buffer using the method below
|
||||
int userStrLen = encodeUrlBufferSize(pushType);
|
||||
|
||||
char encodedUsername[userStrLen];
|
||||
|
||||
encodeUrl(encodedUsername, pushType);
|
||||
|
||||
strcpy(_duoPushType, encodedUsername);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Duo - Set the HTTP Timeout from the default of 30 Seconds
|
||||
void DuoAuthLib::setHttpTimeout(int httpTimeout)
|
||||
{
|
||||
_httpTimeout = httpTimeout;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Duo Authentication API Result
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//https://duo.com/docs/authapi#authentication
|
||||
//Returns True if the Duo Authentication Result is 'allow'
|
||||
bool DuoAuthLib::authSuccessful()
|
||||
{
|
||||
if(_duoApiAuthResponseResult == "allow"){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Duo Authentication API Result
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//https://duo.com/docs/authapi#authentication
|
||||
//Returns True if the Duo Authentication Result is 'waiting'
|
||||
bool DuoAuthLib::pushWaiting()
|
||||
{
|
||||
if(_duoApiAuthResponseResult == "waiting"){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
String DuoAuthLib::getHttpResponse()
|
||||
{
|
||||
return _lastHttpResponse;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getApiStat()
|
||||
{
|
||||
return _duoApiStat;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getAuthResponseResult()
|
||||
{
|
||||
return _duoApiAuthResponseResult;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getAuthResponseStatus()
|
||||
{
|
||||
return _duoApiAuthResponseStatus;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getAuthResponseStatusMessage()
|
||||
{
|
||||
return _duoApiAuthResponseStatusMessage;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getApiFailureCode()
|
||||
{
|
||||
return String(_duoApiFailureCode);
|
||||
}
|
||||
|
||||
String DuoAuthLib::getApiFailureMessage()
|
||||
{
|
||||
return _duoApiFailureMessage;
|
||||
}
|
||||
|
||||
String DuoAuthLib::getAuthTxId()
|
||||
{
|
||||
return _duoAuthTxId;
|
||||
}
|
||||
|
||||
int DuoAuthLib::getHttpCode() {
|
||||
return _httpCode;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//DuoAuthLib Private Methods
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//https://duo.com/docs/authapi#/auth
|
||||
//https://duo.com/docs/authapi#authentication
|
||||
bool DuoAuthLib::performAuth(char* userName, char* userPasscode, enum DUO_AUTH_METHOD authMethod)
|
||||
{
|
||||
bool duoAuthRequestResult = false;
|
||||
|
||||
if(getLocalTime(_timeinfo)){
|
||||
char hmac_password[41];
|
||||
char timeStringBuffer[TIME_BUF_STR_SIZE];
|
||||
|
||||
char authRequestContents[AUTH_REQUEST_BUFFER_SIZE];
|
||||
authRequestContents[0] = '\0';
|
||||
|
||||
//Get Current time from timeinfo
|
||||
strftime(timeStringBuffer, sizeof(timeStringBuffer), "%a, %d %b %Y %T %z", _timeinfo);
|
||||
|
||||
//Check the authentication method and build the request accordingly.
|
||||
if(authMethod == PUSH){
|
||||
if(_async){
|
||||
addRequestParameter(authRequestContents, (char *)ASYNC_PARAM, (char *)"1");
|
||||
}
|
||||
|
||||
addRequestParameter(authRequestContents, (char *)DEVICE_PARAM, (char *)AUTO_PARAM);
|
||||
addRequestParameter(authRequestContents, (char *)FACTOR_PARAM, (char *)PUSH_PARAM);
|
||||
addRequestParameter(authRequestContents, (char *)IPADDR_PARAM, _ipAddress);
|
||||
if(strlen(_duoPushType) > 0){
|
||||
addRequestParameter(authRequestContents, (char *)TYPE_PARAM, _duoPushType);
|
||||
}
|
||||
addRequestParameter(authRequestContents, (char *)USERNAME_PARAM, userName, true);
|
||||
}else if(authMethod == PASSCODE){
|
||||
addRequestParameter(authRequestContents, (char *)FACTOR_PARAM, (char *)PASSCODE_PARAM);
|
||||
addRequestParameter(authRequestContents, (char *)IPADDR_PARAM, _ipAddress);
|
||||
addRequestParameter(authRequestContents, (char *)PASSCODE_PARAM, userPasscode);
|
||||
if(strlen(_duoPushType) > 0){
|
||||
addRequestParameter(authRequestContents, (char *)TYPE_PARAM, _duoPushType);
|
||||
}
|
||||
addRequestParameter(authRequestContents, (char *)USERNAME_PARAM, userName, true);
|
||||
|
||||
}else{
|
||||
return duoAuthRequestResult;
|
||||
}
|
||||
|
||||
// Create empty char array to hold our string
|
||||
char hmacPayload[SIGNATURE_PAYLOAD_BUFFER_SIZE + AUTH_REQUEST_BUFFER_SIZE];
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
generateHmacPayload(hmacPayload, timeStringBuffer, (char *)"POST", _duoHost, DUO_AUTH_API_PATH, authRequestContents);
|
||||
|
||||
// Generate the required URL Request Contents for the DUO API Call
|
||||
calculateHmacSha1((char *)_duoSkey, hmacPayload, hmac_password);
|
||||
|
||||
bool apiResponse = submitApiRequest(1, timeStringBuffer, DUO_AUTH_API_PATH, hmac_password, authRequestContents);
|
||||
|
||||
if(apiResponse == true){
|
||||
bool processResult = processResponse(&_lastHttpResponse);
|
||||
|
||||
if(processResult == true){
|
||||
duoAuthRequestResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duoAuthRequestResult;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Create and Submit API Request to Duo API Server
|
||||
bool DuoAuthLib::submitApiRequest(uint8_t apiMethod, char *timeString, const char* apiPath, char *hmacPassword, char* requestContents)
|
||||
{
|
||||
NetworkClientSecure *clientDuoAuth = new NetworkClientSecure;
|
||||
if (clientDuoAuth)
|
||||
{
|
||||
clientDuoAuth->setCACertBundle(x509_crt_imported_bundle_bin_start, x509_crt_imported_bundle_bin_end - x509_crt_imported_bundle_bin_start);
|
||||
{
|
||||
//Create our HTTPClient Instance
|
||||
HTTPClient http;
|
||||
|
||||
//Build the Request URL based on the Method.
|
||||
//Append the requestContents to the end of
|
||||
//the URL for an HTTP GET request
|
||||
String requestUrl = "https://";
|
||||
requestUrl += _duoHost;
|
||||
requestUrl += apiPath;
|
||||
|
||||
if(apiMethod == 0 && strlen(requestContents) > 0){
|
||||
requestUrl += '?';
|
||||
requestUrl += requestContents;
|
||||
}
|
||||
|
||||
http.begin(requestUrl); //Specify the URL
|
||||
|
||||
// HTTP Connection Timeout
|
||||
http.setTimeout(_httpTimeout);
|
||||
|
||||
//Set User Agent Header
|
||||
http.setUserAgent(_duoUserAgent);
|
||||
|
||||
//Set Host Header
|
||||
http.addHeader("Host", _duoHost);
|
||||
|
||||
//Add the required Date Header for DUO API Calls
|
||||
if(timeString){
|
||||
http.addHeader(F("Date"), String(timeString));
|
||||
}
|
||||
|
||||
//Add Content Type Header for POST requests
|
||||
if(apiMethod == 1){
|
||||
http.addHeader(F("Content-Type"), F("application/x-www-form-urlencoded"));
|
||||
}
|
||||
|
||||
//Add the required HTTP Authorization Header for the DUO API Call
|
||||
if(hmacPassword){
|
||||
http.setAuthorization(_duoIkey, hmacPassword);
|
||||
}
|
||||
|
||||
if(apiMethod == 1){
|
||||
_httpCode = http.POST(requestContents);
|
||||
}else if(apiMethod == 0){
|
||||
_httpCode = http.GET();
|
||||
}else{
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
//Valid Duo API Endpoints HTTP(S) Response codes. Only respond with a valid request for
|
||||
//these values:
|
||||
// 200 - Success
|
||||
// 400 - Invalid or missing parameters.
|
||||
// 401 - The "Authorization" and/or "Date" headers were missing or invalid.
|
||||
//NOTE: Other HTTP Codes exist; however, only those for the API endpoints are noted above,
|
||||
// Please refer to the Duo Auth API Documentation @ https://duo.com/docs/authapi
|
||||
//----------------------------------------------------------------------------------------
|
||||
if ((_httpCode == 200) || (_httpCode == 400) || (_httpCode == 401)) { //Check for the returning code
|
||||
_lastHttpResponse = http.getString();
|
||||
http.end();
|
||||
return true;
|
||||
}else{
|
||||
_lastHttpResponse = "";
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
delete clientDuoAuth;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DuoAuthLib::processResponse(String* serializedJsonData)
|
||||
{
|
||||
JsonDocument doc;
|
||||
|
||||
_duoApiStat = "";
|
||||
_duoAuthTxId = "";
|
||||
_duoApiAuthResponseResult = "";
|
||||
_duoApiAuthResponseStatus = "";
|
||||
|
||||
_duoApiAuthResponseStatusMessage = "";
|
||||
|
||||
_duoApiFailureCode = 0;
|
||||
_duoApiFailureMessage = "";
|
||||
|
||||
//Deserialize the json response from the Duo API Endpoints
|
||||
DeserializationError error = deserializeJson(doc, *serializedJsonData);
|
||||
|
||||
//Check if have an error in our Deserialization before proceeding.
|
||||
if(!error){
|
||||
const char* apiStat = doc["stat"];
|
||||
|
||||
if(apiStat){
|
||||
_duoApiStat = String(apiStat);
|
||||
|
||||
if(strcmp(apiStat,"OK") == 0){
|
||||
JsonObject response = doc["response"];
|
||||
|
||||
if(_async){
|
||||
const char* duoTxId = response["txid"];
|
||||
if(duoTxId){
|
||||
_duoAuthTxId = String(duoTxId);
|
||||
}else{
|
||||
_duoAuthTxId = "";
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
const char* duoResult = response["result"];
|
||||
const char* duoStatus = response["status"];
|
||||
const char* duoStatusMsg = response["status_msg"];
|
||||
|
||||
if(duoResult && duoStatus && duoStatusMsg){
|
||||
_duoApiAuthResponseResult = String(duoResult);
|
||||
_duoApiAuthResponseStatus = String(duoStatus);
|
||||
_duoApiAuthResponseStatusMessage = String(duoStatusMsg);
|
||||
}else{
|
||||
_duoApiAuthResponseResult = "";
|
||||
_duoApiAuthResponseStatus = "";
|
||||
_duoApiAuthResponseStatusMessage = "";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}else if(strcmp(apiStat,"FAIL") == 0){
|
||||
_duoApiFailureCode = doc["code"];
|
||||
const char* failureMessage = doc["message"];
|
||||
|
||||
if(failureMessage){
|
||||
_duoApiFailureMessage = String(failureMessage);
|
||||
}
|
||||
|
||||
return false;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
_duoApiFailureCode = -1;
|
||||
_duoApiFailureMessage = "DuoAuthLib: Error processing received response";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DuoAuthLib::addRequestParameter(char *requestBuffer, char* field, char* value, bool lastEntry)
|
||||
{
|
||||
if(lastEntry == false){
|
||||
strcat(requestBuffer, field);
|
||||
strcat(requestBuffer, EQUALS_PAYLOAD_PARAM);
|
||||
strcat(requestBuffer, value);
|
||||
strcat(requestBuffer, AMPERSAND_PAYLOAD_PARAM);
|
||||
}else{
|
||||
strcat(requestBuffer, field);
|
||||
strcat(requestBuffer, EQUALS_PAYLOAD_PARAM);
|
||||
strcat(requestBuffer, value);
|
||||
}
|
||||
}
|
||||
|
||||
void DuoAuthLib::generateHmacPayload(char *hmacPayload, char* timeBuffer, char* httpMethod, char* duoHost, const char* duoApiPath, char* postContents)
|
||||
{
|
||||
hmacPayload[0] = 0;
|
||||
|
||||
strcat(hmacPayload, timeBuffer);
|
||||
strcat(hmacPayload, NEWLINE_PAYLOAD_PARAM);
|
||||
strcat(hmacPayload, httpMethod);
|
||||
strcat(hmacPayload, NEWLINE_PAYLOAD_PARAM);
|
||||
strcat(hmacPayload, duoHost);
|
||||
strcat(hmacPayload, NEWLINE_PAYLOAD_PARAM);
|
||||
strcat(hmacPayload, duoApiPath);
|
||||
strcat(hmacPayload, NEWLINE_PAYLOAD_PARAM);
|
||||
strcat(hmacPayload, postContents);
|
||||
}
|
||||
|
||||
void DuoAuthLib::calculateHmacSha1(char *signingKey, char *dataPayload, char *hmacSignatureChar)
|
||||
{
|
||||
byte hmacSignature[20];
|
||||
hmacSignatureChar[0] = 0;
|
||||
|
||||
mbedtls_md_context_t mbedTlsContext;
|
||||
|
||||
//Select the SHA1 Hashtype
|
||||
mbedtls_md_type_t mbedTlsHashType = MBEDTLS_MD_SHA1;
|
||||
|
||||
const size_t payloadLength = strlen(dataPayload);
|
||||
const size_t keyLength = strlen(signingKey);
|
||||
|
||||
mbedtls_md_init(&mbedTlsContext);
|
||||
|
||||
mbedtls_md_setup(&mbedTlsContext, mbedtls_md_info_from_type(mbedTlsHashType), 1);
|
||||
|
||||
mbedtls_md_hmac_starts(&mbedTlsContext, (const unsigned char *) signingKey, keyLength);
|
||||
mbedtls_md_hmac_update(&mbedTlsContext, (const unsigned char *) dataPayload, payloadLength);
|
||||
mbedtls_md_hmac_finish(&mbedTlsContext, hmacSignature);
|
||||
|
||||
mbedtls_md_free(&mbedTlsContext);
|
||||
|
||||
for(int i= 0; i< sizeof(hmacSignature); i++){
|
||||
char str[3];
|
||||
|
||||
sprintf(str, "%02x", (int)hmacSignature[i]);
|
||||
strcat(hmacSignatureChar, str);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Functions to read in a character array and output the calculated
|
||||
//buffer size, and Encode the input excluding the below
|
||||
//characters:
|
||||
// 48-57 = Numbers ( 0123456789 )
|
||||
// 65-90 = UPPPERCASE LETTERS ( ABCDEF )
|
||||
// 97-122 = lowercase Letters ( abcdef )
|
||||
// 45 = Dash ( - )
|
||||
// 46 = Period ( . )
|
||||
// 95 = Underscore ( _ )
|
||||
// 126 = Tilde ( ~ )
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Calculate the length of the Character Array based on Input
|
||||
//This function also takes into account Terminating Null
|
||||
int DuoAuthLib::encodeUrlBufferSize(char *stringToEncode)
|
||||
{
|
||||
//Start the count at 1 to account for the terminating null character
|
||||
int newStrLen = 1;
|
||||
|
||||
//Loop Through Char Array t
|
||||
for(int i= 0; i< strlen(stringToEncode); i++){
|
||||
int charAscii = (int)stringToEncode[i];
|
||||
|
||||
if((charAscii > 47 && charAscii < 58) || (charAscii > 64 && charAscii < 91) || (charAscii > 96 && charAscii < 123) || (charAscii == 45) || (charAscii == 46) || (charAscii == 95) || (charAscii == 126)){
|
||||
//Found Regular Character
|
||||
//Increment by 1
|
||||
newStrLen++;
|
||||
}else{
|
||||
//Found Character that requires URL encoding
|
||||
//Increment by 3
|
||||
newStrLen += 3;
|
||||
}
|
||||
}
|
||||
return newStrLen;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//Function to read in a character array and output a URL Encoded
|
||||
//and write the new variable to the destination variable
|
||||
void DuoAuthLib::encodeUrl(char *destination, char *stringToEncode)
|
||||
{
|
||||
//Empty our Character Array before proceeding
|
||||
destination[0] = '\0';
|
||||
|
||||
//Loop Through Char Array to perform urlEncode as required
|
||||
for(int i= 0; i< strlen(stringToEncode); i++){
|
||||
int charAscii = (int)stringToEncode[i];
|
||||
|
||||
if((charAscii > 47 && charAscii < 58) || (charAscii > 64 && charAscii < 91) || (charAscii > 96 && charAscii < 123) || (charAscii == 45) || (charAscii == 95) || (charAscii == 126) || (charAscii == 46)){
|
||||
char str[2];
|
||||
|
||||
//Output only the Single Character as it does not need to be encoded
|
||||
sprintf(str, "%c", (int)stringToEncode[i]);
|
||||
strcat(destination, str);
|
||||
}else{
|
||||
char str[4];
|
||||
|
||||
//Output the URL Encoded Format '%XX'
|
||||
sprintf(str, "%%%02X", (int)stringToEncode[i]);
|
||||
strcat(destination, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
306
lib/DuoAuthLibrary/src/DuoAuthLib.h
Normal file
306
lib/DuoAuthLibrary/src/DuoAuthLib.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
*@license
|
||||
*
|
||||
*Copyright 2020 Cisco Systems, Inc. or its affiliates
|
||||
*
|
||||
*Licensed under the Apache License, Version 2.0 (the "License");
|
||||
*you may not use this file except in compliance with the License.
|
||||
*You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*Unless required by applicable law or agreed to in writing, software
|
||||
*distributed under the License is distributed on an "AS IS" BASIS,
|
||||
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*See the License for the specific language governing permissions and
|
||||
*limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @file DuoAuthLib.h
|
||||
* @brief An Arduino Library to simplify the operations of performing Duo Multi-Factor Authentication within your ESP32 Micro Controller project.
|
||||
*
|
||||
* @url https://github.com/CiscoDevNet/Arduino-DuoAuthLibrary-ESP32
|
||||
* @version 1.0.0
|
||||
* @author Gary Oppel <gaoppel@cisco.com>
|
||||
*/
|
||||
|
||||
//Verify that the Duo Auth Library descriptor is only included once
|
||||
#ifndef DuoAuthLib_H
|
||||
#define DuoAuthLib_H
|
||||
|
||||
#include <memory>
|
||||
#include <Arduino.h>
|
||||
|
||||
enum DUO_AUTH_METHOD
|
||||
{
|
||||
PUSH,
|
||||
PASSCODE
|
||||
};
|
||||
/*!
|
||||
* @brief Main Duo Authentication Library class
|
||||
*/
|
||||
class DuoAuthLib
|
||||
{
|
||||
public:
|
||||
//Public Class Functions
|
||||
//-------------------------
|
||||
//External Functions which abstract Duo API Authentication Requests
|
||||
|
||||
DuoAuthLib();
|
||||
~DuoAuthLib();
|
||||
|
||||
/**
|
||||
* @brief Initializes the Duo Auth Library
|
||||
*
|
||||
* @param duoApiHost Contains the Duo API Hostname
|
||||
* @param duoApiIKey Contains the Duo Integration Key (IKey)
|
||||
* @param duoApiSKey Contains the Duo Secret Key (SKey)
|
||||
* @param timeInfo Contains the Memory Pointer to the timeinfo Structure Declaration
|
||||
*/
|
||||
void begin(const char* duoApiHost, const char* duoApiIKey, const char* duoApiSKey, struct tm* timeInfo);
|
||||
|
||||
/**
|
||||
* @brief Performs a Duo 'ping' API Query to check if alive
|
||||
* @return Returns `true` if Ping API Call is Successful
|
||||
*/
|
||||
bool ping();
|
||||
|
||||
/**
|
||||
* @brief Performs a Duo 'check' API Query to check if provided API-Host, Integration Key (IKey), or Signing Key (SKey) are valid
|
||||
* @return Returns `true` if provided details for the Duo API are correct
|
||||
*/
|
||||
bool checkApiKey();
|
||||
|
||||
/**
|
||||
* @brief Performs a Duo 'auth_status' API query to check if alivethe status of provided transaction Id
|
||||
* @param transactionId Asynchronous Duo Push Transaction Id to get the status
|
||||
* @return Returns `true` API call was successful
|
||||
*/
|
||||
bool authStatus(String transactionId);
|
||||
|
||||
/**
|
||||
* @brief Performs a Duo Push Request
|
||||
* @param userName Username of the user to send a Push Request
|
||||
* @param async Set this to 'true' to enable Asynchronous mode. Duo will return a Transaction ID for checking status updates later.
|
||||
* @return Returns `true` API call was successful
|
||||
*/
|
||||
bool pushAuth(char* userName, bool async = false);
|
||||
|
||||
/**
|
||||
* @brief Performs a Duo Passcode Authentication Request
|
||||
* @param userName Username of the user who is verifying their Duo passcode
|
||||
* @param userPasscode 6 Digit Duo Passcode of the user
|
||||
* @return Returns `true` API call was successful
|
||||
*/
|
||||
bool passcodeAuth(char* userName, char* userPasscode);
|
||||
|
||||
/**
|
||||
* @brief Checks if the last Duo Push/Passcode Request's Authentication was Successful
|
||||
* @return Returns `true` if Duo Authentication was successful (E.g. Duo Auth API Result = 'allow')
|
||||
*/
|
||||
bool authSuccessful();
|
||||
|
||||
/**
|
||||
* @brief Checks if the status returned from the last Duo Request is in the Waiting State (Only Applies to Asynchronous Pushes)
|
||||
* @return Returns `true` if Duo Push is in waiting state (E.g. Duo Auth API Result = 'waiting')
|
||||
*/
|
||||
bool pushWaiting();
|
||||
|
||||
/**
|
||||
* @brief Sets the HTTP Timeout for the Duo API Request (Default: 30000ms [30 seconds])
|
||||
* @param httpTimeout Value in milliseconds for Duo's HTTP API Requests
|
||||
*/
|
||||
void setHttpTimeout(int httpTimeout);
|
||||
|
||||
/**
|
||||
* @brief Sets the IP Address of the device for Duo API Auth Requests (Default: 0.0.0.0)
|
||||
* @param ipAddress Value contains the Public IP Address of the Device for additonal Duo Policy Controls
|
||||
*/
|
||||
void setIPAddress(char* ipAddress);
|
||||
|
||||
/**
|
||||
* @brief Sets the Duo Mobile Application's Push Value. This string is displayed in the Duo Mobile app before the word "request".
|
||||
* @param pushType Value contains the text to be displayed on the Push Notification Screen
|
||||
*/
|
||||
void setPushType(char* pushType);
|
||||
|
||||
/**
|
||||
* @brief Sets the Push Type Notification Text that is displayed to the Enduser's Mobile Device. (Only supported with Duo Push functionality)
|
||||
* @param Character Array of the Duo Push Type Text
|
||||
*/
|
||||
int getHttpCode();
|
||||
|
||||
/**
|
||||
* @brief Gets the RAW HTTP Response from the last Duo Request
|
||||
* @return Returns the RAW HTTP Response
|
||||
*/
|
||||
String getHttpResponse();
|
||||
|
||||
/**
|
||||
* @brief Gets the API Stat Response from the last Duo Request
|
||||
* @return Returns the API Stat Response ('OK', 'FAIL')
|
||||
*/
|
||||
String getApiStat();
|
||||
|
||||
/**
|
||||
* @brief Gets the Auth Response Result from the last Duo Request
|
||||
* @return Returns the Auth Response Result
|
||||
*/
|
||||
String getAuthResponseResult();
|
||||
|
||||
/**
|
||||
* @brief Gets the Auth Response Status from the last Duo Request
|
||||
* @return Returns the Auth Response Status
|
||||
*/
|
||||
String getAuthResponseStatus();
|
||||
|
||||
/**
|
||||
* @brief Gets the Auth Response Status Message from the last Duo Request
|
||||
* @return Returns the Auth Response Status Message
|
||||
*/
|
||||
String getAuthResponseStatusMessage();
|
||||
|
||||
/**
|
||||
* @brief Sets the Duo Mobile Application's Push Value. This string is displayed in the Duo Mobile app before the word "request".
|
||||
* @param pushType Value contains the text to be displayed on the Push Notification Screen
|
||||
*/
|
||||
String getAuthTxId();
|
||||
|
||||
//Duo API Error Code Reference Table
|
||||
//https://help.duo.com/s/article/1338
|
||||
|
||||
/**
|
||||
* @brief Gets the API Failure Code from the last Duo Request
|
||||
* @return Returns the API Failure Code
|
||||
*/
|
||||
String getApiFailureCode();
|
||||
|
||||
/**
|
||||
* @brief Gets the API Failure Message from the last Duo Request
|
||||
* @return Returns the API Failure Message
|
||||
*/
|
||||
String getApiFailureMessage();
|
||||
|
||||
protected:
|
||||
|
||||
//Protected Class Functions
|
||||
//-------------------------
|
||||
|
||||
//Internal Functions to handle abstracting core and common Library Functions
|
||||
bool performAuth(char* userId, char* userPasscode, enum DUO_AUTH_METHOD authMethod);
|
||||
bool submitApiRequest(uint8_t apiMethod, char *timeString, const char* apiPath, char *hmacPassword, char* requestContents);
|
||||
bool processResponse(String* serializedJsonData);
|
||||
|
||||
//Functions to generate the API Request to the required format
|
||||
void generateHmacPayload(char *hmacPayload, char* timeBuffer, char* httpMethod, char* duoHost, const char* duoApiPath, char* postContents);
|
||||
void addRequestParameter(char *requestBuffer, char* field, char* value, bool lastEntry = false);
|
||||
|
||||
//Function that Calculates the HMAC-SHA1 Signature for the Duo API Call
|
||||
void calculateHmacSha1(char *signingKey, char *dataPayload, char *hmacSignatureChar);
|
||||
|
||||
//URL Encoding Functions
|
||||
int encodeUrlBufferSize(char *stringToEncode);
|
||||
void encodeUrl(char *destination, char *stringToEncode);
|
||||
|
||||
//Duo Auth Library Initialized Flag
|
||||
bool _init;
|
||||
|
||||
//Buffer size for Date/Time Output Character Array
|
||||
const int TIME_BUF_STR_SIZE = 36;
|
||||
|
||||
//Maximum IPv4 Length is 16 including null termination
|
||||
static const int MAX_IP_LENGTH = 16;
|
||||
|
||||
//Maximum Length of the 'Push Type' Notification String
|
||||
static const int MAX_TYPE_LENGTH = 21;
|
||||
|
||||
//Maximum Length of various miscellaneous variables
|
||||
static const int MAX_METHOD_LENGTH = 5;
|
||||
static const int MAX_HOSTNAME_LENGTH = 64;
|
||||
static const int MAX_API_ENDPOINT_LENGTH = 24;
|
||||
|
||||
//Maximum Username Length
|
||||
//NOTE: Usernames can contain e-mail addresses.
|
||||
// Tested & validated with 44 character length Email Address (username).
|
||||
// Maximum username length selected is 50, with 49 being the usable maximum.
|
||||
static const int MAX_USER_LENGTH = 50;
|
||||
|
||||
static const int MAX_PARAM_LENGTH = 10;
|
||||
static const int MAX_PAYLOAD_LENGTH = 20;
|
||||
|
||||
const int SIGNATURE_PAYLOAD_BUFFER_SIZE = TIME_BUF_STR_SIZE + MAX_METHOD_LENGTH + MAX_HOSTNAME_LENGTH + MAX_API_ENDPOINT_LENGTH;
|
||||
|
||||
//Required Parameters for Duo API Auth Requests
|
||||
const char* ASYNC_PARAM = "async";
|
||||
const char* AUTO_PARAM = "auto";
|
||||
const char* DEVICE_PARAM = "device";
|
||||
const char* FACTOR_PARAM = "factor";
|
||||
const char* IPADDR_PARAM = "ipaddr";
|
||||
const char* PASSCODE_PARAM = "passcode";
|
||||
const char* PUSH_PARAM = "push";
|
||||
const char* TRANSACTION_PARAM = "txid";
|
||||
const char* TYPE_PARAM = "type";
|
||||
const char* USERNAME_PARAM = "username";
|
||||
|
||||
//Common variables for Duo API Auth Requests
|
||||
const char* NEWLINE_PAYLOAD_PARAM = "\n";
|
||||
const char* AMPERSAND_PAYLOAD_PARAM = "&";
|
||||
const char* EQUALS_PAYLOAD_PARAM = "=";
|
||||
|
||||
//Duo Auth API URL Paths
|
||||
const char* DUO_PING_API_PATH = "/auth/v2/ping";
|
||||
const char* DUO_CHECK_API_PATH = "/auth/v2/check";
|
||||
const char* DUO_AUTH_API_PATH = "/auth/v2/auth";
|
||||
const char* DUO_AUTHSTATUS_API_PATH = "/auth/v2/auth_status";
|
||||
|
||||
//Duo Auth Library HTTP User Agent
|
||||
String _duoUserAgent = "DuoAuthLib/1.0 (ESP32HTTPClient)";
|
||||
|
||||
//Variable to hold IP Address for Duo Authentication Requests
|
||||
char _ipAddress[MAX_IP_LENGTH];
|
||||
|
||||
//Variable to hold Push Type string, which is displayed on Push notifications
|
||||
char _duoPushType[MAX_TYPE_LENGTH];
|
||||
|
||||
//Variable holds if the Push request is Asynchronous
|
||||
bool _async;
|
||||
|
||||
//Duo Auth Library Required Variables for API interface
|
||||
char* _duoHost;
|
||||
char* _duoIkey;
|
||||
char* _duoSkey;
|
||||
|
||||
//Asynchronous Request ID Variable
|
||||
char* _asyncRequestId;
|
||||
|
||||
//Duo Auth Library HTTP Timeout value (Default: 30000 [30 seconds])
|
||||
int _defaultTimeout = 30000;
|
||||
int _httpTimeout;
|
||||
|
||||
//HTTP code returned from the Duo API Request
|
||||
int _httpCode;
|
||||
|
||||
//Return Variables for Public Functions
|
||||
String _duoApiStat;
|
||||
String _duoAuthTxId;
|
||||
String _duoApiAuthResponseResult;
|
||||
String _duoApiAuthResponseStatus;
|
||||
String _duoApiAuthResponseStatusMessage;
|
||||
int _duoApiFailureCode;
|
||||
String _duoApiFailureMessage;
|
||||
String _lastHttpResponse;
|
||||
|
||||
//Buffer sizes for Check Auth API User provided variables
|
||||
const int CHECK_AUTH_BUFFER_SIZE = 42;
|
||||
|
||||
//Buffer size for Authentication Request API.
|
||||
//This buffer multiplies the max user length by 3 as the assumption would be all characters would require URL Encoding
|
||||
const int AUTH_REQUEST_BUFFER_SIZE = 64 + (MAX_USER_LENGTH * 3);
|
||||
|
||||
//Empty Pointer for the `timeinfo` Variable passed in by the end user from the 'begin(...)' function
|
||||
struct tm* _timeinfo = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -75,8 +75,6 @@ env.AddPostAction("$PROJECT_DIR/updater/release/" + get_board_name(env) + "/upda
|
||||
env.AddPostAction("$BUILD_DIR/firmware.bin", package_last_files)
|
||||
env.AddPostAction("$BUILD_DIR/partitions.bin", copy_files)
|
||||
env.AddPostAction("$BUILD_DIR/bootloader.bin", copy_files)
|
||||
|
||||
if env.GetProjectOption("custom_build") == 'debug':
|
||||
env.AddPostAction("$BUILD_DIR/firmware.elf", copy_files)
|
||||
env.AddPostAction("$BUILD_DIR/firmware.elf", copy_files)
|
||||
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", merge_bin)
|
||||
@@ -39,3 +39,25 @@ Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||
MrY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
DigiCert High Assurance EV Root CA
|
||||
==================================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
|
||||
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw
|
||||
KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw
|
||||
MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
|
||||
MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu
|
||||
Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t
|
||||
Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
|
||||
OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3
|
||||
MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ
|
||||
NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
|
||||
h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
|
||||
Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
|
||||
JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ
|
||||
V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
|
||||
myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK
|
||||
mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
@@ -5,7 +5,7 @@
|
||||
#define NUKI_HUB_VERSION "9.08"
|
||||
#define NUKI_HUB_VERSION_INT (uint32_t)908
|
||||
#define NUKI_HUB_BUILD "unknownbuildnr"
|
||||
#define NUKI_HUB_DATE "2025-01-17"
|
||||
#define NUKI_HUB_DATE "2025-01-20"
|
||||
|
||||
#define GITHUB_LATEST_RELEASE_URL (char*)"https://github.com/technyon/nuki_hub/releases/latest"
|
||||
#define GITHUB_OTA_MANIFEST_URL (char*)"https://raw.githubusercontent.com/technyon/nuki_hub/binary/ota/manifest.json"
|
||||
|
||||
@@ -38,7 +38,7 @@ HomeAssistantDiscovery::HomeAssistantDiscovery(NetworkDevice* device, Preference
|
||||
Log->println(curDevId);
|
||||
Log->print("Saved ID: ");
|
||||
Log->println(savedDevId);
|
||||
Log->println("Efuse ID and NukiHub device ID do not match, removing HASS setup for incorrect NukiHub device ID.");
|
||||
Log->println("Efuse ID and Nuki Hub device ID do not match, removing HomeAssistant setup for incorrect Nuki Hub device ID.");
|
||||
char uidString[20];
|
||||
itoa(_preferences->getUInt(preference_device_id_lock, 0), uidString, 10);
|
||||
removeHASSConfig(uidString);
|
||||
@@ -60,7 +60,7 @@ void HomeAssistantDiscovery::setupHASS(int type, uint32_t nukiId, char* nukiName
|
||||
if(type == 0)
|
||||
{
|
||||
publishHASSNukiHubConfig();
|
||||
Log->println("HASS setup for NukiHub completed.");
|
||||
Log->println("HomeAssistant setup for Nuki Hub completed.");
|
||||
}
|
||||
else if(type == 1)
|
||||
{
|
||||
@@ -71,7 +71,7 @@ void HomeAssistantDiscovery::setupHASS(int type, uint32_t nukiId, char* nukiName
|
||||
String lockTopic = _baseTopic;
|
||||
lockTopic.concat("/lock");
|
||||
publishHASSConfig((char*)"SmartLock", lockTopic.c_str(), nukiName, uidString, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad, publishAuthData, (char*)"lock", (char*)"unlock", (char*)"unlatch");
|
||||
Log->println("HASS setup for lock completed.");
|
||||
Log->println("HomeAssistant setup for lock completed.");
|
||||
}
|
||||
else if(type == 2)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ void HomeAssistantDiscovery::setupHASS(int type, uint32_t nukiId, char* nukiName
|
||||
publishHASSConfig((char*)"Opener", openerTopic.c_str(), nukiName, uidString, firmwareVersion, hardwareVersion, hasDoorSensor, hasKeypad, publishAuthData, (char*)"deactivateRTO", (char*)"activateRTO", (char*)"electricStrikeActuation");
|
||||
}
|
||||
|
||||
Log->println("HASS setup for opener completed.");
|
||||
Log->println("HomeAssistant setup for opener completed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ void HomeAssistantDiscovery::publishHASSNukiHubConfig()
|
||||
JsonArray ids = dev["ids"].to<JsonArray>();
|
||||
ids.add(String("nuki_") + _nukiHubUidString);
|
||||
json["dev"]["mf"] = "Technyon";
|
||||
json["dev"]["mdl"] = "NukiHub";
|
||||
json["dev"]["mdl"] = "Nuki Hub";
|
||||
json["dev"]["name"] = _hostname.c_str();
|
||||
json["dev"]["sw"] = NUKI_HUB_VERSION;
|
||||
json["dev"]["hw"] = NUKI_HUB_HW;
|
||||
|
||||
@@ -76,6 +76,17 @@
|
||||
#define preference_mqtt_ssl_enabled (char*)"mqttSSLena"
|
||||
#define preference_lock_gemini_pin (char*)"geminiPin"
|
||||
#define preference_lock_gemini_enabled (char*)"geminiena"
|
||||
#define preference_cred_duo_enabled (char*)"duoena"
|
||||
#define preference_cred_duo_host (char*)"duoHost"
|
||||
#define preference_cred_duo_ikey (char*)"duoIkey"
|
||||
#define preference_cred_duo_skey (char*)"duoSkey"
|
||||
#define preference_cred_duo_user (char*)"duoUser"
|
||||
#define preference_https_fqdn (char*)"httpsFQDN"
|
||||
#define preference_bypass_proxy (char*)"credBypass"
|
||||
#define preference_cred_session_lifetime (char*)"credLf"
|
||||
#define preference_cred_session_lifetime_remember (char*)"credLfRmbr"
|
||||
#define preference_cred_session_lifetime_duo (char*)"credLfDuo"
|
||||
#define preference_cred_session_lifetime_duo_remember (char*)"credLfDuoRmbr"
|
||||
|
||||
// CHANGE DOES NOT REQUIRE REBOOT TO TAKE EFFECT
|
||||
#define preference_find_best_rssi (char*)"nwbestrssi"
|
||||
@@ -130,6 +141,7 @@
|
||||
#define preference_hybrid_reboot_on_disconnect (char*)"hybridRbtLck"
|
||||
|
||||
//NOT USER CHANGABLE
|
||||
#define preference_mfa_reconfigure (char*)"mfaRECONF"
|
||||
#define preference_ntw_reconfigure (char*)"ntwRECONF"
|
||||
#define preference_updater_version (char*)"updVer"
|
||||
#define preference_updater_build (char*)"updBuild"
|
||||
@@ -179,8 +191,15 @@ inline void initPreferences(Preferences* preferences)
|
||||
preferences->putBytes(preference_conf_lock_advanced_acl, (byte*)(&advancedLockConfigAclPrefs), sizeof(advancedLockConfigAclPrefs));
|
||||
uint32_t advancedOpenerConfigAclPrefs[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
preferences->putBytes(preference_conf_opener_advanced_acl, (byte*)(&advancedOpenerConfigAclPrefs), sizeof(advancedOpenerConfigAclPrefs));
|
||||
|
||||
preferences->putString(preference_mqtt_lock_path, "nukihub");
|
||||
preferences->putString(preference_time_server, "pool.ntp.org");
|
||||
preferences->putString(preference_cred_duo_host, "");
|
||||
preferences->putString(preference_cred_duo_ikey, "");
|
||||
preferences->putString(preference_cred_duo_skey, "");
|
||||
preferences->putString(preference_cred_duo_user, "");
|
||||
preferences->putString(preference_https_fqdn, "");
|
||||
preferences->putString(preference_bypass_proxy, "");
|
||||
|
||||
preferences->putBool(preference_check_updates, true);
|
||||
preferences->putBool(preference_opener_continuous_mode, false);
|
||||
@@ -194,7 +213,6 @@ inline void initPreferences(Preferences* preferences)
|
||||
preferences->putBool(preference_enable_bootloop_reset, false);
|
||||
preferences->putBool(preference_show_secrets, false);
|
||||
preferences->putBool(preference_find_best_rssi, true);
|
||||
|
||||
preferences->putBool(preference_conf_info_enabled, true);
|
||||
preferences->putBool(preference_keypad_info_enabled, false);
|
||||
preferences->putBool(preference_keypad_topic_per_entry, false);
|
||||
@@ -208,6 +226,15 @@ inline void initPreferences(Preferences* preferences)
|
||||
preferences->putBool(preference_register_opener_as_app, false);
|
||||
preferences->putBool(preference_mqtt_ssl_enabled, false);
|
||||
preferences->putBool(preference_lock_gemini_enabled, false);
|
||||
preferences->putBool(preference_debug_connect, false);
|
||||
preferences->putBool(preference_debug_communication, false);
|
||||
preferences->putBool(preference_debug_readable_data, false);
|
||||
preferences->putBool(preference_debug_hex_data, false);
|
||||
preferences->putBool(preference_debug_command, false);
|
||||
preferences->putBool(preference_connect_mode, true);
|
||||
preferences->putBool(preference_retain_gpio, false);
|
||||
preferences->putBool(preference_enable_debug_mode, false);
|
||||
preferences->putBool(preference_cred_duo_enabled, false);
|
||||
|
||||
preferences->putInt(preference_mqtt_broker_port, 1883);
|
||||
preferences->putInt(preference_buffer_size, CHAR_BUFFER_SIZE);
|
||||
@@ -227,15 +254,10 @@ inline void initPreferences(Preferences* preferences)
|
||||
preferences->putInt(preference_query_interval_battery, 1800);
|
||||
preferences->putInt(preference_query_interval_keypad, 1800);
|
||||
preferences->putInt(preference_http_auth_type, 0);
|
||||
|
||||
preferences->putBool(preference_debug_connect, false);
|
||||
preferences->putBool(preference_debug_communication, false);
|
||||
preferences->putBool(preference_debug_readable_data, false);
|
||||
preferences->putBool(preference_debug_hex_data, false);
|
||||
preferences->putBool(preference_debug_command, false);
|
||||
preferences->putBool(preference_connect_mode, true);
|
||||
preferences->putBool(preference_retain_gpio, false);
|
||||
preferences->putBool(preference_enable_debug_mode, false);
|
||||
preferences->putInt(preference_cred_session_lifetime, 3600);
|
||||
preferences->putInt(preference_cred_session_lifetime_remember, 720);
|
||||
preferences->putInt(preference_cred_session_lifetime_duo, 3600);
|
||||
preferences->putInt(preference_cred_session_lifetime_duo_remember, 720);
|
||||
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32H2
|
||||
WiFi.begin();
|
||||
@@ -488,16 +510,18 @@ private:
|
||||
preference_update_from_mqtt, preference_show_secrets, preference_ble_tx_power, preference_webserial_enabled, preference_find_best_rssi, preference_lock_gemini_enabled,
|
||||
preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr, preference_network_custom_irq,
|
||||
preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso, preference_network_custom_mosi,
|
||||
preference_network_custom_pwr, preference_network_custom_mdio, preference_ntw_reconfigure, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count,
|
||||
preference_network_custom_pwr, preference_network_custom_mdio, preference_lock_max_auth_entry_count, preference_opener_max_auth_entry_count,
|
||||
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_auth_max_entries, preference_wifi_ssid, preference_wifi_pass,
|
||||
preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_mqtt_hass_enabled, preference_hass_device_discovery, preference_retain_gpio,
|
||||
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode,
|
||||
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id
|
||||
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_nukihub_id,
|
||||
preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_cred_duo_enabled, preference_https_fqdn, preference_bypass_proxy,
|
||||
preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember
|
||||
};
|
||||
std::vector<char*> _redact =
|
||||
{
|
||||
preference_mqtt_user, preference_mqtt_password, preference_cred_user, preference_cred_password, preference_nuki_id_lock, preference_nuki_id_opener, preference_wifi_pass,
|
||||
preference_lock_gemini_pin
|
||||
preference_lock_gemini_pin, preference_cred_duo_host, preference_cred_duo_ikey, preference_cred_duo_skey, preference_cred_duo_user, preference_https_fqdn, preference_bypass_proxy
|
||||
};
|
||||
std::vector<char*> _boolPrefs =
|
||||
{
|
||||
@@ -508,10 +532,10 @@ private:
|
||||
preference_publish_authdata, preference_publish_debug_info, preference_official_hybrid_enabled, preference_mqtt_hass_enabled, preference_retain_gpio,
|
||||
preference_official_hybrid_actions, preference_official_hybrid_retry, preference_conf_info_enabled, preference_disable_non_json, preference_update_from_mqtt,
|
||||
preference_auth_control_enabled, preference_auth_topic_per_entry, preference_auth_info_enabled, preference_webserial_enabled, preference_hass_device_discovery,
|
||||
preference_ntw_reconfigure, preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi,
|
||||
preference_keypad_check_code_enabled, preference_disable_network_not_connected, preference_find_best_rssi,
|
||||
preference_debug_connect, preference_debug_communication, preference_debug_readable_data, preference_debug_hex_data, preference_debug_command, preference_connect_mode,
|
||||
preference_lock_force_id, preference_lock_force_doorsensor, preference_lock_force_keypad, preference_opener_force_id, preference_opener_force_keypad, preference_mqtt_ssl_enabled,
|
||||
preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode
|
||||
preference_hybrid_reboot_on_disconnect, preference_lock_gemini_enabled, preference_enable_debug_mode, preference_cred_duo_enabled
|
||||
};
|
||||
std::vector<char*> _bytePrefs =
|
||||
{
|
||||
@@ -528,7 +552,8 @@ private:
|
||||
preference_task_size_network, preference_task_size_nuki, preference_authlog_max_entries, preference_keypad_max_entries, preference_timecontrol_max_entries,
|
||||
preference_ble_tx_power, preference_network_custom_mdc, preference_network_custom_clk, preference_network_custom_phy, preference_network_custom_addr,
|
||||
preference_network_custom_irq, preference_network_custom_rst, preference_network_custom_cs, preference_network_custom_sck, preference_network_custom_miso,
|
||||
preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_http_auth_type
|
||||
preference_network_custom_mosi, preference_network_custom_pwr, preference_network_custom_mdio, preference_http_auth_type,
|
||||
preference_cred_session_lifetime, preference_cred_session_lifetime_remember, preference_cred_session_lifetime_duo, preference_cred_session_lifetime_duo_remember
|
||||
};
|
||||
std::vector<char*> _uintPrefs =
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -100,14 +100,19 @@ private:
|
||||
std::vector<int> _rssiList;
|
||||
String generateConfirmCode();
|
||||
String _confirmCode = "----";
|
||||
|
||||
void saveSessions();
|
||||
void loadSessions();
|
||||
|
||||
int checkDuoAuth(PsychicRequest *request);
|
||||
bool startDuoAuth();
|
||||
void saveSessions(bool duo = false);
|
||||
void loadSessions(bool duo = false);
|
||||
void clearSessions();
|
||||
esp_err_t logoutSession(PsychicRequest *request, PsychicResponse* resp);
|
||||
bool isAuthenticated(PsychicRequest *request);
|
||||
bool isAuthenticated(PsychicRequest *request, bool duo = false);
|
||||
bool processLogin(PsychicRequest *request, PsychicResponse* resp);
|
||||
int doAuthentication(PsychicRequest *request);
|
||||
esp_err_t buildLoginHtml(PsychicRequest *request, PsychicResponse* resp);
|
||||
esp_err_t buildLoginHtml(PsychicRequest *request, PsychicResponse* resp);
|
||||
esp_err_t buildDuoHtml(PsychicRequest *request, PsychicResponse* resp);
|
||||
esp_err_t buildDuoCheckHtml(PsychicRequest *request, PsychicResponse* resp);
|
||||
esp_err_t buildSSIDListHtml(PsychicRequest *request, PsychicResponse* resp);
|
||||
esp_err_t buildConfirmHtml(PsychicRequest *request, PsychicResponse* resp, const String &message, uint32_t redirectDelay = 5, bool redirect = false, String redirectTo = "/");
|
||||
esp_err_t buildOtaHtml(PsychicRequest *request, PsychicResponse* resp, bool debug = false);
|
||||
@@ -133,8 +138,21 @@ private:
|
||||
char _credUser[31] = {0};
|
||||
char _credPassword[31] = {0};
|
||||
bool _allowRestartToPortal = false;
|
||||
bool _isSSL = false;
|
||||
uint8_t _partitionType = 0;
|
||||
size_t _otaContentLen = 0;
|
||||
String _hostname;
|
||||
JsonDocument _httpSessions;
|
||||
JsonDocument _duoSessions;
|
||||
JsonDocument _sessionsOpts;
|
||||
struct tm timeinfo;
|
||||
bool _duoActiveRequest;
|
||||
bool _duoEnabled = false;
|
||||
String _duoTransactionId;
|
||||
String _duoHost;
|
||||
String _duoSkey;
|
||||
String _duoIkey;
|
||||
String _duoUser;
|
||||
String _duoCheckId;
|
||||
String _duoCheckIP;
|
||||
};
|
||||
|
||||
24
src/main.cpp
24
src/main.cpp
@@ -618,6 +618,18 @@ void setup()
|
||||
file2.close();
|
||||
key[filesize2] = '\0';
|
||||
|
||||
psychicServer = new PsychicHttpServer();
|
||||
psychicServer->config.ctrl_port = 20424;
|
||||
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String url = "https://" + request->host() + request->url();
|
||||
if (preferences->getString(preference_https_fqdn, "") != "")
|
||||
{
|
||||
url = "https://" + preferences->getString(preference_https_fqdn) + request->url();
|
||||
}
|
||||
response->setCode(301);
|
||||
response->addHeader("Cache-Control", "no-cache");
|
||||
return response->redirect(url.c_str()); });
|
||||
|
||||
psychicSSLServer = new PsychicHttpsServer;
|
||||
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
|
||||
psychicSSLServer->setCertificate(cert, key);
|
||||
@@ -780,6 +792,18 @@ void setup()
|
||||
file2.close();
|
||||
key[filesize2] = '\0';
|
||||
|
||||
psychicServer = new PsychicHttpServer();
|
||||
psychicServer->config.ctrl_port = 20424;
|
||||
psychicServer->onNotFound([](PsychicRequest* request, PsychicResponse* response) {
|
||||
String url = "https://" + request->host() + request->url();
|
||||
if (preferences->getString(preference_https_fqdn, "") != "")
|
||||
{
|
||||
url = "https://" + preferences->getString(preference_https_fqdn) + request->url();
|
||||
}
|
||||
|
||||
response->setCode(301);
|
||||
response->addHeader("Cache-Control", "no-cache");
|
||||
return response->redirect(url.c_str()); });
|
||||
psychicSSLServer = new PsychicHttpsServer;
|
||||
psychicSSLServer->ssl_config.httpd.max_open_sockets = 8;
|
||||
psychicSSLServer->setCertificate(cert, key);
|
||||
|
||||
@@ -51,6 +51,7 @@ lib_ignore =
|
||||
lib_deps =
|
||||
PsychicHttp=symlink://../lib/PsychicHttp
|
||||
ArduinoJson=symlink://../lib/ArduinoJson
|
||||
DuoAuthLibrary=symlink://../lib/DuoAuthLibrary
|
||||
|
||||
monitor_speed = 115200
|
||||
monitor_filters =
|
||||
|
||||
Reference in New Issue
Block a user