PyOTP 2.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: kislyuk
@@ -0,0 +1,31 @@
1
+ name: Python package
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ${{ matrix.os }}
8
+ env:
9
+ PIP_DISABLE_PIP_VERSION_CHECK: 1
10
+ strategy:
11
+ matrix:
12
+ os: [ubuntu-22.04, ubuntu-latest, macos-latest]
13
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - name: Set up Python ${{ matrix.python-version }}
17
+ uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - run: make install
21
+ - run: make lint
22
+ - run: make test
23
+ - uses: codecov/codecov-action@v5
24
+ ruff:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - uses: astral-sh/ruff-action@v3
29
+ - uses: astral-sh/ruff-action@v3
30
+ with:
31
+ args: "format --check"
@@ -0,0 +1,21 @@
1
+ name: Publish release to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v[0-9]+.[0-9]+.[0-9]+'
7
+
8
+ jobs:
9
+ pypi-publish:
10
+ name: Build and upload release to PyPI
11
+ runs-on: ubuntu-latest
12
+ environment: release
13
+ permissions:
14
+ id-token: write
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ - run: pip install build
19
+ - run: python -m build
20
+ - name: Publish package distributions to PyPI
21
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,42 @@
1
+ # Reminder:
2
+ # - A leading slash means the pattern is anchored at the root.
3
+ # - No leading slash means the pattern matches at any depth.
4
+
5
+ # Python files
6
+ *.pyc
7
+ __pycache__/
8
+ .tox/
9
+ *.egg-info/
10
+ /build/
11
+ /dist/
12
+ /.eggs/
13
+
14
+ # Sphinx documentation
15
+ /docs/_build/
16
+
17
+ # IDE project files
18
+ /.pydevproject
19
+
20
+ # vim python-mode plugin
21
+ /.ropeproject
22
+
23
+ # IntelliJ IDEA / PyCharm project files
24
+ /.idea
25
+ /*.iml
26
+
27
+ # JS/node/npm/web dev files
28
+ node_modules
29
+ npm-debug.log
30
+
31
+ # OS X metadata files
32
+ .DS_Store
33
+
34
+ # Python coverage
35
+ .coverage
36
+ htmlcov
37
+
38
+ # Virtual environments
39
+ venv
40
+
41
+ # Type checking
42
+ .mypy_cache
@@ -0,0 +1,187 @@
1
+ Changes for v2.10.0 (2026-06-13)
2
+ ================================
3
+
4
+ - Fix parse_uri mis-parsing of encoded colon in issuer/account name
5
+ (#187)
6
+
7
+ - Simplified and improved generate_otp() performance in Steam class
8
+ (#179)
9
+
10
+ - Check digest function to prevent error on OTP Generation (#170)
11
+
12
+ - Ignore non-standard otpauth parameters
13
+
14
+ - use strings as immutable default arguments (#162)
15
+
16
+ Changes for v2.9.0 (2023-07-27)
17
+ ===============================
18
+
19
+ - Add ``parse_uri()`` support for Steam TOTP (#153)
20
+
21
+ - Test and documentation improvements
22
+
23
+ Changes for v2.8.0 (2022-12-13)
24
+ ===============================
25
+
26
+ - Modify OTP generation to run in constant time (#148)
27
+
28
+ - Documentation improvements
29
+
30
+ - Drop Python 3.6 support; introduce Python 3.11 support
31
+
32
+ Changes for v2.7.0 (2022-09-11)
33
+ ===============================
34
+
35
+ - Support Steam TOTP (#142)
36
+
37
+ - Build, test, and documentation updates
38
+
39
+ Changes for v2.6.0 (2021-02-04)
40
+ ===============================
41
+
42
+ - Raise default and minimum base32 secret length to 32, and hex secret
43
+ length to 40 (160 bits as recommended by the RFC) (#115).
44
+
45
+ - Fix issue where provisioning_uri would return invalid results after
46
+ calling verify() (#115).
47
+
48
+ Changes for v2.5.1 (2021-01-29)
49
+ ===============================
50
+
51
+ - parse_uri accepts and ignores optional image parameter (#114)
52
+
53
+ Changes for v2.5.0 (2021-01-29)
54
+ ===============================
55
+
56
+ - Add optional image parameter to provisioning_uri (#113)
57
+
58
+ - Support for 7-digit codes in ‘parse_uri’ (#111)
59
+
60
+ - Raise default and minimum base32 secret length to 26
61
+
62
+ Changes for v2.4.1 (2020-10-16)
63
+ ===============================
64
+
65
+ - parse_uri: Fix handling of period, counter (#108)
66
+
67
+ - Add support for timezone aware datetime as argument to
68
+ ``TOTP.timecode()`` (#107)
69
+
70
+ Changes for v2.4.0 (2020-07-29)
71
+ ===============================
72
+
73
+ - Fix data type for at(for_time) (#85)
74
+
75
+ - Add support for parsing provisioning URIs (#84)
76
+
77
+ - Raise error when trying to generate secret that is too short (The
78
+ secret must be at least 128 bits)
79
+
80
+ - Add random_hex function (#82)
81
+
82
+ Changes for v2.3.0 (2019-07-26)
83
+ ===============================
84
+
85
+ - Fix comparison behavior on Python 2.7
86
+
87
+ Changes for v2.2.8 (2019-07-26)
88
+ ===============================
89
+
90
+ - Fix comparison of unicode chars (#78)
91
+
92
+ - Minor documentation and test fixes
93
+
94
+ Changes for v2.2.7 (2018-11-05)
95
+ ===============================
96
+
97
+ - Have random_base32() use ‘secrets’ as rand source (#66)
98
+
99
+ - Documentation: Add security considerations, minimal security
100
+ checklist, other improvements
101
+
102
+ - Update setup.py to reference correct license
103
+
104
+ Changes for v2.2.6 (2017-06-10)
105
+ ===============================
106
+
107
+ - Fix tests wrt double-quoting in provisioning URIs
108
+
109
+ Changes for v2.2.5 (2017-06-03)
110
+ ===============================
111
+
112
+ - Quote issuer QS parameter in provisioning\_uri. Fixes #47.
113
+
114
+ - Raise an exception if a negative integer is passed to at() (#41).
115
+
116
+ - Documentation and release infrastructure improvements.
117
+
118
+ Changes for v2.2.4 (2017-01-04)
119
+ ===============================
120
+
121
+ - Restore Python 2.6 compatibility (however, Python 2.6 is not
122
+ supported)
123
+
124
+ - Documentation and test improvements
125
+
126
+ - Fix release infra script, part 2
127
+
128
+ Changes for v2.2.3 (2017-01-04)
129
+ ===============================
130
+
131
+ - Restore Python 2.6 compatibility (however, Python 2.6 is not
132
+ supported)
133
+
134
+ - Documentation and test improvements
135
+
136
+ - Fix release infra script
137
+
138
+ Changes for v2.2.2 (2017-01-04)
139
+ ===============================
140
+
141
+ - Restore Python 2.6 compatibility (however, Python 2.6 is not
142
+ supported)
143
+
144
+ - Documentation and test improvements
145
+
146
+ Changes for v2.2.1 (2016-08-30)
147
+ ===============================
148
+
149
+ - Avoid using python-future; it has subdependencies that limit
150
+ compatibility (#34)
151
+ - Make test suite pass on 32-bit platforms (#30)
152
+ - Timing attack resistance fix: don't reveal string length to attacker.
153
+ Thanks to Eeo Jun (#28).
154
+ - Support algorithm, digits, period parameters in provisioning\_uri.
155
+ Thanks to Dionisio E Alonso (#33).
156
+ - Minor style and packaging infrastructure fixes.
157
+
158
+ Changes for v2.2.0 (2016-08-30)
159
+ ===============================
160
+
161
+ - See v2.2.1
162
+
163
+ Version 2.1.0 (2016-05-02)
164
+ --------------------------
165
+ - Add extended range support to TOTP.verify. Thanks to Zeev Rotshtein (PR #19).
166
+ - Handle missing padding of encoded secret. Thanks to Kun Yan (#20).
167
+ - Miscellaneous fixes.
168
+
169
+ Version 2.0.1 (2015-09-28)
170
+ --------------------------
171
+ - Fix packaging issue in v2.0.0 that prevented installation with easy_install.
172
+
173
+ Version 2.0.0 (2015-08-22)
174
+ --------------------------
175
+ - The ``pyotp.HOTP.at()``, ``pyotp.TOTP.at()``, and
176
+ ``pyotp.TOTP.now()`` methods now return strings instead of
177
+ integers. Thanks to Rohan Dhaimade (PR #16).
178
+
179
+ Version 1.4.2 (2015-07-21)
180
+ --------------------------
181
+ - Begin tracking changes in change log.
182
+ - Update documentation.
183
+ - Introduce Travis CI integration.
184
+
185
+ Version 1.3.1 (2012-02-29)
186
+ --------------------------
187
+ - Initial release.
pyotp-2.10.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (C) 2011-2021 Mark Percival <m@mdp.im>,
2
+ Nathan Reynolds <email@nreynolds.co.uk>, Andrey Kislyuk <kislyuk@gmail.com>,
3
+ and PyOTP contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include LICENSE
2
+ include README.rst
3
+ include test.py
pyotp-2.10.0/Makefile ADDED
@@ -0,0 +1,31 @@
1
+ SHELL=/bin/bash
2
+
3
+ test_deps:
4
+ python -m pip install .[test]
5
+
6
+ lint:
7
+ ruff check src
8
+ mypy --install-types --non-interactive --check-untyped-defs src
9
+
10
+ test:
11
+ coverage run --branch --include 'src/*' -m unittest discover -s test -v
12
+
13
+ init_docs:
14
+ cd docs; sphinx-quickstart
15
+
16
+ docs:
17
+ python -m pip install furo sphinx-copybutton sphinxext-opengraph
18
+ sphinx-build docs docs/html
19
+
20
+ install: clean
21
+ python -m pip install build
22
+ python -m build
23
+ python -m pip install --upgrade $$(echo dist/*.whl)[test]
24
+
25
+ clean:
26
+ -rm -rf build dist
27
+ -rm -rf *.egg-info
28
+
29
+ .PHONY: test_deps lint test docs install clean
30
+
31
+ include common.mk
pyotp-2.10.0/PKG-INFO ADDED
@@ -0,0 +1,220 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyOTP
3
+ Version: 2.10.0
4
+ Summary: Python One Time Password Library
5
+ Project-URL: Homepage, https://github.com/pyauth/pyotp
6
+ Project-URL: Documentation, https://github.com/pyauth/pyotp
7
+ Project-URL: Source Code, https://github.com/pyauth/pyotp
8
+ Project-URL: Issue Tracker, https://github.com/pyauth/pyotp/issues
9
+ Project-URL: Change Log, https://github.com/pyauth/pyotp/blob/main/Changes.rst
10
+ Author: Andrey Kislyuk
11
+ Author-email: kislyuk@gmail.com
12
+ Maintainer: Andrey Kislyuk
13
+ Maintainer-email: kislyuk@gmail.com
14
+ License-Expression: MIT
15
+ License-File: LICENSE
16
+ Classifier: Development Status :: 5 - Production/Stable
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Operating System :: MacOS :: MacOS X
19
+ Classifier: Operating System :: POSIX
20
+ Classifier: Programming Language :: Python
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
28
+ Classifier: Programming Language :: Python :: Implementation :: CPython
29
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
30
+ Classifier: Topic :: Software Development
31
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
32
+ Requires-Python: >=3.8
33
+ Provides-Extra: test
34
+ Requires-Dist: coverage; extra == 'test'
35
+ Requires-Dist: mypy; extra == 'test'
36
+ Requires-Dist: ruff; extra == 'test'
37
+ Requires-Dist: wheel; extra == 'test'
38
+ Description-Content-Type: text/x-rst
39
+
40
+ PyOTP - The Python One-Time Password Library
41
+ ============================================
42
+
43
+ PyOTP is a Python library for generating and verifying one-time passwords. It can be used to implement two-factor (2FA)
44
+ or multi-factor (MFA) authentication methods in web applications and in other systems that require users to log in.
45
+
46
+ Open MFA standards are defined in `RFC 4226 <https://tools.ietf.org/html/rfc4226>`_ (HOTP: An HMAC-Based One-Time
47
+ Password Algorithm) and in `RFC 6238 <https://tools.ietf.org/html/rfc6238>`_ (TOTP: Time-Based One-Time Password
48
+ Algorithm). PyOTP implements server-side support for both of these standards. Client-side support can be enabled by
49
+ sending authentication codes to users over SMS or email (HOTP) or, for TOTP, by instructing users to use `Google
50
+ Authenticator <https://en.wikipedia.org/wiki/Google_Authenticator>`_, `Authy <https://www.authy.com/>`_, or another
51
+ compatible app. Users can set up auth tokens in their apps easily by using their phone camera to scan `otpauth://
52
+ <https://github.com/google/google-authenticator/wiki/Key-Uri-Format>`_ QR codes provided by PyOTP.
53
+
54
+ Implementers should read and follow the `HOTP security requirements <https://tools.ietf.org/html/rfc4226#section-7>`_
55
+ and `TOTP security considerations <https://tools.ietf.org/html/rfc6238#section-5>`_ sections of the relevant RFCs. At
56
+ minimum, application implementers should follow this checklist:
57
+
58
+ - Ensure transport confidentiality by using HTTPS
59
+ - Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
60
+ - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
61
+ recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
62
+ seen)
63
+ - Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
64
+ - When implementing a "greenfield" application, consider supporting
65
+ `FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
66
+ addition to or instead of HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which
67
+ strengthens your MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a
68
+ dedicated single-purpose device, which strengthens your clients against client-side attacks. And by automating scoping
69
+ of credentials to relying party IDs (application origin/domain names), U2F adds protection against phishing attacks.
70
+ One implementation of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
71
+
72
+ We also recommend that implementers read the
73
+ `OWASP Authentication Cheat Sheet
74
+ <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
75
+ `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
76
+ authentication best practices.
77
+
78
+ Quick overview of using One Time Passwords on your phone
79
+ --------------------------------------------------------
80
+
81
+ * OTPs involve a shared secret, stored both on the phone and the server
82
+ * OTPs can be generated on a phone without internet connectivity
83
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
84
+ with a password)
85
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
86
+ Code
87
+
88
+ Installation
89
+ ------------
90
+ ::
91
+
92
+ pip install pyotp
93
+
94
+ Usage
95
+ -----
96
+
97
+ Time-based OTPs
98
+ ~~~~~~~~~~~~~~~
99
+ ::
100
+
101
+ import pyotp
102
+ import time
103
+
104
+ totp = pyotp.TOTP('base32secret3232')
105
+ totp.now() # => '492039'
106
+
107
+ # OTP verified for current time
108
+ totp.verify('492039') # => True
109
+ time.sleep(30)
110
+ totp.verify('492039') # => False
111
+
112
+ Counter-based OTPs
113
+ ~~~~~~~~~~~~~~~~~~
114
+ ::
115
+
116
+ import pyotp
117
+
118
+ hotp = pyotp.HOTP('base32secret3232')
119
+ hotp.at(0) # => '260182'
120
+ hotp.at(1) # => '055283'
121
+ hotp.at(1401) # => '316439'
122
+
123
+ # OTP verified with a counter
124
+ hotp.verify('316439', 1401) # => True
125
+ hotp.verify('316439', 1402) # => False
126
+
127
+ Generating a Secret Key
128
+ ~~~~~~~~~~~~~~~~~~~~~~~
129
+ A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
130
+ OTP apps::
131
+
132
+ pyotp.random_base32()
133
+
134
+ Some applications want the secret key to be formatted as a hex-encoded string::
135
+
136
+ pyotp.random_hex() # returns a 40-character hex-encoded secret
137
+
138
+ Google Authenticator Compatible
139
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140
+
141
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
142
+ the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
143
+
144
+ pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
145
+
146
+ >>> 'otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App'
147
+
148
+ pyotp.hotp.HOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name="alice@google.com", issuer_name="Secure App", initial_count=0)
149
+
150
+ >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
151
+
152
+ This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
153
+ scanned and added to the users list of OTP credentials.
154
+
155
+ Parsing these URLs is also supported::
156
+
157
+ pyotp.parse_uri('otpauth://totp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App')
158
+
159
+ >>> <pyotp.totp.TOTP object at 0xFFFFFFFF>
160
+
161
+ pyotp.parse_uri('otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
162
+
163
+ >>> <pyotp.totp.HOTP object at 0xFFFFFFFF>
164
+
165
+ Working example
166
+ ~~~~~~~~~~~~~~~
167
+
168
+ Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):
169
+
170
+ .. image:: https://quickchart.io/qr?size=250&text=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP
171
+
172
+ Now run the following and compare the output::
173
+
174
+ import pyotp
175
+ totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
176
+ print("Current OTP:", totp.now())
177
+
178
+ Third-party contributions
179
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
180
+ The following third-party contributions are not described by a standard, not officially supported, and provided for
181
+ reference only:
182
+
183
+ * ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
184
+
185
+ Links
186
+ ~~~~~
187
+
188
+ * `Project home page (GitHub) <https://github.com/pyauth/pyotp>`_
189
+ * `Documentation <https://pyauth.github.io/pyotp/>`_
190
+ * `Package distribution (PyPI) <https://pypi.python.org/pypi/pyotp>`_
191
+ * `Change log <https://github.com/pyauth/pyotp/blob/master/Changes.rst>`_
192
+ * `RFC 4226: HOTP: An HMAC-Based One-Time Password <https://tools.ietf.org/html/rfc4226>`_
193
+ * `RFC 6238: TOTP: Time-Based One-Time Password Algorithm <https://tools.ietf.org/html/rfc6238>`_
194
+ * `ROTP <https://github.com/mdp/rotp>`_ - Original Ruby OTP library by `Mark Percival <https://github.com/mdp>`_
195
+ * `OTPHP <https://github.com/lelag/otphp>`_ - PHP port of ROTP by `Le Lag <https://github.com/lelag>`_
196
+ * `OWASP Authentication Cheat Sheet <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_
197
+ * `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_
198
+
199
+ For new applications:
200
+
201
+ * `WebAuthn <https://www.w3.org/TR/webauthn/>`_
202
+ * `PyWARP <https://github.com/pyauth/pywarp>`_
203
+
204
+ Versioning
205
+ ~~~~~~~~~~
206
+ This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
207
+ recommended that application developers pin the package version and manage it using `pip-tools
208
+ <https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
209
+ recommended.
210
+
211
+ .. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
212
+ :target: https://github.com/pyauth/pyotp/actions
213
+ .. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
214
+ :target: https://codecov.io/github/pyauth/pyotp?branch=master
215
+ .. image:: https://img.shields.io/pypi/v/pyotp.svg
216
+ :target: https://pypi.python.org/pypi/pyotp
217
+ .. image:: https://img.shields.io/pypi/l/pyotp.svg
218
+ :target: https://pypi.python.org/pypi/pyotp
219
+ .. image:: https://readthedocs.org/projects/pyotp/badge/?version=latest
220
+ :target: https://pyotp.readthedocs.io/