Konfiguration OnBehalfOf Grant
On Behalf Of Grant
Der On-Behalf-Of-Grant (OBO) ist ein von Microsoft zwar offen spezifizierter, aber proprietärer Flow. Der OBO-Flow wird von Microsoft in Szenarien verwendet, bei dem eine Web-API, die eine andere Identität als ihre eigene verwendet, eine weitere Web-API aufrufen soll. Die Absicht besteht darin, die Identität und Berechtigungen eines Benutzers durch die gesamte Anforderungskette zu leiten.
Im Kontext von OAuth, kann dies als eine Variante der Delegierung betrachtet werden. Der OBO-Flow stellt eine abgespeckte, einfachere Alternative zum OAuth 2.0 Token Exchange dar.
Der OBO-Flow wird durch den CMI STS implementiert um folgenden Anwendungsfall abzudecken:
- Eine Web-API authentifiziert Benutzer mit der Hilfe von Azure AD. Die Web-API erhält ein Access-Token von Azure AD. Die Web-API kann auf Ressourcen zugreifen, die dieses Access-Token akzeptieren.
- Die Web-API soll Daten bei einer weiteren Web-API ablegen oder abrufen, dem CMI Server. Der CMI Server akzeptiert jedoch nur Access Tokens die vom CMI STS ausgestellt wurden.
- Der Web-API kann mit Hilfe des OBO-Flows beim CMI STS das Access Token von Azure AD durch ein Access Token des CMI STS austauschen lassen um den CMI Server aufzurufen.
Anforderungen
Damit der CMI STS den Austausch akzeptiert, müssen folgende Bedingungen erfüllt werden:
- Das fremde Access Token muss von einem Identity Provider ausgestellt, der dem CMI STS bereits durch eine Föderierung bekannt ist: Externe Identity Provider (IDP). Mandanten für die kein IDP eingerichtet ist, können den OBO-Flow nicht nutzen.
- Es werden nur OIDC-IDPs unterstützt.
Hinweis: Grundsätzlich kann jeder OIDC-IDP verwendet werden. Das Anwendungsfallszenario sieht jedoch Azure AD als IDP vor. Andere IDPs wurden nicht getestet. Das Kapitel Konfiguration: Azure AD App Registrierung dokumentiert die erforderlichen Parameter, die bei Azure AD konfiguriert werden müssen.
- Der Issuer-Claim des fremden Access Token muss mit dem Issuer übereinstimmen, den der föderierte IDP in seinem Discovery-Dokument deklariert.
- SAML assertions werden nicht unterstützt.
Konfiguration: CMI STS
Ein regulärer Duende-Client kann konfiguriert werden. Siehe auch im Kapitel "Clients". Die folgende Konfiguration dient als Referenzkonfiguration.
{
//...
"tenants": {
"mandant": {
"Clients": [
{
"ClientId": "teamsApps",
"AllowedGrantTypes": ["urn:ietf:params:oauth:grant-type:jwt-bearer"],
"AllowedScopes": [
"metatool"
],
"ClientSecrets": [
{
"value": "..."
}
],
"Properties": {
"OboAudience": "00000000-0000-0000-0000-000000000000",
"OboSkipAudienceCheck": false,
"OboValidationClockSkewSeconds": 600,
"OboClaimValidation_scp": "access_as_user", // Azure AD Spezifisch
"OboClaimValidation_azp": "00000000-0000-0000-0000-000000000000" // Azure AD Spezifisch
}
]
//...
}
}
}
ClientId
(erforderlich): Eine beliebige Client ID.AllowedGrantTypes
(erforderlich): Der Grant Typeurn:ietf:params:oauth:grant-type:jwt-bearer
aktiviert den OBO-Flow.AllowedScopes
(erforderlich): Scopes die im Access Token enthalten sein dürfen. Der CMI Server erwartet den Scopemetatool
.ClientSecrets
(erforderlich): Ein oder mehrere Client Secrets als SHA-512 Hash, siehe Clients.Properties
(erforderlich): Zusätzliche Client-Optionen, die nicht Teil des Duende Identity Server Frameworks sind. Hier werden OBO-spezifische Optionen angegeben.OboAudience
(erforderlich, wennOboSkipAudienceCheck=false
): Im fremden Access Token muss der Audience-Claim dem hier festgelegten Wert entsprechen. Es werden nur Access Tokens für eine bestimmte Audience akzeptiert. Welchen Wert der Identity Provider für die Audience verwendet, muss mit diesem abgestimmt werden. Im Falle von Azure AD wird die Application (client) ID der App-Registrierung verwendet.OboSkipAudienceCheck
(optional, Standardwert:false
): Deaktiviert den Audience-Check. Diese Option sollte nur für Testzwecke aktiviert werden.OboValidationClockSkewSeconds
(optional, Standardwert:600
): Maximaler Uhrendrift in Sekunden, die während der Ablaufvalidierung des fremden Access Tokens akzeptiert wird.OboClaimValidation_*
(optional): Der Asterisk stellt eine Wildcard dar, die durch einen Claim-Type ersetzt wird. Wenn bestimmte Claims mit einem bestimmten Wert im fremden Access Token enthalten sein müssen, kann inProperties
ein Schlüssel-Wert-Paar angelegt werden. Es können mehrere Claims angegeben werden. Alle Einschränkungen müssen erfüllt werden, die Validatoren sind Und-Verknüpft.
json { "Properties": { "OboClaimValidation_{ClaimType}": "{ClaimValue}" } }
*ClaimType
: Claim, dessen Existenz erwartet wird. *ClaimValue
: Claim-Wert, der erwartet wird.
Die Claim-Validierungen dienen dazu, die akzeptierten fremden Access Tokens geeignet einzuschränken, so dass nur die gewünschten Fremd-Anwendungen ein Access Token vom CMI STS erhalten. Es folgen Empfehlungen im Kontext von Azure AD. Azure AD spezifische Claim-Validierungen sind bei anderen IDPs möglicherweise nicht Eins-zu-Eins umsetzbar. Die OboClaimValidation_*
-Validierungen sind in Abhängigkeit vom IDP zu wählen.
Wertung | Claim Type | Zu prüfender Claim Wert |
---|---|---|
Empfohlen | scp | access_as_user |
Optional | azp | ID der Fremd-App |
Optional | tid | Azure Tenant ID |
scp Scope Claim Mit dem SCP (Scope) Claim kann Azure AD mitteilen, dass das Access Token für eine Delegierung verwendet werden darf.
azp Authorized Party Claim Wenn vorhanden, gibt dieser Claim an, für welche App das Access Token ausgestellt wurde. Verwenden unterschiedliche Anwendungen die selbe Azure AD App-Registrierung, kann dieser Claim helfen, die Delegierung auf eine bestimmte Anwendung einzuschränken.
tid Tenant ID Claim Der TID Claim beinhaltet die Mandanten-ID des Azure AD IDP. Da ein Access Token gegen die konfigurierten föderierten IDPs eines Mandanten validiert wird, werden auch ohne diesen Validator nicht beliebige Azure AD Mandanten akzeptiert. Die TID kann jedoch dazu genutzt werden, die Delegierung auf einen bestimmten Azure AD Mandanten einzuschränken, sollten mehrere externe IDP für einen CMI STS Mandanten konfiguriert sein.
Konfiguration: Azure AD App-Registrierung
Der folgende Abschnitt beschreibt eine Konfiguration für eine App-Registrierung, die pro Azure Tenant angelegt. Varianten mit globalen Optionen wurden noch nicht untersucht.
Es wird davon ausgegangen, das der CMI Mandant mit dem Azure AD des Kunden bereits föderiert wurde und die App-Registrierung gemäss der Anleitung Konfiguration von Azure AD eingerichtet wurde und die Benutzenden sich am CMI erfolgreich via Azure AD anmelden können.
An der App-Registrierung werden für den OBO-Flow folgende Ergänzungen benötigt:
- Der Scope
access_as_user
muss durch die App-Registrierung ausgestellt werden. - Zur Vermeidung einer Invalid Issuer Problematik, muss die Version von
accessTokenAcceptedVersion
auf2
festgelegt sein.
Das folgende Beispiel zeigt, wie der Scope eingerichtet werden kann. Ausgenommen vom Scope-Namen, handelt es sich bei allen Eingaben um einen Vorschlag und keine Vorgabe. Die Eingaben sind abhängig vom Anwendungsfall zu tätigen.
- Scope name: access_as_user
- Who can consent?: Admins and users
- Admin consent display name: On Behalf Of Token Exchange
- Admin consent description: The scope permits the CMI STS to execute the On-Behalf-Of-Grant to exchange the app registration access token with a CMI STS access token. That enables the app to interact with the CMI Server.
- User content display name: CMI Zugriffsdelegierung
- User consent description: Erlaubt der App in Ihrem Namen Daten an CMI zu senden und abzurufen.
Hinweis: Sollte die App-Registrierung Authorized client applications hinterlegt haben, muss der erstellte Scope anschliessend allen Client-Anwendungen zugeordnet werden, die den OBO-Flow ausführen dürfen.
Im Manifest der App-Registrierung kann bei accessTokenAcceptedVersion
der Wert von null
auf 2
geändert werden.
Der Audience- und die Claim-Checks beim CMI STS können anschliessend wie gefolgt konfiguriert werden:
OboAudience
: Application (client) ID der App-Registrierung.OboClaimValidation_scp
: access_as_userOboClaimValidation_tid
(Optional): Directory (tenant) ID der App-Registrierung.OboClaimValidation_azp
(Optional): Abhängig von der verwendeten ID der Client-Anwendungen, die diese App-Registrierung nutzen. Situativ auch unter Expose an API / Authorized client applications / Client Id ablesbar.
Grant Request
Der OBO-Flow kann über den Token-Endpunkt ausgeführt werden, der im Discovery Dokument des CMI STS publiziert ist ({tenant id}/identity/.well-known/openid-configuration).
Der Request muss wie gefolgt erfolgen:
(Zeilenumbrüche und fehlende URL-Codierung dienen nur der besseren Lesbarkeit)
POST /{tenant id}/identity/connect/token HTTP/1.1
Host: {CMI STS host}
Content-Type: application/x-www-form-urlencoded
Content-Length: ...
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&client_id={client id}
&client_secret={client secret}&
&assertion={foreign idp access token}&
&scope=metatool&
&requested_token_use=on_behalf_of
Der Wert für den Parameter assertion
enthält das Access-Token des fremden IDP. Es werden ausschliesslich Bearer-JWT-Token unterstützt. SAML-Assertions werden nicht unterstützt. Das Token muss ohne Bearer
-Preamble übermittelt werden.
Das Client Secret wird gemäss Kapitel Client-Secret Validierung übermittelt.
Grant Response
Eine Antwort auf den Grant-Request sieht wie gefolgt aus:
- Ein Status-Code 2XX zeigt einen erfolgreichen Token-Austausch an.
- Ein Status-Code 4XX zeigt an:
- Die Request-Parameter werden nicht akzeptiert.
- Der OBO-Flow wurde für diesen Mandanten nicht oder unvollständig eingerichtet.
- Eine Zuordnung der externen Identität zu einem CMI Benutzer war nicht möglich.
- Ein Status-Code 5XX zeigt unerwartete Fehler an.
Der Response-Body enthält ein JSON-Dokument, dass den OAuth 2.0 Standards entspricht. JSON-Eigenschaften die mit "optional" markiert sind, können, müssen aber nicht vom CMI STS ausgegeben werden.
Die folgenden Nicht-Standard-Eigenschaften sollten, wenn vorhanden, für Log-, Tracing- und Support-Zwecke gespeichert oder den Benutzenden angezeigt werden.
error_codes
(optional): String-Array mit Fehlercodes, die dem Benutzer angezeigt werden können. OBO-spezifische Fehlercodes liegen im Bereich STS9XX.correlation_id
(optional): Eine Correlation-ID, die auch in den CMI Server Logs zu finden ist.timestamp
(optional): Ausführungszeitpunkt des OBO-Flows.trace_id
(optional): Trace-ID der CMI STS Logeinträge, die durch den OBO-Flow erzeugt wurden.
Erfolgsantwort
Die Erfolgsantwort entspricht 5.1. Successful Response aus RFC 6749.
{
"access_token": "Bearer ey...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "metatool",
"error_codes": [
"STS400",
"STS900"
],
"correlation_id": "00000000-0000-0000-0000-000000000000",
"timestamp": "2000-01-31T24:00:00",
"trace_id": "0000000000000:00000000"
}
access_token
(erforderlich): Das ausgestellte Access-Token für den CMI Server.token_type
(erforderlich): Ein Bearer-Token wird ausgestellt.expires_in
(optional): Laufzeit des Access-Tokens in Sekunden.scope
(erforderlich, wenn abweichend vom Request, sonst optional): Ausgestellte Scopes im Access-Token.
Ein Refresh-Token wird nicht ausgestellt.
Fehlerantwort
Die Fehlerantwort entspricht 5.2. Error Response aus RFC 6749.
{
"error": "...",
"error_description": "...",
"error_codes": [
"STS4XX",
"STS9XX"
],
"correlation_id": "00000000-0000-0000-0000-000000000000",
"timestamp": "2000-01-31T24:00:00",
"trace_id": "0000000000000:00000000"
}
error
(erforderlich): Einzelner ASCII-String, der den Fehlerzustand beschreibt (invalid_request
,invalid_client
,invalid_grant
,unauthorized_client
,unsupported_grant_type
).error_description
(optional): Lesbare Fehlermeldung für den Client-Entwickler.
BeispielRequests
OBO
Request
curl --location 'https://sts.cloud.ch/oawmandant/identity/connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \
--data-urlencode 'scope=templateManager' \
--data-urlencode 'client_id=officeatwork' \
--data-urlencode 'client_secret=cmi123' \
--data-urlencode 'requested_token_use=on_behalf_of' \
--data-urlencode 'assertion=eyJ0eXAiOiJKV1QWkdldyJ****' # JWT Token aus der AzureAnmeldung
Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IkExN0****",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "templateManager",
"error_codes": [
"STS400",
"STS900"
],
"correlation_id": "3fa1358c-4781-454d-ac81-c92b29eefafe",
"timestamp": "2023-02-22T10:31:39",
"trace_id": "0HMOKNUJKSMAQ:00000002"
}