PyOTP 2.8.0__tar.gz → 2.9.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.
@@ -1,11 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyotp
3
- Version: 2.8.0
3
+ Version: 2.9.0
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
7
7
  Author-email: kislyuk@gmail.com
8
8
  License: MIT License
9
+ Project-URL: Documentation, https://pyauth.github.io/pyotp
10
+ Project-URL: Source Code, https://github.com/pyauth/pyotp
11
+ Project-URL: Issue Tracker, https://github.com/pyauth/pyotp/issues
12
+ Project-URL: Change Log, https://github.com/pyauth/pyotp/blob/master/Changes.rst
9
13
  Platform: MacOS X
10
14
  Platform: Posix
11
15
  Classifier: Intended Audience :: Developers
@@ -14,13 +18,14 @@ Classifier: Operating System :: MacOS :: MacOS X
14
18
  Classifier: Operating System :: POSIX
15
19
  Classifier: Programming Language :: Python
16
20
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.6
18
21
  Classifier: Programming Language :: Python :: 3.7
19
22
  Classifier: Programming Language :: Python :: 3.8
20
23
  Classifier: Programming Language :: Python :: 3.9
21
24
  Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
22
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
27
  Requires-Python: >=3.7
28
+ Provides-Extra: test
24
29
  License-File: LICENSE
25
30
 
26
31
  PyOTP - The Python One-Time Password Library
@@ -43,19 +48,21 @@ minimum, application implementers should follow this checklist:
43
48
 
44
49
  - Ensure transport confidentiality by using HTTPS
45
50
  - Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
46
- - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
47
- recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is seen)
51
+ - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
52
+ recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
53
+ seen)
48
54
  - Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
49
55
  - When implementing a "greenfield" application, consider supporting
50
56
  `FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
51
57
  addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
52
58
  MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
53
59
  device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
54
- relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation of
55
- FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
60
+ relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
61
+ of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
56
62
 
57
63
  We also recommend that implementers read the
58
- `OWASP Authentication Cheat Sheet <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
64
+ `OWASP Authentication Cheat Sheet
65
+ <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
59
66
  `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
60
67
  authentication best practices.
61
68
 
@@ -64,8 +71,10 @@ Quick overview of using One Time Passwords on your phone
64
71
 
65
72
  * OTPs involve a shared secret, stored both on the phone and the server
66
73
  * OTPs can be generated on a phone without internet connectivity
67
- * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
68
- * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
74
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
75
+ with a password)
76
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
77
+ Code
69
78
 
70
79
  Installation
71
80
  ------------
@@ -108,7 +117,8 @@ Counter-based OTPs
108
117
 
109
118
  Generating a Secret Key
110
119
  ~~~~~~~~~~~~~~~~~~~~~~~
111
- A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other OTP apps::
120
+ A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
121
+ OTP apps::
112
122
 
113
123
  pyotp.random_base32()
114
124
 
@@ -119,8 +129,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
119
129
  Google Authenticator Compatible
120
130
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121
131
 
122
- PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
123
- ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
132
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
133
+ the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
124
134
 
125
135
  pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
126
136
 
@@ -130,8 +140,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
130
140
 
131
141
  >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
132
142
 
133
- This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be scanned
134
- and added to the users list of OTP credentials.
143
+ This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
144
+ scanned and added to the users list of OTP credentials.
135
145
 
136
146
  Parsing these URLs is also supported::
137
147
 
@@ -156,6 +166,13 @@ Now run the following and compare the output::
156
166
  totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
157
167
  print("Current OTP:", totp.now())
158
168
 
169
+ Third-party contributions
170
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
171
+ The following third-party contributions are not described by a standard, not officially supported, and provided for
172
+ reference only:
173
+
174
+ * ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
175
+
159
176
  Links
160
177
  ~~~~~
161
178
 
@@ -175,6 +192,13 @@ For new applications:
175
192
  * `WebAuthn <https://www.w3.org/TR/webauthn/>`_
176
193
  * `PyWARP <https://github.com/pyauth/pywarp>`_
177
194
 
195
+ Versioning
196
+ ~~~~~~~~~~
197
+ This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
198
+ recommended that application developers pin the package version and manage it using `pip-tools
199
+ <https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
200
+ recommended.
201
+
178
202
  .. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
179
203
  :target: https://github.com/pyauth/pyotp/actions
180
204
  .. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
@@ -18,19 +18,21 @@ minimum, application implementers should follow this checklist:
18
18
 
19
19
  - Ensure transport confidentiality by using HTTPS
20
20
  - Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
21
- - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
22
- recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is seen)
21
+ - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
22
+ recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
23
+ seen)
23
24
  - Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
24
25
  - When implementing a "greenfield" application, consider supporting
25
26
  `FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
26
27
  addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
27
28
  MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
28
29
  device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
29
- relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation of
30
- FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
30
+ relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
31
+ of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
31
32
 
32
33
  We also recommend that implementers read the
33
- `OWASP Authentication Cheat Sheet <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
34
+ `OWASP Authentication Cheat Sheet
35
+ <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
34
36
  `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
35
37
  authentication best practices.
36
38
 
@@ -39,8 +41,10 @@ Quick overview of using One Time Passwords on your phone
39
41
 
40
42
  * OTPs involve a shared secret, stored both on the phone and the server
41
43
  * OTPs can be generated on a phone without internet connectivity
42
- * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
43
- * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
44
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
45
+ with a password)
46
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
47
+ Code
44
48
 
45
49
  Installation
46
50
  ------------
@@ -83,7 +87,8 @@ Counter-based OTPs
83
87
 
84
88
  Generating a Secret Key
85
89
  ~~~~~~~~~~~~~~~~~~~~~~~
86
- A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other OTP apps::
90
+ A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
91
+ OTP apps::
87
92
 
88
93
  pyotp.random_base32()
89
94
 
@@ -94,8 +99,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
94
99
  Google Authenticator Compatible
95
100
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96
101
 
97
- PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
98
- ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
102
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
103
+ the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
99
104
 
100
105
  pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
101
106
 
@@ -105,8 +110,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
105
110
 
106
111
  >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
107
112
 
108
- This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be scanned
109
- and added to the users list of OTP credentials.
113
+ This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
114
+ scanned and added to the users list of OTP credentials.
110
115
 
111
116
  Parsing these URLs is also supported::
112
117
 
@@ -131,6 +136,13 @@ Now run the following and compare the output::
131
136
  totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
132
137
  print("Current OTP:", totp.now())
133
138
 
139
+ Third-party contributions
140
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
141
+ The following third-party contributions are not described by a standard, not officially supported, and provided for
142
+ reference only:
143
+
144
+ * ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
145
+
134
146
  Links
135
147
  ~~~~~
136
148
 
@@ -150,6 +162,13 @@ For new applications:
150
162
  * `WebAuthn <https://www.w3.org/TR/webauthn/>`_
151
163
  * `PyWARP <https://github.com/pyauth/pywarp>`_
152
164
 
165
+ Versioning
166
+ ~~~~~~~~~~
167
+ This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
168
+ recommended that application developers pin the package version and manage it using `pip-tools
169
+ <https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
170
+ recommended.
171
+
153
172
  .. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
154
173
  :target: https://github.com/pyauth/pyotp/actions
155
174
  .. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
@@ -1,5 +1,9 @@
1
1
  [tool.black]
2
2
  line-length = 120
3
+
3
4
  [tool.isort]
4
5
  profile = "black"
5
6
  line_length = 120
7
+
8
+ [tool.ruff]
9
+ line-length = 120
pyotp-2.9.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -4,8 +4,14 @@ from setuptools import setup
4
4
 
5
5
  setup(
6
6
  name="pyotp",
7
- version="2.8.0",
7
+ version="2.9.0",
8
8
  url="https://github.com/pyotp/pyotp",
9
+ project_urls={
10
+ "Documentation": "https://pyauth.github.io/pyotp",
11
+ "Source Code": "https://github.com/pyauth/pyotp",
12
+ "Issue Tracker": "https://github.com/pyauth/pyotp/issues",
13
+ "Change Log": "https://github.com/pyauth/pyotp/blob/master/Changes.rst",
14
+ },
9
15
  license="MIT License",
10
16
  author="PyOTP contributors",
11
17
  author_email="kislyuk@gmail.com",
@@ -13,6 +19,9 @@ setup(
13
19
  long_description=open("README.rst").read(),
14
20
  python_requires=">=3.7",
15
21
  install_requires=[],
22
+ extras_require={
23
+ "test": ["coverage", "wheel", "ruff", "mypy"],
24
+ },
16
25
  packages=["pyotp", "pyotp.contrib"],
17
26
  package_dir={"": "src"},
18
27
  package_data={"pyotp": ["py.typed"]},
@@ -26,11 +35,11 @@ setup(
26
35
  "Operating System :: POSIX",
27
36
  "Programming Language :: Python",
28
37
  "Programming Language :: Python :: 3",
29
- "Programming Language :: Python :: 3.6",
30
38
  "Programming Language :: Python :: 3.7",
31
39
  "Programming Language :: Python :: 3.8",
32
40
  "Programming Language :: Python :: 3.9",
33
41
  "Programming Language :: Python :: 3.10",
42
+ "Programming Language :: Python :: 3.11",
34
43
  "Topic :: Software Development :: Libraries :: Python Modules",
35
44
  ],
36
45
  )
@@ -40,6 +40,12 @@ def parse_uri(uri: str) -> OTP:
40
40
  # Secret (to be filled in later)
41
41
  secret = None
42
42
 
43
+ # Encoder (to be filled in later)
44
+ encoder = None
45
+
46
+ # Digits (to be filled in later)
47
+ digits = None
48
+
43
49
  # Data we'll parse to the correct constructor
44
50
  otp_data: Dict[str, Any] = {}
45
51
 
@@ -74,10 +80,10 @@ def parse_uri(uri: str) -> OTP:
74
80
  otp_data["digest"] = hashlib.sha512
75
81
  else:
76
82
  raise ValueError("Invalid value for algorithm, must be SHA1, SHA256 or SHA512")
83
+ elif key == "encoder":
84
+ encoder = value
77
85
  elif key == "digits":
78
86
  digits = int(value)
79
- if digits not in [6, 7, 8]:
80
- raise ValueError("Digits may only be 6, 7, or 8")
81
87
  otp_data["digits"] = digits
82
88
  elif key == "period":
83
89
  otp_data["interval"] = int(value)
@@ -85,11 +91,17 @@ def parse_uri(uri: str) -> OTP:
85
91
  otp_data["initial_count"] = int(value)
86
92
  elif key != "image":
87
93
  raise ValueError("{} is not a valid parameter".format(key))
88
-
94
+
95
+ if encoder != "steam":
96
+ if digits is not None and digits not in [6, 7, 8]:
97
+ raise ValueError("Digits may only be 6, 7, or 8")
98
+
89
99
  if not secret:
90
100
  raise ValueError("No secret found in URI")
91
101
 
92
102
  # Create objects
103
+ if encoder == "steam":
104
+ return contrib.Steam(secret, **otp_data)
93
105
  if parsed_uri.netloc == "totp":
94
106
  return TOTP(secret, **otp_data)
95
107
  elif parsed_uri.netloc == "hotp":
@@ -12,7 +12,14 @@ class Steam(TOTP):
12
12
  Steam's custom TOTP. Subclass of `pyotp.totp.TOTP`.
13
13
  """
14
14
 
15
- def __init__(self, s: str, name: Optional[str] = None, issuer: Optional[str] = None, interval: int = 30) -> None:
15
+ def __init__(
16
+ self,
17
+ s: str,
18
+ name: Optional[str] = None,
19
+ issuer: Optional[str] = None,
20
+ interval: int = 30,
21
+ digits: int = 5
22
+ ) -> None:
16
23
  """
17
24
  :param s: secret in base32 format
18
25
  :param interval: the time interval in seconds for OTP. This defaults to 30.
@@ -1,11 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyotp
3
- Version: 2.8.0
3
+ Version: 2.9.0
4
4
  Summary: Python One Time Password Library
5
5
  Home-page: https://github.com/pyotp/pyotp
6
6
  Author: PyOTP contributors
7
7
  Author-email: kislyuk@gmail.com
8
8
  License: MIT License
9
+ Project-URL: Documentation, https://pyauth.github.io/pyotp
10
+ Project-URL: Source Code, https://github.com/pyauth/pyotp
11
+ Project-URL: Issue Tracker, https://github.com/pyauth/pyotp/issues
12
+ Project-URL: Change Log, https://github.com/pyauth/pyotp/blob/master/Changes.rst
9
13
  Platform: MacOS X
10
14
  Platform: Posix
11
15
  Classifier: Intended Audience :: Developers
@@ -14,13 +18,14 @@ Classifier: Operating System :: MacOS :: MacOS X
14
18
  Classifier: Operating System :: POSIX
15
19
  Classifier: Programming Language :: Python
16
20
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.6
18
21
  Classifier: Programming Language :: Python :: 3.7
19
22
  Classifier: Programming Language :: Python :: 3.8
20
23
  Classifier: Programming Language :: Python :: 3.9
21
24
  Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
22
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
27
  Requires-Python: >=3.7
28
+ Provides-Extra: test
24
29
  License-File: LICENSE
25
30
 
26
31
  PyOTP - The Python One-Time Password Library
@@ -43,19 +48,21 @@ minimum, application implementers should follow this checklist:
43
48
 
44
49
  - Ensure transport confidentiality by using HTTPS
45
50
  - Ensure HOTP/TOTP secret confidentiality by storing secrets in a controlled access database
46
- - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
47
- recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is seen)
51
+ - Deny replay attacks by rejecting one-time passwords that have been used by the client (this requires storing the most
52
+ recently authenticated timestamp, OTP, or hash of the OTP in your database, and rejecting the OTP when a match is
53
+ seen)
48
54
  - Throttle (rate limit) brute-force attacks against your application's login functionality (see RFC 4226, section 7.3)
49
55
  - When implementing a "greenfield" application, consider supporting
50
56
  `FIDO U2F <https://en.wikipedia.org/wiki/Universal_2nd_Factor>`_/`WebAuthn <https://www.w3.org/TR/webauthn/>`_ in
51
57
  addition to HOTP/TOTP. U2F uses asymmetric cryptography to avoid using a shared secret design, which strengthens your
52
58
  MFA solution against server-side attacks. Hardware U2F also sequesters the client secret in a dedicated single-purpose
53
59
  device, which strengthens your clients against client-side attacks. And by automating scoping of credentials to
54
- relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation of
55
- FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
60
+ relying party IDs (application origin/domain names), U2F adds protection against phishing attacks. One implementation
61
+ of FIDO U2F/WebAuthn is PyOTP's sister project, `PyWARP <https://github.com/pyauth/pywarp>`_.
56
62
 
57
63
  We also recommend that implementers read the
58
- `OWASP Authentication Cheat Sheet <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
64
+ `OWASP Authentication Cheat Sheet
65
+ <https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md>`_ and
59
66
  `NIST SP 800-63-3: Digital Authentication Guideline <https://pages.nist.gov/800-63-3/>`_ for a high level overview of
60
67
  authentication best practices.
61
68
 
@@ -64,8 +71,10 @@ Quick overview of using One Time Passwords on your phone
64
71
 
65
72
  * OTPs involve a shared secret, stored both on the phone and the server
66
73
  * OTPs can be generated on a phone without internet connectivity
67
- * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured with a password)
68
- * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR Code
74
+ * OTPs should always be used as a second factor of authentication (if your phone is lost, you account is still secured
75
+ with a password)
76
+ * Google Authenticator and other OTP client apps allow you to store multiple OTP secrets and provision those using a QR
77
+ Code
69
78
 
70
79
  Installation
71
80
  ------------
@@ -108,7 +117,8 @@ Counter-based OTPs
108
117
 
109
118
  Generating a Secret Key
110
119
  ~~~~~~~~~~~~~~~~~~~~~~~
111
- A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other OTP apps::
120
+ A helper function is provided to generate a 32-character base32 secret, compatible with Google Authenticator and other
121
+ OTP apps::
112
122
 
113
123
  pyotp.random_base32()
114
124
 
@@ -119,8 +129,8 @@ Some applications want the secret key to be formatted as a hex-encoded string::
119
129
  Google Authenticator Compatible
120
130
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121
131
 
122
- PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes the
123
- ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
132
+ PyOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. PyOTP includes
133
+ the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps::
124
134
 
125
135
  pyotp.totp.TOTP('JBSWY3DPEHPK3PXP').provisioning_uri(name='alice@google.com', issuer_name='Secure App')
126
136
 
@@ -130,8 +140,8 @@ ability to generate provisioning URIs for use with the QR Code scanner built int
130
140
 
131
141
  >>> 'otpauth://hotp/Secure%20App:alice%40google.com?secret=JBSWY3DPEHPK3PXP&issuer=Secure%20App&counter=0'
132
142
 
133
- This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be scanned
134
- and added to the users list of OTP credentials.
143
+ This URL can then be rendered as a QR Code (for example, using https://github.com/soldair/node-qrcode) which can then be
144
+ scanned and added to the users list of OTP credentials.
135
145
 
136
146
  Parsing these URLs is also supported::
137
147
 
@@ -156,6 +166,13 @@ Now run the following and compare the output::
156
166
  totp = pyotp.TOTP("JBSWY3DPEHPK3PXP")
157
167
  print("Current OTP:", totp.now())
158
168
 
169
+ Third-party contributions
170
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
171
+ The following third-party contributions are not described by a standard, not officially supported, and provided for
172
+ reference only:
173
+
174
+ * ``pyotp.contrib.Steam()``: An implementation of Steam TOTP. Uses the same API as `pyotp.TOTP()`.
175
+
159
176
  Links
160
177
  ~~~~~
161
178
 
@@ -175,6 +192,13 @@ For new applications:
175
192
  * `WebAuthn <https://www.w3.org/TR/webauthn/>`_
176
193
  * `PyWARP <https://github.com/pyauth/pywarp>`_
177
194
 
195
+ Versioning
196
+ ~~~~~~~~~~
197
+ This package follows the `Semantic Versioning 2.0.0 <http://semver.org/>`_ standard. To control changes, it is
198
+ recommended that application developers pin the package version and manage it using `pip-tools
199
+ <https://github.com/jazzband/pip-tools>`_ or similar. For library developers, pinning the major version is
200
+ recommended.
201
+
178
202
  .. image:: https://github.com/pyauth/pyotp/workflows/Python%20package/badge.svg
179
203
  :target: https://github.com/pyauth/pyotp/actions
180
204
  .. image:: https://img.shields.io/codecov/c/github/pyauth/pyotp/master.svg
@@ -2,7 +2,6 @@ LICENSE
2
2
  MANIFEST.in
3
3
  README.rst
4
4
  pyproject.toml
5
- setup.cfg
6
5
  setup.py
7
6
  test.py
8
7
  src/pyotp/__init__.py
@@ -16,6 +15,7 @@ src/pyotp.egg-info/PKG-INFO
16
15
  src/pyotp.egg-info/SOURCES.txt
17
16
  src/pyotp.egg-info/dependency_links.txt
18
17
  src/pyotp.egg-info/not-zip-safe
18
+ src/pyotp.egg-info/requires.txt
19
19
  src/pyotp.egg-info/top_level.txt
20
20
  src/pyotp/contrib/__init__.py
21
21
  src/pyotp/contrib/steam.py
@@ -0,0 +1,6 @@
1
+
2
+ [test]
3
+ coverage
4
+ wheel
5
+ ruff
6
+ mypy
@@ -364,6 +364,13 @@ class ParseUriTest(unittest.TestCase):
364
364
  with self.assertRaises(ValueError) as cm:
365
365
  pyotp.parse_uri("otpauth://totp?algorithm=aes")
366
366
  self.assertEqual("Invalid value for algorithm, must be SHA1, SHA256 or SHA512", str(cm.exception))
367
+
368
+ def test_parse_steam(self):
369
+ otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=SOME_SECRET&encoder=steam")
370
+ self.assertEqual(type(otp), pyotp.contrib.Steam)
371
+
372
+ otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=SOME_SECRET")
373
+ self.assertNotEqual(type(otp), pyotp.contrib.Steam)
367
374
 
368
375
  @unittest.skipIf(sys.version_info < (3, 6), "Skipping test that requires deterministic dict key enumeration")
369
376
  def test_algorithms(self):
@@ -420,6 +427,20 @@ class ParseUriTest(unittest.TestCase):
420
427
  otp = pyotp.parse_uri(otp.provisioning_uri(name="n", issuer_name="i", image="https://test.net/test.png"))
421
428
  self.assertEqual(hashlib.sha512, otp.digest)
422
429
 
430
+ otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=FMXNK4QEGKVPULRTADY6JIDK5VHUBGZW&encoder=steam")
431
+ self.assertEqual(type(otp), pyotp.contrib.Steam)
432
+ self.assertEqual(otp.at(0), "C5V56")
433
+ self.assertEqual(otp.at(30), "QJY8Y")
434
+ self.assertEqual(otp.at(60), "R3WQY")
435
+ self.assertEqual(otp.at(90), "JG3T3")
436
+
437
+ # period and digits should be ignored
438
+ otp = pyotp.parse_uri("otpauth://totp/Steam:?secret=FMXNK4QEGKVPULRTADY6JIDK5VHUBGZW&period=15&digits=7&encoder=steam")
439
+ self.assertEqual(type(otp), pyotp.contrib.Steam)
440
+ self.assertEqual(otp.at(0), "C5V56")
441
+ self.assertEqual(otp.at(30), "QJY8Y")
442
+ self.assertEqual(otp.at(60), "R3WQY")
443
+ self.assertEqual(otp.at(90), "JG3T3")
423
444
 
424
445
  class Timecop(object):
425
446
  """
pyotp-2.8.0/setup.cfg DELETED
@@ -1,8 +0,0 @@
1
- [flake8]
2
- max-line-length = 120
3
- extend-ignore = E203
4
-
5
- [egg_info]
6
- tag_build =
7
- tag_date = 0
8
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes