aiohttp-msal 0.5.2__py3-none-any.whl → 0.6__py3-none-any.whl
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.
- aiohttp_msal/__init__.py +4 -10
- aiohttp_msal/msal_async.py +35 -12
- aiohttp_msal/redis_tools.py +87 -0
- {aiohttp_msal-0.5.2.dist-info → aiohttp_msal-0.6.dist-info}/LICENSE +1 -1
- aiohttp_msal-0.6.dist-info/METADATA +123 -0
- aiohttp_msal-0.6.dist-info/RECORD +17 -0
- {aiohttp_msal-0.5.2.dist-info → aiohttp_msal-0.6.dist-info}/WHEEL +1 -1
- tests/test_init.py +1 -0
- aiohttp_msal-0.5.2.dist-info/METADATA +0 -27
- aiohttp_msal-0.5.2.dist-info/RECORD +0 -16
- {aiohttp_msal-0.5.2.dist-info → aiohttp_msal-0.6.dist-info}/top_level.txt +0 -0
- {aiohttp_msal-0.5.2.dist-info → aiohttp_msal-0.6.dist-info}/zip-safe +0 -0
aiohttp_msal/__init__.py
CHANGED
|
@@ -3,7 +3,6 @@ import logging
|
|
|
3
3
|
from functools import wraps
|
|
4
4
|
from inspect import getfullargspec, iscoroutinefunction
|
|
5
5
|
from typing import Any, Awaitable, Callable, Union
|
|
6
|
-
from urllib.parse import urlparse
|
|
7
6
|
|
|
8
7
|
from aiohttp import ClientSession, web
|
|
9
8
|
from aiohttp_session import get_session
|
|
@@ -14,7 +13,7 @@ from .settings import ENV
|
|
|
14
13
|
|
|
15
14
|
_LOGGER = logging.getLogger(__name__)
|
|
16
15
|
|
|
17
|
-
VERSION = "0.
|
|
16
|
+
VERSION = "0.6"
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def msal_session(*args: Callable[[AsyncMSAL], Union[Any, Awaitable[Any]]]) -> Callable:
|
|
@@ -56,20 +55,15 @@ async def app_init_redis_session(
|
|
|
56
55
|
You can initialize your own aiohttp_session & storage provider.
|
|
57
56
|
"""
|
|
58
57
|
# pylint: disable=import-outside-toplevel
|
|
59
|
-
import aioredis
|
|
60
58
|
from aiohttp_session import redis_storage
|
|
59
|
+
from redis.asyncio import from_url
|
|
61
60
|
|
|
62
61
|
await check_proxy()
|
|
63
62
|
|
|
64
63
|
_LOGGER.info("Connect to Redis %s", ENV.REDIS)
|
|
65
|
-
red = urlparse(ENV.REDIS)
|
|
66
64
|
try:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
else:
|
|
70
|
-
# when aioredis migrates to 2.0...
|
|
71
|
-
ENV.database = aioredis.from_url(ENV.REDIS) # pylint: disable=no-member
|
|
72
|
-
# , encoding="utf-8", decode_responses=True
|
|
65
|
+
ENV.database = from_url(ENV.REDIS) # pylint: disable=no-member
|
|
66
|
+
# , encoding="utf-8", decode_responses=True
|
|
73
67
|
except ConnectionRefusedError as err:
|
|
74
68
|
raise ConnectionError("Could not connect to REDIS server") from err
|
|
75
69
|
|
aiohttp_msal/msal_async.py
CHANGED
|
@@ -7,7 +7,7 @@ Once you have the OAuth tokens store in the session, you are free to make reques
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import json
|
|
9
9
|
from functools import partial, wraps
|
|
10
|
-
from typing import Any, Callable, Optional
|
|
10
|
+
from typing import Any, Callable, Optional, Union
|
|
11
11
|
|
|
12
12
|
from aiohttp import web
|
|
13
13
|
from aiohttp.client import ClientResponse, ClientSession, _RequestContextManager
|
|
@@ -96,11 +96,21 @@ class AsyncMSAL:
|
|
|
96
96
|
_app: ConfidentialClientApplication = None
|
|
97
97
|
_clientsession: ClientSession = None # type: ignore
|
|
98
98
|
|
|
99
|
-
def __init__(
|
|
100
|
-
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
session: Union[Session, dict[str, str]],
|
|
102
|
+
save_cache: Optional[Callable[[Union[Session, dict[str, str]]], None]] = None,
|
|
103
|
+
):
|
|
104
|
+
"""Init the class.
|
|
105
|
+
|
|
106
|
+
**save_token_cache** will be called if the token cache changes. Optional.
|
|
107
|
+
Not required when the session parameter is an aiohttp_session.Session.
|
|
108
|
+
"""
|
|
101
109
|
self.session = session
|
|
102
|
-
if
|
|
103
|
-
|
|
110
|
+
if save_cache:
|
|
111
|
+
self.save_token_cache = save_cache
|
|
112
|
+
if not isinstance(session, (Session, dict)):
|
|
113
|
+
raise ValueError(f"session or dict-like object required {session}")
|
|
104
114
|
|
|
105
115
|
@property
|
|
106
116
|
def token_cache(self) -> SerializableTokenCache:
|
|
@@ -134,11 +144,13 @@ class AsyncMSAL:
|
|
|
134
144
|
"""Save the token cache if it changed."""
|
|
135
145
|
if self.token_cache.has_state_changed:
|
|
136
146
|
self.session[TOKEN_CACHE] = self.token_cache.serialize()
|
|
147
|
+
if hasattr(self, "save_token_cache"):
|
|
148
|
+
self.save_token_cache(self.token_cache)
|
|
137
149
|
|
|
138
150
|
def build_auth_code_flow(self, redirect_uri: str) -> str:
|
|
139
151
|
"""First step - Start the flow."""
|
|
140
|
-
self.session[TOKEN_CACHE] = None
|
|
141
|
-
self.session[USER_EMAIL] = None
|
|
152
|
+
self.session[TOKEN_CACHE] = None # type: ignore
|
|
153
|
+
self.session[USER_EMAIL] = None # type: ignore
|
|
142
154
|
self.session[FLOW_CACHE] = res = self.app.initiate_auth_code_flow(
|
|
143
155
|
MY_SCOPE,
|
|
144
156
|
redirect_uri=redirect_uri,
|
|
@@ -149,8 +161,7 @@ class AsyncMSAL:
|
|
|
149
161
|
# https://msal-python.readthedocs.io/en/latest/#msal.ClientApplication.initiate_auth_code_flow
|
|
150
162
|
return str(res["auth_uri"])
|
|
151
163
|
|
|
152
|
-
|
|
153
|
-
def async_acquire_token_by_auth_code_flow(self, auth_response: Any) -> None:
|
|
164
|
+
def acquire_token_by_auth_code_flow(self, auth_response: Any) -> None:
|
|
154
165
|
"""Second step - Acquire token."""
|
|
155
166
|
# Assume we have it in the cache (added by /login)
|
|
156
167
|
# will raise keryerror if no cache
|
|
@@ -165,8 +176,13 @@ class AsyncMSAL:
|
|
|
165
176
|
"preferred_username"
|
|
166
177
|
)
|
|
167
178
|
|
|
168
|
-
|
|
169
|
-
|
|
179
|
+
async def async_acquire_token_by_auth_code_flow(self, auth_response: Any) -> None:
|
|
180
|
+
"""Second step - Acquire token, async version."""
|
|
181
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
182
|
+
None, self.acquire_token_by_auth_code_flow, auth_response
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def get_token(self) -> Optional[dict[str, Any]]:
|
|
170
186
|
"""Acquire a token based on username."""
|
|
171
187
|
accounts = self.app.get_accounts()
|
|
172
188
|
if accounts:
|
|
@@ -175,6 +191,10 @@ class AsyncMSAL:
|
|
|
175
191
|
return result
|
|
176
192
|
return None
|
|
177
193
|
|
|
194
|
+
async def async_get_token(self) -> Optional[dict[str, Any]]:
|
|
195
|
+
"""Acquire a token based on username."""
|
|
196
|
+
return await asyncio.get_event_loop().run_in_executor(None, self.get_token)
|
|
197
|
+
|
|
178
198
|
async def request(self, method: str, url: str, **kwargs: Any) -> ClientResponse:
|
|
179
199
|
"""Make a request to url using an oauth session.
|
|
180
200
|
|
|
@@ -188,6 +208,8 @@ class AsyncMSAL:
|
|
|
188
208
|
AsyncMSAL._clientsession = ClientSession(trust_env=True)
|
|
189
209
|
|
|
190
210
|
token = await self.async_get_token()
|
|
211
|
+
if token is None:
|
|
212
|
+
raise web.HTTPClientError(text="No login token available.")
|
|
191
213
|
|
|
192
214
|
kwargs = kwargs.copy()
|
|
193
215
|
# Ensure headers exist & make a copy
|
|
@@ -195,7 +217,8 @@ class AsyncMSAL:
|
|
|
195
217
|
|
|
196
218
|
headers["Authorization"] = "Bearer " + token["access_token"]
|
|
197
219
|
|
|
198
|
-
|
|
220
|
+
if method not in HTTP_ALLOWED:
|
|
221
|
+
raise web.HTTPClientError(text=f"HTTP method {method} not allowed")
|
|
199
222
|
|
|
200
223
|
if method == HTTP_GET:
|
|
201
224
|
kwargs.setdefault("allow_redirects", True)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Redis tools for sessions."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
from typing import Any, AsyncGenerator, Optional
|
|
7
|
+
|
|
8
|
+
from redis.asyncio import Redis, from_url
|
|
9
|
+
|
|
10
|
+
from aiohttp_msal.msal_async import AsyncMSAL
|
|
11
|
+
from aiohttp_msal.settings import ENV
|
|
12
|
+
|
|
13
|
+
_LOGGER = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
SES_KEYS = ("mail", "name", "m_mail", "m_name")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_redis() -> Redis:
|
|
19
|
+
"""Get a Redis connection."""
|
|
20
|
+
_LOGGER.info("Connect to Redis %s", ENV.REDIS)
|
|
21
|
+
ENV.database = from_url(ENV.REDIS) # pylint: disable=no-member
|
|
22
|
+
return ENV.database
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def iter_redis(
|
|
26
|
+
redis: Redis, *, clean: bool = False, match: Optional[dict[str, str]] = None
|
|
27
|
+
) -> AsyncGenerator[tuple[str, str, dict], None]:
|
|
28
|
+
"""Iterate over the Redis keys to find a specific session."""
|
|
29
|
+
async for key in redis.scan_iter(count=100, match=f"{ENV.COOKIE_NAME}*"):
|
|
30
|
+
sval = await redis.get(key)
|
|
31
|
+
if not isinstance(sval, str):
|
|
32
|
+
if clean:
|
|
33
|
+
await redis.delete(key)
|
|
34
|
+
continue
|
|
35
|
+
val = json.loads(sval)
|
|
36
|
+
ses = val.get("session")
|
|
37
|
+
created = val.get("created")
|
|
38
|
+
if clean and not ses or not created:
|
|
39
|
+
await redis.delete(key)
|
|
40
|
+
continue
|
|
41
|
+
if match:
|
|
42
|
+
for mkey, mval in match.items():
|
|
43
|
+
if mval not in ses[mkey]:
|
|
44
|
+
continue
|
|
45
|
+
created = val.get("created") or "0"
|
|
46
|
+
session = val.get("session") or {}
|
|
47
|
+
yield key, created, session
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def clean_redis(redis: Redis, max_age: int = 90) -> None:
|
|
51
|
+
"""Clear session entries older than max_age days."""
|
|
52
|
+
expire = int(time.time() - max_age * 24 * 60 * 60)
|
|
53
|
+
async for key, created, ses in iter_redis(redis, clean=True):
|
|
54
|
+
for key in SES_KEYS:
|
|
55
|
+
if not ses.get(key):
|
|
56
|
+
await redis.delete(key)
|
|
57
|
+
continue
|
|
58
|
+
if int(created) < expire:
|
|
59
|
+
await redis.delete(key)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _session_factory(key: str, created: str, session: dict) -> AsyncMSAL:
|
|
63
|
+
"""Create a session with a save callback."""
|
|
64
|
+
|
|
65
|
+
async def async_save_cache(_: dict) -> None:
|
|
66
|
+
"""Save the token cache to Redis."""
|
|
67
|
+
rd2 = get_redis()
|
|
68
|
+
try:
|
|
69
|
+
await rd2.set(key, json.dumps({"created": created, "session": session}))
|
|
70
|
+
finally:
|
|
71
|
+
await rd2.close()
|
|
72
|
+
|
|
73
|
+
def save_cache(*args: Any) -> None:
|
|
74
|
+
"""Save the token cache to Redis."""
|
|
75
|
+
try:
|
|
76
|
+
asyncio.get_event_loop().create_task(async_save_cache(*args))
|
|
77
|
+
except RuntimeError:
|
|
78
|
+
asyncio.run(async_save_cache(*args))
|
|
79
|
+
|
|
80
|
+
return AsyncMSAL(session, save_cache=save_cache)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def get_session(red: Redis, email: str) -> AsyncMSAL:
|
|
84
|
+
"""Get a session from Redis."""
|
|
85
|
+
async for key, created, session in iter_redis(red, match={"mail": email}):
|
|
86
|
+
return _session_factory(key, created, session)
|
|
87
|
+
raise ValueError(f"Session for {email} not found")
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: aiohttp-msal
|
|
3
|
+
Version: 0.6
|
|
4
|
+
Summary: Helper Library to use the Microsoft Authentication Library (MSAL) with aiohttp
|
|
5
|
+
Home-page: https://github.com/kellerza/aiohttp_msal
|
|
6
|
+
Author: Johann Kellerman
|
|
7
|
+
Author-email: kellerza@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: msal,oauth,aiohttp,asyncio
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: msal >=1.24.1
|
|
23
|
+
Requires-Dist: aiohttp-session >=2.12
|
|
24
|
+
Requires-Dist: aiohttp >=3.8
|
|
25
|
+
Provides-Extra: redis
|
|
26
|
+
Requires-Dist: aiohttp-session[aioredis] >=2.12 ; extra == 'redis'
|
|
27
|
+
Provides-Extra: tests
|
|
28
|
+
Requires-Dist: black ==23.9.1 ; extra == 'tests'
|
|
29
|
+
Requires-Dist: pylint ; extra == 'tests'
|
|
30
|
+
Requires-Dist: flake8 ; extra == 'tests'
|
|
31
|
+
Requires-Dist: pytest-aiohttp ; extra == 'tests'
|
|
32
|
+
Requires-Dist: pytest ; extra == 'tests'
|
|
33
|
+
Requires-Dist: pytest-cov ; extra == 'tests'
|
|
34
|
+
Requires-Dist: pytest-asyncio ; extra == 'tests'
|
|
35
|
+
Requires-Dist: pytest-env ; extra == 'tests'
|
|
36
|
+
|
|
37
|
+
# aiohttp_msal Python library
|
|
38
|
+
|
|
39
|
+
AsyncIO based OAuth using the Microsoft Authentication Library (MSAL) for Python.
|
|
40
|
+
|
|
41
|
+
Blocking MSAL functions are executed in the executor thread. Should be useful until such time as MSAL Python gets a true async version...
|
|
42
|
+
|
|
43
|
+
Tested with MSAL Python 1.21.0 onward - [MSAL Python docs](https://github.com/AzureAD/microsoft-authentication-library-for-python)
|
|
44
|
+
|
|
45
|
+
## AsycMSAL class
|
|
46
|
+
|
|
47
|
+
The AsyncMSAL class wraps the behavior in the following example app
|
|
48
|
+
<https://github.com/Azure-Samples/ms-identity-python-webapp/blob/master/app.py#L76>
|
|
49
|
+
|
|
50
|
+
It is responsible to manage tokens & token refreshes and as a client to retrieve data using these tokens.
|
|
51
|
+
|
|
52
|
+
### Acquire the token
|
|
53
|
+
|
|
54
|
+
Firstly you should get the tokens via OAuth
|
|
55
|
+
|
|
56
|
+
1. `initiate_auth_code_flow` [referernce](https://msal-python.readthedocs.io/en/latest/#msal.PublicClientApplication.initiate_auth_code_flow)
|
|
57
|
+
|
|
58
|
+
The caller is expected to:
|
|
59
|
+
1. somehow store this content, typically inside the current session of the server,
|
|
60
|
+
2. guide the end user (i.e. resource owner) to visit that auth_uri, typically with a redirect
|
|
61
|
+
3. and then relay this dict and subsequent auth response to
|
|
62
|
+
acquire_token_by_auth_code_flow().
|
|
63
|
+
|
|
64
|
+
**Step 1** and part of **Step 3** is stored by this class in the aiohttp_session
|
|
65
|
+
|
|
66
|
+
2. `acquire_token_by_auth_code_flow` [referernce](https://msal-python.readthedocs.io/en/latest/#msal.PublicClientApplication.initiate_auth_code_flow)
|
|
67
|
+
|
|
68
|
+
### Use the token
|
|
69
|
+
|
|
70
|
+
Now you are free to make requests (typically from an aiohttp server)
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
session = await get_session(request)
|
|
74
|
+
aiomsal = AsyncMSAL(session)
|
|
75
|
+
async with aiomsal.get("https://graph.microsoft.com/v1.0/me") as res:
|
|
76
|
+
res = await res.json()
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Example web server
|
|
80
|
+
|
|
81
|
+
Complete routes can be found in [routes.py](./aiohttp_msal/routes.py)
|
|
82
|
+
|
|
83
|
+
### Start the login process
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
@ROUTES.get("/user/login")
|
|
87
|
+
async def user_login(request: web.Request) -> web.Response:
|
|
88
|
+
"""Redirect to MS login page."""
|
|
89
|
+
session = await new_session(request)
|
|
90
|
+
|
|
91
|
+
redir = AsyncMSAL(session).build_auth_code_flow(
|
|
92
|
+
redirect_uri=get_route(request, URI_USER_AUTHORIZED)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return web.HTTPFound(redir)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Acquire the token after being redirected back to the server
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
@ROUTES.post(URI_USER_AUTHORIZED)
|
|
102
|
+
async def user_authorized(request: web.Request) -> web.Response:
|
|
103
|
+
"""Complete the auth code flow."""
|
|
104
|
+
session = await get_session(request)
|
|
105
|
+
auth_response = dict(await request.post())
|
|
106
|
+
|
|
107
|
+
aiomsal = AsyncMSAL(session)
|
|
108
|
+
await aiomsal.async_acquire_token_by_auth_code_flow(auth_response)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Helper methods
|
|
112
|
+
|
|
113
|
+
- `@ROUTES.get("/user/photo")`
|
|
114
|
+
|
|
115
|
+
Serve the user's photo from their Microsoft profile
|
|
116
|
+
|
|
117
|
+
- `get_user_info`
|
|
118
|
+
|
|
119
|
+
Get the user's email and display name from MS Graph
|
|
120
|
+
|
|
121
|
+
- `get_manager_info`
|
|
122
|
+
|
|
123
|
+
Get the user's manager info from MS Graph
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
aiohttp_msal/__init__.py,sha256=SUmd1YvN_mSr192WMoMNf9h9TWZJWFRDzwoHjgEghzw,2999
|
|
2
|
+
aiohttp_msal/msal_async.py,sha256=lSwTK2utBVjhQQ921aoq34hNa0z-AJsQleSHXxh9STk,9779
|
|
3
|
+
aiohttp_msal/redis_tools.py,sha256=OQVuugad_46hWDIOC7I0uzq-E6koVmOXvq_qC5q9k7s,2896
|
|
4
|
+
aiohttp_msal/routes.py,sha256=c-w5wHaLAYGEqZvfZ8PnzzRh60asLqdUa30lvSANdYM,8319
|
|
5
|
+
aiohttp_msal/settings.py,sha256=ZZn7D6QmIyQSvuqCAoTacKRXYfopqK4P74eVdPCw-uI,1231
|
|
6
|
+
aiohttp_msal/settings_base.py,sha256=pmVmzTtaGEgRh-AMGy0HdhF1JvoZhZp42G3PL_ILHLw,2892
|
|
7
|
+
aiohttp_msal/user_info.py,sha256=oIu90lgi71G27MVkw4pGH8wZqjXk8kxmW0fn2LKxOHc,1753
|
|
8
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
tests/test_init.py,sha256=sHmt7yNDlcu5JHrpM_gim0NeLce0NwUAMM3HAdGoo58,75
|
|
10
|
+
tests/test_msal_async.py,sha256=31MCoAbUiyUhc4SkebUKpjLDHozEBko-QgEBSHjfSoM,332
|
|
11
|
+
tests/test_settings.py,sha256=z-qtUs1zl5Q9NEux051eebyPnArLZ_OfZu65FKz0N4Y,333
|
|
12
|
+
aiohttp_msal-0.6.dist-info/LICENSE,sha256=H1aGfkSfZFwK3q4INn9mUldOJGZy-ZXu5-65K9Glunw,1080
|
|
13
|
+
aiohttp_msal-0.6.dist-info/METADATA,sha256=e3p4JvDqsf2BPlJklTd2S2pCSpclj-B1GobMpLFdP98,4205
|
|
14
|
+
aiohttp_msal-0.6.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
15
|
+
aiohttp_msal-0.6.dist-info/top_level.txt,sha256=QPWOi5JtacVEdbaU5bJExc9o-cCT2Lufx0QhUpsv5_E,19
|
|
16
|
+
aiohttp_msal-0.6.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
17
|
+
aiohttp_msal-0.6.dist-info/RECORD,,
|
tests/test_init.py
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: aiohttp-msal
|
|
3
|
-
Version: 0.5.2
|
|
4
|
-
Summary: Helper Library to use MSAL with aiohttp
|
|
5
|
-
Home-page: https://github.com/kellerza/aiohttp_msal
|
|
6
|
-
Author: Johann Kellerman
|
|
7
|
-
Author-email: kellerza@gmail.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Keywords: msal,oauth,aiohttp,asyncio
|
|
10
|
-
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Natural Language :: English
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Requires-Python: >=3.9
|
|
19
|
-
Description-Content-Type: text/markdown
|
|
20
|
-
License-File: LICENSE
|
|
21
|
-
Requires-Dist: msal (>=1.18.0b1)
|
|
22
|
-
Requires-Dist: aiohttp-session (>=2.11)
|
|
23
|
-
Requires-Dist: aiohttp (>=3.8)
|
|
24
|
-
Provides-Extra: redis
|
|
25
|
-
Requires-Dist: aiohttp-session[redis] ; extra == 'redis'
|
|
26
|
-
|
|
27
|
-
aiohttp_msal library
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
aiohttp_msal/__init__.py,sha256=EENbQw1X9Md4BTqLWcMERfh9-RoiFxB4NEoR7xUyDzc,3261
|
|
2
|
-
aiohttp_msal/msal_async.py,sha256=194tsWvx_V0iLR8aZoKLvK7lLAq0CH0f5t8_bLWYtQI,8656
|
|
3
|
-
aiohttp_msal/routes.py,sha256=c-w5wHaLAYGEqZvfZ8PnzzRh60asLqdUa30lvSANdYM,8319
|
|
4
|
-
aiohttp_msal/settings.py,sha256=ZZn7D6QmIyQSvuqCAoTacKRXYfopqK4P74eVdPCw-uI,1231
|
|
5
|
-
aiohttp_msal/settings_base.py,sha256=pmVmzTtaGEgRh-AMGy0HdhF1JvoZhZp42G3PL_ILHLw,2892
|
|
6
|
-
aiohttp_msal/user_info.py,sha256=oIu90lgi71G27MVkw4pGH8wZqjXk8kxmW0fn2LKxOHc,1753
|
|
7
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
tests/test_init.py,sha256=HPcL6bXaM95b3J908tMKLznHnwVf1fvKM3tuFhSuX84,40
|
|
9
|
-
tests/test_msal_async.py,sha256=31MCoAbUiyUhc4SkebUKpjLDHozEBko-QgEBSHjfSoM,332
|
|
10
|
-
tests/test_settings.py,sha256=z-qtUs1zl5Q9NEux051eebyPnArLZ_OfZu65FKz0N4Y,333
|
|
11
|
-
aiohttp_msal-0.5.2.dist-info/LICENSE,sha256=J6cRBikbciilokG0-Hlz2fO72XET1ebYm4LMEfknoF8,1075
|
|
12
|
-
aiohttp_msal-0.5.2.dist-info/METADATA,sha256=4kfu7BI-LR1MMrBxokTbY1SZ_tNSuObEyEnKbpB6iBk,942
|
|
13
|
-
aiohttp_msal-0.5.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
|
14
|
-
aiohttp_msal-0.5.2.dist-info/top_level.txt,sha256=QPWOi5JtacVEdbaU5bJExc9o-cCT2Lufx0QhUpsv5_E,19
|
|
15
|
-
aiohttp_msal-0.5.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
16
|
-
aiohttp_msal-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|