<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Authentication on kenji.blog</title><link>http://kenji.blog/de/tags/authentication/</link><description>Recent content in Authentication on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 12:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/tags/authentication/index.xml" rel="self" type="application/rss+xml"/><item><title>Die Funktionsweise von OAuth 2.0 illustriert: Lernen durch die Implementierung einer Slack-App-Integration</title><link>http://kenji.blog/de/p/oauth2-architecture-slack-app-integration/</link><pubDate>Sun, 13 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/de/p/oauth2-architecture-slack-app-integration/</guid><description>&lt;img src="http://kenji.blog/p/oauth2-architecture-slack-app-integration/img/eyecatch.jpg" alt="Featured image of post Die Funktionsweise von OAuth 2.0 illustriert: Lernen durch die Implementierung einer Slack-App-Integration" />&lt;h1 id="einführung-warum-oauth-20-lernen">Einführung: Warum OAuth 2.0 lernen?
&lt;/h1>&lt;p>In modernen Webanwendungen ist es selbstverständlich geworden, dass mehrere Dienste zusammenarbeiten. Beispiele hierfür sind &amp;ldquo;Mit Google-Konto anmelden&amp;rdquo;, &amp;ldquo;Eine Benachrichtigung an Slack senden, wenn eine Trello-Aufgabe aktualisiert wird&amp;rdquo; oder &amp;ldquo;Einen Zoom-Meeting-Link automatisch in Google Kalender einfügen&amp;rdquo;. Hinter all diesen Funktionen arbeitet &lt;strong>OAuth 2.0 (Open Authorization 2.0)&lt;/strong>, ein Autorisierungs-Framework.&lt;/p>
&lt;p>Früher wurden beim Datenaustausch zwischen verschiedenen Diensten sehr gefährliche Methoden wie &amp;ldquo;Basic Authentication&amp;rdquo; oder &amp;ldquo;Password Sharing&amp;rdquo; verwendet, bei denen Benutzer ihre ID und ihr Passwort direkt an den Zieldienst weitergaben. Bei dieser Methode hat der verknüpfte Dienst jedoch die volle Kontrolle über die Benutzerrechte, was mit fatalen Sicherheitsrisiken verbunden ist.&lt;/p>
&lt;p>OAuth 2.0 wurde als Standardprotokoll (RFC 6749) entwickelt, um diese &amp;ldquo;Weitergabe von Passwörtern&amp;rdquo; zu vermeiden und Drittanbieteranwendungen &amp;ldquo;nur bestimmte Berechtigungen (Scopes)&amp;rdquo; für &amp;ldquo;eine begrenzte Zeit&amp;rdquo; zu delegieren.&lt;/p>
&lt;p>In diesem Artikel werden die Mechanismen von OAuth 2.0 äußerst detailliert und praxisnah anhand der Implementierung einer Anwendung (Slack App) für &lt;strong>Slack (Slack API)&lt;/strong>, dem De-facto-Standard für geschäftliche Kommunikationstools, erläutert. Es handelt sich um den ultimativen Leitfaden mit über 10.000 Zeichen, der Node.js (Express) Codebeispiele, Sequenzdiagramme zur Veranschaulichung des Protokollflusses und Einblicke in die mathematischen und kryptografischen Hintergründe wichtiger Sicherheitskonzepte wie dem &lt;code>state&lt;/code>-Parameter und PKCE enthält.&lt;/p>
&lt;hr>
&lt;h1 id="1-grundkonzepte-von-oauth-20-die-vier-rollen-roles">1. Grundkonzepte von OAuth 2.0: Die vier Rollen (Roles)
&lt;/h1>&lt;p>Der erste Schritt zum Verständnis von OAuth 2.0 besteht darin, die beteiligten Akteure (Roles) genau zu kennen. In RFC 6749 sind die folgenden vier Rollen definiert.&lt;/p>
&lt;pre class="mermaid">
graph TD
RO[&amp;#34;Resource Owner (Benutzer)&amp;#34;] -- &amp;#34;Gewährt Autorisierung&amp;#34; --&amp;gt; C[&amp;#34;Client (Slack App)&amp;#34;]
C -- &amp;#34;Fordert Autorisierung an&amp;#34; --&amp;gt; AS[&amp;#34;Authorization Server (Slack Autorisierungsserver)&amp;#34;]
AS -- &amp;#34;Stellt Access Token aus&amp;#34; --&amp;gt; C
C -- &amp;#34;Greift mit Token zu&amp;#34; --&amp;gt; RS[&amp;#34;Resource Server (Slack API-Server)&amp;#34;]
RO -- &amp;#34;Meldet sich mit ID/Passwort an&amp;#34; --&amp;gt; AS
&lt;/pre>
&lt;ol>
&lt;li>&lt;strong>Resource Owner&lt;/strong>
&lt;ul>
&lt;li>Die Entität, die berechtigt ist, Zugriff auf eine Ressource zu gewähren. Dies bezieht sich normalerweise auf den &amp;ldquo;Endbenutzer (Mensch)&amp;rdquo;. In unserem Beispiel sind das &amp;ldquo;Sie selbst, der zu einem Slack-Workspace gehört und die Berechtigung hat, Nachrichten in Kanälen zu posten&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Client&lt;/strong>
&lt;ul>
&lt;li>Eine Anwendung, die versucht, mit Zustimmung des Resource Owners auf den Resource Server zuzugreifen. In unserem Beispiel ist dies &amp;ldquo;die von Ihnen entwickelte Node.js-Anwendung (Slack App)&amp;rdquo;. Obwohl sie &amp;ldquo;Client&amp;rdquo; genannt wird, werden auch serverseitige Webanwendungen im Kontext von OAuth als &amp;ldquo;Clients&amp;rdquo; bezeichnet.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Authorization Server&lt;/strong>
&lt;ul>
&lt;li>Der Server, der den Resource Owner authentifiziert, die Autorisierung vom Resource Owner einholt und ein Access Token an den Client ausstellt. In unserem Beispiel ist dies die Slack-Authentifizierungsinfrastruktur, die &lt;code>slack.com/oauth/v2/authorize&lt;/code> bereitstellt.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Resource Server&lt;/strong>
&lt;ul>
&lt;li>Der Server, der die geschützten Ressourcen hostet, Zugriffsanforderungen auf Ressourcen unter Verwendung von Access Tokens entgegennimmt und darauf antwortet. In unserem Beispiel sind dies die Endpunkte unter &lt;code>slack.com/api/&lt;/code>, die APIs wie &lt;code>chat.postMessage&lt;/code> bereitstellen.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Der OAuth-Flow ist kurz gesagt &lt;strong>&amp;ldquo;eine Abfolge von Schritten, bei der der Client mit Zustimmung des Resource Owners ein Access Token vom Authorization Server erhält und dieses verwendet, um Daten vom Resource Server abzurufen oder zu bearbeiten&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="2-vollständige-analyse-des-authorization-code-grant">2. Vollständige Analyse des Authorization Code Grant
&lt;/h1>&lt;p>Es gibt mehrere Flows (Grant Types) in OAuth 2.0, aber in Umgebungen wie Webanwendungen, in denen ein geheimer Schlüssel (Client Secret) serverseitig sicher aufbewahrt werden kann, ist der &lt;strong>Authorization Code Grant&lt;/strong> am meisten empfohlen und am weitesten verbreitet.&lt;/p>
&lt;p>Das wichtigste Merkmal des Authorization Code Grants ist die klare Trennung von &lt;strong>Front-Channel (Kommunikation über den Browser)&lt;/strong> und &lt;strong>Back-Channel (direkte Kommunikation zwischen Servern)&lt;/strong>. Im Front-Channel wird nur ein temporärer &amp;ldquo;Authorization Code&amp;rdquo; ausgetauscht, während der endgültige Abruf des &amp;ldquo;Access Tokens&amp;rdquo; im Back-Channel erfolgt. Dadurch wird das Risiko drastisch reduziert, dass Token im Browserverlauf oder im Referrer durchsickern.&lt;/p>
&lt;p>Das folgende Sequenzdiagramm zeigt den gesamten Ablauf des Authorization Code Grants in einer Slack App.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
autonumber
participant U as &amp;#34;Resource Owner (Web Browser)&amp;#34;
participant C as &amp;#34;Client (Node.js App)&amp;#34;
participant AS as &amp;#34;Authorization Server (Slack Auth)&amp;#34;
participant RS as &amp;#34;Resource Server (Slack API)&amp;#34;
U-&amp;gt;&amp;gt;C: &amp;#34;Klickt auf den App-Installationsbutton (GET /slack/install)&amp;#34;
Note over C: &amp;#34;Generierung des state-Parameters&amp;#34;
C--&amp;gt;&amp;gt;U: &amp;#34;Weiterleitung: 302 Found (Location: Slack Auth URL)&amp;#34;
U-&amp;gt;&amp;gt;AS: &amp;#34;GET /oauth/v2/authorize?client_id=...&amp;amp;scope=...&amp;amp;state=...&amp;#34;
AS--&amp;gt;&amp;gt;U: &amp;#34;Slack-Anmeldebildschirm &amp;amp; Zustimmungsbildschirm (Consent Screen)&amp;#34;
U-&amp;gt;&amp;gt;AS: &amp;#34;Berechtigungen erlauben (Allow)&amp;#34;
Note over AS: &amp;#34;Generierung des Authorization Code (code)&amp;#34;
AS--&amp;gt;&amp;gt;U: &amp;#34;Weiterleitung: 302 Found (Location: Client Callback URL?code=...&amp;amp;state=...)&amp;#34;
U-&amp;gt;&amp;gt;C: &amp;#34;GET /slack/oauth_redirect?code=...&amp;amp;state=...&amp;#34;
Note over C: &amp;#34;Validierung des state-Parameters (CSRF-Schutz)&amp;#34;
C-&amp;gt;&amp;gt;AS: &amp;#34;POST /api/oauth.v2.access (code, client_id, client_secret)&amp;#34;
Note over C,AS: &amp;#34;Back-Channel-Kommunikation (Umgeht den Browser)&amp;#34;
AS--&amp;gt;&amp;gt;C: &amp;#34;200 OK (JSON: Ausgabe des access_token etc.)&amp;#34;
C-&amp;gt;&amp;gt;RS: &amp;#34;POST /api/chat.postMessage (Authorization: Bearer &amp;lt;access_token&amp;gt;)&amp;#34;
RS--&amp;gt;&amp;gt;C: &amp;#34;200 OK (Nachricht erfolgreich gepostet)&amp;#34;
&lt;/pre>
&lt;p>Lassen Sie uns diesen Flow Schritt für Schritt durch eine konkrete Node.js (Express) Implementierung aufschlüsseln.&lt;/p>
&lt;hr>
&lt;h1 id="3-vorbereitung-für-die-implementierung-einstellungen-in-der-slack-developer-console">3. Vorbereitung für die Implementierung: Einstellungen in der Slack Developer Console
&lt;/h1>&lt;p>Bevor Sie Code schreiben, müssen Sie das Slack-System darüber informieren, dass &amp;ldquo;ein neuer Client existiert&amp;rdquo;.&lt;/p>
&lt;ol>
&lt;li>Gehen Sie zu &lt;a class="link" href="https://api.slack.com/apps" target="_blank" rel="noopener"
>Slack API: Applications&lt;/a> und klicken Sie auf &amp;ldquo;Create New App&amp;rdquo;.&lt;/li>
&lt;li>Wählen Sie &amp;ldquo;From scratch&amp;rdquo; und geben Sie den Namen der App (z.B. &lt;code>My First OAuth App&lt;/code>) sowie den Ziel-Workspace für die Installation an.&lt;/li>
&lt;li>Im Bildschirm &amp;ldquo;Basic Information&amp;rdquo; nach der Erstellung erhalten Sie die folgenden zwei wichtigen Anmeldeinformationen (Credentials):
&lt;ul>
&lt;li>&lt;strong>Client ID&lt;/strong>: Eine ID, die Ihre App öffentlich eindeutig identifiziert. Es ist in Ordnung, diese in Browser-Anfragen (Front-Channel) einzuschließen.&lt;/li>
&lt;li>&lt;strong>Client Secret&lt;/strong>: Eine geheime Zeichenfolge, die nur Ihre App kennt. &lt;strong>Setzen Sie dies niemals im Browser frei und committen Sie es niemals auf GitHub oder ähnlichem.&lt;/strong>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Gehen Sie zum Bildschirm &amp;ldquo;OAuth &amp;amp; Permissions&amp;rdquo; und registrieren Sie die Callback-URL unter &amp;ldquo;Redirect URLs&amp;rdquo;. Für die lokale Entwicklung richten wir hier Folgendes ein:
&lt;ul>
&lt;li>&lt;code>http://localhost:3000/slack/oauth_redirect&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Die Vorbereitungen sind nun abgeschlossen. Wir beginnen mit der Serverimplementierung.&lt;/p>
&lt;hr>
&lt;h1 id="4-implementierungsschritt-1-slackinstall-und-der-state-parameter-für-csrf-schutz">4. Implementierungsschritt 1: &lt;code>/slack/install&lt;/code> und der &lt;code>state&lt;/code>-Parameter für CSRF-Schutz
&lt;/h1>&lt;p>Wir erstellen den ersten Endpunkt, damit Benutzer die Nutzung der App starten können (Installation im Workspace). Die größte Verantwortung hier besteht darin, den Benutzer zum Autorisierungsserver von Slack weiterzuleiten. Sicherheitstechnisch von entscheidender Bedeutung ist jedoch die &lt;strong>Generierung und Speicherung des &lt;code>state&lt;/code>-Parameters&lt;/strong>.&lt;/p>
&lt;h2 id="die-notwendigkeit-des-state-parameters-verhinderung-von-csrf-angriffen">Die Notwendigkeit des state-Parameters (Verhinderung von CSRF-Angriffen)
&lt;/h2>&lt;p>Wenn der &lt;code>state&lt;/code>-Parameter nicht vorhanden wäre, könnte ein böswilliger Angreifer den Autorisierungsprozess mit seinem eigenen Slack-Konto starten und das Opfer dazu bringen, auf die Callback-URL zu klicken, die den abgerufenen &amp;ldquo;Authorization Code&amp;rdquo; enthält (z. B. &lt;code>http://localhost:3000/slack/oauth_redirect?code=ATTACKER_CODE&lt;/code>). Wenn der Browser des Opfers dies ausführt, wird in der Sitzung des Opfers eine Verknüpfung mit dem Slack-Konto des Angreifers hergestellt, was zu Informationslecks oder unbeabsichtigten Aktionen (Login-CSRF) führen kann.&lt;/p>
&lt;p>Um dies zu verhindern, ist &lt;code>state&lt;/code> eine unvorhersehbare, zufällige Zeichenfolge, die verwendet wird, um zu überprüfen, ob der Browser, der die Anfrage gestartet hat, derselbe ist wie der, der den Callback empfängt.&lt;/p>
&lt;h2 id="entropie-des-state-mathematischer-hintergrund">Entropie des state (Mathematischer Hintergrund)
&lt;/h2>&lt;p>Um einen sicheren &lt;code>state&lt;/code> zu erzeugen, wird eine Zufallszahl mit ausreichender &amp;ldquo;Entropie (Informationsgehalt)&amp;rdquo; benötigt. Die Entropie $E$ hängt von der Anzahl der möglichen Zeichenfolgen $N$ ab und wird durch die folgende Formel ausgedrückt:&lt;/p>
$$
E = \log_2(N) \quad (\text{Einheit: bits})
$$&lt;p>Wenn Sie beispielsweise eine 16-Byte kryptografisch sichere Pseudozufallszahl (CSPRNG) generieren und in einen hexadezimalen String umwandeln, beträgt die Anzahl der möglichen Zustände $2^{128}$.&lt;/p>
$$
E = \log_2(2^{128}) = 128 \text{ bits}
$$&lt;p>Mit 128 Bit Entropie ist es in der modernen Informatik praktisch unmöglich (astronomische Wahrscheinlichkeit), eine Kollision durch einen Brute-Force-Angriff zu finden. Als Sicherheitsanforderung wird in der Regel ein &lt;code>state&lt;/code> mit einer Entropie von mindestens 128 Bit empfohlen.&lt;/p>
&lt;h2 id="implementierung-mit-nodejs">Implementierung mit Node.js
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// app.js (Auszug)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">crypto&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;crypto&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;express-session&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">dotenv&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;dotenv&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">dotenv&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">config&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Konfiguration der Session-Middleware (zur Speicherung des state)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">secret&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SESSION_SECRET&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">resave&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">saveUninitialized&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">cookie&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">secure&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">false&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="c1">// In der Produktionsumgebung auf true setzen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">SLACK_CLIENT_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SLACK_CLIENT_ID&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">SLACK_AUTHORIZE_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://slack.com/oauth/v2/authorize&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/slack/install&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Generiert eine starke 16-Byte-Zufallszahl und wandelt sie in einen Hex-String um (Entropie: 128 bits)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">crypto&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">randomBytes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hex&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// In der Session speichern, um sie beim Callback validieren zu können
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">oauth_state&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Liste der angeforderten Scopes (Berechtigungen) (kommagetrennt)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// chat:write = Berechtigung, Nachrichten an einen Kanal zu senden
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// channels:read = Berechtigung, Informationen über öffentliche Kanäle abzurufen
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">scope&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;chat:write,channels:read&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// URL-Parameter zur Konstruktion des Slack-Autorisierungsservers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">URLSearchParams&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">client_id&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">SLACK_CLIENT_ID&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">scope&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">scope&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">state&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">redirect_uri&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;http://localhost:3000/slack/oauth_redirect&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">authUrl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sb">`&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">SLACK_AUTHORIZE_URL&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">?&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Benutzer zum Slack-Autorisierungsbildschirm weiterleiten (302 Found)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">redirect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">authUrl&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Wenn Sie auf diesen Endpunkt zugreifen, sieht die HTTP-Antwort folgendermaßen aus:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-http" data-lang="http">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">HTTP&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="m">1.1&lt;/span> &lt;span class="m">302&lt;/span> &lt;span class="ne">Found&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="l">https://slack.com/oauth/v2/authorize?client_id=123.456&amp;amp;scope=chat%3Awrite%2Cchannels%3Aread&amp;amp;state=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6&amp;amp;redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fslack%2Foauth_redirect&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Set-Cookie&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="l">connect.sid=...; Path=/; HttpOnly&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Der Browser des Benutzers wird sofort zu der angegebenen &lt;code>Location&lt;/code> navigiert, der Slack-Bildschirm (Consent Screen) wird angezeigt, und der vertraute Bildschirm &amp;ldquo;My First OAuth App fordert Zugriff auf den Workspace an&amp;rdquo; erscheint.&lt;/p>
&lt;hr>
&lt;h1 id="5-implementierungsschritt-2-empfang-des-callbacks-und-austausch-für-access-token">5. Implementierungsschritt 2: Empfang des Callbacks und Austausch für Access Token
&lt;/h1>&lt;p>Wenn der Benutzer auf dem Slack-Bildschirm auf &amp;ldquo;Erlauben (Allow)&amp;rdquo; klickt, leitet der Slack-Server den Browser des Benutzers an die von Ihnen konfigurierte &lt;code>redirect_uri&lt;/code> weiter. Dabei werden &lt;code>code&lt;/code> (Authorization Code) und der zuvor gesendete &lt;code>state&lt;/code> als URL-Abfrageparameter angehängt.&lt;/p>
&lt;p>Im Backend führen wir die folgenden Schritte aus:&lt;/p>
&lt;ol>
&lt;li>Überprüfen, ob der gesendete &lt;code>state&lt;/code> exakt mit dem in der Session gespeicherten &lt;code>state&lt;/code> übereinstimmt.&lt;/li>
&lt;li>Wenn sie übereinstimmen, verwenden Sie den empfangenen &lt;code>code&lt;/code>, Ihre eigene &lt;code>client_id&lt;/code> und das geheime &lt;code>client_secret&lt;/code>, um über den Back-Channel mit der Slack API zu kommunizieren und das Access Token anzufordern.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">axios&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;axios&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">SLACK_CLIENT_SECRET&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">process&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">env&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">SLACK_CLIENT_SECRET&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">SLACK_ACCESS_TOKEN_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://slack.com/api/oauth.v2.access&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/slack/oauth_redirect&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">code&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">state&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">error&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Behandlung für den Fall, dass der Benutzer die Autorisierung verweigert
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s1">&amp;#39;access_denied&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">403&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Zugriff verweigert.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Validierung des state (CSRF-Schutz)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">savedState&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">oauth_state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">state&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">state&lt;/span> &lt;span class="o">!==&lt;/span> &lt;span class="nx">savedState&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">400&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Invalid State Parameter (CSRF Attack Detected)&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Löschen des verwendeten state (Schutz vor Replay-Angriffen)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">delete&lt;/span> &lt;span class="nx">req&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">oauth_state&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Austausch des Authorization Code gegen ein Access Token (Back-Channel-Kommunikation)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tokenResponse&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">axios&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">SLACK_ACCESS_TOKEN_URL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">URLSearchParams&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">client_id&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">SLACK_CLIENT_ID&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">client_secret&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">SLACK_CLIENT_SECRET&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">code&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">code&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">redirect_uri&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;http://localhost:3000/slack/oauth_redirect&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}).&lt;/span>&lt;span class="nx">toString&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">headers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;Content-Type&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;application/x-www-form-urlencoded&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">tokenResponse&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">ok&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Token Exchange Error:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Slack API Error: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Erfolg! Access Token erhalten
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">accessToken&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">access_token&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">teamName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">team&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">botUserId&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">bot_user_id&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Successfully installed to &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">teamName&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">. Access Token: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">accessToken&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Normalerweise würden Sie das Token hier verschlüsseln und in einer Datenbank speichern
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// saveToDatabase(data.team.id, encrypt(accessToken));
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Installation abgeschlossen! Workspace: &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">teamName&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Network Error:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Ein Kommunikationsfehler ist aufgetreten.&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Als Antwort auf diese &lt;code>/api/oauth.v2.access&lt;/code> gibt Slack folgendes JSON zurück:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;ok&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;app_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;A12345678&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;authed_user&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;U12345678&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;scope&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;chat:write,channels:read&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;token_type&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;bot&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;access_token&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;YOUR_BOT_TOKEN_HERE&amp;gt;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;bot_user_id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;B12345678&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;team&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;T12345678&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;My Workspace&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;enterprise&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Diese Zeichenfolge, die mit &lt;code>xoxb-&lt;/code> beginnt, ist das &lt;strong>Bot Access Token&lt;/strong> in Slack. Wenn die Anwendung in Zukunft eine Anfrage an die Slack API (Resource Server) sendet, wird durch Hinzufügen von &lt;code>Authorization: Bearer xoxb-...&lt;/code> zum HTTP-Header eine Authentifizierung und der Nachweis von Berechtigungen durchgeführt.&lt;/p>
&lt;hr>
&lt;h1 id="6-token-scopes-und-das-prinzip-der-geringsten-rechte-principle-of-least-privilege">6. Token-Scopes und das Prinzip der geringsten Rechte (Principle of Least Privilege)
&lt;/h1>&lt;p>Eines der wichtigsten Konzepte in OAuth 2.0 ist der &amp;ldquo;Scope&amp;rdquo; (Berechtigungsbereich). Der Scope bezieht sich auf den Bereich der Berechtigungen, die mit dem Access Token verbunden sind.&lt;/p>
&lt;p>In Slack sind Berechtigungen sehr detailliert kategorisiert und werden grob in &lt;strong>Bot Token Scopes&lt;/strong> und &lt;strong>User Token Scopes&lt;/strong> unterteilt.&lt;/p>
&lt;ul>
&lt;li>&lt;code>chat:write&lt;/code> (Bot): Berechtigung, Nachrichten als App (Bot) selbst im Kanal zu posten.&lt;/li>
&lt;li>&lt;code>chat:write&lt;/code> (User): Berechtigung, Nachrichten im Namen des Benutzers zu posten, der die App installiert hat (mit dem Namen und Symbol des Benutzers).&lt;/li>
&lt;li>&lt;code>channels:read&lt;/code>: Berechtigung, eine Liste von Kanälen abzurufen.&lt;/li>
&lt;li>&lt;code>channels:history&lt;/code>: Berechtigung, den vergangenen Nachrichtenverlauf von Kanälen zu lesen.&lt;/li>
&lt;/ul>
&lt;p>Nach dem &amp;ldquo;Prinzip der geringsten Rechte&amp;rdquo; (Principle of Least Privilege), einer wichtigen Sicherheitsregel, ist es ein eiserner Grundsatz, &lt;strong>nur die Scopes anzufordern, die für die von der App bereitgestellten Funktionen absolut notwendig sind&lt;/strong>. Beispielsweise sollte eine App, die &amp;ldquo;nur Benachrichtigungen sendet&amp;rdquo;, nur &lt;code>chat:write&lt;/code> anfordern und darf nicht &lt;code>channels:history&lt;/code> (die Berechtigung, alle vergangenen Unterhaltungen zu lesen) anfordern. Dies dient dazu, den Schaden zu minimieren, falls die App jemals gehackt wird und das Token durchsickert.&lt;/p>
&lt;hr>
&lt;h1 id="7-erweiterte-sicherheit-pkce-proof-key-for-code-exchange">7. Erweiterte Sicherheit: PKCE (Proof Key for Code Exchange)
&lt;/h1>&lt;p>In letzter Zeit hat sich &lt;strong>PKCE (Proof Key for Code Exchange, RFC 7636, ausgesprochen &amp;ldquo;Pixy&amp;rdquo;)&lt;/strong> als Standard für die weitere Stärkung der Sicherheit von OAuth 2.0 etabliert und ist weit verbreitet.&lt;/p>
&lt;p>Ursprünglich wurde PKCE für &amp;ldquo;öffentliche Clients&amp;rdquo; (Public Clients) entwickelt, die das &lt;code>client_secret&lt;/code> nicht sicher speichern können, wie native Apps (iOS/Android) oder SPAs (Single Page Applications). Heutzutage wird jedoch im Rahmen von Sicherheits-Best-Practices (OAuth 2.1 Draft) dringend empfohlen, PKCE auch bei serverseitigen &amp;ldquo;vertraulichen Clients&amp;rdquo; (Confidential Clients) einzusetzen.&lt;/p>
&lt;h2 id="funktionsweise-und-mathematischer-hintergrund-von-pkce">Funktionsweise und mathematischer Hintergrund von PKCE
&lt;/h2>&lt;p>PKCE beweist kryptografisch, dass &amp;ldquo;die Entität, die die Autorisierungsanforderung gestartet hat&amp;rdquo;, und &amp;ldquo;die Entität, die den Token-Austausch anfordert&amp;rdquo;, identisch sind.&lt;/p>
&lt;ol>
&lt;li>Der Client generiert eine zufällige Zeichenfolge &lt;strong>&lt;code>code_verifier&lt;/code>&lt;/strong> (43 bis 128 Zeichen).&lt;/li>
&lt;li>Diese wird mit &lt;strong>SHA-256&lt;/strong> gehasht und BASE64URL-kodiert, was als &lt;strong>&lt;code>code_challenge&lt;/code>&lt;/strong> bezeichnet wird.&lt;/li>
&lt;/ol>
&lt;p>Als Formel ausgedrückt sieht das so aus:&lt;/p>
$$
\text{code\_challenge} = \text{BASE64URL-ENCODE}( \text{SHA256}( \text{ASCII}(\text{code\_verifier}) ) )
$$&lt;ol start="3">
&lt;li>Wenn &lt;code>/slack/install&lt;/code> ausgeführt wird, sendet der Client zusätzlich zu &lt;code>state&lt;/code> die &lt;code>code_challenge&lt;/code> und &lt;code>code_challenge_method=S256&lt;/code> an den Autorisierungsserver (Slack). (Slack speichert dies vorübergehend).&lt;/li>
&lt;li>Nach dem Callback, während des Token-Austauschs (&lt;code>/api/oauth.v2.access&lt;/code>), wird der ursprüngliche &lt;strong>&lt;code>code_verifier&lt;/code>&lt;/strong> vor dem Hashing gesendet.&lt;/li>
&lt;li>Der Autorisierungsserver (Slack) hasht den empfangenen &lt;code>code_verifier&lt;/code> selbst mit SHA-256 und überprüft, ob er exakt mit der in Schritt 3 gespeicherten &lt;code>code_challenge&lt;/code> übereinstimmt.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
sequenceDiagram
participant C as &amp;#34;Client&amp;#34;
participant AS as &amp;#34;Authorization Server&amp;#34;
Note over C: &amp;#34;code_verifier = Zufällige Zeichenfolge&amp;#34;&amp;lt;br/&amp;gt;&amp;#34;code_challenge = SHA256(code_verifier)&amp;#34;
C-&amp;gt;&amp;gt;AS: &amp;#34;Autorisierungsanforderung (sendet code_challenge)&amp;#34;
Note over AS: &amp;#34;Speichert code_challenge&amp;#34;
AS--&amp;gt;&amp;gt;C: &amp;#34;Stellt Authorization Code (code) aus&amp;#34;
C-&amp;gt;&amp;gt;AS: &amp;#34;Token-Anforderung (sendet code + code_verifier)&amp;#34;
Note over AS: &amp;#34;SHA256(empfangener verifier) == gespeicherte challenge?&amp;#34;
AS--&amp;gt;&amp;gt;C: &amp;#34;Validierung erfolgreich: Stellt Access Token aus&amp;#34;
&lt;/pre>
&lt;p>Dank dieses Mechanismus können Angreifer, selbst wenn der &amp;ldquo;Authorization Code (code)&amp;rdquo; durch eine bösartige App oder Abhören der Kommunikationswege gestohlen wird, kein Access Token erhalten, da sie den ursprünglichen &lt;code>code_verifier&lt;/code> nicht kennen (es ist aufgrund der Natur der unidirektionalen Hashfunktion SHA-256 unmöglich, den Verifier aus der Challenge zurückzurechnen).&lt;/p>
&lt;p>Derzeit unterstützen einige der neueren Flows der Slack API und andere moderne SaaS APIs (Auth0, Okta, X/Twitter API v2 usw.) PKCE, weshalb Entwickler diese Technologie aktiv nutzen sollten.&lt;/p>
&lt;hr>
&lt;h1 id="8-sicheres-management-und-betrieb-von-access-tokens">8. Sicheres Management und Betrieb von Access Tokens
&lt;/h1>&lt;p>Abschließend hier die Best Practices zum Speichern der abgerufenen Access Tokens.&lt;/p>
&lt;h2 id="1-verschlüsselung-ist-für-die-speicherung-in-der-datenbank-obligatorisch">1. Verschlüsselung ist für die Speicherung in der Datenbank obligatorisch
&lt;/h2>&lt;p>Das Access Token (&lt;code>xoxb-...&lt;/code>) ist sozusagen der &amp;ldquo;Master-Schlüssel&amp;rdquo; zu Ihrem Slack-Workspace. Es darf nicht als Klartext in Datenbanken (MySQL, PostgreSQL, MongoDB etc.) gespeichert werden. Sollte es durch SQL-Injection oder Ähnliches zu einem Datenleck kommen, wäre dies eine Katastrophe, bei der alle Slack-Workspaces der Kunden kompromittiert würden.&lt;/p>
&lt;p>Achten Sie darauf, es auf Anwendungsebene mit einer starken symmetrischen Verschlüsselung wie &lt;strong>AES-256-GCM&lt;/strong> zu verschlüsseln, bevor Sie es in der DB speichern. Der Master Key für die Verschlüsselung/Entschlüsselung sollte mit einem sicheren Schlüsselverwaltungsdienst wie AWS KMS (Key Management Service) oder GCP Cloud KMS streng verwaltet werden.&lt;/p>
&lt;h2 id="2-token-rotation">2. Token-Rotation
&lt;/h2>&lt;p>Es ist riskant, langlebige Token kontinuierlich zu verwenden. In neueren OAuth-Implementierungen wird empfohlen, ein &amp;ldquo;Refresh Token&amp;rdquo; zu verwenden und alle paar Stunden ein neues Access Token auszustellen (Token Rotation). Die Slack API ermöglicht es Ihnen ebenfalls, Token Rotation durch Optionseinstellungen zu aktivieren.&lt;/p>
&lt;hr>
&lt;h1 id="zusammenfassung">Zusammenfassung
&lt;/h1>&lt;p>In diesem Artikel haben wir den Authorization Code Grant Flow von OAuth 2.0 im Detail erläutert und dabei konkreten Node.js-Implementierungscode für eine Slack App-Integration verwendet.&lt;/p>
&lt;ol>
&lt;li>Das Bewusstsein für die &lt;strong>vier Rollen (RO, Client, AS, RS)&lt;/strong> verdeutlicht die Architektur des gesamten Systems.&lt;/li>
&lt;li>Der &lt;strong>Authorization Code Grant&lt;/strong> gewährleistet Sicherheit durch die geschickte Nutzung der Kommunikationspfade zwischen Browser und Server (Front/Back-Channel).&lt;/li>
&lt;li>Das Verständnis der kryptografischen Mechanismen im Hintergrund, wie der CSRF-Schutz durch den &lt;strong>&lt;code>state&lt;/code>-Parameter&lt;/strong> und die Verhinderung von Authorization-Code-Intercept-Angriffen durch &lt;strong>PKCE&lt;/strong>, ist der direkteste Weg zu einer sicheren Implementierung.&lt;/li>
&lt;li>Scope-Design basierend auf dem &lt;strong>Prinzip der geringsten Rechte&lt;/strong> und Verschlüsselung beim Speichern in der DB sind operativ absolut unerlässlich.&lt;/li>
&lt;/ol>
&lt;p>OAuth 2.0 ist sehr tiefgründig und allein die RFC-Spezifikationen sind riesig, aber durch praktisches Lernen mit Fokus auf eine echte Plattform (Slack) werden Sie in der Lage sein, dessen raffinierte Designphilosophie und robusten Sicherheitsmechanismen wirklich zu verstehen. Wir hoffen, dass Ihnen das Wissen aus diesem Artikel bei der zukünftigen Anwendungsentwicklung und API-Integration von Nutzen sein wird.&lt;/p></description></item></channel></rss>