memydev-auth-sdk 0.1.0__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.
memyauth/_types.py ADDED
@@ -0,0 +1,309 @@
1
+ """
2
+ @fileoverview Shared wire/projection type shapes and injection-seam protocols.
3
+ @module memyauth._types
4
+ @description TypedDict/Literal shapes for every public return value plus the Clock/RandomSource
5
+ protocols injected into every service (discovery, oidc, iam, idtoken, session, state). No
6
+ pydantic, no dataclasses — zero extra runtime dependency beyond httpx + PyJWT, matching the
7
+ customer SDK's zero-deps posture (js/ has zero runtime deps beyond jose). `UserContext` and its
8
+ nested shapes are deliberately `total=False`: the provider's `/iam/me` may add fields at any
9
+ time (C.5.1 / D2) and this SDK must never reject an unknown key — see `_iam.py` for the
10
+ hand-written validator that enforces "permissive about unknown fields, strict about known ones."
11
+ @dependencies typing (stdlib only)
12
+ @relatedFiles _iam.py, _discovery.py, _oidc.py, _idtoken.py, _session.py, _state.py, _config.py
13
+ @created 2026-09-06
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import time
18
+ from typing import Any, Literal, Protocol, TypedDict
19
+
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Injection seams (present on every service; §C preamble)
23
+ # ---------------------------------------------------------------------------
24
+
25
+
26
+ class Clock(Protocol):
27
+ def now(self) -> int:
28
+ """Epoch milliseconds. Mirrors the JS `Clock` seam exactly."""
29
+ ...
30
+
31
+
32
+ class RandomSource(Protocol):
33
+ def bytes(self, n: int) -> bytes:
34
+ """`n` cryptographically-random bytes. Mirrors the JS `RandomSource` seam exactly."""
35
+ ...
36
+
37
+
38
+ class SystemClock:
39
+ """Default `Clock`: the wall clock, in epoch milliseconds."""
40
+
41
+ def now(self) -> int:
42
+ return int(time.time() * 1000)
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Literal enums (§F, §C.10)
47
+ # ---------------------------------------------------------------------------
48
+
49
+ TokenEndpointAuthMethod = Literal["client_secret_post", "client_secret_basic"]
50
+ IdTokenValidationMode = Literal["local-jwks", "tls-token-endpoint"]
51
+ IamCallerMode = Literal["auto", "appiam", "appidp", "m2m"]
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Capability 1 — Discovery (§C.1)
56
+ # ---------------------------------------------------------------------------
57
+
58
+
59
+ class OidcDiscoveryDocument(TypedDict, total=False):
60
+ issuer: str
61
+ authorization_endpoint: str
62
+ token_endpoint: str
63
+ userinfo_endpoint: str
64
+ jwks_uri: str
65
+ end_session_endpoint: str
66
+ scopes_supported: list[str]
67
+ id_token_signing_alg_values_supported: list[str]
68
+
69
+
70
+ class DiscoveryHealth(TypedDict):
71
+ state: Literal["ok", "failed", "unknown"]
72
+ last_success_at: str | None
73
+ last_error: str | None
74
+
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Capability 2 — Authorization URL (§C.2)
78
+ # ---------------------------------------------------------------------------
79
+
80
+
81
+ class AuthorizationRequest(TypedDict):
82
+ url: str
83
+ code_verifier: str
84
+ code_challenge: str
85
+ state: str
86
+ nonce: str
87
+ redirect_uri: str
88
+ scope: str
89
+
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # Capability 3 — Token exchange / refresh (§C.3)
93
+ # ---------------------------------------------------------------------------
94
+
95
+
96
+ class OidcTokenResponse(TypedDict, total=False):
97
+ access_token: str
98
+ token_type: str
99
+ expires_in: int
100
+ id_token: str
101
+ refresh_token: str
102
+ scope: str
103
+
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Capability 4 — UserInfo (§C.4)
107
+ # ---------------------------------------------------------------------------
108
+
109
+
110
+ class UserInfoTenantMirror(TypedDict, total=False):
111
+ id: str
112
+ code: str
113
+ idp_users_sub: str
114
+
115
+
116
+ class UserInfo(TypedDict, total=False):
117
+ sub: str
118
+ email: str
119
+ email_verified: bool
120
+ name: str
121
+ picture: str
122
+ roles: list[str]
123
+ custom_data: dict[str, Any]
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Capability 5 — IAM (§C.5, §B.1) — vendored (by hand, since this SDK has no
128
+ # sync-contract.mjs equivalent for Python) from iam/api/src/lib/user-context.ts:59-87.
129
+ # Deliberately `total=False` at every level: unknown keys are tolerated (C.5.1); the
130
+ # hand-written validator in _iam.py enforces the KNOWN keys strictly before this shape
131
+ # is ever handed to a caller.
132
+ # ---------------------------------------------------------------------------
133
+
134
+
135
+ class UserContextTenant(TypedDict, total=False):
136
+ id: str
137
+ code: str
138
+ idp_users_sub: str
139
+ key: str # 64-char lowercase hex; bearer-equivalent — see tenantKeyRedaction rule.
140
+ created_at: str
141
+ updated_at: str
142
+
143
+
144
+ class UserContextOrgEntry(TypedDict, total=False):
145
+ id: str
146
+ name: str
147
+ roles: list[str]
148
+ tenant: dict[str, Any] | None
149
+
150
+
151
+ class UserContext(TypedDict, total=False):
152
+ user: dict[str, Any]
153
+ tenant: UserContextTenant
154
+ organizations: dict[str, list[UserContextOrgEntry]]
155
+ permissions: list[str]
156
+
157
+
158
+ # Re-exported alias, matching the JS `export type IamUserContext = UserContext;`
159
+ IamUserContext = UserContext
160
+
161
+
162
+ class IamOrgEntryTenant(TypedDict, total=False):
163
+ id: str
164
+ code: str
165
+ idp_users_sub: str
166
+ key: str
167
+
168
+
169
+ class IamOrgEntry(TypedDict):
170
+ id: str
171
+ name: str
172
+ roles: list[str]
173
+ tenant: IamOrgEntryTenant | None
174
+
175
+
176
+ class IamTenant(TypedDict, total=False):
177
+ id: str
178
+ code: str
179
+ idp_users_sub: str
180
+ key: str
181
+ created_at: str
182
+ updated_at: str
183
+
184
+
185
+ class IamOrganizations(TypedDict):
186
+ own: list[IamOrgEntry]
187
+ as_member: list[IamOrgEntry]
188
+
189
+
190
+ class IamSession(TypedDict, total=False):
191
+ user_id: str
192
+ idp_subject: str
193
+ email: str
194
+ email_verified: bool | None
195
+ display_name: str
196
+ tenant_id: str
197
+ tenant_code: str
198
+ tenant_key: str
199
+ idp_roles: list[str]
200
+ roles: list[str]
201
+ scopes: list[str]
202
+ permissions: list[str]
203
+ is_global_admin: bool
204
+ organizations: IamOrganizations
205
+
206
+
207
+ # ---------------------------------------------------------------------------
208
+ # Capability 7 — Session tokens (§C.7) — an SDK-owned family, structurally
209
+ # distinct from provider (ES384) tokens. See _session.py for the six-way separation.
210
+ # ---------------------------------------------------------------------------
211
+
212
+
213
+ class SessionAccessInput(TypedDict, total=False):
214
+ sub: str
215
+ tenants_id: str | None
216
+ roles: list[str]
217
+ email: str
218
+ email_verified: bool
219
+ is_global_admin: bool
220
+ auth_method: Literal["oidc", "local"]
221
+ iam_tenant_key: str
222
+
223
+
224
+ class SessionAccessClaims(SessionAccessInput, total=False):
225
+ type: Literal["access"]
226
+ iss: str
227
+ iat: int
228
+ exp: int
229
+
230
+
231
+ class SessionRefreshInput(TypedDict, total=False):
232
+ sub: str
233
+ tenants_id: str
234
+ jti: str
235
+ iam_tenant_key: str
236
+
237
+
238
+ class SessionRefreshClaims(SessionRefreshInput, total=False):
239
+ type: Literal["refresh"]
240
+ iss: str
241
+ iat: int
242
+ exp: int
243
+
244
+
245
+ # ---------------------------------------------------------------------------
246
+ # Capability 8 — State cookie (§C.8)
247
+ # ---------------------------------------------------------------------------
248
+
249
+
250
+ class StateCookieInput(TypedDict, total=False):
251
+ code_verifier: str
252
+ nonce: str
253
+ state: str
254
+ return_to: str
255
+
256
+
257
+ class StateCookiePayload(TypedDict):
258
+ code_verifier: str
259
+ nonce: str
260
+ state: str
261
+ return_to: str
262
+
263
+
264
+ # ---------------------------------------------------------------------------
265
+ # Capability C0 — ID-token trust strategies (§C.10)
266
+ # ---------------------------------------------------------------------------
267
+
268
+
269
+ class IdpTokenClaims(TypedDict, total=False):
270
+ iss: str
271
+ sub: str
272
+ aud: str | list[str]
273
+ exp: int
274
+ iat: int
275
+ nonce: str
276
+ auth_time: int
277
+ azp: str
278
+
279
+
280
+ __all__ = [
281
+ "Clock",
282
+ "RandomSource",
283
+ "SystemClock",
284
+ "TokenEndpointAuthMethod",
285
+ "IdTokenValidationMode",
286
+ "IamCallerMode",
287
+ "OidcDiscoveryDocument",
288
+ "DiscoveryHealth",
289
+ "AuthorizationRequest",
290
+ "OidcTokenResponse",
291
+ "UserInfoTenantMirror",
292
+ "UserInfo",
293
+ "UserContextTenant",
294
+ "UserContextOrgEntry",
295
+ "UserContext",
296
+ "IamUserContext",
297
+ "IamOrgEntryTenant",
298
+ "IamOrgEntry",
299
+ "IamTenant",
300
+ "IamOrganizations",
301
+ "IamSession",
302
+ "SessionAccessInput",
303
+ "SessionAccessClaims",
304
+ "SessionRefreshInput",
305
+ "SessionRefreshClaims",
306
+ "StateCookieInput",
307
+ "StateCookiePayload",
308
+ "IdpTokenClaims",
309
+ ]
memyauth/py.typed ADDED
File without changes
@@ -0,0 +1,147 @@
1
+ """
2
+ @fileoverview Public synchronous facade — GENERATED twins from `_sync/` + the shared pure modules.
3
+ @module memyauth.sync
4
+ @description Thin re-export layer, hand-written (this file itself is NOT generated — only
5
+ `_sync/*.py` is). `RoleMapper`, `SessionTokenService`, `StateCookieService`,
6
+ `resolve_config`/`ResolvedConfig`, every error class, and every `_types.py` shape are SHARED —
7
+ the identical object the async surface imports, since they carry no `async`/`await` and
8
+ therefore have no sync/async distinction to generate. Only `MemyAuth`, `DiscoveryService`,
9
+ `OidcService`, `IamService` and `IdTokenVerifier` come from `_sync/`, the unasync-generated
10
+ twins of `_client.py`/`_discovery.py`/`_oidc.py`/`_iam.py`/`_idtoken.py`.
11
+ @dependencies ../_sync/* (generated), ../_roles.py, ../_session.py, ../_state.py, ../_config.py,
12
+ ../_errors.py, ../_types.py, ../_pkce.py (all shared/pure)
13
+ @relatedFiles ../scripts/gen_sync.py (the generator), ../_sync/__init__.py (generated package
14
+ marker), ../../tests/test_sync_parity.py, ../../tests/test_sync_functional.py
15
+ @created 2026-09-06
16
+ """
17
+ from .._config import ResolvedConfig, RetryConfig, resolve_config
18
+ from .._errors import (
19
+ BadRequestError,
20
+ ConfigError,
21
+ DiscoveryError,
22
+ DiscoveryEscapeError,
23
+ ForbiddenError,
24
+ GoneError,
25
+ IdTokenError,
26
+ IssuerMismatchError,
27
+ MemyAuthError,
28
+ NetworkError,
29
+ NotFoundError,
30
+ ProtocolError,
31
+ RefreshRejectedError,
32
+ ServiceUnavailableError,
33
+ SessionTokenError,
34
+ StateCookieError,
35
+ StateMismatchError,
36
+ TimeoutError,
37
+ TokenExchangeError,
38
+ UnauthorizedError,
39
+ UnsupportedAlgorithmError,
40
+ UnsupportedAlgorithmPinError,
41
+ UserInfoError,
42
+ decode_error,
43
+ )
44
+ from .._pkce import SystemRandomSource, generate_code_challenge, generate_code_verifier
45
+ from .._roles import CONSUMER_ROLES, DEFAULT_CONSUMER_ROLE, DEFAULT_ROLE_MAP, GLOBAL_ADMIN_IDP_ROLES, RoleMapper
46
+ from .._session import SessionTokenService
47
+ from .._state import StateCookieService
48
+ from .._sync._client import SDK_VERSION, MemyAuth
49
+ from .._sync._discovery import DiscoveryService
50
+ from .._sync._iam import IamService
51
+ from .._sync._idtoken import IdTokenVerifier
52
+ from .._sync._oidc import OidcService
53
+ from .._types import (
54
+ AuthorizationRequest,
55
+ Clock,
56
+ DiscoveryHealth,
57
+ IamOrgEntry,
58
+ IamSession,
59
+ IamTenant,
60
+ IamUserContext,
61
+ IdpTokenClaims,
62
+ OidcDiscoveryDocument,
63
+ OidcTokenResponse,
64
+ RandomSource,
65
+ SessionAccessClaims,
66
+ SessionAccessInput,
67
+ SessionRefreshClaims,
68
+ SessionRefreshInput,
69
+ StateCookieInput,
70
+ StateCookiePayload,
71
+ SystemClock,
72
+ UserContext,
73
+ UserContextOrgEntry,
74
+ UserContextTenant,
75
+ UserInfo,
76
+ )
77
+
78
+ __version__ = SDK_VERSION
79
+
80
+ __all__ = [
81
+ "MemyAuth",
82
+ "SDK_VERSION",
83
+ "__version__",
84
+ "resolve_config",
85
+ "ResolvedConfig",
86
+ "RetryConfig",
87
+ "DiscoveryService",
88
+ "OidcService",
89
+ "IamService",
90
+ "IdTokenVerifier",
91
+ "RoleMapper",
92
+ "DEFAULT_ROLE_MAP",
93
+ "CONSUMER_ROLES",
94
+ "DEFAULT_CONSUMER_ROLE",
95
+ "GLOBAL_ADMIN_IDP_ROLES",
96
+ "SessionTokenService",
97
+ "StateCookieService",
98
+ "generate_code_verifier",
99
+ "generate_code_challenge",
100
+ "SystemRandomSource",
101
+ "MemyAuthError",
102
+ "ConfigError",
103
+ "UnsupportedAlgorithmPinError",
104
+ "NetworkError",
105
+ "TimeoutError",
106
+ "ProtocolError",
107
+ "BadRequestError",
108
+ "UnauthorizedError",
109
+ "ForbiddenError",
110
+ "NotFoundError",
111
+ "GoneError",
112
+ "ServiceUnavailableError",
113
+ "DiscoveryError",
114
+ "IssuerMismatchError",
115
+ "DiscoveryEscapeError",
116
+ "TokenExchangeError",
117
+ "RefreshRejectedError",
118
+ "UserInfoError",
119
+ "IdTokenError",
120
+ "UnsupportedAlgorithmError",
121
+ "SessionTokenError",
122
+ "StateCookieError",
123
+ "StateMismatchError",
124
+ "decode_error",
125
+ "Clock",
126
+ "RandomSource",
127
+ "SystemClock",
128
+ "OidcDiscoveryDocument",
129
+ "DiscoveryHealth",
130
+ "AuthorizationRequest",
131
+ "OidcTokenResponse",
132
+ "UserInfo",
133
+ "UserContext",
134
+ "UserContextTenant",
135
+ "UserContextOrgEntry",
136
+ "IamUserContext",
137
+ "IamSession",
138
+ "IamOrgEntry",
139
+ "IamTenant",
140
+ "SessionAccessInput",
141
+ "SessionAccessClaims",
142
+ "SessionRefreshInput",
143
+ "SessionRefreshClaims",
144
+ "StateCookieInput",
145
+ "StateCookiePayload",
146
+ "IdpTokenClaims",
147
+ ]
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: memydev-auth-sdk
3
+ Version: 0.1.0
4
+ Summary: Official MemyAuth (OIDC + IAM) client SDK for Python
5
+ License: Proprietary and unlicensed pending approved service terms.
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: httpx<1,>=0.27
8
+ Requires-Dist: pyjwt[crypto]<3,>=2.9
9
+ Provides-Extra: dev
10
+ Requires-Dist: editables==0.5; extra == 'dev'
11
+ Requires-Dist: hatchling==1.27.0; extra == 'dev'
12
+ Requires-Dist: pytest-asyncio==0.24.0; extra == 'dev'
13
+ Requires-Dist: pytest==8.3.5; extra == 'dev'
14
+ Requires-Dist: respx==0.22.0; extra == 'dev'
15
+ Requires-Dist: unasync==0.6.0; extra == 'dev'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # memydev-auth-sdk
19
+
20
+ Official Python client for **MemyAuth** — the estate's OIDC + IAM provider. Import name: `memyauth`.
21
+
22
+ ```python
23
+ import asyncio
24
+ from memyauth import MemyAuth
25
+
26
+ async def main() -> None:
27
+ async with MemyAuth(
28
+ issuer_url="https://auth.memy.dev/oidc",
29
+ client_id="my-app",
30
+ client_secret="...",
31
+ public_base_url="https://my-app.example.com",
32
+ id_token_validation="local-jwks", # or "tls-token-endpoint" — see below, no default
33
+ ) as auth:
34
+ req = await auth.oidc.build_authorization_url()
35
+ # redirect the browser to req.url, persist req via auth.state.sign(...)
36
+ ...
37
+
38
+ asyncio.run(main())
39
+ ```
40
+
41
+ A synchronous facade is available for non-async callers:
42
+
43
+ ```python
44
+ from memyauth.sync import MemyAuth
45
+
46
+ with MemyAuth(issuer_url=..., client_id=..., client_secret=..., id_token_validation="local-jwks") as auth:
47
+ tokens = auth.oidc.exchange_code(code=..., code_verifier=...)
48
+ ```
49
+
50
+ ## Choosing `id_token_validation`
51
+
52
+ MemyAuth signs ID tokens with **ES384 only**. Two ID-token trust strategies are both in production
53
+ use across the estate and neither is a silent default — you must choose:
54
+
55
+ - `local-jwks` (**recommended for new consumers**): verifies the ID token's signature locally
56
+ against the provider's JWKS, with an explicit algorithm allow-list (`["ES384"]` by default).
57
+ Stateless, no extra round trip, defends against ID-token substitution.
58
+ - `tls-token-endpoint`: performs **no** local signature verification; identity is derived from the
59
+ direct TLS channel to the token endpoint (OIDC Core §3.1.3.7 item 6) plus `/oidc/me` or
60
+ `/iam/me`. Choose this only when you never process the ID token directly.
61
+
62
+ An `allowed_algorithms` pin that cannot verify any token this provider issues (e.g. `["RS256"]`)
63
+ fails loudly at construction with `UnsupportedAlgorithmPinError` — this SDK exists in large part to
64
+ make that historically-real defect (shipped twice against this provider) impossible to repeat
65
+ silently.
66
+
67
+ ## Async is the source of truth
68
+
69
+ `memyauth/_*.py` are hand-written and authoritative. `memyauth/_sync/*.py` and the `memyauth.sync`
70
+ facade are **mechanically generated** from them by `scripts/gen_sync.py` (via `unasync`) — never
71
+ hand-edit `_sync/`. Regenerate after any async change:
72
+
73
+ ```
74
+ python scripts/gen_sync.py # write the twins
75
+ python scripts/gen_sync.py --check # verify no drift; exits non-zero if the twins are stale
76
+ ```
77
+
78
+ ## Development
79
+
80
+ ```
81
+ python3 -m venv .venv
82
+ .venv/bin/python -m pip install --upgrade pip
83
+ .venv/bin/python -m pip install hatchling==1.27.0 editables==0.5
84
+ .venv/bin/python -m pip install --no-build-isolation -e ".[dev]"
85
+ .venv/bin/python -m pytest -q
86
+ ```
@@ -0,0 +1,26 @@
1
+ memyauth/__init__.py,sha256=2suvRP3XrsOxdCw34S6lg4Gr-8kH1ilVx9qxqPtoIDU,3836
2
+ memyauth/_client.py,sha256=Inye4y6vocOiKiedodv1jSTfBX85nLEDL-WMupwTa_M,7191
3
+ memyauth/_config.py,sha256=N0eEPEHN_ICgkKUMKroX-buXZpHgpht_XMackPX72kM,19233
4
+ memyauth/_discovery.py,sha256=HKDNtRgmTc_s9EOrWwJAxktqLu_Bh44SF8uClXJErSw,7617
5
+ memyauth/_errors.py,sha256=9_DlqVwKaPn0BuAQOzKm1iNYPAe2t_dzmxfh_yu1c_c,8554
6
+ memyauth/_iam.py,sha256=l_BmdCESPrY31qljMwMl_eIA90ImuQmmNj15B9SHWC0,13432
7
+ memyauth/_idtoken.py,sha256=WgjEcbr0eqXiDrNIRDFKdXo9QBP7H1DXn7AeLyjdWko,7672
8
+ memyauth/_oidc.py,sha256=MsdrspJSimNflmLzyIE1d5xFw_rutzL7jj_cDjevLVc,10810
9
+ memyauth/_pkce.py,sha256=5cR2jV-5_xeLbOOCNltttWS9YldyWg7OKcDP4CJIk28,2155
10
+ memyauth/_roles.py,sha256=F2_k6jlVRRical9rcKRdPpT-hze3jV01RfQ9GijjGfc,4539
11
+ memyauth/_session.py,sha256=A2UqBtFAWHLXeNtFAoYXjkKj5IbuQU0y26RLKInKe6w,6840
12
+ memyauth/_state.py,sha256=pM75vFDv_k9xSGIMjo79LUn3I1KmbZ0CO1ss2wINqnM,5594
13
+ memyauth/_transport.py,sha256=mgJlXbz1sLYW6dj5B-dIvzX_iz2uaB4_u4I1OvD7zig,9153
14
+ memyauth/_types.py,sha256=gdCLzlYOwKgIw69p4ar23d1oPmC9RLsrkGqD5uX7PW0,8431
15
+ memyauth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ memyauth/_sync/__init__.py,sha256=C9HwbhD1iuIiCuRc8lXQr04PRZXNvYQQz7Vld5M4VMk,253
17
+ memyauth/_sync/_client.py,sha256=lbNWcGqLAtG_AKYjDy1sudn6gWM2zzjSxRHPC3TA35c,7300
18
+ memyauth/_sync/_discovery.py,sha256=fa5kAzwMInWqj8Dhb-XfNgYY7Sr8DiIQw1itIzH35Mc,7719
19
+ memyauth/_sync/_iam.py,sha256=HGH8ndzKqHoDESHlM2e_7K9OgBeLw4xTKCj1hMeV-b0,13528
20
+ memyauth/_sync/_idtoken.py,sha256=d7MKijTerr7vT-zhtBZGANFngMLeNuQjNkCJ9oHBX2Y,7778
21
+ memyauth/_sync/_oidc.py,sha256=6mt6emqrYm0elGu5Kd_8JRlpW3lpR1XSWeYwH2kQ8V4,10871
22
+ memyauth/_sync/_transport.py,sha256=QK_dcoR7LODYDNIErRjeG39f9RDCShu-WAVdMYxG4bY,9242
23
+ memyauth/sync/__init__.py,sha256=65nT-87ibWzsF-dQuwkF_E8DHbzd1lknxQQwLYeu8Z4,4298
24
+ memydev_auth_sdk-0.1.0.dist-info/METADATA,sha256=c_v3hmb3PPgtcbqrgIJyiRsJhsdGOwpq2Efi5Dy4DFk,3327
25
+ memydev_auth_sdk-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ memydev_auth_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any