<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Auth on PHP Boy Scout</title><link>https://phpboyscout.uk/tags/auth/</link><description>Recent content in Auth on PHP Boy Scout</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>Matt Cockayne</copyright><lastBuildDate>Mon, 27 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://phpboyscout.uk/tags/auth/index.xml" rel="self" type="application/rss+xml"/><item><title>Who holds the client secret?</title><link>https://phpboyscout.uk/who-holds-the-client-secret/</link><pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate><guid>https://phpboyscout.uk/who-holds-the-client-secret/</guid><description>&lt;img src="https://phpboyscout.uk/who-holds-the-client-secret/cover-who-holds-the-client-secret.png" alt="Featured image of post Who holds the client secret?" /&gt;&lt;p&gt;keryx posts to TikTok and YouTube on your behalf, which meant I had to answer a question every OAuth tutorial skips: who actually holds the client secret? They all assume you&amp;rsquo;re a server that can keep one, and never say so. You register your app, you get a client ID and a client secret, you keep the secret on your backend, and the whole flow hangs off the fact that one trusted party can hold a secret. That assumption is doing a lot of work, and it falls apart the moment the thing doing the OAuth is a command-line tool that posts to TikTok or YouTube on your behalf.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no server. keryx runs on your machine. So the secret would have to live somewhere on the client side, and the two obvious places are both wrong.&lt;/p&gt;
&lt;p&gt;You could bake the client secret into the binary. But keryx is open source, and a secret baked into an open-source binary isn&amp;rsquo;t a secret at all. Anyone can pull it out of the source or the compiled artifact in about ten seconds. Worse, it&amp;rsquo;s a &lt;em&gt;shared&lt;/em&gt; secret: every install carries the same one, so a single extraction compromises the app for everyone, and rotating it means shipping a new release to the entire userbase. That&amp;rsquo;s not a credential, it&amp;rsquo;s a liability with a version number.&lt;/p&gt;
&lt;p&gt;Or you could make every user register their own OAuth app at every platform they want to post to. No shipped secret, which is the right instinct, but now the setup story is &amp;ldquo;before you can use this tool, go and create developer apps on four different social networks.&amp;rdquo; For a lot of people that&amp;rsquo;s where the tool gets closed and forgotten.&lt;/p&gt;
&lt;p&gt;The way out is to name what keryx actually is: a tool with no safe way to keep a client secret in the first place. OAuth already has a name for it: a &lt;em&gt;public client&lt;/em&gt;. It&amp;rsquo;s the category for native and CLI apps that demonstrably can&amp;rsquo;t keep a client secret confidential, and the whole flow is designed around that admission. No client secret is shipped at all. The thing that actually matters, the durable credential, is the per-user &lt;em&gt;refresh token&lt;/em&gt; you get after authorising, and that&amp;rsquo;s yours, tied to your account, living on your machine. keryx stores it the careful way, preferring an environment variable, then the OS keychain, then config, which is a whole topic in &lt;a class="link" href="https://phpboyscout.uk/where-should-a-cli-keep-your-api-keys/" &gt;its own right&lt;/a&gt;. Nothing secret ever goes in the binary, because there&amp;rsquo;s nothing secret to put there.&lt;/p&gt;
&lt;p&gt;That leaves the client &lt;em&gt;ID&lt;/em&gt;, which isn&amp;rsquo;t a secret (it&amp;rsquo;s fine for it to be public), and the question of where it comes from. For now keryx is bring-your-own-app: you register an app on the platform, you get your client ID, keryx uses it with the public-client flow. It&amp;rsquo;s a bit of one-time setup, but it&amp;rsquo;s setup that leaks nothing, because there&amp;rsquo;s no shared secret anywhere in the system to leak.&lt;/p&gt;
&lt;p&gt;And this is the trade-off I&amp;rsquo;m sitting in, openly, rather than pretending there&amp;rsquo;s a free option. The convenient version is a hosted broker: keryx runs a single registered app, you authorise against that, and you never register anything. Zero setup. It&amp;rsquo;s genuinely nicer for the user. But it means I&amp;rsquo;m now running a piece of trusted infrastructure that holds the app registration for everyone, that has to stay up, that becomes a target, and that turns a local-first tool into one with a dependency on a service I operate. That&amp;rsquo;s a real commitment, not a weekend&amp;rsquo;s work, so the broker is deferred rather than dismissed. Bring-your-own-app is the right default until I can stand a broker up properly.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re ever distributing an OAuth client that isn&amp;rsquo;t a web app, the temptation is to recreate the server-side flow you know, secret and all, somewhere that can&amp;rsquo;t keep a secret. The better move is to stop pretending there&amp;rsquo;s a secret to keep: name it a public client, lean on the per-user token, and most of the problem just evaporates. You were never going to keep that secret anyway.&lt;/p&gt;</description></item></channel></rss>