Skip to content

Commit f80ac0e

Browse files
groksrcclaude[bot]
andauthored
fix: replace deprecated datetime.utcnow() with timezone-aware alternatives and suppress SQLAlchemy warnings (#211)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent 23ddf19 commit f80ac0e

4 files changed

Lines changed: 19 additions & 17 deletions

File tree

src/basic_memory/mcp/auth_provider.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""OAuth authentication provider for Basic Memory MCP server."""
22

33
import secrets
4-
from datetime import datetime, timedelta
4+
from datetime import datetime, timedelta, timezone
55
from typing import Dict, Optional
66

77
import jwt
@@ -92,7 +92,7 @@ async def authorize(
9292
self.authorization_codes[auth_code] = BasicMemoryAuthorizationCode(
9393
code=auth_code,
9494
scopes=params.scopes or [],
95-
expires_at=(datetime.utcnow() + timedelta(minutes=10)).timestamp(),
95+
expires_at=(datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp(),
9696
client_id=client.client_id,
9797
code_challenge=params.code_challenge,
9898
redirect_uri=params.redirect_uri,
@@ -119,7 +119,7 @@ async def load_authorization_code(
119119

120120
if code and code.client_id == client.client_id:
121121
# Check if expired
122-
if datetime.utcnow().timestamp() > code.expires_at:
122+
if datetime.now(timezone.utc).timestamp() > code.expires_at:
123123
del self.authorization_codes[authorization_code]
124124
return None
125125
return code
@@ -135,7 +135,7 @@ async def exchange_authorization_code(
135135
refresh_token = secrets.token_urlsafe(32)
136136

137137
# Store tokens
138-
expires_at = (datetime.utcnow() + timedelta(hours=1)).timestamp()
138+
expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
139139

140140
self.access_tokens[access_token] = BasicMemoryAccessToken(
141141
token=access_token,
@@ -187,7 +187,7 @@ async def exchange_refresh_token(
187187
new_refresh_token = secrets.token_urlsafe(32)
188188

189189
# Store new tokens
190-
expires_at = (datetime.utcnow() + timedelta(hours=1)).timestamp()
190+
expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
191191

192192
self.access_tokens[new_access_token] = BasicMemoryAccessToken(
193193
token=new_access_token,
@@ -220,7 +220,7 @@ async def load_access_token(self, token: str) -> Optional[BasicMemoryAccessToken
220220

221221
if access_token:
222222
# Check if expired
223-
if access_token.expires_at and datetime.utcnow().timestamp() > access_token.expires_at:
223+
if access_token.expires_at and datetime.now(timezone.utc).timestamp() > access_token.expires_at:
224224
logger.debug("Token found in memory but expired, removing")
225225
del self.access_tokens[token]
226226
return None
@@ -262,8 +262,8 @@ def _generate_access_token(self, client_id: str, scopes: list[str]) -> str:
262262
"iss": self.issuer_url,
263263
"sub": client_id,
264264
"aud": "basic-memory",
265-
"exp": datetime.utcnow() + timedelta(hours=1),
266-
"iat": datetime.utcnow(),
265+
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
266+
"iat": datetime.now(timezone.utc),
267267
"scopes": scopes,
268268
}
269269

src/basic_memory/mcp/supabase_auth_provider.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import os
44
import secrets
55
from dataclasses import dataclass
6-
from datetime import datetime, timedelta
6+
from datetime import datetime, timedelta, timezone
77
from typing import Optional, Dict, Any
88

99
import httpx
@@ -123,7 +123,7 @@ async def authorize(
123123
self.pending_auth_codes[state] = SupabaseAuthorizationCode(
124124
code=state,
125125
scopes=params.scopes or [],
126-
expires_at=(datetime.utcnow() + timedelta(minutes=10)).timestamp(),
126+
expires_at=(datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp(),
127127
client_id=client.client_id,
128128
code_challenge=params.code_challenge,
129129
redirect_uri=params.redirect_uri,
@@ -218,7 +218,7 @@ async def load_authorization_code(
218218

219219
if code and code.client_id == client.client_id:
220220
# Check expiration
221-
if datetime.utcnow().timestamp() > code.expires_at:
221+
if datetime.now(timezone.utc).timestamp() > code.expires_at:
222222
del self.pending_auth_codes[authorization_code]
223223
return None
224224
return code
@@ -453,8 +453,8 @@ def _generate_mcp_token(
453453
"email": email,
454454
"scopes": scopes,
455455
"supabase_token": supabase_access_token[:10] + "...", # Reference only
456-
"exp": datetime.utcnow() + timedelta(hours=1),
457-
"iat": datetime.utcnow(),
456+
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
457+
"iat": datetime.now(timezone.utc),
458458
}
459459

460460
# Use Supabase JWT secret if available

src/basic_memory/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ def setup_logging(
173173
"httpx": logging.WARNING,
174174
# File watching logs
175175
"watchfiles.main": logging.WARNING,
176+
# SQLAlchemy deprecation warnings
177+
"sqlalchemy": logging.WARNING,
176178
}
177179

178180
# Set log levels for noisy loggers

tests/mcp/test_auth_provider.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Tests for OAuth authentication provider."""
22

33
import pytest
4-
from datetime import datetime, timedelta
4+
from datetime import datetime, timedelta, timezone
55
from mcp.server.auth.provider import AuthorizationParams
66
from mcp.shared.auth import OAuthClientInformationFull
77
from pydantic import AnyHttpUrl
@@ -185,7 +185,7 @@ async def test_token_revocation(self, provider, client):
185185
token=token_str,
186186
client_id=client.client_id,
187187
scopes=["read", "write"],
188-
expires_at=int((datetime.utcnow() + timedelta(hours=1)).timestamp()),
188+
expires_at=int((datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()),
189189
)
190190
provider.access_tokens[token_str] = access_token
191191

@@ -226,7 +226,7 @@ async def test_expired_authorization_code(self, provider, client):
226226
provider.authorization_codes[auth_code] = BasicMemoryAuthorizationCode(
227227
code=auth_code,
228228
scopes=["read"],
229-
expires_at=(datetime.utcnow() - timedelta(minutes=1)).timestamp(),
229+
expires_at=(datetime.now(timezone.utc) - timedelta(minutes=1)).timestamp(),
230230
client_id=client.client_id,
231231
code_challenge="challenge",
232232
redirect_uri=AnyHttpUrl("http://localhost:3000/callback"),
@@ -288,7 +288,7 @@ async def test_expired_access_token_in_memory(self, provider):
288288
token=expired_token_str,
289289
client_id="test-client",
290290
scopes=["read"],
291-
expires_at=int((datetime.utcnow() - timedelta(minutes=1)).timestamp()), # Expired
291+
expires_at=int((datetime.now(timezone.utc) - timedelta(minutes=1)).timestamp()), # Expired
292292
)
293293
provider.access_tokens[expired_token_str] = expired_access_token
294294

0 commit comments

Comments
 (0)