Skip to content

Commit d0a4ede

Browse files
committed
server: revamp web session
1 parent 6fe0c44 commit d0a4ede

18 files changed

+259
-111
lines changed

dev/setup-environment

+1-1
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def _write_server_env_file(github_app: dict[str, typing.Any] | None = None) -> N
235235
),
236236
"POLAR_FRONTEND_BASE_URL": f"https://{os.environ['CODESPACE_NAME']}-8080.{os.environ['GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN']}",
237237
"POLAR_CHECKOUT_BASE_URL": f"https://{os.environ['CODESPACE_NAME']}-8080.{os.environ['GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN']}/v1/checkout-links/{{client_secret}}/redirect",
238-
"POLAR_AUTH_COOKIE_DOMAIN": f"{os.environ['CODESPACE_NAME']}-8080.{os.environ['GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN']}",
238+
"POLAR_USER_SESSION_COOKIE_DOMAIN": f"{os.environ['CODESPACE_NAME']}-8080.{os.environ['GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN']}",
239239
}
240240
if github_app is not None:
241241
replacements = {

render.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ envVarGroups:
366366

367367
- name: backend-production
368368
envVars:
369-
- key: POLAR_AUTH_COOKIE_DOMAIN
369+
- key: POLAR_USER_SESSION_COOKIE_DOMAIN
370370
value: polar.sh
371371
- key: POLAR_BASE_URL
372372
value: https://api.polar.sh/v1
@@ -479,9 +479,9 @@ envVarGroups:
479479

480480
- name: backend-sandbox
481481
envVars:
482-
- key: POLAR_AUTH_COOKIE_KEY
482+
- key: POLAR_USER_SESSION_COOKIE_KEY
483483
value: polar_sandbox_session
484-
- key: POLAR_AUTH_COOKIE_DOMAIN
484+
- key: POLAR_USER_SESSION_COOKIE_DOMAIN
485485
value: polar.sh
486486
- key: POLAR_BASE_URL
487487
value: https://sandbox-api.polar.sh/v1

server/.env.template

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ POLAR_CORS_ORIGINS='["http://localhost:3000", "http://127.0.0.1:3000", "https://
77
POLAR_ALLOWED_HOSTS='["localhost:3000", "127.0.0.1:3000"]'
88
POLAR_FRONTEND_BASE_URL="http://127.0.0.1:3000"
99
POLAR_CHECKOUT_BASE_URL="http://127.0.0.1:8000/v1/checkout-links/{client_secret}/redirect"
10-
POLAR_AUTH_COOKIE_DOMAIN="127.0.0.1"
10+
11+
POLAR_USER_SESSION_COOKIE_DOMAIN="127.0.0.1"
1112

1213
POLAR_POSTGRES_USER=polar
1314
POLAR_POSTGRES_PWD=polar

server/.env.testing

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ POLAR_GITHUB_CLIENT_ID="Iv1.fakefakefake"
1212
POLAR_GITHUB_CLIENT_SECRET="fakefakefakefakefakefakefakefakefakefake"
1313

1414
POLAR_CORS_ORIGINS='["http://127.0.0.1:3000", "http://localhost:3000", "http://test"]'
15-
POLAR_AUTH_COOKIE_DOMAIN="test"
15+
POLAR_USER_SESSION_COOKIE_DOMAIN="test"
1616

1717
POLAR_DEBUG="false"
1818
POLAR_TESTING=1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Add UserSession
2+
3+
Revision ID: 2daaffbdf32e
4+
Revises: 1769a6e618a4
5+
Create Date: 2024-11-29 14:11:51.513854
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# Polar Custom Imports
13+
14+
# revision identifiers, used by Alembic.
15+
revision = "2daaffbdf32e"
16+
down_revision = "1769a6e618a4"
17+
branch_labels: tuple[str] | None = None
18+
depends_on: tuple[str] | None = None
19+
20+
21+
def upgrade() -> None:
22+
# ### commands auto generated by Alembic - please adjust! ###
23+
op.create_table(
24+
"user_sessions",
25+
sa.Column("token", sa.CHAR(length=64), nullable=False),
26+
sa.Column("expires_at", sa.TIMESTAMP(timezone=True), nullable=False),
27+
sa.Column("user_agent", sa.Text(), nullable=False),
28+
sa.Column("user_id", sa.Uuid(), nullable=False),
29+
sa.Column("id", sa.Uuid(), nullable=False),
30+
sa.Column("created_at", sa.TIMESTAMP(timezone=True), nullable=False),
31+
sa.Column("modified_at", sa.TIMESTAMP(timezone=True), nullable=True),
32+
sa.Column("deleted_at", sa.TIMESTAMP(timezone=True), nullable=True),
33+
sa.ForeignKeyConstraint(
34+
["user_id"],
35+
["users.id"],
36+
name=op.f("user_sessions_user_id_fkey"),
37+
ondelete="cascade",
38+
),
39+
sa.PrimaryKeyConstraint("id", name=op.f("user_sessions_pkey")),
40+
sa.UniqueConstraint("token", name=op.f("user_sessions_token_key")),
41+
)
42+
op.create_index(
43+
op.f("ix_user_sessions_created_at"),
44+
"user_sessions",
45+
["created_at"],
46+
unique=False,
47+
)
48+
op.create_index(
49+
op.f("ix_user_sessions_deleted_at"),
50+
"user_sessions",
51+
["deleted_at"],
52+
unique=False,
53+
)
54+
op.create_index(
55+
op.f("ix_user_sessions_expires_at"),
56+
"user_sessions",
57+
["expires_at"],
58+
unique=False,
59+
)
60+
op.create_index(
61+
op.f("ix_user_sessions_modified_at"),
62+
"user_sessions",
63+
["modified_at"],
64+
unique=False,
65+
)
66+
# ### end Alembic commands ###
67+
68+
69+
def downgrade() -> None:
70+
# ### commands auto generated by Alembic - please adjust! ###
71+
op.drop_index(op.f("ix_user_sessions_modified_at"), table_name="user_sessions")
72+
op.drop_index(op.f("ix_user_sessions_expires_at"), table_name="user_sessions")
73+
op.drop_index(op.f("ix_user_sessions_deleted_at"), table_name="user_sessions")
74+
op.drop_index(op.f("ix_user_sessions_created_at"), table_name="user_sessions")
75+
op.drop_table("user_sessions")
76+
# ### end Alembic commands ###

server/polar/article/tasks.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import structlog
55
from sqlalchemy.orm import joinedload
66

7-
from polar.auth.service import AuthService
87
from polar.config import settings
98
from polar.email.sender import get_email_sender
109
from polar.logging import Logger
@@ -52,7 +51,7 @@ async def articles_send_to_user(
5251
subject = "[TEST] " if is_test else ""
5352
subject += article.title
5453

55-
(jwt, _) = AuthService.generate_token(user)
54+
(jwt, _) = "", ""
5655

5756
# _, magic_link_token = await magic_link_service.request(
5857
# session,

server/polar/auth/dependencies.py

+18-18
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
from makefun import with_signature
66

77
from polar.auth.scope import RESERVED_SCOPES, Scope
8-
from polar.config import settings
98
from polar.exceptions import NotPermitted, Unauthorized
10-
from polar.models import OAuth2Token, PersonalAccessToken
9+
from polar.models import OAuth2Token, PersonalAccessToken, UserSession
1110
from polar.oauth2.dependencies import get_optional_token
1211
from polar.oauth2.exceptions import InsufficientScopeError, InvalidTokenError
1312
from polar.personal_access_token.dependencies import get_optional_personal_access_token
@@ -24,32 +23,33 @@
2423
User,
2524
is_anonymous,
2625
)
27-
from .service import AuthService
26+
from .service import auth as auth_service
2827

2928

30-
async def _get_cookie_token(request: Request) -> str | None:
31-
return request.cookies.get(settings.AUTH_COOKIE_KEY)
29+
async def get_user_session(
30+
request: Request, session: AsyncSession = Depends(get_db_session)
31+
) -> UserSession | None:
32+
return await auth_service.authenticate(session, request)
3233

3334

3435
async def get_auth_subject(
35-
cookie_token: str | None = Depends(_get_cookie_token),
36+
user_session: UserSession | None = Depends(get_user_session),
3637
oauth2_credentials: tuple[OAuth2Token | None, bool] = Depends(get_optional_token),
3738
personal_access_token_credentials: tuple[
3839
PersonalAccessToken | None, bool
3940
] = Depends(get_optional_personal_access_token),
40-
session: AsyncSession = Depends(get_db_session),
4141
) -> AuthSubject[Subject]:
42-
if cookie_token is not None:
43-
user = await AuthService.get_user_from_cookie(session, cookie=cookie_token)
44-
if user:
45-
scopes = {Scope.web_default}
46-
if user.github_username in {
47-
"birkjernstrom",
48-
"frankie567",
49-
"emilwidlund",
50-
}:
51-
scopes.add(Scope.admin)
52-
return AuthSubject(user, scopes, AuthMethod.COOKIE)
42+
# Web session
43+
if user_session is not None:
44+
user = user_session.user
45+
scopes = {Scope.web_default}
46+
if user.github_username in {
47+
"birkjernstrom",
48+
"frankie567",
49+
"emilwidlund",
50+
}:
51+
scopes.add(Scope.admin)
52+
return AuthSubject(user, scopes, AuthMethod.COOKIE)
5353

5454
oauth2_token, oauth2_authorization_set = oauth2_credentials
5555
personal_access_token, personal_access_token_authorization_set = (

server/polar/auth/endpoints.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1+
from fastapi import Depends, Request
12
from fastapi.responses import RedirectResponse
23

3-
from polar.config import settings
4+
from polar.models import UserSession as UserSession
45
from polar.openapi import APITag
6+
from polar.postgres import AsyncSession, get_db_session
57
from polar.routing import APIRouter
68

7-
from .service import AuthService
9+
from .dependencies import get_user_session
10+
from .service import auth as auth_service
811

912
router = APIRouter(tags=["auth", APITag.private])
1013

1114

1215
@router.get("/auth/logout")
13-
async def logout() -> RedirectResponse:
14-
response = RedirectResponse(settings.FRONTEND_BASE_URL)
15-
AuthService.set_auth_cookie(response=response, value="", expires=0)
16-
return response
16+
async def logout(
17+
request: Request,
18+
user_session: UserSession | None = Depends(get_user_session),
19+
session: AsyncSession = Depends(get_db_session),
20+
) -> RedirectResponse:
21+
return await auth_service.get_logout_response(session, request, user_session)

0 commit comments

Comments
 (0)