pyauthenticator 0.2.0__tar.gz → 0.3.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.
Files changed (30) hide show
  1. pyauthenticator-0.3.0/.gitignore +13 -0
  2. {pyauthenticator-0.2.0/pyauthenticator.egg-info → pyauthenticator-0.3.0}/PKG-INFO +32 -10
  3. {pyauthenticator-0.2.0 → pyauthenticator-0.3.0}/README.md +4 -4
  4. pyauthenticator-0.3.0/pyproject.toml +79 -0
  5. pyauthenticator-0.3.0/src/pyauthenticator/__init__.py +19 -0
  6. pyauthenticator-0.3.0/src/pyauthenticator/__main__.py +9 -0
  7. pyauthenticator-0.2.0/pyauthenticator/__main__.py → pyauthenticator-0.3.0/src/pyauthenticator/_cmd.py +34 -24
  8. pyauthenticator-0.3.0/src/pyauthenticator/_config.py +42 -0
  9. pyauthenticator-0.3.0/src/pyauthenticator/_core.py +64 -0
  10. pyauthenticator-0.3.0/src/pyauthenticator/_user.py +65 -0
  11. pyauthenticator-0.3.0/src/pyauthenticator/_version.py +34 -0
  12. pyauthenticator-0.3.0/src/pyauthenticator/api.py +70 -0
  13. pyauthenticator-0.2.0/MANIFEST.in +0 -3
  14. pyauthenticator-0.2.0/PKG-INFO +0 -104
  15. pyauthenticator-0.2.0/pyauthenticator/__init__.py +0 -33
  16. pyauthenticator-0.2.0/pyauthenticator/_version.py +0 -21
  17. pyauthenticator-0.2.0/pyauthenticator/share.py +0 -166
  18. pyauthenticator-0.2.0/pyauthenticator.egg-info/SOURCES.txt +0 -20
  19. pyauthenticator-0.2.0/pyauthenticator.egg-info/dependency_links.txt +0 -1
  20. pyauthenticator-0.2.0/pyauthenticator.egg-info/entry_points.txt +0 -2
  21. pyauthenticator-0.2.0/pyauthenticator.egg-info/requires.txt +0 -4
  22. pyauthenticator-0.2.0/pyauthenticator.egg-info/top_level.txt +0 -1
  23. pyauthenticator-0.2.0/setup.cfg +0 -11
  24. pyauthenticator-0.2.0/setup.py +0 -31
  25. pyauthenticator-0.2.0/tests/test_cmd.py +0 -89
  26. pyauthenticator-0.2.0/tests/test_core.py +0 -55
  27. pyauthenticator-0.2.0/tests/test_share.py +0 -70
  28. pyauthenticator-0.2.0/tests/test_user_interface.py +0 -30
  29. pyauthenticator-0.2.0/versioneer.py +0 -1822
  30. {pyauthenticator-0.2.0 → pyauthenticator-0.3.0}/LICENSE +0 -0
@@ -0,0 +1,13 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+
7
+ # Coverage reports
8
+ .coverage
9
+ .coverage.*
10
+ htmlcov/
11
+
12
+ # Test artifacts
13
+ *.png
@@ -1,16 +1,38 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pyauthenticator
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Similar to the Google authenticator just written in python.
5
- Home-page: https://github.com/jan-janssen/pyauthenticator
6
- Author: Jan Janssen
7
- Author-email: jan.janssen@outlook.com
8
- License: BSD
9
- Description-Content-Type: text/markdown
5
+ Project-URL: Homepage, https://github.com/jan-janssen/pyauthenticator
6
+ Project-URL: Documentation, https://github.com/jan-janssen/pyauthenticator/blob/master/README.md
7
+ Project-URL: Source, https://github.com/jan-janssen/pyauthenticator
8
+ Project-URL: Tracker, https://github.com/jan-janssen/pyauthenticator/issues
9
+ Author-email: Jan Janssen <jan.janssen@outlook.com>
10
+ License: BSD-3-Clause
10
11
  License-File: LICENSE
12
+ Keywords: 2FA,MFA,authenticator
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: BSD License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.7
19
+ Classifier: Programming Language :: Python :: 3.8
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: Utilities
27
+ Requires-Python: >=3.7
28
+ Requires-Dist: pillow>=11.3.0
29
+ Requires-Dist: pyotp>=2.9.0
30
+ Requires-Dist: pyzbar>=0.1.9
31
+ Requires-Dist: qrcode>=8.2
32
+ Description-Content-Type: text/markdown
11
33
 
12
34
  # pyauthenticator
13
- [![Python package](https://github.com/jan-janssen/pyauthenticator/actions/workflows/unittest.yml/badge.svg?branch=main)](https://github.com/jan-janssen/pyauthenticator/actions/workflows/unittest.yml)
35
+ [![Pipeline](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml/badge.svg)](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml)
14
36
  [![Coverage Status](https://coveralls.io/repos/github/jan-janssen/pyauthenticator/badge.svg?branch=main)](https://coveralls.io/github/jan-janssen/pyauthenticator?branch=main)
15
37
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
16
38
 
@@ -85,7 +107,7 @@ For any support requests feel free to open an [issue on Github](https://github.c
85
107
  # For Developers
86
108
  ## Python Interface
87
109
  The same functionality which is available on the command line is also available via the python interface:
88
- ```
110
+ ```python
89
111
  from pyauthenticator import get_two_factor_code
90
112
  get_two_factor_code(service)
91
113
  ```
@@ -95,7 +117,7 @@ factor authentication.
95
117
  ## Configuration
96
118
  The configuration is stored in `~/.pyauthenticator` it is written in the JSON format. For a given service like `github`
97
119
  the config file contains:
98
- ```
120
+ ```JSON
99
121
  {"google": "otpauth://totp/Google:<username>?secret=<secret>&issuer=Google"}
100
122
  ```
101
123
  With the Google username `<username>` and the corresponding secret `<secret>` being contained in the QR code.
@@ -1,5 +1,5 @@
1
1
  # pyauthenticator
2
- [![Python package](https://github.com/jan-janssen/pyauthenticator/actions/workflows/unittest.yml/badge.svg?branch=main)](https://github.com/jan-janssen/pyauthenticator/actions/workflows/unittest.yml)
2
+ [![Pipeline](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml/badge.svg)](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml)
3
3
  [![Coverage Status](https://coveralls.io/repos/github/jan-janssen/pyauthenticator/badge.svg?branch=main)](https://coveralls.io/github/jan-janssen/pyauthenticator?branch=main)
4
4
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
5
5
 
@@ -74,7 +74,7 @@ For any support requests feel free to open an [issue on Github](https://github.c
74
74
  # For Developers
75
75
  ## Python Interface
76
76
  The same functionality which is available on the command line is also available via the python interface:
77
- ```
77
+ ```python
78
78
  from pyauthenticator import get_two_factor_code
79
79
  get_two_factor_code(service)
80
80
  ```
@@ -84,10 +84,10 @@ factor authentication.
84
84
  ## Configuration
85
85
  The configuration is stored in `~/.pyauthenticator` it is written in the JSON format. For a given service like `github`
86
86
  the config file contains:
87
- ```
87
+ ```JSON
88
88
  {"google": "otpauth://totp/Google:<username>?secret=<secret>&issuer=Google"}
89
89
  ```
90
90
  With the Google username `<username>` and the corresponding secret `<secret>` being contained in the QR code.
91
91
 
92
92
  ## License
93
- The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
93
+ The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = [
3
+ "hatchling>=1.27.0",
4
+ "hatch-vcs>=0.5.0",
5
+ "pyotp>=2.9.0",
6
+ "qrcode>=8.2",
7
+ "pyzbar>=0.1.9",
8
+ "pillow>=11.3.0",
9
+ ]
10
+ build-backend = "hatchling.build"
11
+
12
+ [project]
13
+ name = "pyauthenticator"
14
+ dynamic = ["version"]
15
+ description = "Similar to the Google authenticator just written in python."
16
+ readme = "README.md"
17
+ requires-python = ">=3.7"
18
+ license = { text = "BSD-3-Clause" }
19
+ authors = [
20
+ { name = "Jan Janssen", email = "jan.janssen@outlook.com" },
21
+ ]
22
+ keywords = ["authenticator", "2FA", "MFA"]
23
+ classifiers = [
24
+ "Development Status :: 5 - Production/Stable",
25
+ "Intended Audience :: Developers",
26
+ "License :: OSI Approved :: BSD License",
27
+ "Operating System :: OS Independent",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3.7",
30
+ "Programming Language :: Python :: 3.8",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Topic :: Security",
37
+ "Topic :: Utilities",
38
+ ]
39
+ dependencies = [
40
+ "pyotp>=2.9.0",
41
+ "qrcode>=8.2",
42
+ "pyzbar>=0.1.9",
43
+ "pillow>=11.3.0",
44
+ ]
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/jan-janssen/pyauthenticator"
48
+ Documentation = "https://github.com/jan-janssen/pyauthenticator/blob/master/README.md"
49
+ Source = "https://github.com/jan-janssen/pyauthenticator"
50
+ Tracker = "https://github.com/jan-janssen/pyauthenticator/issues"
51
+
52
+ [project.scripts]
53
+ pyauthenticator = "pyauthenticator._cmd:command_line_parser"
54
+
55
+ [tool.hatch.build]
56
+ include = [
57
+ "src/pyauthenticator"
58
+ ]
59
+
60
+ [tool.hatch.build.hooks.vcs]
61
+ version-file = "src/pyauthenticator/_version.py"
62
+
63
+ [tool.hatch.build.targets.sdist]
64
+ include = [
65
+ "src/pyauthenticator"
66
+ ]
67
+
68
+ [tool.hatch.build.targets.wheel]
69
+ packages = [
70
+ "src/pyauthenticator"
71
+ ]
72
+
73
+ [tool.hatch.version]
74
+ source = "vcs"
75
+ path = "src/pyauthenticator/_version.py"
76
+
77
+ [tool.coverage.run]
78
+ source = ["pyauthenticator"]
79
+ command_line = "-m unittest discover tests"
@@ -0,0 +1,19 @@
1
+ """
2
+ Generate two factor authentication codes on the command line
3
+ """
4
+
5
+ from . import _version
6
+ from pyauthenticator._user import (
7
+ write_qrcode_to_file,
8
+ get_two_factor_code,
9
+ add_two_factor_provider,
10
+ list_two_factor_providers,
11
+ )
12
+
13
+ __all__ = [
14
+ "write_qrcode_to_file",
15
+ "get_two_factor_code",
16
+ "add_two_factor_provider",
17
+ "list_two_factor_providers",
18
+ ]
19
+ __version__: str = _version.__version__
@@ -0,0 +1,9 @@
1
+ """
2
+ Generate two factor authentication codes on the command line
3
+ """
4
+
5
+ from pyauthenticator._cmd import command_line_parser
6
+
7
+
8
+ if __name__ == "__main__":
9
+ command_line_parser()
@@ -1,19 +1,21 @@
1
1
  """
2
2
  Generate two factor authentication codes on the command line
3
3
  """
4
+
4
5
  import argparse
5
6
  import sys
7
+ from typing import Optional, Sequence
6
8
 
7
- from pyauthenticator.share import (
9
+ from pyauthenticator._config import load_config
10
+ from pyauthenticator.api import (
8
11
  add_service,
9
12
  generate_qrcode,
10
- get_two_factor_code,
13
+ get_totp_for_key_in_dict,
11
14
  list_services,
12
- load_config,
13
15
  )
14
16
 
15
17
 
16
- def command_line_parser(cmd_args=None):
18
+ def command_line_parser(cmd_args: Optional[Sequence[str]] = None) -> None:
17
19
  """
18
20
  Main function primarly used for the command line interface
19
21
  """
@@ -45,8 +47,14 @@ def command_line_parser(cmd_args=None):
45
47
  )
46
48
  args = parser.parse_args(args=cmd_args)
47
49
  if args.qrcode:
48
- generate_qrcode(key=args.service, config_dict=config_dict)
49
- print("The qrcode file <" + args.service + ".png> was generated.")
50
+ try:
51
+ generate_qrcode(key=args.service, config_dict=config_dict)
52
+ except KeyError:
53
+ _print_service_does_not_exists(
54
+ config_dict=config_dict, service=args.service
55
+ )
56
+ else:
57
+ print("The qrcode file <" + args.service + ".png> was generated.")
50
58
  elif args.add:
51
59
  add_service(
52
60
  key=args.service, qrcode_png_file_name=args.add, config_dict=config_dict
@@ -60,23 +68,25 @@ def command_line_parser(cmd_args=None):
60
68
  )
61
69
  else:
62
70
  try:
63
- print(get_two_factor_code(key=args.service, config_dict=config_dict))
64
- except ValueError:
65
- if len(config_dict) > 0:
66
- print(
67
- 'The service "'
68
- + args.service
69
- + '" does not exist.\n\nThe config file ~/.pyauthenticator contains the following services:'
70
- )
71
- for service in list_services(config_dict=config_dict):
72
- print(" * " + service)
73
- print("\nChoose one of these or add a new service using:")
74
- else:
75
- print(
76
- "The config file ~/.pyauthenticator does not contain any services. To add a new service use:"
77
- )
78
- print(" pyauthenticator --add <qr-code.png> <servicename>\n")
71
+ print(get_totp_for_key_in_dict(key=args.service, config_dict=config_dict))
72
+ except KeyError:
73
+ _print_service_does_not_exists(
74
+ config_dict=config_dict, service=args.service
75
+ )
79
76
 
80
77
 
81
- if __name__ == "__main__":
82
- command_line_parser()
78
+ def _print_service_does_not_exists(config_dict: dict, service: str) -> None:
79
+ if len(config_dict) > 0:
80
+ print(
81
+ 'The service "'
82
+ + service
83
+ + '" does not exist.\n\nThe config file ~/.pyauthenticator contains the following services:'
84
+ )
85
+ for service_in_config in list_services(config_dict=config_dict):
86
+ print(" * " + service_in_config)
87
+ print("\nChoose one of these or add a new service using:")
88
+ else:
89
+ print(
90
+ "The config file ~/.pyauthenticator does not contain any services. To add a new service use:"
91
+ )
92
+ print(" pyauthenticator --add <qr-code.png> <servicename>\n")
@@ -0,0 +1,42 @@
1
+ """
2
+ Configuration file handling for pyauthenticator
3
+ """
4
+
5
+ import json
6
+ from os.path import exists, abspath, expanduser
7
+ from typing import Any, Dict
8
+
9
+ # default configuration file
10
+ default_config_file: str = "~/.pyauthenticator"
11
+
12
+
13
+ def load_config(config_file_to_load: str = default_config_file) -> Dict[str, Any]:
14
+ """
15
+ Load configuration file
16
+
17
+ Args:
18
+ config_file_to_load (str): path to config file
19
+
20
+ Returns:
21
+ dict: Dictionary with service names as keys and the otpauth url as values
22
+ """
23
+ abs_config_path = abspath(expanduser(config_file_to_load))
24
+ if exists(abs_config_path):
25
+ with open(abs_config_path, "r") as f:
26
+ return json.load(f)
27
+ else:
28
+ return {}
29
+
30
+
31
+ def write_config(
32
+ config_dict: Dict[str, Any], config_file_to_write: str = default_config_file
33
+ ) -> None:
34
+ """
35
+ Write configuration file
36
+
37
+ Args:
38
+ config_dict (dict): configuration dictionary
39
+ config_file_to_write (str): path to config file
40
+ """
41
+ with open(abspath(expanduser(config_file_to_write)), "w") as f:
42
+ json.dump(config_dict, f)
@@ -0,0 +1,64 @@
1
+ """
2
+ Core functionality to generate two factor authentication codes
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ import pyotp
8
+ import qrcode
9
+ from PIL import Image
10
+ from pyzbar.pyzbar import decode
11
+
12
+
13
+ def decode_qrcode(qrcode_png_file_name: str) -> str:
14
+ """
15
+ Decode qrcode from png file
16
+
17
+ Args:
18
+ qrcode_png_file_name (str): path to the png file which contains the qr code
19
+
20
+ Returns:
21
+ str: decoded otpauth string
22
+ """
23
+ return decode(Image.open(qrcode_png_file_name))[0].data.decode("utf-8")
24
+
25
+
26
+ def encode_qrcode(otpauth_str: str, file_name: str) -> None:
27
+ """
28
+ Encode otpauth string into qrcode image saved as png file
29
+
30
+ Args:
31
+ otpauth_str (str): otpauth string
32
+ file_name (str): path to the png file which will contain the qr code
33
+ """
34
+ qrcode.make(otpauth_str).save(file_name, "PNG")
35
+
36
+
37
+ def get_totp(otpauth_str: str) -> str:
38
+ """
39
+ Get TOTP code for a specific service based on its otpauth dictionary
40
+
41
+ Args:
42
+ otpauth_str (str): otpauth string for the service
43
+
44
+ Returns:
45
+ str: TOTP code for the service
46
+ """
47
+ otpauth_dict = {
48
+ kv[0]: kv[1]
49
+ for kv in [
50
+ otpvar.split("=") for otpvar in otpauth_str.replace("?", "&").split("&")[1:]
51
+ ]
52
+ }
53
+
54
+ kwargs: dict[str, Any] = {}
55
+ if "digits" in otpauth_dict.keys():
56
+ kwargs["digits"] = int(otpauth_dict["digits"])
57
+ if "period" in otpauth_dict.keys():
58
+ kwargs["interval"] = int(otpauth_dict["period"])
59
+ if "issuer" in otpauth_dict.keys():
60
+ kwargs["issuer"] = otpauth_dict["issuer"]
61
+ return pyotp.TOTP(
62
+ **kwargs,
63
+ s=otpauth_dict["secret"],
64
+ ).now()
@@ -0,0 +1,65 @@
1
+ """
2
+ Generate two factor authentication codes on the command line
3
+ """
4
+
5
+ from typing import Optional
6
+ from pyauthenticator._config import default_config_file, load_config
7
+ from pyauthenticator.api import (
8
+ add_service,
9
+ generate_qrcode,
10
+ get_totp_for_key_in_dict,
11
+ list_services,
12
+ )
13
+
14
+
15
+ def write_qrcode_to_file(service: str, file_name: Optional[str] = None) -> None:
16
+ """
17
+ Write qrcode to file to scan it with a mobile application
18
+
19
+ Args:
20
+ service (str): lower case name of the service
21
+ file_name (str/ None): default file name <service.png>
22
+ """
23
+ generate_qrcode(key=service, config_dict=load_config(), file_name=file_name)
24
+
25
+
26
+ def get_two_factor_code(service: str) -> str:
27
+ """
28
+ Generate two factor authentication code
29
+
30
+ Args:
31
+ service (str): lower case name of the service
32
+
33
+ Returns:
34
+ str: two factor authentication code
35
+ """
36
+ return get_totp_for_key_in_dict(
37
+ key=service,
38
+ config_dict=load_config(),
39
+ )
40
+
41
+
42
+ def add_two_factor_provider(service: str, qrcode_png_file_name: str) -> None:
43
+ """
44
+ Add new two factor authentication provider to configuration file
45
+
46
+ Args:
47
+ service (str): lower case name of the service
48
+ qrcode_png_file_name (str): path to the png file which contains the qr code
49
+ """
50
+ add_service(
51
+ key=service,
52
+ qrcode_png_file_name=qrcode_png_file_name,
53
+ config_dict=load_config(),
54
+ config_file_to_write=default_config_file,
55
+ )
56
+
57
+
58
+ def list_two_factor_providers() -> list[str]:
59
+ """
60
+ List all two factor authentication providers in the configuration file
61
+
62
+ Returns:
63
+ list[str]: list of two factor authentication providers
64
+ """
65
+ return list_services(config_dict=load_config())
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.3.0'
32
+ __version_tuple__ = version_tuple = (0, 3, 0)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,70 @@
1
+ """
2
+ Shared functionality to generate two factor authentication codes
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from pyauthenticator._core import decode_qrcode, encode_qrcode, get_totp
8
+ from pyauthenticator._config import default_config_file, write_config
9
+
10
+
11
+ def get_totp_for_key_in_dict(key: str, config_dict: Dict[str, Any]) -> str:
12
+ """
13
+ Generate the two factor authentication code
14
+
15
+ Args:
16
+ key (str): lower case name of the service
17
+ config_dict (dict): configuration dictionary
18
+
19
+ Returns:
20
+ str: two factor authentication code as string
21
+ """
22
+ return get_totp(otpauth_str=config_dict[key])
23
+
24
+
25
+ def add_service(
26
+ key: str,
27
+ qrcode_png_file_name: str,
28
+ config_dict: Dict[str, Any],
29
+ config_file_to_write: str = default_config_file,
30
+ ) -> None:
31
+ """
32
+ Add new service to configuration file
33
+
34
+ Args:
35
+ key (str): lower case name of the service
36
+ qrcode_png_file_name (str): path to the png file which contains the qr code
37
+ config_dict (dict): configuration dictionary
38
+ config_file_to_write (str): path to config file
39
+ """
40
+ config_dict[key] = decode_qrcode(qrcode_png_file_name=qrcode_png_file_name)
41
+ write_config(config_dict=config_dict, config_file_to_write=config_file_to_write)
42
+
43
+
44
+ def generate_qrcode(
45
+ key: str, config_dict: Dict[str, Any], file_name: Optional[str] = None
46
+ ) -> None:
47
+ """
48
+ Write qrcode to file to scan it with a mobile application
49
+
50
+ Args:
51
+ key (str): lower case name of the service
52
+ config_dict (dict): configuration dictionary
53
+ file_name (str/ None): default file name <service.png>
54
+ """
55
+ if file_name is None:
56
+ file_name = key + ".png"
57
+ encode_qrcode(otpauth_str=config_dict[key], file_name=file_name)
58
+
59
+
60
+ def list_services(config_dict: Dict[str, Any]) -> List[str]:
61
+ """
62
+ List available services
63
+
64
+ Args:
65
+ config_dict (dict): configuration dictionary
66
+
67
+ Returns:
68
+ list: list of available services
69
+ """
70
+ return list(config_dict.keys())
@@ -1,3 +0,0 @@
1
- include LICENSE
2
- include versioneer.py
3
- include pyauthenticator/_version.py