Skip to content

Commit 31406ca

Browse files
committed
refac
1 parent 9c64d84 commit 31406ca

2 files changed

Lines changed: 43 additions & 32 deletions

File tree

backend/open_webui/models/oauth_sessions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ async def create_session(
123123
'user_id': user_id,
124124
'provider': provider,
125125
'token': self._encrypt_token(token),
126-
'expires_at': token.get('expires_at'),
126+
'expires_at': token.get('expires_at') or int(time.time() + 3600),
127127
'created_at': current_time,
128128
'updated_at': current_time,
129129
}
@@ -274,7 +274,7 @@ async def update_session_by_id(
274274
.filter_by(id=session_id)
275275
.values(
276276
token=self._encrypt_token(token),
277-
expires_at=token.get('expires_at'),
277+
expires_at=token.get('expires_at') or int(time.time() + 3600),
278278
updated_at=current_time,
279279
)
280280
)

backend/open_webui/utils/oauth.py

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,41 @@ class OAuthClientInformationFull(OAuthClientMetadata):
140140
auth_manager_config.OAUTH_AUDIENCE = OAUTH_AUDIENCE
141141

142142

143+
# Conservative default when the provider omits both expires_in and expires_at.
144+
# Matches the value recommended by Authlib's compliance_fix documentation.
145+
DEFAULT_TOKEN_EXPIRY_SECONDS = 3600
146+
147+
148+
def _normalize_token_expiry(token: dict) -> dict:
149+
"""Ensure a token dict always has a numeric ``expires_at``.
150+
151+
Resolution order:
152+
1. If *expires_at* is already present and non-None, trust it.
153+
2. Else if *expires_in* is present and non-None, compute *expires_at*.
154+
3. Otherwise fall back to ``DEFAULT_TOKEN_EXPIRY_SECONDS`` and log a
155+
warning so operators can identify providers that omit expiration.
156+
157+
Also stamps *issued_at* for auditing.
158+
"""
159+
token['issued_at'] = datetime.now().timestamp()
160+
161+
if token.get('expires_at') is not None:
162+
token['expires_at'] = int(token['expires_at'])
163+
return token
164+
165+
if token.get('expires_in') is not None:
166+
token['expires_at'] = int(datetime.now().timestamp() + token['expires_in'])
167+
return token
168+
169+
# Neither field present — conservative fallback
170+
log.warning(
171+
"OAuth token response missing both 'expires_in' and 'expires_at'; "
172+
f"defaulting to {DEFAULT_TOKEN_EXPIRY_SECONDS}s from now"
173+
)
174+
token['expires_at'] = int(datetime.now().timestamp() + DEFAULT_TOKEN_EXPIRY_SECONDS)
175+
return token
176+
177+
143178
FERNET = None
144179

145180
if len(OAUTH_CLIENT_INFO_ENCRYPTION_KEY) != 44:
@@ -712,7 +747,7 @@ async def get_oauth_token(self, user_id: str, client_id: str, force_refresh: boo
712747
log.warning(f'No OAuth session found for user {user_id}, client_id {client_id}')
713748
return None
714749

715-
if force_refresh or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at):
750+
if force_refresh or session.expires_at is None or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at):
716751
log.debug(f'Token refresh needed for user {user_id}, client_id {session.provider}')
717752
refreshed_token = await self._refresh_token(session)
718753
if refreshed_token:
@@ -823,14 +858,7 @@ async def _perform_token_refresh(self, session) -> dict:
823858
if 'refresh_token' not in new_token_data:
824859
new_token_data['refresh_token'] = token_data['refresh_token']
825860

826-
# Add timestamp for tracking
827-
new_token_data['issued_at'] = datetime.now().timestamp()
828-
829-
# Calculate expires_at if we have expires_in
830-
if 'expires_in' in new_token_data and 'expires_at' not in new_token_data:
831-
new_token_data['expires_at'] = int(
832-
datetime.now().timestamp() + new_token_data['expires_in']
833-
)
861+
_normalize_token_expiry(new_token_data)
834862

835863
log.debug(f'Token refresh successful for client_id {client_id}')
836864
return new_token_data
@@ -883,12 +911,7 @@ async def handle_callback(self, request, client_id: str, user_id: str, response)
883911

884912
if token:
885913
try:
886-
# Add timestamp for tracking
887-
token['issued_at'] = datetime.now().timestamp()
888-
889-
# Calculate expires_at if we have expires_in
890-
if 'expires_in' in token and 'expires_at' not in token:
891-
token['expires_at'] = datetime.now().timestamp() + token['expires_in']
914+
_normalize_token_expiry(token)
892915

893916
# Clean up any existing sessions for this user/client_id first
894917
sessions = await OAuthSessions.get_sessions_by_user_id(user_id)
@@ -975,7 +998,7 @@ async def get_oauth_token(self, user_id: str, session_id: str, force_refresh: bo
975998
log.warning(f'No OAuth session found for user {user_id}, session {session_id}')
976999
return None
9771000

978-
if force_refresh or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at):
1001+
if force_refresh or session.expires_at is None or datetime.now() + timedelta(minutes=5) >= datetime.fromtimestamp(session.expires_at):
9791002
log.debug(f'Token refresh needed for user {user_id}, provider {session.provider}')
9801003
refreshed_token = await self._refresh_token(session)
9811004
if refreshed_token:
@@ -1089,14 +1112,7 @@ async def _perform_token_refresh(self, session) -> dict:
10891112
if 'refresh_token' not in new_token_data:
10901113
new_token_data['refresh_token'] = token_data['refresh_token']
10911114

1092-
# Add timestamp for tracking
1093-
new_token_data['issued_at'] = datetime.now().timestamp()
1094-
1095-
# Calculate expires_at if we have expires_in
1096-
if 'expires_in' in new_token_data and 'expires_at' not in new_token_data:
1097-
new_token_data['expires_at'] = int(
1098-
datetime.now().timestamp() + new_token_data['expires_in']
1099-
)
1115+
_normalize_token_expiry(new_token_data)
11001116

11011117
log.debug(f'Token refresh successful for provider {provider}')
11021118
return new_token_data
@@ -1694,12 +1710,7 @@ async def handle_callback(self, request, provider, response, db=None):
16941710
)
16951711

16961712
try:
1697-
# Add timestamp for tracking
1698-
token['issued_at'] = datetime.now().timestamp()
1699-
1700-
# Calculate expires_at if we have expires_in
1701-
if 'expires_in' in token and 'expires_at' not in token:
1702-
token['expires_at'] = datetime.now().timestamp() + token['expires_in']
1713+
_normalize_token_expiry(token)
17031714

17041715
# Enforce max concurrent sessions per user/provider to prevent
17051716
# unbounded growth while allowing multi-device usage

0 commit comments

Comments
 (0)