Nach einigen eher philosophischen Ausflügen möchte ich in diesem Blog tiefer in die Technik von OIDC und OAuth einsteigen. Den Zweck und die grobe Technik hatte ich bereits in mehreren Artikeln beschrieben. Jetzt geht es ein wenig ins „Eingemachte“.

Grundsätzlich ist Open ID Connect eine Erweiterung des OAuth Autorisierungsrequests, um die Authentifizierung des Benutzers und Übermittlung dessen Daten an den Client. Dazu sendet ein OIDC Client einfach den SCOPE „openid“ mit dem OAuth Request. Der IdP weiß dann, dass es sich um einen OIDC Request handelt und reagiert entsprechend mit dem OIDC JWT als Antwort. Zusätzlich wird eine API, der User Info Endpoint eingeführt, über den ein berechtigter Client weitere Informationen über den User abfragen kann.

Blog1

Greifen wir das Beispiel der App aus meinem vorangegangenen Blog auf. Die Bilder sollen ausgetauscht werden. Die App sendet also einen Authentifizierungs- Request auf der Basis des OAuth-Grant-Type-Autorisierungscode wie folgt:

HTTP/1.1 302 Found
Location: https://login.tta.com/oauth/authservice?
response_type=code
&scope=openid
&client_id=SDBNJSPLUZTREXHZJA
&state=30096519678904076037
&redirect_uri=https%3A%2F%2Fiapp.tta.com%2Flogin_cb

Dieser Request leitet den Browser des Benutzers mittels HTTP Redirect um und sendet einen Authentifizierungs-Request an den Autorisierungsserver. Wichtig ist der Parameter „&scope=openid“. Ansonsten handelt es sich um einen normalen OAuth Autorisierungs Request. Wie der Server den Benutzer authentifiziert, ist nicht spezifiziert und liegt in der Entscheidung des OpenID Provider. Das OIDC Protokoll bietet dem Client allerdings eine Reihe von Möglichkeiten darauf Einfluss zu nehmen. Details dazu können im OIDC Standard nachgelesen werden (https://openid.net/developers/specs/).

Die Anmeldung und Bereitstellung der Identitätsdaten sind damit von der Anwendung komplett entkoppelt. Das bietet den entscheidenden Vorteil, dass die Anwendung sich nicht um diesen Vorgang kümmern muss und der IdP (OP) entsprechende unabhängig von der Anwendung unterschiedliche Methoden zur Verfügung stellen sowie durchsetzen kann.

Der OP stellt nach erfolgreicher Anmeldung einen Auth_Code aus und sendet diesen über den User Agent per HTTP Redirect an die App.

HTTP/1.1 302 Found
Location: https://app.tta.com/login_cb?
code=702123256742394222480946
&state=30096519678904076037

Die App geht nun auf den Authorization Server zu und erhält gegen Vorlage des codes das ID-Token und Access Token. Je nach Flow kann auch ein Refresh Token zusätzlich ausgestellt werden.

POST /oauth/token HTTP/1.1
Host: accounts.tta.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZWx1c3VhcmlvOMlsYWNsYXZ5grant_type=authorization_code&
code=702123256742394222480946&
redirect_uri=https%3A%2F%2Fapp.tta.com%2Flogin_cb

In der nun folgenden Response des Autorisierungs Servers erscheint zum ersten Mal eine OIDC Besonderheit, das ID-Token

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
“access_token”: “pNeZGWWSxpSakZwWKQsqTjLwJpkJIIy”,
“token_type”: “Bearer”,
“expires_in”: 3600,
“id_token”: “eyJhbGciOiJub25lIn0.
eyAiaXNzIjogImh0dHBzOi8vYWNjb3VudHMuZnVuY29ycC5jb20
iLAogICJzdWIiOiAiNzg2NzY3Njc3IiwKICAiYXVkIjogIlNEQk5KU
1BMVVpUUkVYSFpKQSIsCiAgImV4cCI6IDEyODU2OTkzNjAw
MDAsCiAgImlhdCI6IDEyODU2OTM4NjAwMDAsCiAgImF1dGh
fdGltZSI6IDEyODU2OTM4NjAwMDAKICAiYWNyIjogInVybjp0d
GE6Y29tOmxldmVsLTAifQ==.”

}

Der Parameter id_token enthält das Base64 encodete JWT mit den Informationen zur ID. Der OIDC Standard erlaubt digitale Signatur und Verschlüsselung.  Bis zum Trennzeichen steht die verwendete Methode, hier:

{“alg”:”none”}

Im Beispiel werden weder Signatur noch Verschlüsselung verwendet. In der Praxis sollte auch über HTTPS immer eine Signatur verwendet werden, alleine damit der Client kontrollieren kann, ob das Token von einem vertrautem IdP kommt. Aber auch zur Sicherstellung der Integrität der Nachricht. Es gibt diverse Angriffe auf das Protokoll, die damit weitgehend blockiert werden können.

Beim Implicit Grant (JavaScript Clients) z.B. erfolgt die Antwort des OP an den Client direkt in Form von URL Parametern. Damit ist der HTTPS Schutz des ID Tokens nicht vorhanden:

HTTP/1.1 302 Found
Location: https://app.tta.com/cb#
access_token=a9AV3GbS7F
&token_type=bearer
&id_token=eyJ0 … NiJ9.eyNz1 … Gh4
&expires_in=3600
&state=30096519678904076037

Aber auch bei anderen Grant Typen ist Vorsicht geboten, solange der Client nicht in einer kontrollierten Umgebung läuft. Die App auf dem Mobile eines Benutzers ist nur eine sehr bedingt kontrollierbare Umgebung.

Nach dem Trennzeichen folgt das eigentliche ID Token:

{
“iss”: “https://login.tta.com”,
“sub”: “testuser”,
“aud”: “SDBNJSPLUZTREXHZJA”,
“exp”: 1285699360000,
“iat”: 1285693860000,
“auth_time”: 1285693860000
“acr”: “urn:tta:com:level-0”
}

Jedes Feld des JWT enthält einen “Claim” zum Benutzer bzw zur Auth-Method. Die bedeutung der einzelnen Felder und weiterer, die im Token vorkommen können, lassen sich aus der OIDC Dokumentation gut nachvollziehen. Der Link (https://openid.net/specs/openid-connect-core-1_0.html#IDToken) zeigt direkt auf das entsprechende Kapitel.

Der Claim “sub” (Subject) enthält die Benutzer-ID des in diesem Prozess authentifizierten User Account. Dieser Wert ist für einen OP eindeutig. Werden unterschiedliche OP’s vom Client verwendet, sollte zur Identifikation immer zusätzliche das Claim „iss“ mit herangezogen werden.

Wie schon die kurze Einführung zeigt, sind bei der Implementierung einige Sicherheitsaspekte zu berücksichtigen. Weiteres hierzu folgt in einem der folgenden Blogs.