fixed ext logins msft and google
This commit is contained in:
parent
d7e220682d
commit
3c63488377
2 changed files with 58 additions and 15 deletions
|
|
@ -14,6 +14,7 @@ from modules.shared.configuration import APP_CONFIG
|
||||||
from modules.interfaces.interfaceDbAppObjects import getInterface, getRootInterface
|
from modules.interfaces.interfaceDbAppObjects import getInterface, getRootInterface
|
||||||
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
||||||
from modules.security.auth import getCurrentUser, limiter
|
from modules.security.auth import getCurrentUser, limiter
|
||||||
|
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
||||||
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
|
|
@ -202,7 +203,7 @@ async def login(
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/auth/callback")
|
@router.get("/auth/callback")
|
||||||
async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse:
|
async def auth_callback(code: str, state: str, request: Request, response: Response) -> HTMLResponse:
|
||||||
"""Handle Google OAuth callback"""
|
"""Handle Google OAuth callback"""
|
||||||
try:
|
try:
|
||||||
# Import Token at function level to avoid scoping issues
|
# Import Token at function level to avoid scoping issues
|
||||||
|
|
@ -337,34 +338,47 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create JWT token data (like Microsoft does)
|
# Create JWT token data (like Microsoft does)
|
||||||
from modules.security.jwtService import createAccessToken
|
|
||||||
jwt_token_data = {
|
jwt_token_data = {
|
||||||
"sub": user.username,
|
"sub": user.username,
|
||||||
"mandateId": str(user.mandateId),
|
"mandateId": str(user.mandateId),
|
||||||
"userId": str(user.id),
|
"userId": str(user.id),
|
||||||
"authenticationAuthority": AuthAuthority.GOOGLE
|
"authenticationAuthority": AuthAuthority.GOOGLE.value
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create JWT access token
|
# Create JWT access token
|
||||||
jwt_token, jwt_expires_at = createAccessToken(jwt_token_data)
|
jwt_token, jwt_expires_at = createAccessToken(jwt_token_data)
|
||||||
|
|
||||||
|
# Create refresh token
|
||||||
|
refresh_token, _refresh_expires = createRefreshToken(jwt_token_data)
|
||||||
|
|
||||||
|
# Decode token to get jti for database record
|
||||||
|
from jose import jwt
|
||||||
|
from modules.security.auth import SECRET_KEY, ALGORITHM
|
||||||
|
payload = jwt.decode(jwt_token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
jti = payload.get("jti")
|
||||||
|
|
||||||
# Create JWT token
|
# Create JWT token with matching id
|
||||||
token = Token(
|
token = Token(
|
||||||
|
id=jti,
|
||||||
userId=user.id, # Use local user's ID
|
userId=user.id, # Use local user's ID
|
||||||
authority=AuthAuthority.GOOGLE,
|
authority=AuthAuthority.GOOGLE,
|
||||||
tokenAccess=jwt_token, # Use JWT token instead of Google access token
|
tokenAccess=jwt_token, # Use JWT token instead of Google access token
|
||||||
tokenRefresh=token_response.get("refresh_token", ""),
|
tokenRefresh=token_response.get("refresh_token", ""),
|
||||||
tokenType="bearer",
|
tokenType="bearer",
|
||||||
expiresAt=jwt_expires_at.timestamp(),
|
expiresAt=jwt_expires_at.timestamp(),
|
||||||
createdAt=getUtcTimestamp()
|
createdAt=getUtcTimestamp(),
|
||||||
|
mandateId=str(user.mandateId)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save access token (no connectionId)
|
# Save access token (no connectionId)
|
||||||
appInterface = getInterface(user)
|
appInterface = getInterface(user)
|
||||||
appInterface.saveAccessToken(token)
|
appInterface.saveAccessToken(token)
|
||||||
|
|
||||||
# Return success page with token data
|
# Convert token to dict and ensure proper timestamp handling
|
||||||
return HTMLResponse(
|
token_dict = token.model_dump()
|
||||||
|
|
||||||
|
# Create HTML response
|
||||||
|
html_response = HTMLResponse(
|
||||||
content=f"""
|
content=f"""
|
||||||
<html>
|
<html>
|
||||||
<head><title>Authentication Successful</title></head>
|
<head><title>Authentication Successful</title></head>
|
||||||
|
|
@ -374,7 +388,7 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
window.opener.postMessage({{
|
window.opener.postMessage({{
|
||||||
type: 'google_auth_success',
|
type: 'google_auth_success',
|
||||||
access_token: {json.dumps(token_response["access_token"])},
|
access_token: {json.dumps(token_response["access_token"])},
|
||||||
token_data: {json.dumps(token.model_dump())}
|
token_data: {json.dumps(token_dict)}
|
||||||
}}, '*');
|
}}, '*');
|
||||||
}}
|
}}
|
||||||
setTimeout(() => window.close(), 1000);
|
setTimeout(() => window.close(), 1000);
|
||||||
|
|
@ -383,6 +397,15 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Set access token as httpOnly cookie (like local login)
|
||||||
|
# HTMLResponse inherits from Response, so we can set cookies directly on it
|
||||||
|
setAccessTokenCookie(html_response, jwt_token, expiresDelta=None)
|
||||||
|
|
||||||
|
# Set refresh token as httpOnly cookie
|
||||||
|
setRefreshTokenCookie(html_response, refresh_token)
|
||||||
|
|
||||||
|
return html_response
|
||||||
else:
|
else:
|
||||||
# Handle connection flow
|
# Handle connection flow
|
||||||
if not connection_id or not user_id:
|
if not connection_id or not user_id:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ from modules.interfaces.interfaceDbAppObjects import getInterface, getRootInterf
|
||||||
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
from modules.datamodels.datamodelUam import AuthAuthority, User, ConnectionStatus, UserConnection
|
||||||
from modules.datamodels.datamodelSecurity import Token
|
from modules.datamodels.datamodelSecurity import Token
|
||||||
from modules.security.auth import getCurrentUser, limiter
|
from modules.security.auth import getCurrentUser, limiter
|
||||||
from modules.security.jwtService import createAccessToken
|
from modules.security.jwtService import createAccessToken, setAccessTokenCookie, createRefreshToken, setRefreshTokenCookie
|
||||||
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
from modules.shared.timezoneUtils import createExpirationTimestamp, getUtcTimestamp
|
||||||
|
|
||||||
# Configure logger
|
# Configure logger
|
||||||
|
|
@ -123,7 +123,7 @@ async def login(
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/auth/callback")
|
@router.get("/auth/callback")
|
||||||
async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse:
|
async def auth_callback(code: str, state: str, request: Request, response: Response) -> HTMLResponse:
|
||||||
"""Handle Microsoft OAuth callback"""
|
"""Handle Microsoft OAuth callback"""
|
||||||
try:
|
try:
|
||||||
# Parse state
|
# Parse state
|
||||||
|
|
@ -212,20 +212,31 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
"sub": user.username,
|
"sub": user.username,
|
||||||
"mandateId": str(user.mandateId),
|
"mandateId": str(user.mandateId),
|
||||||
"userId": str(user.id),
|
"userId": str(user.id),
|
||||||
"authenticationAuthority": AuthAuthority.MSFT
|
"authenticationAuthority": AuthAuthority.MSFT.value
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create JWT access token
|
# Create JWT access token
|
||||||
jwt_token, jwt_expires_at = createAccessToken(jwt_token_data)
|
jwt_token, jwt_expires_at = createAccessToken(jwt_token_data)
|
||||||
|
|
||||||
|
# Create refresh token
|
||||||
|
refresh_token, _refresh_expires = createRefreshToken(jwt_token_data)
|
||||||
|
|
||||||
|
# Decode token to get jti for database record
|
||||||
|
from jose import jwt
|
||||||
|
from modules.security.auth import SECRET_KEY, ALGORITHM
|
||||||
|
payload = jwt.decode(jwt_token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
jti = payload.get("jti")
|
||||||
|
|
||||||
# Create JWT token
|
# Create JWT token with matching id
|
||||||
jwt_token_obj = Token(
|
jwt_token_obj = Token(
|
||||||
|
id=jti,
|
||||||
userId=user.id,
|
userId=user.id,
|
||||||
authority=AuthAuthority.MSFT,
|
authority=AuthAuthority.MSFT,
|
||||||
tokenAccess=jwt_token,
|
tokenAccess=jwt_token,
|
||||||
tokenType="bearer",
|
tokenType="bearer",
|
||||||
expiresAt=jwt_expires_at.timestamp(),
|
expiresAt=jwt_expires_at.timestamp(),
|
||||||
createdAt=getUtcTimestamp()
|
createdAt=getUtcTimestamp(),
|
||||||
|
mandateId=str(user.mandateId)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save JWT access token
|
# Save JWT access token
|
||||||
|
|
@ -236,8 +247,8 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
# Remove datetime conversion logic - models now handle this automatically
|
# Remove datetime conversion logic - models now handle this automatically
|
||||||
# The token model already returns float timestamps
|
# The token model already returns float timestamps
|
||||||
|
|
||||||
# Return success page with token data
|
# Create HTML response
|
||||||
return HTMLResponse(
|
html_response = HTMLResponse(
|
||||||
content=f"""
|
content=f"""
|
||||||
<html>
|
<html>
|
||||||
<head><title>Authentication Successful</title></head>
|
<head><title>Authentication Successful</title></head>
|
||||||
|
|
@ -255,6 +266,15 @@ async def auth_callback(code: str, state: str, request: Request) -> HTMLResponse
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Set access token as httpOnly cookie (like local login)
|
||||||
|
# HTMLResponse inherits from Response, so we can set cookies directly on it
|
||||||
|
setAccessTokenCookie(html_response, jwt_token, expiresDelta=None)
|
||||||
|
|
||||||
|
# Set refresh token as httpOnly cookie
|
||||||
|
setRefreshTokenCookie(html_response, refresh_token)
|
||||||
|
|
||||||
|
return html_response
|
||||||
else:
|
else:
|
||||||
# Handle connection flow
|
# Handle connection flow
|
||||||
if not connection_id or not user_id:
|
if not connection_id or not user_id:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue