microdot 2.4.0__tar.gz → 2.5.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 (110) hide show
  1. {microdot-2.4.0/src/microdot.egg-info → microdot-2.5.0}/PKG-INFO +7 -19
  2. {microdot-2.4.0 → microdot-2.5.0}/README.md +5 -18
  3. microdot-2.5.0/docs/api/asgi.rst +6 -0
  4. microdot-2.5.0/docs/api/auth.rst +7 -0
  5. microdot-2.5.0/docs/api/cors.rst +5 -0
  6. microdot-2.5.0/docs/api/csrf.rst +5 -0
  7. microdot-2.5.0/docs/api/index.rst +21 -0
  8. microdot-2.5.0/docs/api/jinja.rst +5 -0
  9. microdot-2.5.0/docs/api/login.rst +7 -0
  10. microdot-2.5.0/docs/api/microdot.rst +14 -0
  11. microdot-2.5.0/docs/api/multipart.rst +5 -0
  12. microdot-2.5.0/docs/api/sessions.rst +5 -0
  13. microdot-2.5.0/docs/api/sse.rst +5 -0
  14. microdot-2.5.0/docs/api/test_client.rst +5 -0
  15. microdot-2.5.0/docs/api/utemplate.rst +5 -0
  16. microdot-2.5.0/docs/api/websocket.rst +5 -0
  17. microdot-2.5.0/docs/api/wsgi.rst +6 -0
  18. {microdot-2.4.0 → microdot-2.5.0}/docs/conf.py +2 -7
  19. microdot-2.5.0/docs/contributing.rst +7 -0
  20. microdot-2.5.0/docs/extensions/auth.rst +112 -0
  21. microdot-2.5.0/docs/extensions/cors.rst +34 -0
  22. microdot-2.5.0/docs/extensions/csrf.rst +94 -0
  23. microdot-2.5.0/docs/extensions/index.rst +21 -0
  24. microdot-2.5.0/docs/extensions/login.rst +85 -0
  25. microdot-2.5.0/docs/extensions/multipart.rst +73 -0
  26. microdot-2.5.0/docs/extensions/production.rst +120 -0
  27. microdot-2.5.0/docs/extensions/sessions.rst +68 -0
  28. microdot-2.5.0/docs/extensions/sse.rst +60 -0
  29. microdot-2.5.0/docs/extensions/templates.rst +123 -0
  30. microdot-2.5.0/docs/extensions/test_client.rst +36 -0
  31. microdot-2.5.0/docs/extensions/websocket.rst +63 -0
  32. microdot-2.5.0/docs/implementation/index.rst +11 -0
  33. {microdot-2.4.0 → microdot-2.5.0}/docs/index.rst +6 -5
  34. microdot-2.5.0/docs/intro.rst +44 -0
  35. microdot-2.5.0/docs/users-guide/concurrency.rst +36 -0
  36. microdot-2.5.0/docs/users-guide/defining-routes.rst +378 -0
  37. microdot-2.5.0/docs/users-guide/index.rst +18 -0
  38. microdot-2.5.0/docs/users-guide/intro.rst +160 -0
  39. microdot-2.5.0/docs/users-guide/request-object.rst +169 -0
  40. microdot-2.5.0/docs/users-guide/responses.rst +200 -0
  41. {microdot-2.4.0 → microdot-2.5.0}/pyproject.toml +2 -1
  42. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/__init__.py +1 -1
  43. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/asgi.py +35 -2
  44. microdot-2.5.0/src/microdot/csrf.py +122 -0
  45. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/login.py +15 -6
  46. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/microdot.py +16 -5
  47. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/session.py +2 -1
  48. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/test_client.py +8 -4
  49. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/wsgi.py +2 -1
  50. {microdot-2.4.0 → microdot-2.5.0/src/microdot.egg-info}/PKG-INFO +7 -19
  51. {microdot-2.4.0 → microdot-2.5.0}/src/microdot.egg-info/SOURCES.txt +39 -4
  52. {microdot-2.4.0 → microdot-2.5.0}/src/microdot.egg-info/requires.txt +1 -0
  53. {microdot-2.4.0 → microdot-2.5.0}/tests/__init__.py +1 -0
  54. {microdot-2.4.0 → microdot-2.5.0}/tests/test_asgi.py +100 -0
  55. microdot-2.5.0/tests/test_csrf.py +298 -0
  56. {microdot-2.4.0 → microdot-2.5.0}/tests/test_login.py +3 -1
  57. {microdot-2.4.0 → microdot-2.5.0}/tests/test_microdot.py +24 -0
  58. {microdot-2.4.0 → microdot-2.5.0}/tox.ini +2 -1
  59. microdot-2.4.0/docs/api.rst +0 -95
  60. microdot-2.4.0/docs/extensions.rst +0 -778
  61. microdot-2.4.0/docs/intro.rst +0 -986
  62. {microdot-2.4.0 → microdot-2.5.0}/LICENSE +0 -0
  63. {microdot-2.4.0 → microdot-2.5.0}/MANIFEST.in +0 -0
  64. {microdot-2.4.0 → microdot-2.5.0}/docs/Makefile +0 -0
  65. {microdot-2.4.0 → microdot-2.5.0}/docs/_static/css/custom.css +0 -0
  66. {microdot-2.4.0/docs → microdot-2.5.0/docs/implementation}/freezing.rst +0 -0
  67. {microdot-2.4.0/docs → microdot-2.5.0/docs/implementation}/migrating.rst +0 -0
  68. {microdot-2.4.0 → microdot-2.5.0}/docs/make.bat +0 -0
  69. {microdot-2.4.0 → microdot-2.5.0}/setup.cfg +0 -0
  70. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/auth.py +0 -0
  71. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/cors.py +0 -0
  72. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/helpers.py +0 -0
  73. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/jinja.py +0 -0
  74. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/multipart.py +0 -0
  75. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/sse.py +0 -0
  76. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/utemplate.py +0 -0
  77. {microdot-2.4.0 → microdot-2.5.0}/src/microdot/websocket.py +0 -0
  78. {microdot-2.4.0 → microdot-2.5.0}/src/microdot.egg-info/dependency_links.txt +0 -0
  79. {microdot-2.4.0 → microdot-2.5.0}/src/microdot.egg-info/not-zip-safe +0 -0
  80. {microdot-2.4.0 → microdot-2.5.0}/src/microdot.egg-info/top_level.txt +0 -0
  81. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.bin +0 -0
  82. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.css +0 -0
  83. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.gif +0 -0
  84. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.gz +0 -0
  85. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.html +0 -0
  86. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.jpg +0 -0
  87. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.js +0 -0
  88. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.json +0 -0
  89. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.png +0 -0
  90. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.txt +0 -0
  91. {microdot-2.4.0 → microdot-2.5.0}/tests/files/test.txt.gz +0 -0
  92. {microdot-2.4.0 → microdot-2.5.0}/tests/mock_socket.py +0 -0
  93. {microdot-2.4.0 → microdot-2.5.0}/tests/templates/hello.jinja.txt +0 -0
  94. {microdot-2.4.0 → microdot-2.5.0}/tests/templates/hello.utemplate.txt +0 -0
  95. {microdot-2.4.0 → microdot-2.5.0}/tests/templates/hello_utemplate_txt.py +0 -0
  96. {microdot-2.4.0 → microdot-2.5.0}/tests/test_auth.py +0 -0
  97. {microdot-2.4.0 → microdot-2.5.0}/tests/test_cors.py +0 -0
  98. {microdot-2.4.0 → microdot-2.5.0}/tests/test_end2end.py +0 -0
  99. {microdot-2.4.0 → microdot-2.5.0}/tests/test_jinja.py +0 -0
  100. {microdot-2.4.0 → microdot-2.5.0}/tests/test_multidict.py +0 -0
  101. {microdot-2.4.0 → microdot-2.5.0}/tests/test_multipart.py +0 -0
  102. {microdot-2.4.0 → microdot-2.5.0}/tests/test_request.py +0 -0
  103. {microdot-2.4.0 → microdot-2.5.0}/tests/test_response.py +0 -0
  104. {microdot-2.4.0 → microdot-2.5.0}/tests/test_session.py +0 -0
  105. {microdot-2.4.0 → microdot-2.5.0}/tests/test_sse.py +0 -0
  106. {microdot-2.4.0 → microdot-2.5.0}/tests/test_url_pattern.py +0 -0
  107. {microdot-2.4.0 → microdot-2.5.0}/tests/test_urlencode.py +0 -0
  108. {microdot-2.4.0 → microdot-2.5.0}/tests/test_utemplate.py +0 -0
  109. {microdot-2.4.0 → microdot-2.5.0}/tests/test_websocket.py +0 -0
  110. {microdot-2.4.0 → microdot-2.5.0}/tests/test_wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microdot
3
- Version: 2.4.0
3
+ Version: 2.5.0
4
4
  Summary: The impossibly small web framework for MicroPython
5
5
  Author-email: Miguel Grinberg <miguel.grinberg@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/miguelgrinberg/microdot
@@ -18,6 +18,7 @@ Provides-Extra: dev
18
18
  Requires-Dist: tox; extra == "dev"
19
19
  Provides-Extra: docs
20
20
  Requires-Dist: sphinx; extra == "docs"
21
+ Requires-Dist: furo; extra == "docs"
21
22
  Requires-Dist: pyjwt; extra == "docs"
22
23
  Dynamic: license-file
23
24
 
@@ -42,24 +43,15 @@ async def index(request):
42
43
  app.run()
43
44
  ```
44
45
 
45
- ## Migrating to Microdot 2
46
-
47
- Version 2 of Microdot incorporates feedback received from users of earlier
48
- releases, and attempts to improve and correct some design decisions that have
49
- proven to be problematic.
50
-
51
- For this reason most applications built for earlier versions will need to be
52
- updated to work correctly with Microdot 2. The
53
- [Migration Guide](https://microdot.readthedocs.io/en/stable/migrating.html)
54
- describes the backwards incompatible changes that were made.
55
-
56
46
  ## Resources
57
47
 
58
48
  - [Change Log](https://github.com/miguelgrinberg/microdot/blob/main/CHANGES.md)
59
49
  - Documentation
60
50
  - [Latest](https://microdot.readthedocs.io/en/latest/)
61
51
  - [Stable (v2)](https://microdot.readthedocs.io/en/stable/)
62
- - [Legacy (v1)](https://microdot.readthedocs.io/en/v1/) ([Code](https://github.com/miguelgrinberg/microdot/tree/v1))
52
+ - Legacy (v1)
53
+ - [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
54
+ - [Documentation](https://microdot.readthedocs.io/en/v1/)
63
55
 
64
56
  ## Roadmap
65
57
 
@@ -68,12 +60,8 @@ MicroPython and CPython:
68
60
 
69
61
  - Authentication support, similar to [Flask-Login](https://github.com/maxcountryman/flask-login) for Flask (**Added in version 2.1**)
70
62
  - Support for forms encoded in `multipart/form-data` format (**Added in version 2.2**)
63
+ - CSRF protection extension
64
+ - Pub/sub mini-framework for WebSocket and SSE
71
65
  - OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
72
66
 
73
- In addition to the above, the following extensions are also under consideration,
74
- but only for CPython:
75
-
76
- - Database integration through [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy)
77
- - Socket.IO support through [python-socketio](https://github.com/miguelgrinberg/python-socketio)
78
-
79
67
  Do you have other ideas to propose? Let's [discuss them](https://github.com/:miguelgrinberg/microdot/discussions/new?category=ideas)!
@@ -19,24 +19,15 @@ async def index(request):
19
19
  app.run()
20
20
  ```
21
21
 
22
- ## Migrating to Microdot 2
23
-
24
- Version 2 of Microdot incorporates feedback received from users of earlier
25
- releases, and attempts to improve and correct some design decisions that have
26
- proven to be problematic.
27
-
28
- For this reason most applications built for earlier versions will need to be
29
- updated to work correctly with Microdot 2. The
30
- [Migration Guide](https://microdot.readthedocs.io/en/stable/migrating.html)
31
- describes the backwards incompatible changes that were made.
32
-
33
22
  ## Resources
34
23
 
35
24
  - [Change Log](https://github.com/miguelgrinberg/microdot/blob/main/CHANGES.md)
36
25
  - Documentation
37
26
  - [Latest](https://microdot.readthedocs.io/en/latest/)
38
27
  - [Stable (v2)](https://microdot.readthedocs.io/en/stable/)
39
- - [Legacy (v1)](https://microdot.readthedocs.io/en/v1/) ([Code](https://github.com/miguelgrinberg/microdot/tree/v1))
28
+ - Legacy (v1)
29
+ - [Code](https://github.com/miguelgrinberg/microdot/tree/v1)
30
+ - [Documentation](https://microdot.readthedocs.io/en/v1/)
40
31
 
41
32
  ## Roadmap
42
33
 
@@ -45,12 +36,8 @@ MicroPython and CPython:
45
36
 
46
37
  - Authentication support, similar to [Flask-Login](https://github.com/maxcountryman/flask-login) for Flask (**Added in version 2.1**)
47
38
  - Support for forms encoded in `multipart/form-data` format (**Added in version 2.2**)
39
+ - CSRF protection extension
40
+ - Pub/sub mini-framework for WebSocket and SSE
48
41
  - OpenAPI integration, similar to [APIFairy](https://github.com/miguelgrinberg/apifairy) for Flask
49
42
 
50
- In addition to the above, the following extensions are also under consideration,
51
- but only for CPython:
52
-
53
- - Database integration through [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy)
54
- - Socket.IO support through [python-socketio](https://github.com/miguelgrinberg/python-socketio)
55
-
56
43
  Do you have other ideas to propose? Let's [discuss them](https://github.com/:miguelgrinberg/microdot/discussions/new?category=ideas)!
@@ -0,0 +1,6 @@
1
+ ASGI
2
+ ----
3
+
4
+ .. autoclass:: microdot.asgi.Microdot
5
+ :members:
6
+ :exclude-members: shutdown, run
@@ -0,0 +1,7 @@
1
+ Authentication
2
+ --------------
3
+
4
+ .. automodule:: microdot.auth
5
+ :inherited-members:
6
+ :special-members: __call__
7
+ :members:
@@ -0,0 +1,5 @@
1
+ Cross-Origin Resource Sharing (CORS)
2
+ ------------------------------------
3
+
4
+ .. automodule:: microdot.cors
5
+ :members:
@@ -0,0 +1,5 @@
1
+ Cross-Site Request Forgery (CSRF) Protection
2
+ --------------------------------------------
3
+
4
+ .. automodule:: microdot.csrf
5
+ :members:
@@ -0,0 +1,21 @@
1
+ API Reference
2
+ =============
3
+
4
+ .. toctree::
5
+ :maxdepth: 1
6
+
7
+ microdot
8
+ multipart
9
+ websocket
10
+ sse
11
+ utemplate
12
+ jinja
13
+ sessions
14
+ auth
15
+ login
16
+ cors
17
+ csrf
18
+ test_client
19
+ asgi
20
+ wsgi
21
+
@@ -0,0 +1,5 @@
1
+ Templates (Jinja)
2
+ -----------------
3
+
4
+ .. automodule:: microdot.jinja
5
+ :members:
@@ -0,0 +1,7 @@
1
+ User Logins
2
+ -----------
3
+
4
+ .. automodule:: microdot.login
5
+ :inherited-members:
6
+ :special-members: __call__
7
+ :members:
@@ -0,0 +1,14 @@
1
+ Core API
2
+ --------
3
+
4
+ .. autoclass:: microdot.Microdot
5
+ :members:
6
+
7
+ .. autoclass:: microdot.Request
8
+ :members:
9
+
10
+ .. autoclass:: microdot.Response
11
+ :members:
12
+
13
+ .. autoclass:: microdot.URLPattern
14
+ :members:
@@ -0,0 +1,5 @@
1
+ Multipart Forms
2
+ ---------------
3
+
4
+ .. automodule:: microdot.multipart
5
+ :members:
@@ -0,0 +1,5 @@
1
+ User Sessions
2
+ -------------
3
+
4
+ .. automodule:: microdot.session
5
+ :members:
@@ -0,0 +1,5 @@
1
+ Server-Sent Events (SSE)
2
+ ------------------------
3
+
4
+ .. automodule:: microdot.sse
5
+ :members:
@@ -0,0 +1,5 @@
1
+ Test Client
2
+ -----------
3
+
4
+ .. automodule:: microdot.test_client
5
+ :members:
@@ -0,0 +1,5 @@
1
+ Templates (uTemplate)
2
+ ---------------------
3
+
4
+ .. automodule:: microdot.utemplate
5
+ :members:
@@ -0,0 +1,5 @@
1
+ WebSocket
2
+ ---------
3
+
4
+ .. automodule:: microdot.websocket
5
+ :members:
@@ -0,0 +1,6 @@
1
+ WSGI
2
+ ----
3
+
4
+ .. autoclass:: microdot.wsgi.Microdot
5
+ :members:
6
+ :exclude-members: shutdown, run
@@ -46,7 +46,8 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
46
46
  # The theme to use for HTML and HTML Help pages. See the documentation for
47
47
  # a list of builtin themes.
48
48
  #
49
- html_theme = 'alabaster'
49
+ html_theme = 'furo'
50
+ html_title = 'Microdot'
50
51
 
51
52
  # Add any paths that contain custom static files (such as style sheets) here,
52
53
  # relative to this directory. They are copied after the builtin static files,
@@ -58,12 +59,6 @@ html_css_files = [
58
59
  ]
59
60
 
60
61
  html_theme_options = {
61
- 'github_user': 'miguelgrinberg',
62
- 'github_repo': 'microdot',
63
- 'github_banner': True,
64
- 'github_button': True,
65
- 'github_type': 'star',
66
- 'fixed_sidebar': True,
67
62
  }
68
63
 
69
64
  autodoc_default_options = {
@@ -0,0 +1,7 @@
1
+ Contributing
2
+ ------------
3
+
4
+ Thank you for your interest in Microdot!
5
+
6
+ Please visit the `GitHub repository <https://github.com/miguelgrinberg/microdot>`_
7
+ to learn about the project and find open issues and pull requests.
@@ -0,0 +1,112 @@
1
+ Authentication
2
+ ~~~~~~~~~~~~~~
3
+
4
+ .. list-table::
5
+ :align: left
6
+
7
+ * - Compatibility
8
+ - | CPython & MicroPython
9
+
10
+ * - Required Microdot source files
11
+ - | `auth.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/auth.py>`_
12
+
13
+ * - Required external dependencies
14
+ - | None
15
+
16
+ * - Examples
17
+ - | `basic_auth.py <https://github.com/miguelgrinberg/microdot/blob/main/examples/auth/basic_auth.py>`_
18
+ | `token_auth.py <https://github.com/miguelgrinberg/microdot/blob/main/examples/auth/token_auth.py>`_
19
+
20
+ The authentication extension provides helper classes for two commonly used
21
+ authentication patterns, described below.
22
+
23
+ Basic Authentication
24
+ ^^^^^^^^^^^^^^^^^^^^
25
+
26
+ `Basic Authentication <https://en.wikipedia.org/wiki/Basic_access_authentication>`_
27
+ is a method of authentication that is part of the HTTP specification. It allows
28
+ clients to authenticate to a server using a username and a password. Web
29
+ browsers have native support for Basic Authentication and will automatically
30
+ prompt the user for a username and a password when a protected resource is
31
+ accessed.
32
+
33
+ To use Basic Authentication, create an instance of the :class:`BasicAuth <microdot.auth.BasicAuth>`
34
+ class::
35
+
36
+ from microdot.auth import BasicAuth
37
+
38
+ auth = BasicAuth(app)
39
+
40
+ Next, create an authentication function. The function must accept a request
41
+ object and a username and password pair provided by the user. If the
42
+ credentials are valid, the function must return an object that represents the
43
+ user. If the authentication function cannot validate the user provided
44
+ credentials it must return ``None``. Decorate the function with
45
+ ``@auth.authenticate``::
46
+
47
+ @auth.authenticate
48
+ async def verify_user(request, username, password):
49
+ user = await load_user_from_database(username)
50
+ if user and user.verify_password(password):
51
+ return user
52
+
53
+ To protect a route with authentication, add the ``auth`` instance as a
54
+ decorator::
55
+
56
+ @app.route('/')
57
+ @auth
58
+ async def index(request):
59
+ return f'Hello, {request.g.current_user}!'
60
+
61
+ While running an authenticated request, the user object returned by the
62
+ authenticaction function is accessible as ``request.g.current_user``.
63
+
64
+ If an endpoint is intended to work with or without authentication, then it can
65
+ be protected with the ``auth.optional`` decorator::
66
+
67
+ @app.route('/')
68
+ @auth.optional
69
+ async def index(request):
70
+ if request.g.current_user:
71
+ return f'Hello, {request.g.current_user}!'
72
+ else:
73
+ return 'Hello, anonymous user!'
74
+
75
+ As shown in the example, a route can check ``request.g.current_user`` to
76
+ determine if the user is authenticated or not.
77
+
78
+ Token Authentication
79
+ ^^^^^^^^^^^^^^^^^^^^
80
+
81
+ To set up token authentication, create an instance of
82
+ :class:`TokenAuth <microdot.auth.TokenAuth>`::
83
+
84
+ from microdot.auth import TokenAuth
85
+
86
+ auth = TokenAuth()
87
+
88
+ Then add a function that verifies the token and returns the user it belongs to,
89
+ or ``None`` if the token is invalid or expired::
90
+
91
+ @auth.authenticate
92
+ async def verify_token(request, token):
93
+ return load_user_from_token(token)
94
+
95
+ As with Basic authentication, the ``auth`` instance is used as a decorator to
96
+ protect your routes, and the authenticated user is accessible from the request
97
+ object as ``request.g.current_user``::
98
+
99
+ @app.route('/')
100
+ @auth
101
+ async def index(request):
102
+ return f'Hello, {request.g.current_user}!'
103
+
104
+ Optional authentication can also be used with tokens::
105
+
106
+ @app.route('/')
107
+ @auth.optional
108
+ async def index(request):
109
+ if request.g.current_user:
110
+ return f'Hello, {request.g.current_user}!'
111
+ else:
112
+ return 'Hello, anonymous user!'
@@ -0,0 +1,34 @@
1
+ Cross-Origin Resource Sharing (CORS)
2
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+
4
+ .. list-table::
5
+ :align: left
6
+
7
+ * - Compatibility
8
+ - | CPython & MicroPython
9
+
10
+ * - Required Microdot source files
11
+ - | `cors.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/cors.py>`_
12
+
13
+ * - Required external dependencies
14
+ - | None
15
+
16
+ * - Examples
17
+ - | `app.py <https://github.com/miguelgrinberg/microdot/blob/main/examples/cors/app.py>`_
18
+
19
+ The CORS extension provides support for `Cross-Origin Resource Sharing
20
+ (CORS) <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS>`_. CORS is a
21
+ mechanism that allows web applications running on different origins to access
22
+ resources from each other. For example, a web application running on
23
+ ``https://example.com`` can access resources from ``https://api.example.com``.
24
+
25
+ To enable CORS support, create an instance of the
26
+ :class:`CORS <microdot.cors.CORS>` class and configure the desired options.
27
+ Example::
28
+
29
+ from microdot import Microdot
30
+ from microdot.cors import CORS
31
+
32
+ app = Microdot()
33
+ cors = CORS(app, allowed_origins=['https://example.com'],
34
+ allow_credentials=True)
@@ -0,0 +1,94 @@
1
+ Cross-Site Request Forgery (CSRF) Protection
2
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+
4
+ .. list-table::
5
+ :align: left
6
+
7
+ * - Compatibility
8
+ - | CPython & MicroPython
9
+
10
+ * - Required Microdot source files
11
+ - | `csrf.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/csrf.py>`_
12
+
13
+ * - Required external dependencies
14
+ - | None
15
+
16
+ * - Examples
17
+ - | `app.py <https://github.com/miguelgrinberg/microdot/blob/main/examples/csrf/app.py>`_
18
+
19
+ The CSRF extension provides protection against `Cross-Site Request Forgery
20
+ (CSRF) <https://owasp.org/www-community/attacks/csrf>`_ attacks. This
21
+ protection defends against attackers attempting to submit forms or other
22
+ state-changing requests from their own site on behalf of unsuspecting victims,
23
+ while taking advantage of the victims previously established sessions or
24
+ cookies to impersonate them.
25
+
26
+ This extension checks the ``Sec-Fetch-Site`` header sent by all modern web
27
+ browsers to achieve this protection. As a fallback mechanism for older browsers
28
+ that do not support this header, this extension can be linked to the CORS
29
+ extension to validate the ``Origin`` header. If you are interested in the
30
+ details of this protection mechanism, it is described in the
31
+ `OWASP CSRF Prevention Cheat Sheet <https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#fetch-metadata-headers>`_
32
+ page.
33
+
34
+ .. note::
35
+ As of December 2025, OWASP considers the use of Fetch Metadata Headers for
36
+ CSRF protection a
37
+ `defense in depth <https://en.wikipedia.org/wiki/Defence_in_depth>`_
38
+ technique that is insufficient on its own.
39
+
40
+ There is an interesting
41
+ `discussion <https://github.com/OWASP/CheatSheetSeries/issues/1803>`_ on
42
+ this topic in the OWASP GitHub repository where it appears to be agreement
43
+ that this technique provides complete protection for the vast majority of
44
+ use cases. If you are unsure if this method works for your use case, please
45
+ read this discussion to have more context and make the right decision.
46
+
47
+ To enable CSRF protection, create an instance of the
48
+ :class:`CSRF <microdot.csrf.CSRF>` class and configure the desired options.
49
+ Example::
50
+
51
+ from microdot import Microdot
52
+ from microdot.cors import CORS
53
+ from microdot.csrf import CSRF
54
+
55
+ app = Microdot()
56
+ cors = CORS(app, allowed_origins=['https://example.com'])
57
+ csrf = CSRF(app, cors)
58
+
59
+ This will protect all routes that use a state-changing method (``POST``,
60
+ ``PUT``, ``PATCH`` or ``DELETE``) and will return a 403 status code response to
61
+ any requests that fail the CSRF check.
62
+
63
+ If there are routes that need to be exempted from the CSRF check, they can be
64
+ decorated with the :meth:`csrf.exempt <microdot.csrf.CSRF.exempt>` decorator::
65
+
66
+ @app.post('/webhook')
67
+ @csrf.exempt
68
+ async def webhook(request):
69
+ # ...
70
+
71
+ For some applications it may be more convenient to have CSRF checks turned off
72
+ by default, and only apply them to explicitly selected routes. In this case,
73
+ pass ``protect_all=False`` when you construct the ``CSRF`` instance and use the
74
+ :meth:`csrf.protect <microdot.csrf.CSRF.protect>` decorator::
75
+
76
+ csrf = CSRF(app, cors, protect_all=False)
77
+
78
+ @app.post('/submit-form')
79
+ @csrf.protect
80
+ async def submit_form(request):
81
+ # ...
82
+
83
+ By default, requests coming from different subdomains are considered to be
84
+ cross-site, and as such they will not pass the CSRF check. If you'd like
85
+ subdomain requests to be considered safe, then set the
86
+ ``allow_subdomains=True`` option when you create the ``CSRF`` class.
87
+
88
+ .. note::
89
+ This extension is designed to block requests issued by web browsers when
90
+ they are found to be unsafe or unauthorized by the application owner. The
91
+ method used to determine if a request should be allowed or not is based on
92
+ the value of headers that are only sent by web browsers. Clients other than
93
+ web browsers are not affected by this extension and can send requests
94
+ freely.
@@ -0,0 +1,21 @@
1
+ Core Extensions
2
+ ---------------
3
+
4
+ Microdot is a highly extensible web application framework. The extensions
5
+ described in this section are maintained as part of the Microdot project in
6
+ the same source code repository.
7
+
8
+ .. toctree::
9
+ :maxdepth: 1
10
+
11
+ multipart
12
+ websocket
13
+ sse
14
+ templates
15
+ sessions
16
+ auth
17
+ login
18
+ cors
19
+ csrf
20
+ test_client
21
+ production
@@ -0,0 +1,85 @@
1
+ User Logins
2
+ ~~~~~~~~~~~
3
+
4
+ .. list-table::
5
+ :align: left
6
+
7
+ * - Compatibility
8
+ - | CPython & MicroPython
9
+
10
+ * - Required Microdot source files
11
+ - | `login.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/auth.py>`_
12
+ | `session.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/session.py>`_
13
+ | `helpers.py <https://github.com/miguelgrinberg/microdot/tree/main/src/microdot/helpers.py>`_
14
+ * - Required external dependencies
15
+ - | CPython: `PyJWT <https://pyjwt.readthedocs.io/>`_
16
+ | MicroPython: `jwt.py <https://github.com/micropython/micropython-lib/blob/master/python-ecosys/pyjwt/jwt.py>`_,
17
+ `hmac.py <https://github.com/micropython/micropython-lib/blob/master/python-stdlib/hmac/hmac.py>`_
18
+ * - Examples
19
+ - | `login.py <https://github.com/miguelgrinberg/microdot/blob/main/examples/login/login.py>`_
20
+
21
+ The login extension provides user login functionality. The logged in state of
22
+ the user is stored in the user session cookie, and an optional "remember me"
23
+ cookie can also be added to keep the user logged in across browser sessions.
24
+
25
+ To use this extension, create instances of the
26
+ :class:`Session <microdot.session.Session>` and :class:`Login <microdot.login.Login>`
27
+ class::
28
+
29
+ Session(app, secret_key='top-secret!')
30
+ login = Login()
31
+
32
+ The ``Login`` class accept an optional argument with the URL of the login page.
33
+ The default for this URL is */login*.
34
+
35
+ The application must represent users as objects with an ``id`` attribute. A
36
+ function decorated with ``@login.user_loader`` is used to load a user object::
37
+
38
+ @login.user_loader
39
+ async def get_user(user_id):
40
+ return database.get_user(user_id)
41
+
42
+ The application must implement the login form. At the point in which the user
43
+ credentials have been received and verified, a call to the
44
+ :func:`login_user() <microdot.login.Login.login_user>` function must be made to
45
+ record the user in the user session::
46
+
47
+ @app.route('/login', methods=['GET', 'POST'])
48
+ async def login(request):
49
+ # ...
50
+ if user.check_password(password):
51
+ return await login.login_user(request, user, remember=remember_me)
52
+ return redirect('/login')
53
+
54
+ The optional ``remember`` argument is used to add a remember me cookie that
55
+ will log the user in automatically in future sessions. A value of ``True`` will
56
+ keep the log in active for 30 days. Alternatively, an integer number of days
57
+ can be passed in this argument.
58
+
59
+ Any routes that require the user to be logged in must be decorated with
60
+ :func:`@login <microdot.login.Login.__call__>`::
61
+
62
+ @app.route('/')
63
+ @login
64
+ async def index(request):
65
+ # ...
66
+
67
+ Routes that are of a sensitive nature can be decorated with
68
+ :func:`@login.fresh <microdot.login.Login.fresh>`
69
+ instead. This decorator requires that the user has logged in during the current
70
+ session, and will ask the user to logged in again if the session was
71
+ authenticated through a remember me cookie::
72
+
73
+ @app.get('/fresh')
74
+ @login.fresh
75
+ async def fresh(request):
76
+ # ...
77
+
78
+ To log out a user, the :func:`logout_user() <microdot.auth.Login.logout_user>`
79
+ is used::
80
+
81
+ @app.post('/logout')
82
+ @login
83
+ async def logout(request):
84
+ await login.logout_user(request)
85
+ return redirect('/')