Skip to content

Commit

Permalink
bluesky api article
Browse files Browse the repository at this point in the history
  • Loading branch information
Pxtl committed Dec 13, 2023
1 parent 10e2df4 commit 1d9d6fe
Showing 1 changed file with 223 additions and 0 deletions.
223 changes: 223 additions & 0 deletions SnowSite/Snow/_posts/2023-12-13-bluesky-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
published: published
layout: post
category: Programming
title: Bluesky API
---

Personally in the post-Muskening twitter world, I'm pulling for
[Mastodon](https://mastodon.social/@pxtl), but [Bluesky](https://bsky.app) is a
somewhat interesting platform too. One feature of Bluesky that stands out is
that you can set your name to be based on a domain-name you own. I own Pxtl.ca,
so I thought I'd take a crack at leveraging that feature.

What follows is a guide on how to do this in Powershell, which is a programming
language you *already have installed* if you use Windows.

<!--excerpt-->

# Preamble

## What you need

I'm going to be doing this in Powershell 5.1, which is what's built into
Windows. If you're not on Windows or you're a pro-star, you can install
Powershell 7 on any operating system.

Second, you need your own domain. Those cost money annually, but that's how you
can have your own snazzy site like "Pxtl.ca". Personally I use Google Domains,
but sadly this is a sunsetting product so going forwards I'm going to need a new
service to run my domain-name subscription.

## Intro to Powershell

First up, we open up our Powershell console. You can type `⊞ Win` + `R` to
launch the **run** menu to access it, and then type "powershell" to use it. If
this is your first time running powershell you may need to do some fiddling to
enable it properly. This will give you a console to run powershell commands.

Now, if you're following along and copying and pasting Powershell code from this
article into your Powershell window, a warning: Copying and pasting code from a
stranger into your console and running it is very dangerous because a computer
does what it's told and this stranger may have told your computer to do
something very bad. In this situation, I am basically a stranger with candy.

So, don't ever copy/paste code from an article without fully understanding what
it does. So, I'll be explaining everything I do.

# Logging In to the API

We're going to be sending HTTP commands (like your web browser does, but through
the Powershell console) using a powershell command called `Invoke-RestMethod`.
"Rest" is just a programmer-jargon for "HTTP that uses data structures instead
of websites".

So first, we have to create a session in our console that we can use to do
stuff. Bluesky/Atproto (their programmer-interface protocol) makes you first
"login" before you can do stuff.

So we run the following:

```ps
$sessionResponse = Invoke-RestMethod -Method POST -Uri https://bsky.social/xrpc/com.atproto.server.createSession `
-body (@{identifier = 'email@example.com'; password ='PASSWORDGOESHERE'} | ConvertTo-Json) `
-ContentType 'application/json'
```

Where "email@example.com" should be replaced with your own email address
associated with your Bluesky account (we don't use your username, just the
email) and PASSWORDGOESHERE is, obviously, your Bluesky password. If your password
contains a ' char, you'll have to escape it.

So, what this does: We're using Invoke-RestMethod to send a POST action (like
submitting a form on a website) to the url
[https://bsky.social/xrpc/com.atproto.server.createSession](https://bsky.social/xrpc/com.atproto.server.createSession).
We know that the target is trustworthy with your Bluesky password because it's
in the "bsky.social" domain -- that is, we're sending your password to Bluesky,
not to some stranger.

The little backtick at the end of the line is just the simplest way to do
multi-line commands in Powershell. The backtick-line-continuation is considered
bad form by Powershell people but Powershell is a bad language so imho turnabout
is fair play.

The `-body` parameter is the body of data we're sending to Bluesky.
`@{identifier = 'email@example.com'; password ='PASSWORDGOESHERE'}` is a
Powershell object we're building with the login and password, and then we're
piping it into `| ConvertTo-Json` to turn it into the JSON text format so it can
be sent over the wire to Bluesky.

Finally we let Bluesky know we're sending it as JSON with the parameter
`-ContentType 'application/json'`.

And we store the result of this command into `$sessionResponse`.

## Inspecting the Result

You can inspect `$sessionResponse` by just running `$sessionResponse` in your
Powershell console. You'll see an object with several members like `did` and
`handle` and `email` and `accessJwt`. The important bits of data we need going
forwards are `did`, which is your true user-account-ID in Bluesky, and
`accessJwt`, which is the temporary super-long secret code we use going forwards
to do other commands.

Now, the `accessJwt` code is short-lived (a few minutes) before the Bluesky
server decides it doesn't like it anymore, so going forwards you might have to
re-run the `createSession` command above to get a new one if commands start
failing and complaining about security.

# Creating the Domain Record

Okay, so we've proven to Bluesky that we are who we say we are, but how do we
prove to Bluesky that we own the domain? I mean, I could say I'm google.com or
whatever if they don't check. But they check.

So, let's say you own "mydomain.com". You'll have to go into your domain
registrar's website and create a new TXT record on "mydomain.com" that proves to
Bluesky you own it. So get the `did` value from `$sessionResponse` by typing
`$sessionResponse.did`. For example, my `did` value is
`did:plc:otu6mg5xkk47y3ghqpq2w3oo` (this isn't a secret it's fine that you know
that).

Create a new TXT record on "mydomain.com" called "_atproto", and give it a value
of `did=YOURDIDGOESHERE`. So for me, I created "_atproto" as a TXT record with
the value `did=did:plc:otu6mg5xkk47y3ghqpq2w3oo`. Yes it's weirdly
double-barreled with the did=did part. This is fine.

If you want your name to be some subdomain of your domain, you can even make
"_atproto.somesubdomain". I tried this out by making "_atproto.martin" so I
could be "@martin.pxtl.ca". This is great if you're running an organization and
you've got many users on that domain - you can create one TXT record per-user,
each with their own subdomain, so like if my son got his own Bluesky account (it would have a different `did`) I
could give him "_atproto.gooseguy" so he'd be "@gooseguy.pxtl.ca".

## Testing the Domain Record

Once the domain record has been created, you can test that it exists with the Powershell command `Resolve-DnsName`

So, to test it, mash together the TXT record's host name and your domain name, and call it as follows:

```ps
Resolve-DnsName _atproto.mydomain.ca
```

So for example for my "@martin.pxtl.ca" name, it would be,

```ps
Resolve-DnsName _atproto.martin.pxtl.ca
```

If you get a result talking about the domain and servers and authorities? It
worked. If you get ugly red error messages, it didn't. That might mean you
screwed up, or it just hasn't propagated yet (DNS/domain updates can be slow,
give it a few more minutes).

# Updating your Username

Finally, now that we can log into Bluesky and we've created proof for Bluesky
that we own the domain, we can tell Bluesky to change our name to match our
domain.

```ps
Invoke-RestMethod `
-Method POST `
-Uri https://bsky.social/xrpc/com.atproto.identity.updateHandle `
-Headers @{Authorization = "Bearer $($sessionResponse.accessJwt)"} `
-Body (@{handle ='mydomain.ca'} | ConvertTo-Json) `
-ContentType 'application/json'
```

So again, we're doing an HTTP POST to "bsky.social", but this time we're calling
`updateHandle` instead of `createSession`. Since we've already *got* the
session, we need to reference a token (that short-lived secret I mentioned
before) to let them know "yes, I'm still the person you were talking to
earlier". So we use the parameter `-Headers @{Authorization = "Bearer
$($sessionResponse.accessJwt)"}` which adds the `accessJwt` token to this
request as an `Authorization` header entry.

Again, we're doing a JSON body, but this time the body is just a single entry in
the object "handle". That should match the @ handle you're trying to get, the
same one you created a TXT entry for - like, since I did both
`_atproto.martin.pxtl.ca` and `_atproto.pxtl.ca`, I can do either
`handle='pxtl.ca'` or `handle=martin.pxtl.ca`.

This action won't send any result if it works. It will only give you feedback
if it fails.

So, check your Bluesky account. There will be errors for a minute or so, but
your user handle should be updated to match your domain.

# TL;DR

To review, in Powershell:

Run

```ps
$sessionResponse = Invoke-RestMethod -Method POST -Uri https://bsky.social/xrpc/com.atproto.server.createSession `
-body (@{identifier = 'email@example.com'; password ='PASSWORDGOESHERE'} | ConvertTo-Json) `
-ContentType 'application/json'
```

with your bluesky email and password.

Get `$sessionResponse.did` and use that when you create a new TXT record on
"yourdomain.com" called "_atproto", and give it a value of
`did=YOURDIDGOESHERE`. If you want to use a subdomain of "yourdomain.com"
because you feel like it or you've got friends who want to use the same domain,
you can make "_atproto.mysubmodmain" to get that.

Now, let Bluesky know about it by calling

```ps
Invoke-RestMethod `
-Method POST `
-Uri https://bsky.social/xrpc/com.atproto.identity.updateHandle `
-Headers @{Authorization = "Bearer $($sessionResponse.accessJwt)"} `
-Body (@{handle ='yourdomain.com'} | ConvertTo-Json) `
-ContentType 'application/json'
```

or whatever your domain (or subdomain or whatever) is.

Questions? Scroll up to the detailed part. Don't @ me.

0 comments on commit 1d9d6fe

Please sign in to comment.