enbas/login.go

142 lines
3.4 KiB
Go
Raw Normal View History

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)
}
if err := saveAuthenticationConfig(account.Username, client.authentication); err != nil {
return fmt.Errorf("unable to save the authentication details; %w", err)
}
return nil
}
func authCodeURL(account Authentication) string {
config := oauth2.Config{
ClientID: account.ClientID,
ClientSecret: account.ClientSecret,
Scopes: []string{"read", "write"},
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()
}