From 95dbbb836068186a416857f26efeb13b68c20326 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 13 Jan 2026 21:38:03 +0100
Subject: [PATCH] fixed msft access
---
modules/routes/routeSecurityMsft.py | 35 +++++++++++++++++++++++++----
1 file changed, 31 insertions(+), 4 deletions(-)
diff --git a/modules/routes/routeSecurityMsft.py b/modules/routes/routeSecurityMsft.py
index ac9b5af3..4b2964db 100644
--- a/modules/routes/routeSecurityMsft.py
+++ b/modules/routes/routeSecurityMsft.py
@@ -53,12 +53,15 @@ if not REDIRECT_URI:
if CLIENT_SECRET and CLIENT_SECRET.startswith(("PROD_ENC:", "INT_ENC:", "DEV_ENC:")):
logger.warning("Service_MSFT_CLIENT_SECRET appears to be encrypted - ensure decryption is working")
SCOPES = [
- "Mail.ReadWrite", # Read and write mail (user's mailbox only)
- "Mail.Send", # Send mail (user's mailbox only)
"User.Read", # Read user profile
- "Sites.ReadWrite", # Read and write user's SharePoint sites (not org-wide)
- "Files.ReadWrite" # Read and write user's files (not org-wide)
+ "Mail.ReadWrite", # Read and write mail
+ "Mail.Send", # Send mail
+ "Files.ReadWrite.All", # Read and write files (SharePoint/OneDrive)
+ "Sites.ReadWrite.All" # Read and write SharePoint sites
]
+# NOTE: Sites.ReadWrite.All and Files.ReadWrite.All require admin consent.
+# An admin must grant consent ONCE at: /api/msft/adminconsent
+# After that, all users can connect without individual admin approval.
@router.get("/login")
@limiter.limit("5/minute")
@@ -133,6 +136,30 @@ async def login(
detail=f"Failed to initiate Microsoft login: {str(e)}"
)
+@router.get("/adminconsent")
+@limiter.limit("5/minute")
+async def adminconsent(request: Request) -> RedirectResponse:
+ """Initiate Microsoft Admin Consent flow.
+
+ An Azure AD admin must visit this URL once to grant consent for the entire tenant.
+ After admin consent, all users can connect without individual approval.
+ """
+ try:
+ adminConsentRedirectUri = REDIRECT_URI.replace("/auth/callback", "/adminconsent/callback")
+ adminConsentUrl = (
+ f"{AUTHORITY}/adminconsent"
+ f"?client_id={CLIENT_ID}"
+ f"&redirect_uri={adminConsentRedirectUri}"
+ )
+ logger.info(f"Redirecting to admin consent URL for tenant: {TENANT_ID}")
+ return RedirectResponse(adminConsentUrl)
+ except Exception as e:
+ logger.error(f"Error generating admin consent URL: {str(e)}")
+ raise HTTPException(
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+ detail=f"Failed to generate admin consent URL: {str(e)}"
+ )
+
@router.get("/adminconsent/callback")
async def adminconsent_callback(
admin_consent: Optional[str] = Query(None),