<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Sso on inetshell</title><link>https://inet.sh/en/tags/sso/</link><description>Recent content in Sso on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/tags/sso/index.xml" rel="self" type="application/rss+xml"/><item><title>Grafana with SSO: logging in with the local user (?disableAutoLogin), roles overwritten on every login (skip_org_role_sync) and provisioned data sources with secrets</title><link>https://inet.sh/en/posts/monitoring/grafana-sso-disableautologin-roles-provisioning/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/monitoring/grafana-sso-disableautologin-roles-provisioning/</guid><description>Three things that end up biting on any Grafana with OAuth: auto_login sends you to the provider before you see the form and locks you out when SSO breaks (the way out is /login?disableAutoLogin=true), roles you assign by hand revert on the next login unless you use skip_org_role_sync or map role_attribute_path properly, and data sources provisioned from YAML cannot be edited in the UI, keep secrets in secureJsonData and only go away with deleteDatasources or prune.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://grafana.example.com/login?disableAutoLogin=true
</span></span></code></pre></td></tr></table>
</div>
</div><p>That takes you to the username and password form even when <code>auto_login = true</code> keeps redirecting you to the OAuth provider. If you also do not remember the local admin password:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># container / pod</span>
</span></span><span class="line"><span class="cl">grafana cli --homepath /usr/share/grafana admin reset-admin-password <span class="s1">&#39;new&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># package</span>
</span></span><span class="line"><span class="cl">grafana-cli admin reset-admin-password <span class="s1">&#39;new&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>And if the form is disabled (<code>[auth] disable_login_form = true</code>), first set <code>GF_AUTH_DISABLE_LOGIN_FORM=false</code> in the environment and restart.</p>
<h2 id="roles-that-undo-themselves">Roles that undo themselves</h2>
<p>You give someone <em>Editor</em> in <em>Administration → Users</em>, and the next day they are <em>Viewer</em> again. Not a bug: on every OAuth login Grafana <strong>syncs the organization role</strong> with whatever the provider says (or with <code>auto_assign_org_role</code> when the provider says nothing) and overwrites what you set by hand.</p>
<p>Two ways to live with it:</p>
<p><strong>1. Let the provider really be in charge.</strong> Map groups or claims to roles with JMESPath and make it strict, so a user with no group does not get in as a default Viewer:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[auth.github]</span>
</span></span><span class="line"><span class="cl"><span class="na">enabled</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">allow_sign_up</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">auto_login</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">client_id</span> <span class="o">=</span> <span class="s">$__file{/etc/secrets/github-client-id}</span>
</span></span><span class="line"><span class="cl"><span class="na">client_secret</span> <span class="o">=</span> <span class="s">$__file{/etc/secrets/github-client-secret}</span>
</span></span><span class="line"><span class="cl"><span class="na">scopes</span> <span class="o">=</span> <span class="s">user:email,read:org</span>
</span></span><span class="line"><span class="cl"><span class="na">allowed_organizations</span> <span class="o">=</span> <span class="s">my-org</span>
</span></span><span class="line"><span class="cl"><span class="na">role_attribute_strict</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">allow_assign_grafana_admin</span> <span class="o">=</span> <span class="s">true</span>
</span></span><span class="line"><span class="cl"><span class="na">role_attribute_path</span> <span class="o">=</span> <span class="s">contains(groups[*], &#39;@my-org/grafana-admin&#39;) &amp;&amp; &#39;GrafanaAdmin&#39; || contains(groups[*], &#39;@my-org/grafana-editor&#39;) &amp;&amp; &#39;Editor&#39; || contains(groups[*], &#39;@my-org/grafana-viewer&#39;) &amp;&amp; &#39;Viewer&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>2. Keep Grafana away from the roles.</strong> If you would rather manage them in the UI:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="cl"><span class="k">[auth.github]</span>
</span></span><span class="line"><span class="cl"><span class="na">skip_org_role_sync</span> <span class="o">=</span> <span class="s">true</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>skip_org_role_sync</code> is <strong>per provider</strong> (<code>auth.github</code>, <code>auth.google</code>, <code>auth.generic_oauth</code>, <code>auth.azuread</code>, <code>auth.okta</code>, <code>auth.gitlab</code>, SAML). The old global switch <code>[auth] oauth_skip_org_role_update_sync</code> is deprecated; if it is still in your <code>.ini</code>, replace it with the per-provider one.</p>
<p>With Google, <code>hosted_domain</code> and <code>allowed_domains</code> restrict who can log in; without <code>role_attribute_path</code> everyone comes in with <code>auto_assign_org_role</code> (Viewer by default).</p>
<h2 id="provisioned-data-sources">Provisioned data sources</h2>
<p>Data sources defined in <code>provisioning/datasources/*.yaml</code> (in the Helm chart, <code>datasources.datasources.yaml</code>) have three surprising properties:</p>
<ul>
<li><strong>They cannot be edited in the UI</strong> when they carry <code>editable: false</code>: the form shows up locked. That is the right thing in production; the change goes in the YAML.</li>
<li><strong>Secrets go in <code>secureJsonData</code></strong>, not in <code>jsonData</code>: Grafana encrypts them with <code>secret_key</code> before storing them in its database (<code>/var/lib/grafana/grafana.db</code>, SQLite by default) and never returns them through the API. Tokens, passwords and client certificates go there; in <code>jsonData</code> they sit in plain text.</li>
<li><strong>They do not disappear when removed from the YAML.</strong> Grafana only inserts and updates; deleting has to be explicit:</li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">prune: true                  # recent versions</span><span class="p">:</span><span class="w"> </span><span class="l">removes provisioned sources no longer in this file</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">deleteDatasources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Sentry-old</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">datasources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">access</span><span class="p">:</span><span class="w"> </span><span class="l">proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">http://prometheus-server.monitoring.svc.cluster.local</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">isDefault</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">editable</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Sentry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">grafana-sentry-datasource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">access</span><span class="p">:</span><span class="w"> </span><span class="l">proxy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">orgId</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">editable</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">jsonData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://sentry.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">orgSlug</span><span class="p">:</span><span class="w"> </span><span class="l">my-org</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">secureJsonData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">authToken</span><span class="p">:</span><span class="w"> </span><span class="l">${SENTRY_TOKEN}     </span><span class="w"> </span><span class="c"># environment variable of the Grafana process</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Environment variables expand with <code>$VAR</code> or <code>${VAR}</code>; a literal <code>$</code> in a secret is written <code>$$</code>.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong><code>auto_login</code> with several providers</strong> is ignored: it only works with a single provider configured for auto-login.</li>
<li><strong><code>allow_sign_up = false</code></strong> keeps out any new user from the provider, including the one you just added to the group; the log says <em>&ldquo;user signup disabled&rdquo;</em>.</li>
<li><strong><code>role_attribute_strict = true</code> with no mapped group</strong> rejects the login with <em>&ldquo;Role attribute not found&rdquo;</em>; that is what you want, but warn people.</li>
<li><strong>The local admin still exists</strong> with SSO: change its password and keep it in the secret manager, it is your emergency door together with <code>disableAutoLogin</code>.</li>
<li><strong>The default <code>secret_key</code></strong> (<code>SW2YcwTIb9zpOOhoPsMm</code>) encrypts <code>secureJsonData</code> identically on every Grafana; set your own (<code>[security] secret_key</code>) before provisioning anything, because changing it later invalidates what was encrypted.</li>
</ul>
<p>References: <a href="https://grafana.com/docs/grafana/latest/setup-grafana/configure-access/configure-authentication/">Configure authentication</a> and <a href="https://grafana.com/docs/grafana/latest/administration/provisioning/">Provisioning</a> in the Grafana documentation.</p>
<h2 id="see-also">See also</h2>
<ul>
<li><a href="/en/posts/proxmox/keycloak-openid-connect/">Proxmox with Keycloak (OpenID)</a> — the same SSO pattern, with Keycloak as the provider.</li>
</ul>
]]></content:encoded></item><item><title>Proxmox VE with Keycloak: OpenID Connect login (openid realm)</title><link>https://inet.sh/en/posts/proxmox/keycloak-openid-connect/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/proxmox/keycloak-openid-connect/</guid><description>Authenticate to Proxmox VE with Keycloak through an OpenID Connect realm: confidential client on Keycloak 26, pveum realm add with issuer-url, client-id, client-key and username-claim preferred_username, auto-created users, permissions and groups. No extra mappers, no /auth/ in the URL.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>In Keycloak: <strong>Clients → Create client</strong> → OpenID Connect, Client ID <code>proxmox</code>, <em>Client authentication</em> <strong>On</strong>, <em>Standard flow</em> <strong>On</strong>, <em>Valid redirect URIs</em> <code>https://pve.example.local:8006</code> and <code>https://pve.example.local:8006/*</code>. Copy the secret from the <strong>Credentials</strong> tab.</p>
<p>On a Proxmox node:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pveum realm add keycloak --type openid <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --issuer-url https://id.example.local/realms/lab <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --client-id proxmox <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --client-key <span class="s1">&#39;&lt;client-secret&gt;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --username-claim preferred_username <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --autocreate <span class="m">1</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --comment <span class="s1">&#39;Keycloak&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Users log in as <code>alice@keycloak</code>. They are created on first login, <strong>with no permissions</strong>; permissions are still Proxmox ACLs:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pveum group add sso-admins
</span></span><span class="line"><span class="cl">pveum acl modify / --groups sso-admins --roles Administrator
</span></span><span class="line"><span class="cl">pveum user modify alice@keycloak --groups sso-admins     <span class="c1"># after their first login</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="how-it-works">How it works</h2>
<p>Proxmox has supported <code>openid</code> realms since PVE 7. From <code>issuer-url</code> it downloads <code>/.well-known/openid-configuration</code> and takes the endpoints from there; the login screen shows the realm in the dropdown and the <strong>Login</strong> button redirects to Keycloak. On the way back, the value of the claim you picked in <code>username-claim</code> becomes the user <code>&lt;claim&gt;@&lt;realm&gt;</code>.</p>
<p><code>username-claim</code> accepts any claim name. Keycloak sends <code>preferred_username</code> by default (it comes from the <em>username</em> mapper in the <code>profile</code> client scope), so the <strong>extra mapper</strong> old guides added to expose a claim called <code>username</code> <strong>is not needed</strong>. If you prefer the email, <code>--username-claim email</code>.</p>
<h2 id="keycloak-step-by-step-26">Keycloak, step by step (26)</h2>
<ol>
<li><strong>Realm.</strong> Use the one you have (<code>lab</code> in the example). The issuer is <code>https://id.example.local/realms/lab</code>. Since Keycloak 17 there is no <code>/auth/</code> in the path; if you copy <code>/auth/realms/...</code> from an old guide, discovery fails.</li>
<li><strong>Clients → Create client.</strong>
<ul>
<li><em>Client type</em>: OpenID Connect. <em>Client ID</em>: <code>proxmox</code>. <em>Name</em>: whatever you like.</li>
<li><em>Capability config</em>: <strong>Client authentication On</strong> (confidential client, with secret). <strong>Standard flow On</strong>. Everything else Off: <em>Direct access grants</em>, <em>Implicit flow</em>, <em>Service accounts</em>, <em>Authorization</em>.</li>
<li><em>Login settings</em>: <em>Root URL</em> <code>https://pve.example.local:8006</code>; <em>Valid redirect URIs</em> <code>https://pve.example.local:8006</code> and <code>https://pve.example.local:8006/*</code>. <em>Web origins</em> empty: it is a redirect flow, there is no CORS.</li>
</ul>
</li>
<li><strong>Credentials</strong> → <em>Client secret</em>. That is the <code>--client-key</code>.</li>
<li><strong>Groups (optional).</strong> Clients → <code>proxmox</code> → <em>Client scopes</em> → <code>proxmox-dedicated</code> → <em>Add mapper</em> → <em>By configuration</em> → <strong>Group Membership</strong>: <em>Name</em> <code>groups</code>, <em>Token Claim Name</em> <code>groups</code>, <em>Full group path</em> Off, <em>Add to ID token</em> and <em>Add to userinfo</em> On.</li>
</ol>
<h2 id="proxmox-step-by-step">Proxmox, step by step</h2>
<p>Through the GUI: <strong>Datacenter → Permissions → Realms → Add → OpenID Connect Server</strong>: <em>Issuer URL</em>, <em>Realm</em> (the short name, <code>keycloak</code>), <em>Client ID</em>, <em>Client Key</em>, <em>Username Claim</em> <code>preferred_username</code>, <em>Autocreate Users</em>. Or the <code>pveum realm add</code> from the TL;DR; the configuration lands in <code>/etc/pve/domains.cfg</code> and replicates across the cluster.</p>
<p>With groups coming from Keycloak, on recent PVE 8 releases and on PVE 9 (<code>pveum realm add --help</code> tells you whether your version has <code>--groups-claim</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pveum realm modify keycloak --groups-claim groups --groups-autocreate <span class="m">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Each group in the claim is created as <code>&lt;group&gt;-keycloak</code> and the user joins them on every login; you assign the ACLs to those groups.</p>
<p>Test before touching anything else: login screen → Realm <strong>Keycloak</strong> → <strong>Login</strong> → you come back authenticated and <code>pveum user list</code> shows <code>alice@keycloak</code>.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong><code>Invalid parameter: redirect_uri</code></strong> in Keycloak: the URL you typed in the browser (name, port <code>8006</code>, <code>https</code>) is not in <em>Valid redirect URIs</em>. If you use both the IP and the FQDN, add both.</li>
<li><strong>The node must trust Keycloak&rsquo;s certificate</strong>, not just your browser. With an internal CA: copy the <code>.crt</code> to <code>/usr/local/share/ca-certificates/</code> and run <code>update-ca-certificates</code>.</li>
<li><strong><code>issuer-url</code> has to match exactly</strong> the <code>iss</code> in the tokens: <code>https</code>, no trailing slash, <code>/realms/&lt;realm&gt;</code>.</li>
<li><strong>Time.</strong> A skewed clock invalidates tokens; <code>chrony</code> on the nodes.</li>
<li><strong>Do not let users edit their own username</strong> in Keycloak (a warning from Proxmox&rsquo;s own docs): renaming yourself to <code>root</code> is switching accounts in Proxmox.</li>
<li><strong>Do not set <code>--default 1</code> until it works</strong>, and keep <code>root@pam</code> as the emergency door: if Keycloak is down, the <code>openid</code> realm cannot authenticate anyone.</li>
<li><strong>API and CLI.</strong> OIDC users have no password in Proxmox; for Terraform, Ansible, or <code>pvesh</code> from outside create a token: <code>pveum user token add alice@keycloak automation --privsep 0</code>.</li>
</ul>
<p>Reference: <a href="https://pve.proxmox.com/pve-docs/chapter-pveum.html#user-realms-oidc">User Management — OpenID Connect</a> in the Proxmox guide and <a href="https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_clients">client creation</a> in the Keycloak documentation.</p>
]]></content:encoded></item></channel></rss>