Skip to content

Commit 9998a02

Browse files
committed
fix: Authentication flow
Update authentication flow to be aligned with mastodon documentation Refactor authetnication to be more explicit and easier to understand Add working examples for each authentication method Update Documenation Deprectaed client methods: Authenticate(), AuthenticateApp(), AuthenticateToken(), authenticate() New client methods: GetAppAccessToken(), GetUserAccessToken(), getAccessToken()
1 parent d74af49 commit 9998a02

File tree

7 files changed

+396
-134
lines changed

7 files changed

+396
-134
lines changed

README.md

Lines changed: 25 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -8,141 +8,33 @@
88

99
## Usage
1010

11-
### Application
12-
13-
```go
14-
package main
15-
16-
import (
17-
"context"
18-
"fmt"
19-
"log"
20-
21-
"github.com/mattn/go-mastodon"
22-
)
23-
24-
func main() {
25-
app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
26-
Server: "https://mstdn.jp",
27-
ClientName: "client-name",
28-
Scopes: "read write follow",
29-
Website: "https://github.com/mattn/go-mastodon",
30-
})
31-
if err != nil {
32-
log.Fatal(err)
33-
}
34-
fmt.Printf("client-id : %s\n", app.ClientID)
35-
fmt.Printf("client-secret: %s\n", app.ClientSecret)
36-
}
37-
```
11+
There are three ways to authenticate users. Fully working examples can be found in the [examples](./examples) directory.
3812

39-
### Client
40-
41-
```go
42-
package main
43-
44-
import (
45-
"context"
46-
"fmt"
47-
"log"
48-
49-
"github.com/mattn/go-mastodon"
50-
)
51-
52-
func main() {
53-
c := mastodon.NewClient(&mastodon.Config{
54-
Server: "https://mstdn.jp",
55-
ClientID: "client-id",
56-
ClientSecret: "client-secret",
57-
})
58-
err := c.Authenticate(context.Background(), "your-email", "your-password")
59-
if err != nil {
60-
log.Fatal(err)
61-
}
62-
timeline, err := c.GetTimelineHome(context.Background(), nil)
63-
if err != nil {
64-
log.Fatal(err)
65-
}
66-
for i := len(timeline) - 1; i >= 0; i-- {
67-
fmt.Println(timeline[i])
68-
}
69-
}
70-
```
13+
### User Credentials
14+
15+
This method is the simplest and allows you to use an application registered in your account to interact with the Mastodon API on your behalf.
16+
17+
* Create an application on Mastodon by navigating to: Preferences > Development > New Application
18+
* Select the necessary scopes
19+
20+
**Working example:** [examples/user-credentials/main.go](./examples/user-credentials/main.go)
21+
22+
### Public Application
23+
24+
Public applications use application tokens and have limited access to the API, allowing access only to public data.
25+
26+
**Learn more at:** [Mastodon docs](https://docs.joinmastodon.org/client/token/)
27+
28+
**Working example:** [examples/public-application/main.go](./examples/public-application/main.go)
29+
30+
### Application with Client Credentials (OAuth)
31+
32+
This option allows you to create an application that can interact with the Mastodon API on behalf of a user. It registers the application and requests user authorization to obtain an access token.
33+
34+
**Learn more at:** [Mastodon docs](https://docs.joinmastodon.org/client/authorized/)
35+
36+
**Working example:** [examples/user-oauth-authorization/main.go](./examples/user-oauth-authorization/main.go)
7137

72-
### Client with Token
73-
This option lets the user avoid storing login credentials in the application. Instead, the user's Mastodon server
74-
provides an access token which is used to authenticate. This token can be stored in the application, but should be guarded.
75-
76-
```go
77-
package main
78-
79-
import (
80-
"context"
81-
"fmt"
82-
"log"
83-
"net/url"
84-
85-
"github.com/mattn/go-mastodon"
86-
)
87-
88-
func main() {
89-
appConfig := &mastodon.AppConfig{
90-
Server: "https://stranger.social",
91-
ClientName: "client-name",
92-
Scopes: "read write follow",
93-
Website: "https://github.com/mattn/go-mastodon",
94-
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
95-
}
96-
app, err := mastodon.RegisterApp(context.Background(), appConfig)
97-
if err != nil {
98-
log.Fatal(err)
99-
}
100-
101-
// Have the user manually get the token and send it back to us
102-
u, err := url.Parse(app.AuthURI)
103-
if err != nil {
104-
log.Fatal(err)
105-
}
106-
fmt.Printf("Open your browser to \n%s\n and copy/paste the given token\n", u)
107-
var token string
108-
fmt.Print("Paste the token here:")
109-
fmt.Scanln(&token)
110-
config := &mastodon.Config{
111-
Server: "https://stranger.social",
112-
ClientID: app.ClientID,
113-
ClientSecret: app.ClientSecret,
114-
AccessToken: token,
115-
}
116-
117-
c := mastodon.NewClient(config)
118-
err = c.AuthenticateToken(context.Background(), token, "urn:ietf:wg:oauth:2.0:oob")
119-
if err != nil {
120-
log.Fatal(err)
121-
}
122-
123-
acct, err := c.GetAccountCurrentUser(context.Background())
124-
if err != nil {
125-
log.Fatal(err)
126-
}
127-
fmt.Printf("Account is %v\n", acct)
128-
129-
finalText := "This is the content of my new post!"
130-
visibility := "public"
131-
132-
// Post a toot
133-
toot := mastodon.Toot{
134-
Status: finalText,
135-
Visibility: visibility,
136-
}
137-
post, err := c.PostStatus(context.Background(), &toot)
138-
139-
if err != nil {
140-
log.Fatalf("%#v\n", err)
141-
}
142-
143-
fmt.Printf("My new post is %v\n", post)
144-
}
145-
```
14638

14739
## Status of implementations
14840

examples/public-application/main.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
"github.com/mattn/go-mastodon"
9+
)
10+
11+
func main() {
12+
// Register the application
13+
appConfig := &mastodon.AppConfig{
14+
Server: "https://mastodon.social",
15+
ClientName: "publicApp",
16+
Scopes: "read write push",
17+
Website: "https://github.com/mattn/go-mastodon",
18+
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
19+
}
20+
21+
app, err := mastodon.RegisterApp(context.Background(), appConfig)
22+
if err != nil {
23+
log.Fatal(err)
24+
}
25+
26+
config := &mastodon.Config{
27+
Server: "https://mastodon.social",
28+
ClientID: app.ClientID,
29+
ClientSecret: app.ClientSecret,
30+
}
31+
32+
// Create the client
33+
c := mastodon.NewClient(config)
34+
35+
// Get an Access Token & Sets it in the client config
36+
err = c.GetAppAccessToken(context.Background(), app.RedirectURI)
37+
if err != nil {
38+
log.Fatal(err)
39+
}
40+
41+
// Save credentials for later usage if you wish to do so, config file, database, etc...
42+
fmt.Println("ClientID:", c.Config.ClientID)
43+
fmt.Println("ClientSecret:", c.Config.ClientSecret)
44+
fmt.Println("Access Token:", c.Config.AccessToken)
45+
46+
// Lookup and account id
47+
acc, err := c.AccountLookup(context.Background(), "coolapso")
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
fmt.Println(acc)
52+
53+
pager := mastodon.Pagination{
54+
Limit: 10,
55+
}
56+
57+
// Get the the usernames of users following the account ID
58+
followers, err := c.GetAccountFollowers(context.Background(), acc.ID, &pager)
59+
if err != nil {
60+
log.Fatal(err)
61+
}
62+
63+
for _, f := range followers {
64+
fmt.Println(f.Username)
65+
}
66+
}

examples/user-credentials/main.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
"github.com/mattn/go-mastodon"
9+
)
10+
11+
// Create client with credentials from user generated application
12+
func main() {
13+
config := &mastodon.Config{
14+
Server: "https://mastodon.social",
15+
ClientID: "ClientKey",
16+
ClientSecret: "ClientSecret",
17+
AccessToken: "AccessToken",
18+
}
19+
20+
// Create the client
21+
c := mastodon.NewClient(config)
22+
23+
// Post a toot
24+
finalText := "this is the content of my new post!"
25+
visibility := "public"
26+
27+
toot := mastodon.Toot{
28+
Status: finalText,
29+
Visibility: visibility,
30+
}
31+
32+
post, err := c.PostStatus(context.Background(), &toot)
33+
if err != nil {
34+
log.Fatalf("%#v\n", err)
35+
}
36+
37+
fmt.Println("My new post is:", post)
38+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/url"
8+
"os"
9+
10+
"github.com/mattn/go-mastodon"
11+
)
12+
13+
func ConfigureClient() {
14+
appConfig := &mastodon.AppConfig{
15+
Server: "https://mastodon.social",
16+
ClientName: "publicApp",
17+
Scopes: "read write follow",
18+
Website: "https://github.com/mattn/go-mastodon",
19+
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
20+
}
21+
22+
app, err := mastodon.RegisterApp(context.Background(), appConfig)
23+
if err != nil {
24+
log.Fatal(err)
25+
}
26+
27+
// Have the user manually get the token and send it back to us
28+
u, err := url.Parse(app.AuthURI)
29+
if err != nil {
30+
log.Fatal(err)
31+
}
32+
fmt.Printf("Open your browser to \n%s\n and copy/paste the given authroization code\n", u)
33+
var userAuthorizationCode string
34+
fmt.Print("Paste the code here:")
35+
fmt.Scanln(&userAuthorizationCode)
36+
37+
config := &mastodon.Config{
38+
Server: "https://mastodon.social",
39+
ClientID: app.ClientID,
40+
ClientSecret: app.ClientSecret,
41+
}
42+
43+
// Create the client
44+
c := mastodon.NewClient(config)
45+
46+
// Exchange the User authentication code with an access token, that can be used to interact with the api on behalf of the user
47+
err = c.GetUserAccessToken(context.Background(), userAuthorizationCode, app.RedirectURI)
48+
if err != nil {
49+
log.Fatal(err)
50+
}
51+
52+
// Lets Export the secrets so we can use them later to preform actions on behalf of the user
53+
// Without having to request authroization all the time.
54+
// Exporting this as Environment variables, but it can be a configuration file, or database, anywhere you'd like to keep this credentials
55+
os.Setenv("MASTODON_CLIENT_ID", c.Config.ClientID)
56+
os.Setenv("MASTODON_CLIENT_SECRET", c.Config.ClientSecret)
57+
os.Setenv("MASTODON_ACCESS_TOKEN", c.Config.AccessToken)
58+
}
59+
60+
// Preform user actions wihtout having to re-authenticate again
61+
func doUserActions() {
62+
// Load Environment variables, config file, secrets from db
63+
clientID := os.Getenv("MASTODON_CLIENT_ID")
64+
clientSecret := os.Getenv("MASTODON_CLIENT_SECRET")
65+
accessToken := os.Getenv("MASTODON_ACCESS_TOKEN")
66+
67+
config := &mastodon.Config{
68+
Server: "https://mastodon.social",
69+
ClientID: clientID,
70+
ClientSecret: clientSecret,
71+
AccessToken: accessToken,
72+
}
73+
74+
// instanciate the new client
75+
c := mastodon.NewClient(config)
76+
77+
// Let's do some actions on behalf of the user!
78+
acct, err := c.GetAccountCurrentUser(context.Background())
79+
if err != nil {
80+
log.Fatal(err)
81+
}
82+
fmt.Printf("Account is %v\n", acct)
83+
84+
finalText := "this is the content of my new post!"
85+
visibility := "public"
86+
87+
// Post a toot
88+
toot := mastodon.Toot{
89+
Status: finalText,
90+
Visibility: visibility,
91+
}
92+
post, err := c.PostStatus(context.Background(), &toot)
93+
94+
if err != nil {
95+
log.Fatalf("%#v\n", err)
96+
}
97+
98+
fmt.Printf("My new post is %v\n", post)
99+
100+
}
101+
102+
func main() {
103+
ConfigureClient()
104+
doUserActions()
105+
}

go.work.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
22
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
3+
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
4+
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
5+
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
36
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
7+
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
48
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
9+
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
510
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)