Dan Schnau

A Simple Application of OAuth: Mastodon's API

A User Story:

As Dan Schnau,
When I publish a new article on Dan Schnau Dot Com,
I want a 'toot' with the title and a link to the article to be posted automatically,
So that followers of the feed can go read the article.

When this post is published, the DanSchnauDotCom application will publish a 'toot' to my profile on toot.cafe.

In this article are the steps I took to accomplish this goal. If you are looking for someone to implement modern, safe, and secure authentication and authorization flows, you can hire me.

Step One: Register an Application

In order to do so, I first created an app in the development portion of my account at toot.cafe.

Mastodon New App

Mastodon App DanSchnauDotCom

Step Two: Get User Authorization

Then, I could use the authorization endpoint to authorize the app with my account at toot.cafe.

The authorization endpoint requires several parameters, which are serialized into the requests query string.

  1. client_id: my application Client Key from step one.
  2. response_type: code
  3. scope: write:statuses
  4. redirect_uri: urn:ietf:wg:oauth:2.0:oob

The client id is the important part for our user story: it informs the API which application, amongst all that might be running on toot.cafe, is asking for authorization. The rest are literals related to OAuth and out of scope for this article.

The serialized request is a big URL: https://toot.cafe/oauth/authorize?response_type=code&client_id=mz3T_application_client_key_goes_here_nnJk&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=write:statuses

And opening that URL in a browser logged in to toot.cafe and authorizing the app will spit out an authorization code.

Mastodon Authorization Code

Step Three: Use Authorization Code To Generate A Bearer Token

With this authorization code, we can now make an HTTP POST call to get a bearer token.

We need the client_id and client_secret from step one and the Authorization code from step two.

The request is an HTTP POST to https://toot.cafe/oauth/token with form-data in the request body.

HTTP POST https://toot.cafe/oauth/token
content-type: multipart/form-data

code:KXEcode_from_step_three_goes_hereadk41rtB4
client_id:mz3T4application_client_key_goes_herewnnJk
client_secret:Ul6japplication_client_secret_goes_here3TAILc4Ric
redirect_uri:urn:ietf:wg:oauth:2.0:oob
grant_type:authorization_code
scope:write:statuses

The response:

HTTP 200 OK
{
    "access_token": "oaQC_____________________Crc",
    "token_type": "Bearer",
    "scope": "write:statuses",
    "created_at": 1694018755
}

The access_token in this response is the special secret needed to start making HTTP POST requests to write status updates.

Step Four: Write Some Code To Make the Post with the Access Token

I implemented a new function that would make the http request using the generated authorization code.

    public class SocialService
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public SocialService(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        /*
        * Make an HTTP request to toot.cafe with an authorization code to write a status to toot.cafe/@danschnau about the new blog post.
        * 
        * This is a "happy path" implementation with a hard-coded secret, no logging, and no error handling.
        * In "big kid" applications logging, error handling, and secret management must be implemented.
        */
        public async Task PostToMastodonAsync(Blog blog)
        {
            using HttpClient client = _httpClientFactory.CreateClient();

            client.DefaultRequestHeaders.Add("Authorization", "Bearer oaQ____________________________iCrc");

            // Make HTTP Post Request
            var url = "https://toot.cafe/api/v1/statuses";
            var content = new
            {
                status = $"New Post: {blog.Title} - https://danschnau.com/blog/{blog.Slug}"
            };

            await client.PostAsync(url, JsonContent.Create(content));
        }
    }

In Conlusion

It took more time to write this blog post than it did to implement the functionality discussed in this blog post. The usage of OAuth in this article is narrow in scope, and has much more to do with the protocol than it does with toot.cafe, Mastodon, or the fediverse. I can quickly, safely, and securely implement Authorization and Authentication flows for your business applications using some of the skills I've demonstrated here and you can hire me to do so.