-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c710948
Showing
27 changed files
with
1,684 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[flake8] | ||
# Ignore line too long errors | ||
ignore = E501 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
*.egg-info* | ||
*.pyc | ||
*.pyo | ||
*.swp | ||
*~ | ||
.env | ||
.gitconfig | ||
.idea | ||
__pycache__ | ||
build | ||
dist | ||
env/ | ||
MANIFEST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
language: python | ||
python: | ||
- '2.7' | ||
- '3.5' | ||
- '3.6' | ||
- '3.7' | ||
- '3.8' | ||
install: | ||
- pip install -r requirements.txt | ||
- pip install -r tests/requirements.txt | ||
script: | ||
- flake8 | ||
- nose2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Copyright (c) 2020, Duo Security, Inc. | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions | ||
are met: | ||
|
||
1. Redistributions of source code must retain the above copyright | ||
notice, this list of conditions and the following disclaimer. | ||
2. Redistributions in binary form must reproduce the above copyright | ||
notice, this list of conditions and the following disclaimer in the | ||
documentation and/or other materials provided with the distribution. | ||
3. The name of the author may not be used to endorse or promote products | ||
derived from this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Duo Universal Python SDK | ||
|
||
This SDK allows a web developer to quickly add Duo's interactive, self-service, two-factor authentication to any Python web login form. Both Python 2 and Python 3 are supported. | ||
|
||
What's here: | ||
* `duo_universal` - The Python Duo SDK for interacting with the Duo Universal Prompt | ||
* `demo` - An example web application with Duo integrated | ||
* `tests` - Test cases | ||
|
||
## Getting Started | ||
To use the SDK in your existing development environment, install it from pypi (https://pypi.org/project/duo_universal). | ||
``` | ||
pip install duo_universal | ||
``` | ||
Once it's installed, see our developer documentation at https://duo.com/docs/duoweb and `demo/app.py` in this repo for guidance on integrating Duo 2FA into your web application. | ||
|
||
## Contribute | ||
To contribute, fork this repo and make a pull request with your changes when they're ready. | ||
|
||
If you're not already working from a dedicated development environment, it's recommended a virtual environment is used. Assuming a virtual environment named `env`, create and activate the environment: | ||
``` | ||
# Python 3 | ||
python -m venv env | ||
source env/bin/activate | ||
# Python 2 | ||
virtualenv env | ||
source env/bin/activate | ||
``` | ||
|
||
Build and install the SDK form source: | ||
``` | ||
pip install -r requirements.txt | ||
pip install . | ||
``` | ||
|
||
## Tests | ||
Install the test requirements: | ||
``` | ||
cd tests | ||
pip install -r requirements.txt | ||
``` | ||
Then run tests from the `test` directory: | ||
``` | ||
# Run an individual test file | ||
python <test_name>.py | ||
# Run all tests with nose | ||
nose2 | ||
# Run all tests with unittest | ||
python -m unittest | ||
``` | ||
|
||
## Lint | ||
``` | ||
flake8 | ||
``` | ||
|
||
## Support | ||
|
||
Please report any bugs, feature requests, or issues to us directly at support@duosecurity.com. | ||
|
||
Thank you for using Duo! | ||
|
||
https://duo.com/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Duo Universal Python SDK Demo | ||
|
||
A simple Python web application that serves a logon page integrated with Duo 2FA. | ||
|
||
## Setup | ||
The following steps assume the SDK is already installed and that you're working in your preferred environment. See the top-level README for installation and environment setup instructions. | ||
|
||
Install the demo requirements: | ||
``` | ||
cd demo | ||
pip install -r requirements.txt | ||
``` | ||
|
||
Then, create a `Web SDK` application in the Duo Admin Panel. See https://duo.com/docs/protecting-applications for more details. | ||
## Using the App | ||
|
||
1. Using the Client ID, Client Secret, and API Hostname for your `Web SDK` application, start the app. | ||
``` | ||
DUO_CLIENT_ID=<client_id> DUO_CLIENT_SECRET=<client_secret> DUO_API_HOST=<api_host> python app.py | ||
``` | ||
1. Navigate to http://localhost:8080. | ||
1. Log in with the user you would like to enroll in Duo or with an already enrolled user (any password will work). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import json | ||
import os | ||
import traceback | ||
|
||
from flask import Flask, request, redirect, session, render_template | ||
|
||
from duo_universal.client import Client, DuoException | ||
|
||
duo_client = Client( | ||
client_id=os.environ.get("DUO_CLIENT_ID"), | ||
client_secret=os.environ.get("DUO_CLIENT_SECRET"), | ||
host=os.environ.get("DUO_API_HOST"), | ||
redirect_uri="http://localhost:8080/duo-callback" | ||
) | ||
|
||
duo_failmode = os.environ.get("DUO_FAILMODE", "open") | ||
|
||
app = Flask(__name__) | ||
|
||
|
||
@app.route("/", methods=['GET']) | ||
def login(): | ||
return render_template("login.html", message="This is a demo") | ||
|
||
|
||
@app.route("/", methods=['POST']) | ||
def login_post(): | ||
""" | ||
respond to HTTP POST with 2FA as long as health check passes | ||
""" | ||
try: | ||
duo_client.health_check() | ||
except DuoException: | ||
traceback.print_exc() | ||
if duo_failmode.upper() == "OPEN": | ||
# If we're failing open errors in 2FA still allow for success | ||
return render_template("success.html", | ||
message="Login 'Successful', but 2FA Not Performed. Confirm Duo client/secret/host values are correct") | ||
else: | ||
# Otherwise the login fails and redirect user to the login page | ||
return render_template("login.html", | ||
message="2FA Unavailable. Confirm Duo client/secret/host values are correct") | ||
|
||
username = request.form.get('username') | ||
|
||
# Generate random string to act as a state for the exchange | ||
state = duo_client.generate_state() | ||
session['state'] = state | ||
session['username'] = username | ||
prompt_uri = duo_client.create_auth_url(username, state) | ||
|
||
# Redirect to prompt URI which will redirect to the client's redirect URI | ||
# after 2FA | ||
return redirect(prompt_uri) | ||
|
||
|
||
# This route URL must match the redirect_uri passed to the duo client | ||
@app.route("/duo-callback") | ||
def duo_callback(): | ||
if request.args.get('error'): | ||
return "Got Error: {}".format(request.args) | ||
|
||
# Get state to verify consistency and originality | ||
state = request.args.get('state') | ||
|
||
# Get authorization token to trade for 2FA | ||
code = request.args.get('code') | ||
|
||
if 'state' in session and 'username' in session: | ||
saved_state = session['state'] | ||
username = session['username'] | ||
else: | ||
# For flask, if url used to get to login.html is not localhost, | ||
# (ex: 127.0.0.1) then the sessions will be different | ||
# and the localhost session does not have the state | ||
return render_template("login.html", | ||
message="No saved state please login again") | ||
|
||
# Ensure nonce matches from initial request | ||
if state != saved_state: | ||
return render_template("login.html", | ||
message="Duo state does not match saved state") | ||
|
||
decoded_token = duo_client.exchange_authorization_code_for_2fa_result(code, username) | ||
|
||
# Exchange happened successfully so render success page | ||
return render_template("success.html", | ||
message=json.dumps(decoded_token, indent=2, sort_keys=True)) | ||
|
||
|
||
if __name__ == '__main__': | ||
app.secret_key = os.urandom(32) | ||
app.run(host="localhost", port=8080) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Flask==1.1.2 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
body, input { | ||
font: 17px arial, sans-serif; | ||
} | ||
|
||
.content, | ||
.input-form { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} | ||
|
||
img { | ||
padding: 20px; | ||
height: 84px; | ||
margin-top: 40px; | ||
} | ||
|
||
input { | ||
width: 250px; | ||
padding: 12px; | ||
margin-top: 10px; | ||
} | ||
|
||
input[type=text] { | ||
margin-left: 44px; | ||
} | ||
|
||
input[type=password] { | ||
margin-left: 10px; | ||
} | ||
|
||
h3 { | ||
margin-top: 23px; | ||
} | ||
|
||
pre.language-json { | ||
font-size: 20px; | ||
} | ||
|
||
div.success { | ||
margin-top: 20px; | ||
} | ||
|
||
pre.auth-token { | ||
display: inline-block; | ||
text-align: left; | ||
} | ||
|
||
button { | ||
background-color: #6BBF4E; | ||
color: white; | ||
border: none; | ||
padding: 9px 18px; | ||
margin-top: 17px; | ||
border-radius: 4px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<html> | ||
<body> | ||
<div class="content"> | ||
<link rel="stylesheet" href='/static/style.css'> | ||
<div class="logo"> | ||
<img src="/static/images/logo.png"> | ||
</div> | ||
<div class="output"> | ||
<pre class="language-json"><code class="language-json" id="message">{{ message }}</code></pre> | ||
</div> | ||
<form class="input-form" action="/" method="POST"> | ||
<div class="form-group"> | ||
<label for="exampleInputEmail1"><b>Name:</b></label> | ||
<input class="form-control" name="username" type="text" placeholder="username" id="exampleInputEmail1"> | ||
</div> | ||
<div class="form-group"> | ||
<label for="exampleInputPassword1"><b>Password:</b></label> | ||
<input class="form-control" type="password" placeholder="password" id="exampleInputPassword1"> | ||
</div> | ||
<div class="buttons"> | ||
<button type="submit" class="btn btn-primary">Submit</button> | ||
</div> | ||
</form> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<html> | ||
<body> | ||
<div class="content"> | ||
<link rel="stylesheet" href='/static/style.css'> | ||
<div class="logo"> | ||
<img src="/static/images/logo.png"> | ||
</div> | ||
<div class="auth-resp"> | ||
<h3><b>Auth Response:</b></h3> | ||
</div> | ||
<div class="success"> | ||
<pre class="auth-token"><code class="auth-token" id="message">{{ message }}</code></pre> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from duo_universal.client import * | ||
from duo_universal.version import __version__ |
Oops, something went wrong.