Terence Eden’s Blog<p><strong>Creating a generic "Log-in with Mastodon" service</strong></p><p><a href="https://shkspr.mobi/blog/2024/12/creating-a-generic-log-in-with-mastodon-service/" rel="nofollow noopener noreferrer" translate="no" target="_blank"><span class="invisible">https://</span><span class="ellipsis">shkspr.mobi/blog/2024/12/creat</span><span class="invisible">ing-a-generic-log-in-with-mastodon-service/</span></a></p><p>Let's say you have a website - <code>your_website.tld</code> - and you want people to log in to it using their Mastodon account.</p><p>For a traditional social-media site like Twitter or Facebook, you would create an OAuth app on the service that you want. But there are <em>hundreds</em> of Mastodon servers. So you need to create a new app for each one. That sounds hard, but it isn't. Well… not <em>too</em> hard.</p><p>Here's some <a href="https://infosec.press/jerry/how-to-user-mastodons-built-on-oauth-provider-as-the-authentication-provider" rel="nofollow noopener noreferrer" target="_blank">code adapted from Infosec.press</a>. It's all written using cURL on the command line - so you should be able to adapt it to your preferred programming language.</p><p><strong>Register an app on the user's Mastodon instance</strong></p><p>Let's assume the user has given you the name of their Mastodon server - <code>example.social</code></p><p>You then send a request for an app to be created on <code>example.social</code> with your website's details. All it requests is the ability to read a user's details, nothing else.</p><pre><code>curl -X POST \ -F "client_name=Login to your_website.tld" \ -F "redirect_uris=https://your_website.tld/oauth/mastodon?server=example.social&" \ -F "scopes=read:accounts" \ -F "website=https://your_website.tld" \ -A "user-agent/0.1" https://example.social/api/v1/apps</code></pre><p>You can set the User Agent to be anything suitable. Some servers won't work if it is omitted.</p><p>If the request was successful, <code>example.social</code> will send you this JSON in response:</p><pre><code>{ "id": "12345", "name": "Login to your_website.tld", "website": "https://your_website.tld", "scopes": [ "read:accounts" ], "redirect_uris": [ "https://your_website.tld/oauth/mastodon?server=example.social&" ], "vapid_key": "qwertyuiop-asdfghjkl-zxcvbnm", "redirect_uri": "https://your_website.tld/oauth/mastodon?server=example.social&", "client_id": "qw_asdfghjkl_zxcvbnm", "client_secret": "qwertyuiop1234567890"}</code></pre><p>Save the server's address, the <code>client_id</code>, and the <code>client_secret</code>. You will need all three later.</p><p><strong>The user logs in to their Mastodon instance</strong></p><p>You need to redirect the user to their server so they can log in. You need to construct a Mastodon URl using the data you received back. Don't forget to URl encode the <code>redirect_uri</code>.</p><p>For example, redirect the user to:</p><pre><code>https://example.social/oauth/authorize?client_id=qw_asdfghjkl_zxcvbnm&scope=read:accounts&redirect_uri=https://your_website.tld/oauth/mastodon%3Fserver=example.social%26&response_type=code</code></pre><p>When the user visits that URl they can then log in. If they're successful, they'll be redirected back to your server using your specified redirect URI:</p><pre><code>https://your_website.tld/oauth/mastodon?server=example.social&code=qazwsxedcrfvtgbyhnujm</code></pre><p><strong>Get a Bearer token</strong></p><p>Your website has received a GET request with the user's server name and an authorisation code. As per <a href="https://docs.joinmastodon.org/client/authorized/#token" rel="nofollow noopener noreferrer" target="_blank">the Mastodon documentation</a>, your app uses that code to request a Bearer token:</p><pre><code>curl -X POST \ -F "client_id=qw_asdfghjkl_zxcvbnm" \ -F "client_secret=qwertyuiop1234567890" \ -F "redirect_uri=https://your_website.tld/oauth/mastodon?server=example.social&" \ -F "grant_type=authorization_code" \ -F "code=qazwsxedcrfvtgbyhnujm" \ -F "scope=read:accounts" \ -A "user-agent/0.1" https://example.social/oauth/token</code></pre><p>If that's worked, the user's server will return a Bearer token like this:</p><pre><code>{ "access_token": "abcdefg_123456", "token_type": "Bearer", "scope": "read:accounts", "created_at": 1732916685}</code></pre><p><strong>Get the user's details</strong></p><p>Finally(!) you can use that token to verify the user's credentials with the server:</p><pre><code>curl \ -H "Authorization: Bearer abcdefg_123456" \ -A "user-agent/0.1" https://example.social/api/v1/accounts/verify_credentials</code></pre><p>If that works, you'll get back all the user's details. Something like this:</p><pre><code>{ "id": "7112", "username": "Edent", "acct": "Edent", "display_name": "Terence Eden", "url": "https://mastodon.social/@Edent", "avatar": "https://files.mastodon.social/accounts/avatars/000/007/112/original/37df032a5951b96c.jpg",...}</code></pre><p><strong>Putting it all together</strong></p><ol><li>User providers their Mastodon instance's domain name</li><li>Your service looks up the domain name in its database<ul><li>If there are no results, request to create a new app on the Mastodon instance and save the returned <code>client_id</code> and <code>client_secret</code></li></ul></li><li>Redirect the User to their Mastodon instance, using a URl which contains the <code>client_id</code> & callback URl</li><li>User logs in to their Mastodon instance</li><li>The User's Mastodon instance redirects the User to your service's callback URl which includes an the instance's domain name and User's authorisation code</li><li>Your service reads the User's domain name and authorisation code</li><li>Your service exchanges those details for a Bearer token</li><li>Your service uses the Bearer token to get the User's account details</li></ol><p><strong>Next steps?</strong></p><p>This basic code works. For my next trick, can I integrate it into Auth0?</p><p><a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://shkspr.mobi/blog/tag/auth0/" target="_blank">#Auth0</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://shkspr.mobi/blog/tag/mastodonapi/" target="_blank">#MastodonAPI</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://shkspr.mobi/blog/tag/oauth/" target="_blank">#oauth</a></p>