Duo Auth
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user