Closed coolapso closed 1 month ago
Giving also some bit more info, in the "semi working" scenario ....
This works:
func mastodonClientConfig() *gomasto.Config {
return &gomasto.Config{
Server: viper.GetString("mastodon_server"),
ClientID: viper.GetString("mastodon_client_id"),
ClientSecret: viper.GetString("mastodon_client_secret"),
AccessToken: viper.GetString("mastodon_access_token"),
}
}
func postMastodon(text, mediaPath string) (err error) {
config := mastodonClientConfig()
client := gomasto.NewClient(config)
if mediaPath != "" {
return
}
if err := client.AuthenticateToken(context.Background(), config.AccessToken, redirectUri); err != nil {
return fmt.Errorf("Mastodon Authentication failed, %v\n", err)
}
id, err := mastodon.CreatePost(context.Background(), client, text, "public")
if err != nil {
return fmt.Errorf("Failed to post to mastodon, %v\n", err)
}
fmt.Println("Toot created with ID:", id)
return nil
}
This not doesn't (shouldn't it be the same thing??):
func mastodonClientConfig() *gomasto.Config {
return &gomasto.Config{
Server: viper.GetString("mastodon_server"),
ClientID: viper.GetString("mastodon_client_id"),
ClientSecret: viper.GetString("mastodon_client_secret"),
AccessToken: viper.GetString("mastodon_access_token"),
}
}
func authenticateToken(ctx context.Context, accessToken string) error {
client := gomasto.NewClient(mastodonClientConfig())
return client.AuthenticateToken(ctx, accessToken, redirectUri)
}
func postMastodon(text, mediaPath string) (err error) {
config := mastodonClientConfig()
client := gomasto.NewClient(config)
fmt.Println(client.Config)
fmt.Println(client.UserAgent)
if mediaPath != "" {
return
}
if err := authenticateToken(context.Background(), c.m.GetAccessToken()); err != nil {
return fmt.Errorf("Failed to authenticate access token, %v\n", err)
}
id, err := mastodon.CreatePost(context.Background(), client, text, "public")
if err != nil {
return fmt.Errorf("Failed to post to mastodon, %v\n", err)
}
fmt.Println("Toot created with ID:", id)
return nil
}
It looks like the requests have to always be made by the same client that authenticated, otherwise it doesn't work, even tho the clients look exactly the same and use exactly the same configurations :thinking: so confused.
Hey, here's what I think the issue is ... unless I am understanding this wrong, I believe this is actually a bug in the library and a unfortunate naming issue.
the c.authenticate()
method, is broken. It should return the access token in order to save it, but instead sets it in the current instance of the client.
AuthenticateToken
is an unfortunate naming, because when calling c.authenticate
what it is actually doing is to exchange an authorization code for an AccessToken
When using oauth authentication with mastodon the flow is as follow:
Register the app and get in exchange a client_id
and a client_secret
Craft the URL and initiate the user authroization grant in exchange for a (and here is the key word), access code
Use the client_id
and the client_secret
to exchange that access code with a access token
Save that access token and use it to do whatever your app wants to do
Here's What is happening in the the go-mastodon library, and how things are named.
the RegisterApp()
method, registers the application and returns the client_id
and the client_secret
(so far so good)
the AuthenticateToken()
returns only a error
when it should be returning the actual access token.
Looking deeper into the code we can see that AuthenticateToken()
returns c.authenticate()
and that c.authenticate
actually makes the request to the mastodon oauth/token
endpoint, which according to the mastodon documentation here is used to obtain the Access Token and not to "authenticate" the token.
Here's the curl example:
curl -X POST \
-F "client_id=${CLIENT_ID}" \
-F "client_secret=${CLIENT_SECRET}" \
-F 'redirect_uri=urn:ietf:wg:oauth:2.0:oob' \
-F 'grant_type=authorization_code' \
-F 'code=********************************' \
-F 'scope=read write push' \
https://mastodon.social/oauth/token
{"access_token":"*********************","token_type":"Bearer","scope":"read write push","created_at":1729194979}%
this being said the c.authenticate()
method should be returning both a error and the token string, so it could be saved and be re-used but instead it is setting the AccessToken only in the current instance of the client which explains why it works the first time and not the second time and why it works in one side of the code and not in the other.
a) The client is effectively different b) every time we call the AuthenticateToken we are actually trying to exchange the the access code by a token, however that access code will be already invalided because it was already used to exchange for a token.
Until a PR is accepted and merged, after using AuthenticateToken the value that should be saved is the access token in the current instance of the client. accessToken := client.Config.AccessToken
Got bit by this aswell.
The README.md code excerpt should show how to fetch (without necessarily stating how to store) the AccessToken.
Changing authenticate
's return values would bubble up in several other function signatures and this would be somewhat breaking.
I am planning to put up a PR with a RFC with a fix for this during this week.
Changing the method is definitely not a great idea ... but my thoughts are around marking the method as deprecated,, to create the necessary methods to fix this issue and of course adjust all the documentation regarding this. This should ensure backwards compatibility and allow the improvement of the whole authentication flow.
Apologies if this has been answered here, but I am having some trouble with authentication, wonder what I could be missing and if you can point me in the right direction
I'm writing a CLI application, and it contains a "configure" action
myapplication configure
that:Then an Action to create a post
myapplication -m "text"
NewClient()
Now, the problem with this is:
I am able to create a post After generating a configuration file, the next time I try to make a post re-using the exact same accessToken, I will get
Invalid_grant
If I move theAuthenticate access token
to the configuration step (before saving everything into the configuration file), When I try to create a post I getThe access token is invalid
.Do I need to request users to copy pate the link get a new token and paste and Authenticate the token every time I want to make the post?
I took a look at this issue and it's mentioned in this comment that
AuthenticateToken()
only needs to be used once, But it seems the token becomes invalid as soon as it is used once, and its not accepted if its authenticated and then re-used on another "session" of the same application.Genuinely confused. Thanks a lot for your time and help!