From: Adam Shamblin Date: Sun, 11 Dec 2022 20:01:32 +0000 (-0700) Subject: Add Account type, getting ready for making real API calls X-Git-Url: https://git.vexinglabs.com/?a=commitdiff_plain;h=d755aaaf9632afedf65247c6606485935f8c5bb8;p=dead-tooter.git Add Account type, getting ready for making real API calls --- diff --git a/main.go b/main.go index 54131b6..8c18a42 100644 --- a/main.go +++ b/main.go @@ -1,31 +1,16 @@ package main import ( - "fmt" - "git.vexingworkshop.com/signal9/dead-tooter/pkg/mastodon" ) func main() { app := mastodon.Application{} err := app.Load("dead-tooter") - /* - app, err := mastodon.App{ - ClientName: "dead-tooter", - RedirectUris: "urn:ietf:wg:oauth:2.0:oob", - Scopes: "read write follow push", - Website: "https://dead-tooter.vexingworkshop.com", - }.Create() - */ - if err != nil { panic(err.Error()) } - //app.Save() - - fmt.Printf("%v", app) - - bearer := app.Login() - fmt.Printf("%v", bearer) + token := app.Login() + app.VerifyCredentials(token) } diff --git a/pkg/mastodon/account.go b/pkg/mastodon/account.go new file mode 100644 index 0000000..a9240cd --- /dev/null +++ b/pkg/mastodon/account.go @@ -0,0 +1,70 @@ +package mastodon + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os/exec" +) + +type Account struct { +} + +func (a *Account) Authorize(app Application) (code string) { + v := url.Values{} + v.Set("client_id", app.ClientID) + v.Set("response_type", "code") + v.Set("redirect_uri", RedirectUris) + + u := url.URL{ + Host: "hackers.town", + Path: "oauth/authorize", + RawQuery: v.Encode(), + } + u.Scheme = "https" + + err := exec.Command("xdg-open", u.String()).Start() + if err != nil { + panic(err.Error()) + } + + fmt.Print("Enter returned code: ") + fmt.Scanln(&code) + + return +} + +func (a *Account) RequestToken(app Application, code string) (token Token) { + v := url.Values{} + v.Set("client_id", app.ClientID) + v.Set("code", code) + v.Set("redirect_uri", app.RedirectURI) + v.Set("grant_type", "authorization_code") + + u := url.URL{ + Host: "hackers.town", + Path: "oauth/token", + RawQuery: v.Encode(), + } + u.Scheme = "https" + + resp, err := http.PostForm(u.String(), v) + if err != nil { + panic(err.Error()) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + panic(err.Error()) + } + + err = json.Unmarshal(body, &token) + if err != nil { + panic(err.Error()) + } + + return +} diff --git a/pkg/mastodon/app.go b/pkg/mastodon/app.go index 8d42b74..69d8898 100644 --- a/pkg/mastodon/app.go +++ b/pkg/mastodon/app.go @@ -7,6 +7,10 @@ import ( "net/url" ) +// RedirectUris when passed to the redirect_uris parameter, will +// show return the authorization code instead of redirecting the client. +const RedirectUris = "urn:ietf:wg:oauth:2.0:oob" + // App represents the basic and methods necessary to register an application // with a Mastodon server. type App struct { @@ -18,7 +22,14 @@ type App struct { // Create calls the Mastodon API to register the App. It returns an Application // instance. -func (a App) Create() (application Application, err error) { +// +// app, err := mastodon.App{ +// ClientName: "dead-tooter", +// RedirectUris: mastodon.RedirectUris, +// Scopes: "read write follow push", +// Website: "https://dead-tooter.vexingworkshop.com", +// }.Create() +func (a *App) Create() (application Application, err error) { resp, err := http.PostForm("https://hackers.town/api/v1/apps", url.Values{ "client_name": {a.ClientName}, diff --git a/pkg/mastodon/application.go b/pkg/mastodon/application.go index 47b8d72..1b4fe5d 100644 --- a/pkg/mastodon/application.go +++ b/pkg/mastodon/application.go @@ -2,6 +2,7 @@ package mastodon import ( "encoding/json" + "fmt" "io" "net/http" "net/url" @@ -10,6 +11,14 @@ import ( const configdir = "foodir/" +// Token struct contains the data returned by the Application login request. +type Token struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + CreatedAt int `json:"created_at"` +} + // Application represents the data and functions that establish a // Mastodon API application. type Application struct { @@ -23,7 +32,7 @@ type Application struct { } // Save will store a serialized version of the Application struct to a file. -func (a Application) Save() { +func (a *Application) Save() { err := os.Mkdir(configdir, 0750) if err != nil && !os.IsExist(err) { panic(err.Error()) @@ -58,7 +67,7 @@ func (a *Application) Load(name string) error { // Login authenticates the application to the Mastodon API, returning // a bearer token to be used with future requests. -func (a Application) Login() (bearer Bearer) { +func (a *Application) Login() (token Token) { resp, err := http.PostForm("https://hackers.town/oauth/token", url.Values{ "client_id": {a.ClientID}, @@ -76,7 +85,7 @@ func (a Application) Login() (bearer Bearer) { panic(err.Error()) } - err = json.Unmarshal(body, &bearer) + err = json.Unmarshal(body, &token) if err != nil { panic(err.Error()) } @@ -84,10 +93,36 @@ func (a Application) Login() (bearer Bearer) { return } -// Bearer struct contains the data returned by the Application login request. -type Bearer struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - Scope string `json:"scope"` - CreatedAt int `json:"created_at"` +// VerifyCredentials accepts a Token object and validates the contained +// token against the Mastodon API. +func (a *Application) VerifyCredentials(token Token) { + client := &http.Client{} + + req, err := http.NewRequest("GET", + "https://hackers.town/api/v1/apps/verify_credentials", nil) + if err != nil { + panic(err.Error()) + } + req.Header.Add("Authorization", "Bearer "+token.AccessToken) + + resp, err := client.Do(req) + if err != nil { + panic(err.Error()) + } + defer resp.Body.Close() + + fmt.Printf("%s", resp.Status) + + body, err := io.ReadAll(resp.Body) + if err != nil { + panic(err.Error()) + } + + var dummy Application + err = json.Unmarshal(body, &dummy) + if err != nil { + panic(err.Error()) + } + + fmt.Printf("%+v", dummy) }