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.
In order to do so, I first created an app in the development
portion of my account at toot.cafe.
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.
client_id
: my application Client Key from step one.response_type
: code
scope
: write:statuses
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.
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.
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));
}
}
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.