py-auth-sqlalchemy 0.0.2__tar.gz → 0.0.3__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.
- py_auth_sqlalchemy-0.0.3/PKG-INFO +141 -0
- py_auth_sqlalchemy-0.0.3/README.md +118 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/pyproject.toml +1 -1
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/__init__.py +1 -1
- py_auth_sqlalchemy-0.0.3/src/py_auth_sqlalchemy.egg-info/PKG-INFO +141 -0
- py_auth_sqlalchemy-0.0.2/PKG-INFO +0 -22
- py_auth_sqlalchemy-0.0.2/README.md +0 -0
- py_auth_sqlalchemy-0.0.2/src/py_auth_sqlalchemy.egg-info/PKG-INFO +0 -22
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/LICENSE +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/setup.cfg +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/_utils.py +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/core.py +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/py.typed +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/utc_datetime.py +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/SOURCES.txt +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/dependency_links.txt +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/requires.txt +0 -0
- {py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-auth-sqlalchemy
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
|
|
5
|
+
Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
|
|
8
|
+
Project-URL: Repository, https://github.com/jamaldeen09/py-auth
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
20
|
+
Requires-Dist: greenlet>=3.0.0
|
|
21
|
+
Requires-Dist: py-auth-core>=0.0.3
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# py-auth-sqlalchemy
|
|
25
|
+
|
|
26
|
+
Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
|
|
27
|
+
|
|
28
|
+
You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
|
|
29
|
+
|
|
30
|
+
Requires Python 3.10+ and SQLAlchemy 2.0+.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install py-auth-sqlalchemy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install an async driver as well:
|
|
39
|
+
|
|
40
|
+
| Database | Driver | Example URL |
|
|
41
|
+
| ---------- | ---------- | ----------- |
|
|
42
|
+
| PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
|
|
43
|
+
| MySQL | `aiomysql` | `mysql+aiomysql://...` |
|
|
44
|
+
| SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install asyncpg # or aiomysql, or aiosqlite
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A sync engine is rejected. Only those three drivers are accepted.
|
|
51
|
+
|
|
52
|
+
## Models
|
|
53
|
+
|
|
54
|
+
Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
|
|
55
|
+
|
|
56
|
+
`nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
|
|
57
|
+
|
|
58
|
+
If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
|
|
59
|
+
|
|
60
|
+
**Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
|
|
61
|
+
|
|
62
|
+
| Model | When you pass it | Required column *names* |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
|
|
65
|
+
| User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
|
|
66
|
+
| Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
|
|
67
|
+
|
|
68
|
+
Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from uuid import uuid4
|
|
72
|
+
from datetime import datetime
|
|
73
|
+
from sqlalchemy import ForeignKey, String, UniqueConstraint
|
|
74
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
75
|
+
from py_auth_sqlalchemy import UTCDateTime
|
|
76
|
+
|
|
77
|
+
class Base(DeclarativeBase):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
class User(Base):
|
|
81
|
+
__tablename__ = "users"
|
|
82
|
+
|
|
83
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
84
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
85
|
+
name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
86
|
+
image: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
87
|
+
password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
88
|
+
|
|
89
|
+
class Session(Base):
|
|
90
|
+
__tablename__ = "sessions"
|
|
91
|
+
|
|
92
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
93
|
+
session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
|
|
94
|
+
user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
|
|
95
|
+
expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
|
96
|
+
|
|
97
|
+
class Account(Base):
|
|
98
|
+
__tablename__ = "accounts"
|
|
99
|
+
|
|
100
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
101
|
+
user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
|
|
102
|
+
type: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
103
|
+
provider: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
104
|
+
provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
105
|
+
access_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
106
|
+
refresh_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
107
|
+
expires_at: Mapped[int | None] = mapped_column(nullable=True)
|
|
108
|
+
token_type: Mapped[str | None] = mapped_column(nullable=True)
|
|
109
|
+
scope: Mapped[str | None] = mapped_column(nullable=True)
|
|
110
|
+
id_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
111
|
+
session_state: Mapped[str | None] = mapped_column(nullable=True)
|
|
112
|
+
|
|
113
|
+
__table_args__ = (
|
|
114
|
+
UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
122
|
+
from py_auth import PyAuth
|
|
123
|
+
from py_auth_sqlalchemy import SqlAlchemyAdapter
|
|
124
|
+
|
|
125
|
+
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
|
|
126
|
+
|
|
127
|
+
adapter = SqlAlchemyAdapter(
|
|
128
|
+
engine=engine,
|
|
129
|
+
session_model=Session,
|
|
130
|
+
user_model=User,
|
|
131
|
+
account_model=Account,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
auth = PyAuth(adapter=adapter, providers=[...])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# py-auth-sqlalchemy
|
|
2
|
+
|
|
3
|
+
Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
|
|
4
|
+
|
|
5
|
+
You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
|
|
6
|
+
|
|
7
|
+
Requires Python 3.10+ and SQLAlchemy 2.0+.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install py-auth-sqlalchemy
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Install an async driver as well:
|
|
16
|
+
|
|
17
|
+
| Database | Driver | Example URL |
|
|
18
|
+
| ---------- | ---------- | ----------- |
|
|
19
|
+
| PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
|
|
20
|
+
| MySQL | `aiomysql` | `mysql+aiomysql://...` |
|
|
21
|
+
| SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install asyncpg # or aiomysql, or aiosqlite
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A sync engine is rejected. Only those three drivers are accepted.
|
|
28
|
+
|
|
29
|
+
## Models
|
|
30
|
+
|
|
31
|
+
Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
|
|
32
|
+
|
|
33
|
+
`nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
|
|
34
|
+
|
|
35
|
+
If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
|
|
36
|
+
|
|
37
|
+
**Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
|
|
38
|
+
|
|
39
|
+
| Model | When you pass it | Required column *names* |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
|
|
42
|
+
| User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
|
|
43
|
+
| Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
|
|
44
|
+
|
|
45
|
+
Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from uuid import uuid4
|
|
49
|
+
from datetime import datetime
|
|
50
|
+
from sqlalchemy import ForeignKey, String, UniqueConstraint
|
|
51
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
52
|
+
from py_auth_sqlalchemy import UTCDateTime
|
|
53
|
+
|
|
54
|
+
class Base(DeclarativeBase):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
class User(Base):
|
|
58
|
+
__tablename__ = "users"
|
|
59
|
+
|
|
60
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
61
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
62
|
+
name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
63
|
+
image: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
64
|
+
password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
65
|
+
|
|
66
|
+
class Session(Base):
|
|
67
|
+
__tablename__ = "sessions"
|
|
68
|
+
|
|
69
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
70
|
+
session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
|
|
71
|
+
user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
|
|
72
|
+
expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
|
73
|
+
|
|
74
|
+
class Account(Base):
|
|
75
|
+
__tablename__ = "accounts"
|
|
76
|
+
|
|
77
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
78
|
+
user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
|
|
79
|
+
type: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
80
|
+
provider: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
81
|
+
provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
82
|
+
access_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
83
|
+
refresh_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
84
|
+
expires_at: Mapped[int | None] = mapped_column(nullable=True)
|
|
85
|
+
token_type: Mapped[str | None] = mapped_column(nullable=True)
|
|
86
|
+
scope: Mapped[str | None] = mapped_column(nullable=True)
|
|
87
|
+
id_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
88
|
+
session_state: Mapped[str | None] = mapped_column(nullable=True)
|
|
89
|
+
|
|
90
|
+
__table_args__ = (
|
|
91
|
+
UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
99
|
+
from py_auth import PyAuth
|
|
100
|
+
from py_auth_sqlalchemy import SqlAlchemyAdapter
|
|
101
|
+
|
|
102
|
+
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
|
|
103
|
+
|
|
104
|
+
adapter = SqlAlchemyAdapter(
|
|
105
|
+
engine=engine,
|
|
106
|
+
session_model=Session,
|
|
107
|
+
user_model=User,
|
|
108
|
+
account_model=Account,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
auth = PyAuth(adapter=adapter, providers=[...])
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-auth-sqlalchemy
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
|
|
5
|
+
Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
|
|
8
|
+
Project-URL: Repository, https://github.com/jamaldeen09/py-auth
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
20
|
+
Requires-Dist: greenlet>=3.0.0
|
|
21
|
+
Requires-Dist: py-auth-core>=0.0.3
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# py-auth-sqlalchemy
|
|
25
|
+
|
|
26
|
+
Async SQLAlchemy adapter for [`py-auth-core`](https://pypi.org/project/py-auth-core/).
|
|
27
|
+
|
|
28
|
+
You bring your own SQLAlchemy models and an `AsyncEngine`. The adapter implements the py-auth storage contract: users, linked accounts, and sessions. Integrity errors are mapped to `py-auth-core` exceptions (`DuplicateEntryError`, `ForeignKeyViolationError`, and so on).
|
|
29
|
+
|
|
30
|
+
Requires Python 3.10+ and SQLAlchemy 2.0+.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install py-auth-sqlalchemy
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Install an async driver as well:
|
|
39
|
+
|
|
40
|
+
| Database | Driver | Example URL |
|
|
41
|
+
| ---------- | ---------- | ----------- |
|
|
42
|
+
| PostgreSQL | `asyncpg` | `postgresql+asyncpg://...` |
|
|
43
|
+
| MySQL | `aiomysql` | `mysql+aiomysql://...` |
|
|
44
|
+
| SQLite | `aiosqlite`| `sqlite+aiosqlite:///...` |
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install asyncpg # or aiomysql, or aiosqlite
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A sync engine is rejected. Only those three drivers are accepted.
|
|
51
|
+
|
|
52
|
+
## Models
|
|
53
|
+
|
|
54
|
+
Pass your own SQLAlchemy declarative classes. On construction, the adapter checks that each provided model **defines the columns py-auth reads and writes internally**. That check is about **whether the column exists**, not about database constraints.
|
|
55
|
+
|
|
56
|
+
`nullable`, `unique`, `ForeignKey`, defaults, indexes, and extra columns are yours. You can make `email` unique or not, `name` nullable or not, and so on. The adapter does not inspect those.
|
|
57
|
+
|
|
58
|
+
If a required column is missing (for example a `Session` model with no `session_token_hash`), `validate_sqlalchemy_model` raises `AdapterError` immediately. py-auth looks those attributes up by name; without them, later operations would fail unpredictably.
|
|
59
|
+
|
|
60
|
+
**Session** is always required. **User** and **Account** are optional on the adapter; omit them if you do not need user or OAuth-account operations. Calling those methods without the matching model also raises `AdapterError`. Extra columns beyond the lists below are allowed.
|
|
61
|
+
|
|
62
|
+
| Model | When you pass it | Required column *names* |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Session | Always | `id`, `session_token_hash`, `user_id`, `expires` |
|
|
65
|
+
| User | User CRUD / credentials | `id`, `email`, `name`, `image`, `password_hash` |
|
|
66
|
+
| Account | OAuth account linking | `id`, `user_id`, `type`, `provider`, `provider_account_id`, `access_token`, `refresh_token`, `expires_at`, `token_type`, `scope`, `id_token`, `session_state` |
|
|
67
|
+
|
|
68
|
+
Use `UTCDateTime` for `expires` so values stay timezone-aware UTC across backends. Naive datetimes are rejected. Constraints in the example below (`unique=True`, `nullable=...`) are typical choices, not adapter rules.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from uuid import uuid4
|
|
72
|
+
from datetime import datetime
|
|
73
|
+
from sqlalchemy import ForeignKey, String, UniqueConstraint
|
|
74
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
75
|
+
from py_auth_sqlalchemy import UTCDateTime
|
|
76
|
+
|
|
77
|
+
class Base(DeclarativeBase):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
class User(Base):
|
|
81
|
+
__tablename__ = "users"
|
|
82
|
+
|
|
83
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
84
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
|
|
85
|
+
name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
86
|
+
image: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
87
|
+
password_hash: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
88
|
+
|
|
89
|
+
class Session(Base):
|
|
90
|
+
__tablename__ = "sessions"
|
|
91
|
+
|
|
92
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
93
|
+
session_token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
|
|
94
|
+
user_id: Mapped[str] = mapped_column(String(255), ForeignKey("users.id"), nullable=False)
|
|
95
|
+
expires: Mapped[datetime] = mapped_column(UTCDateTime(), nullable=False)
|
|
96
|
+
|
|
97
|
+
class Account(Base):
|
|
98
|
+
__tablename__ = "accounts"
|
|
99
|
+
|
|
100
|
+
id: Mapped[str] = mapped_column(String(255), primary_key=True, default=lambda: str(uuid4()))
|
|
101
|
+
user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False)
|
|
102
|
+
type: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
103
|
+
provider: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
104
|
+
provider_account_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
105
|
+
access_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
106
|
+
refresh_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
107
|
+
expires_at: Mapped[int | None] = mapped_column(nullable=True)
|
|
108
|
+
token_type: Mapped[str | None] = mapped_column(nullable=True)
|
|
109
|
+
scope: Mapped[str | None] = mapped_column(nullable=True)
|
|
110
|
+
id_token: Mapped[str | None] = mapped_column(nullable=True)
|
|
111
|
+
session_state: Mapped[str | None] = mapped_column(nullable=True)
|
|
112
|
+
|
|
113
|
+
__table_args__ = (
|
|
114
|
+
UniqueConstraint("provider", "provider_account_id", name="uq_accounts_provider_account"),
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
|
122
|
+
from py_auth import PyAuth
|
|
123
|
+
from py_auth_sqlalchemy import SqlAlchemyAdapter
|
|
124
|
+
|
|
125
|
+
engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
|
|
126
|
+
|
|
127
|
+
adapter = SqlAlchemyAdapter(
|
|
128
|
+
engine=engine,
|
|
129
|
+
session_model=Session,
|
|
130
|
+
user_model=User,
|
|
131
|
+
account_model=Account,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
auth = PyAuth(adapter=adapter, providers=[...])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Rows come back as dicts keyed by column name. Create the tables yourself (`Base.metadata.create_all` or Alembic); this package does not run migrations.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: py-auth-sqlalchemy
|
|
3
|
-
Version: 0.0.2
|
|
4
|
-
Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
|
|
5
|
-
Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
|
|
8
|
-
Project-URL: Repository, https://github.com/jamaldeen09/py-auth
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Requires-Dist: sqlalchemy>=2.0.0
|
|
20
|
-
Requires-Dist: greenlet>=3.0.0
|
|
21
|
-
Requires-Dist: py-auth-core>=0.0.3
|
|
22
|
-
Dynamic: license-file
|
|
File without changes
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: py-auth-sqlalchemy
|
|
3
|
-
Version: 0.0.2
|
|
4
|
-
Summary: High-performance, async SQLAlchemy adapter for py-auth-core.
|
|
5
|
-
Author-email: Olatunji Jamaldeen Omotoyosi <jamaldeen.o@yahoo.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/jamaldeen09/py-auth
|
|
8
|
-
Project-URL: Repository, https://github.com/jamaldeen09/py-auth
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Requires-Dist: sqlalchemy>=2.0.0
|
|
20
|
-
Requires-Dist: greenlet>=3.0.0
|
|
21
|
-
Requires-Dist: py-auth-core>=0.0.3
|
|
22
|
-
Dynamic: license-file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy/utc_datetime.py
RENAMED
|
File without changes
|
{py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/requires.txt
RENAMED
|
File without changes
|
{py_auth_sqlalchemy-0.0.2 → py_auth_sqlalchemy-0.0.3}/src/py_auth_sqlalchemy.egg-info/top_level.txt
RENAMED
|
File without changes
|