144 lines
3.4 KiB
Go
144 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os/exec"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
var errEmptyAccessToken = errors.New("received an empty access token")
|
|
|
|
var consentMessageFormat = `
|
|
You'll need to sign into your GoToSocial's consent page in order to generate the out-of-band token to continue with
|
|
the application's login process. Your browser may have opened the link to the consent page already. If not, please
|
|
copy and paste the link below to your browser:
|
|
|
|
%s
|
|
|
|
Once you have the code please copy and paste it below.
|
|
|
|
`
|
|
|
|
func loginWithOauth2() error {
|
|
var err error
|
|
|
|
var instance string
|
|
|
|
fmt.Print("Please enter the instance URL: ")
|
|
|
|
if _, err := fmt.Scanln(&instance); err != nil {
|
|
return fmt.Errorf("unable to read user input for the instance value; %w", err)
|
|
}
|
|
|
|
if !strings.HasPrefix(instance, "https") || !strings.HasPrefix(instance, "http") {
|
|
instance = "https://" + instance
|
|
}
|
|
|
|
for strings.HasSuffix(instance, "/") {
|
|
instance = instance[:len(instance)-1]
|
|
}
|
|
|
|
authentication := Authentication{
|
|
Instance: instance,
|
|
}
|
|
|
|
client := newGtsClient(authentication)
|
|
|
|
if err := client.register(); err != nil {
|
|
return fmt.Errorf("unable to register the application; %w", err)
|
|
}
|
|
|
|
consentPageURL := authCodeURL(client.authentication)
|
|
|
|
openLink(consentPageURL)
|
|
|
|
fmt.Printf(consentMessageFormat, consentPageURL)
|
|
|
|
var code string
|
|
fmt.Print("Out-of-band token: ")
|
|
|
|
if _, err := fmt.Scanln(&code); err != nil {
|
|
return fmt.Errorf("failed to read access code; %w", err)
|
|
}
|
|
|
|
client.authentication, err = addAccessToken(client.authentication, code)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to get the access token; %w", err)
|
|
}
|
|
|
|
account, err := client.verifyCredentials()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to verify the credentials; %w", err)
|
|
}
|
|
|
|
loginName, err := saveAuthenticationConfig(account.Username, client.authentication)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to save the authentication details; %w", err)
|
|
}
|
|
|
|
fmt.Printf("Successfully logged into %s\n", loginName)
|
|
|
|
return nil
|
|
}
|
|
|
|
func authCodeURL(account Authentication) string {
|
|
config := oauth2.Config{
|
|
ClientID: account.ClientID,
|
|
ClientSecret: account.ClientSecret,
|
|
Scopes: []string{"read"},
|
|
RedirectURL: redirectUri,
|
|
Endpoint: oauth2.Endpoint{
|
|
AuthURL: account.Instance + "/oauth/authorize",
|
|
TokenURL: account.Instance + "/oauth/token",
|
|
},
|
|
}
|
|
|
|
url := config.AuthCodeURL("state", oauth2.AccessTypeOffline) + fmt.Sprintf("&client_name=%s", applicationName)
|
|
|
|
return url
|
|
}
|
|
|
|
func addAccessToken(authentication Authentication, code string) (Authentication, error) {
|
|
config := oauth2.Config{
|
|
ClientID: authentication.ClientID,
|
|
ClientSecret: authentication.ClientSecret,
|
|
Scopes: []string{"read", "write"},
|
|
RedirectURL: redirectUri,
|
|
Endpoint: oauth2.Endpoint{
|
|
AuthURL: authentication.Instance + "/oauth/authorize",
|
|
TokenURL: authentication.Instance + "/oauth/token",
|
|
},
|
|
}
|
|
|
|
token, err := config.Exchange(context.Background(), code)
|
|
if err != nil {
|
|
return Authentication{}, fmt.Errorf("unable to exchange the code for an access token; %w", err)
|
|
}
|
|
|
|
if token == nil || token.AccessToken == "" {
|
|
return Authentication{}, errEmptyAccessToken
|
|
}
|
|
|
|
authentication.AccessToken = token.AccessToken
|
|
|
|
return authentication, nil
|
|
}
|
|
|
|
func openLink(url string) {
|
|
var open string
|
|
|
|
if runtime.GOOS == "linux" {
|
|
open = "xdg-open"
|
|
} else {
|
|
return
|
|
}
|
|
|
|
command := exec.Command(open, url)
|
|
|
|
_ = command.Start()
|
|
}
|