forked from Sandra/Sandra.Snow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
223 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |