A friend and I were out editing for OpenStreetMap, and we wondered how we could tweak an existing open changeset. Because the client we used does not seem to provide that feature, and we are messy gremlins who like to mess around with data to do things (like adding review_requested=yes
to a changeset manually).
So we defaulted to the next most reasonable thing to use on our phones in the middle of the street: curl
.
So I decided to finally figure out how OAuth2 works, but also fell into the trap of forgetting that application/x-www-form-urlencoded
is a thing.
OAuth2 for OSM
For the purpose of the experiment, I created a changeset with a change I had not uploaded yet, and decided to play around with the API.
OSM uses HTTP Basic Authentication, so I needed a token. The fastest way to get one seemed to be getting it through OAuth2. For that, I first registered the application.
Note that the $REDIRECT_URI
you provide will be useful later. It won't really serve any technical purpose (the authorization API just redirects you to that URI with the code you get), but if you're missing it/not providing the same one given at registration, you will get errors.
With registration done you get two components: $CLIENT_ID
and $CLIENT_SECRET
. Next, open
https://www.openstreetmap.org/oauth2/authorize?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&response_type=code&scope=write_api
in your browser while logged in to OSM. It'll give you the usual "do you want to authorize this app" spiel, and you press yes. Here the scope of authorizations used here means you can only write stuff through the map API.
After authorization you will be redirected to $REDIRECT_URI?code=$CODE
. Get that code, and your next step is:
curl -v -X POST -L 'https://www.openstreetmap.org/oauth2/token' \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode "redirect_uri=$REDIRECT_URI" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=$CODE" \
--data-urlencode "client_secret=$CLIENT_SECRET"
And if you are lucky, you get:
{"access_token":"[REDACTED]","token_type":"Bearer","scope":"write_api","created_at":[REDACTED]}
Hurray! But I was not so lucky.
It took me an hour and several unhelpful online pages to even get the code in the first place. OpenStreetMap automatically closes changesets after one hour of inactivity, meaning mine was gone.
My friend was also tweaking things at the same time. She was surprised I got the code, so I gave her the mechanism and explained what I had tried so far, and moved on. I had tried a lot of things. For some reason, some of them made OpenStreetMap spit out errors in korean or spanish.
My friend tinkered and came back with a functional method to get the bearer token. We had both found two important things:
- The order of query parameters in the authorization URL was important (it's 2023…)
- The
token
URL accepted neitherapplication/json
, query params, ortext/xml
for its input. After a while, I wondered ifcurl
sending aContent-Type: application/x-www-form-urlencoded
header even without a body of data would be the problem, but removing that header also proved useless. My friend found out that you needed to provide parameters as separate fields through--data-urlencode
(and we are still not sure the order does not matter).
Eventually, after a good night's sleep, I got my token, roughly 14 hours, including a good night's sleep, after my changeset closed. At least, next time, I know how I can tinker with the API (albeit, perhaps, not the production API).