From 38df33fd37838415327dfc284c6861f55bf4acb9 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 18 Nov 2025 09:40:04 +0100
Subject: [PATCH] add admin route for admin consent
---
AZURE_AD_CONSENT_LINKS.md | 121 ++++++++++++++++++++++++++++
modules/routes/routeSecurityMsft.py | 74 +++++++++++++++++
2 files changed, 195 insertions(+)
create mode 100644 AZURE_AD_CONSENT_LINKS.md
diff --git a/AZURE_AD_CONSENT_LINKS.md b/AZURE_AD_CONSENT_LINKS.md
new file mode 100644
index 00000000..45cf6511
--- /dev/null
+++ b/AZURE_AD_CONSENT_LINKS.md
@@ -0,0 +1,121 @@
+# Azure AD Consent Links
+
+## Konfiguration
+- **Client ID**: `c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`
+- **Tenant ID**: `common` (Multi-Tenant)
+- **Redirect URI (Prod)**: `https://gateway-prod.poweron-center.net/api/msft/auth/callback`
+- **Redirect URI (Int)**: `https://gateway-int.poweron-center.net/api/msft/auth/callback`
+
+## Berechtigungen (Scopes)
+- `Mail.ReadWrite` - E-Mails lesen und schreiben
+- `Mail.Send` - E-Mails senden
+- `Mail.ReadWrite.Shared` - Zugriff auf geteilte Postfächer
+- `User.Read` - Benutzerprofil lesen
+- `Sites.ReadWrite.All` - Alle SharePoint-Standorte lesen und schreiben
+- `Files.ReadWrite.All` - Alle Dateien lesen und schreiben
+
+## Admin Consent Link (für Tenant-Administrator)
+
+**WICHTIG:** Der Admin Consent Endpoint gibt `admin_consent` und `tenant` zurück, nicht `code` und `state`.
+Der bestehende `/auth/callback` Handler erwartet `code` und `state` für den normalen OAuth-Flow.
+
+**Option 1: Admin Consent über Azure Portal (für eigenen Tenant)**
+1. Gehe zu Azure Portal → Azure Active Directory → App registrations
+2. Wähle die App `c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`
+3. Gehe zu "API permissions"
+4. Klicke auf "Grant admin consent for [Tenant Name]"
+
+**Option 1b: App für andere Tenants verfügbar machen**
+
+Um die App für andere Tenants sichtbar zu machen, müssen folgende Schritte durchgeführt werden:
+
+1. **Multi-Tenant Konfiguration prüfen:**
+ - Azure Portal → Azure Active Directory → App registrations
+ - Wähle die App `c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`
+ - Gehe zu "Authentication"
+ - Stelle sicher, dass "Supported account types" auf **"Accounts in any organizational directory and personal Microsoft accounts"** oder **"Accounts in any organizational directory"** gesetzt ist
+
+2. **App für andere Tenants verfügbar machen:**
+
+ **Methode A: Direkter Admin Consent Link (empfohlen)**
+ - Andere Tenant-Administratoren können den Admin Consent Link verwenden:
+ ```
+ https://login.microsoftonline.com/{TENANT_ID}/adminconsent?client_id=c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c&redirect_uri=https://gateway-prod.poweron-center.net/api/msft/adminconsent/callback
+ ```
+ - Ersetze `{TENANT_ID}` durch die Tenant-ID des Ziel-Tenants (oder verwende `common` für Multi-Tenant)
+
+ **Methode B: Manuell über Azure Portal (für andere Tenants)**
+ - Tenant-Administrator des anderen Tenants:
+ 1. Gehe zu Azure Portal → Azure Active Directory → Enterprise applications
+ 2. Klicke auf "+ New application"
+ 3. Wähle "Browse Azure AD Gallery" (optional) oder "Create your own application"
+ 4. Wenn nicht in Gallery: Wähle "Non-gallery application"
+ 5. Gib die Client ID ein: `c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`
+ 6. Oder verwende direkt diesen Link:
+ ```
+ https://portal.azure.com/#blade/Microsoft_AAD_IAM/ManagedAppMenuBlade/Overview/objectId/{CLIENT_ID}
+ ```
+ (Ersetze `{CLIENT_ID}` mit `c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`)
+ 7. Gehe zu "Permissions" → "Grant admin consent"
+
+ **Methode C: App in Azure AD Gallery veröffentlichen (optional)**
+ - Für größere Sichtbarkeit kann die App in der Azure AD App Gallery veröffentlicht werden
+ - Azure Portal → App registrations → App → "Branding & properties"
+ - Kontaktiere Microsoft für Gallery-Veröffentlichung
+
+3. **Wichtig für Multi-Tenant Apps:**
+ - Die Redirect URIs müssen öffentlich erreichbar sein
+ - Die App muss die richtigen Berechtigungen deklarieren
+ - Tenant-Administratoren müssen explizit zustimmen (Admin Consent)
+
+**Option 2: Admin Consent Link (mit Callback-Handler)**
+### Production
+```
+https://login.microsoftonline.com/common/adminconsent?client_id=c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c&redirect_uri=https://gateway-prod.poweron-center.net/api/msft/adminconsent/callback
+```
+
+### Integration
+```
+https://login.microsoftonline.com/common/adminconsent?client_id=c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c&redirect_uri=https://gateway-int.poweron-center.net/api/msft/adminconsent/callback
+```
+
+**Hinweis:** Der `/adminconsent/callback` Endpoint ist implementiert und verarbeitet die `admin_consent` und `tenant` Parameter. Nach erfolgreichem Admin Consent wird eine Bestätigungsseite angezeigt.
+
+## User Consent Link (für einzelne Benutzer)
+
+### Production
+```
+https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c&response_type=code&redirect_uri=https://gateway-prod.poweron-center.net/api/msft/auth/callback&response_mode=query&scope=Mail.ReadWrite Mail.Send Mail.ReadWrite.Shared User.Read Sites.ReadWrite.All Files.ReadWrite.All offline_access openid profile&state=login
+```
+
+### Integration
+```
+https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c&response_type=code&redirect_uri=https://gateway-int.poweron-center.net/api/msft/auth/callback&response_mode=query&scope=Mail.ReadWrite Mail.Send Mail.ReadWrite.Shared User.Read Sites.ReadWrite.All Files.ReadWrite.All offline_access openid profile&state=login
+```
+
+## Hinweise
+
+1. **Admin Consent**: Muss von einem Tenant-Administrator durchgeführt werden, um die App für alle Benutzer im Tenant zu genehmigen
+2. **User Consent**: Jeder Benutzer kann individuell zustimmen (wenn Admin Consent nicht durchgeführt wurde)
+3. **Multi-Tenant**: Da `common` als Tenant verwendet wird, funktioniert die App für alle Azure AD Tenants
+4. **Redirect URI**: Muss exakt in der Azure AD App-Registrierung konfiguriert sein
+
+## Azure Portal Konfiguration
+
+Stelle sicher, dass in der Azure AD App-Registrierung (`c7e7112d-61dc-4f3a-8cd3-08cc4cd7504c`) folgendes konfiguriert ist:
+
+1. **Redirect URIs**:
+ - `https://gateway-prod.poweron-center.net/api/msft/auth/callback`
+ - `https://gateway-int.poweron-center.net/api/msft/auth/callback`
+
+2. **API Permissions** (Delegated):
+ - ✅ Mail.ReadWrite
+ - ✅ Mail.Send
+ - ✅ Mail.ReadWrite.Shared
+ - ✅ User.Read
+ - ✅ Sites.ReadWrite.All
+ - ✅ Files.ReadWrite.All
+
+3. **Supported account types**:
+ - "Accounts in any organizational directory and personal Microsoft accounts" (Multi-tenant)
+
diff --git a/modules/routes/routeSecurityMsft.py b/modules/routes/routeSecurityMsft.py
index b72f4fa3..be7f6476 100644
--- a/modules/routes/routeSecurityMsft.py
+++ b/modules/routes/routeSecurityMsft.py
@@ -132,6 +132,80 @@ async def login(
detail=f"Failed to initiate Microsoft login: {str(e)}"
)
+@router.get("/adminconsent/callback")
+async def adminconsent_callback(
+ admin_consent: Optional[str] = Query(None),
+ tenant: Optional[str] = Query(None),
+ error: Optional[str] = Query(None),
+ error_description: Optional[str] = Query(None),
+ request: Request = None
+) -> HTMLResponse:
+ """Handle Microsoft Admin Consent callback"""
+ try:
+ if error:
+ logger.error(f"Admin consent error: {error} - {error_description}")
+ return HTMLResponse(
+ content=f"""
+
+ Admin Consent Failed
+
+ Admin Consent Failed
+ Error: {error}
+ Description: {error_description or 'No description provided'}
+ Please contact your administrator.
+
+
+ """,
+ status_code=400
+ )
+
+ if admin_consent == "True" and tenant:
+ logger.info(f"Admin consent granted for tenant: {tenant}")
+ return HTMLResponse(
+ content=f"""
+
+ Admin Consent Successful
+
+ Admin Consent Successful
+ The application has been granted admin consent for tenant: {tenant}
+ All users in this tenant can now use the application without individual consent.
+ You can close this window.
+
+
+
+ """
+ )
+ else:
+ logger.warning(f"Admin consent callback received unexpected parameters: admin_consent={admin_consent}, tenant={tenant}")
+ return HTMLResponse(
+ content=f"""
+
+ Admin Consent Status
+
+ Admin Consent Status
+ Admin Consent: {admin_consent or 'Not provided'}
+ Tenant: {tenant or 'Not provided'}
+
+
+ """
+ )
+ except Exception as e:
+ logger.error(f"Error in admin consent callback: {str(e)}", exc_info=True)
+ return HTMLResponse(
+ content=f"""
+
+ Admin Consent Error
+
+ Error Processing Admin Consent
+ {str(e)}
+
+
+ """,
+ status_code=500
+ )
+
@router.get("/auth/callback")
async def auth_callback(code: str, state: str, request: Request, response: Response) -> HTMLResponse:
"""Handle Microsoft OAuth callback"""