2018-05-12 Mastodon OAuth2 Issues

I signed up for a new instance and noticed that Mastodon Archive doesn’t allow me to log in using OAuth2. What’s going on? Let’s look at the API again.

Mastodon Archive

Register the application:

`curl -X POST -d "client_name=mastodon-archive&redirect_uris=urn:ietf:wg:oauth:2.0:oob&scopes=read" -Ss https://tabletop.social/api/v1/apps`

This gives me a JSON containing `client_id` and `client_secret`. Store these as environment variables of the same name.

Store the password in an environment variable, too.

export client_id=...
export client_secret=...
export password=...

Login to get the access token:

`curl -X POST -d "client_id=$client_id&client_secret=$client_secret&grant_type=password&username=kensanata@gmail.com&password=$password" -Ss https://tabletop.social/oauth/token`

Store it in an environment variable, of course.

export access=token=...

Test it:

`curl --header "Authorization: Bearer $access_token" -sS https://tabletop.social/api/v1/timelines/home`

This seems to work even though it doesn’t work for *Mastodon Archive*!

Let’s create the necessary files, manually:

echo $client_id > tabletop.social.client.secret
echo $client_secret >> tabletop.social.client.secret
echo $access_token > tabletop.social.user.kensanata.secret

And test it:

$ mastodon-archive archive kensanata@tabletop.social
Get user info
Get statuses (this may take a while)
Get favourites (this may take a while)
Skipping mentions
Skipping followers
Saving 33 statuses, 13 favourites, 0 mentions, and 0 followers

Thus, we now know that the problem is the app registration and obtaining the access token. Everything else works as expected.

​#Mastodon ​#Mastodon Archive

Comments

(Please contact me if you want to remove your comment.)

Debugging what `Mastodon.py` does... Here is what my code does, essentially:

print("Registering app")
Mastodon.create_app(
    'mastodon-archive',
    api_base_url = url,
    to_file = client_secret)

print("Log in")
mastodon = Mastodon(
    client_id = client_secret,
    api_base_url = url,
    debug_requests = True)

url = mastodon.auth_request_url(
    client_id = client_secret,
    scopes=scopes)

print("Visit the following URL and authorize the app:")
print(url)

print("Then paste the access token here:")
token = sys.stdin.readline().rstrip()

mastodon.log_in(
    username = username,
    code = token,
    to_file = user_secret,
    scopes=scopes)

And I get the error in the last statement.

Registering app
Log in
Mastodon: Request to endpoint "/api/v1/instance/" using method "GET".
Parameters: {}
Headers: None
Files: {}
Mastodon: Response received with code 200.
response headers: {...}
Response text content: {...}
Visit the following URL and authorize the app:
https://social.nasqueron.org/oauth/authorize?client_id=...8aba&response_type=code&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=read
Then paste the access token here:
...033d
Mastodon: Request to endpoint "/oauth/token" using method "POST".
Parameters: {'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'code': '...033d', 'grant_type': 'authorization_code', 'client_id': '...8aba', 'client_secret': '...b138', 'scope': 'read'}
Headers: None
Files: {}
Mastodon: Response received with code 401.
response headers: {... 'WWW-Authenticate': 'Bearer realm="Doorkeeper", error="invalid_grant", error_description="The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."' ...}
Response text content: {"error":"invalid_grant","error_description":"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."}

I started wondering: is this because of the `scope` parameter? I was missing the `scope` parameter in my call to `create_app`. I made that change but saw no difference.

I then thought perhaps the problem is the missing `redirect_uris` in `auth_request_url` and `redirect_uri` in `log_in`. I added them both but saw no difference.

I then thought perhaps something about scopes was wrong so I removed `scopes`, `redirect_uris` and `redirect_uri` from the various calls but saw no difference.

Sadly, the Mastodon.py documentation and the Doorkeeper documentation it refers to is very sparse indeed. `Mastodon.py` doesn’t tell me how to really do OAuth and Doorkeeper only tells me how to do it with a password (Resource Owner Password Credentials) and not how to use Client Credentials.

Mastodon.py documentation

Doorkeeper documentation

Resource Owner Password Credentials

Client Credentials

It is all quite frustrating.

And most disturbing of all: it continues to work for *octodon.social* but doesn’t work for any of the other instances I have tried, e.g. *social.nasqueron.org*.

As I said, it is all quite frustrating.

As expected, switching the entire thing to password credentials “works”:

mastodon = Mastodon(
    client_id = client_secret,
    api_base_url = url)

print("We need your credentials just this once to log in.")
sys.stdout.write("Email: ")
sys.stdout.flush()
email = sys.stdin.readline().rstrip()
sys.stdout.write("Password: ")
sys.stdout.flush()
password = sys.stdin.readline().rstrip()

mastodon.log_in(
    username = email,
    password = password,
    to_file = user_secret,
    scopes=scopes)

The result:

$ mastodon-archive login kensanata@social.nasqueron.org
We need your credentials just this once to log in.
Email: kensanata+nasqueron@gmail.com
Password: *secret*
Get user info
Login OK

The word of the day is “frustrating”.

– Alex Schroeder 2018-05-13 19:23 UTC