add admin route for admin consent

This commit is contained in:
ValueOn AG 2025-11-18 09:40:04 +01:00
parent 3cb6fb2544
commit 38df33fd37
2 changed files with 195 additions and 0 deletions

121
AZURE_AD_CONSENT_LINKS.md Normal file
View file

@ -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)

View file

@ -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"""
<html>
<head><title>Admin Consent Failed</title></head>
<body>
<h1>Admin Consent Failed</h1>
<p>Error: {error}</p>
<p>Description: {error_description or 'No description provided'}</p>
<p>Please contact your administrator.</p>
</body>
</html>
""",
status_code=400
)
if admin_consent == "True" and tenant:
logger.info(f"Admin consent granted for tenant: {tenant}")
return HTMLResponse(
content=f"""
<html>
<head><title>Admin Consent Successful</title></head>
<body>
<h1>Admin Consent Successful</h1>
<p>The application has been granted admin consent for tenant: <strong>{tenant}</strong></p>
<p>All users in this tenant can now use the application without individual consent.</p>
<p>You can close this window.</p>
<script>
setTimeout(() => window.close(), 3000);
</script>
</body>
</html>
"""
)
else:
logger.warning(f"Admin consent callback received unexpected parameters: admin_consent={admin_consent}, tenant={tenant}")
return HTMLResponse(
content=f"""
<html>
<head><title>Admin Consent Status</title></head>
<body>
<h1>Admin Consent Status</h1>
<p>Admin Consent: {admin_consent or 'Not provided'}</p>
<p>Tenant: {tenant or 'Not provided'}</p>
</body>
</html>
"""
)
except Exception as e:
logger.error(f"Error in admin consent callback: {str(e)}", exc_info=True)
return HTMLResponse(
content=f"""
<html>
<head><title>Admin Consent Error</title></head>
<body>
<h1>Error Processing Admin Consent</h1>
<p>{str(e)}</p>
</body>
</html>
""",
status_code=500
)
@router.get("/auth/callback")
async def auth_callback(code: str, state: str, request: Request, response: Response) -> HTMLResponse:
"""Handle Microsoft OAuth callback"""