Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/auth/src/supabase_auth/_async/gotrue_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,7 @@ async def _recover_and_refresh(self) -> None:
try:
await self._call_refresh_token(refresh_token)
self._network_retries = 0
return
except Exception as e:
if (
isinstance(e, AuthRetryableError)
Expand Down
1 change: 1 addition & 0 deletions src/auth/src/supabase_auth/_sync/gotrue_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ def _recover_and_refresh(self) -> None:
try:
self._call_refresh_token(refresh_token)
self._network_retries = 0
return
except Exception as e:
if (
isinstance(e, AuthRetryableError)
Expand Down
58 changes: 58 additions & 0 deletions src/auth/tests/_async/test_gotrue.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,3 +719,61 @@ async def test_sign_out() -> None:

# Verify that _notify_all_subscribers was still called despite the error
mock_notify.assert_called_once_with("SIGNED_OUT", None)


async def test_initialize_from_storage_keeps_refreshed_session() -> None:
# A stored session whose access token has expired must be refreshed on
# startup, and the refreshed session must then stay persisted.
from httpx import AsyncClient, MockTransport, Request, Response
from supabase_auth import AsyncGoTrueClient
from supabase_auth.types import Session, User

user = {
"id": str(uuid4()),
"aud": "authenticated",
"app_metadata": {},
"user_metadata": {},
"created_at": "2024-01-01T00:00:00Z",
}

def handler(request: Request) -> Response:
assert request.url.path == "/token"
assert request.url.params["grant_type"] == "refresh_token"
return Response(
200,
json={
"access_token": "new-access-token",
"refresh_token": "new-refresh-token",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": round(time.time()) + 3600,
"user": user,
},
)

client = AsyncGoTrueClient(
url="http://auth.test",
http_client=AsyncClient(transport=MockTransport(handler)),
)
expired_session = Session(
access_token="expired-access-token",
refresh_token="old-refresh-token",
token_type="bearer",
expires_in=3600,
expires_at=round(time.time()) - 60,
user=User.model_validate(user),
)
await client._storage.set_item(
client._storage_key, expired_session.model_dump_json()
)
events = []
client.on_auth_state_change(lambda event, _session: events.append(event))

await client.initialize_from_storage()

assert events == ["TOKEN_REFRESHED"]
stored = await client._storage.get_item(client._storage_key)
assert stored is not None
assert Session.model_validate_json(stored).access_token == "new-access-token"
assert client._refresh_token_timer is not None
client._refresh_token_timer.cancel()
56 changes: 56 additions & 0 deletions src/auth/tests/_sync/test_gotrue.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,3 +717,59 @@ def test_sign_out() -> None:

# Verify that _notify_all_subscribers was still called despite the error
mock_notify.assert_called_once_with("SIGNED_OUT", None)


def test_initialize_from_storage_keeps_refreshed_session() -> None:
# A stored session whose access token has expired must be refreshed on
# startup, and the refreshed session must then stay persisted.
from httpx import Client, MockTransport, Request, Response
from supabase_auth import SyncGoTrueClient
from supabase_auth.types import Session, User

user = {
"id": str(uuid4()),
"aud": "authenticated",
"app_metadata": {},
"user_metadata": {},
"created_at": "2024-01-01T00:00:00Z",
}

def handler(request: Request) -> Response:
assert request.url.path == "/token"
assert request.url.params["grant_type"] == "refresh_token"
return Response(
200,
json={
"access_token": "new-access-token",
"refresh_token": "new-refresh-token",
"token_type": "bearer",
"expires_in": 3600,
"expires_at": round(time.time()) + 3600,
"user": user,
},
)

client = SyncGoTrueClient(
url="http://auth.test",
http_client=Client(transport=MockTransport(handler)),
)
expired_session = Session(
access_token="expired-access-token",
refresh_token="old-refresh-token",
token_type="bearer",
expires_in=3600,
expires_at=round(time.time()) - 60,
user=User.model_validate(user),
)
client._storage.set_item(client._storage_key, expired_session.model_dump_json())
events = []
client.on_auth_state_change(lambda event, _session: events.append(event))

client.initialize_from_storage()

assert events == ["TOKEN_REFRESHED"]
stored = client._storage.get_item(client._storage_key)
assert stored is not None
assert Session.model_validate_json(stored).access_token == "new-access-token"
assert client._refresh_token_timer is not None
client._refresh_token_timer.cancel()