add token creation
This commit is contained in:
parent
ac2830ba0d
commit
ad3ecadfb1
|
@ -0,0 +1,33 @@
|
|||
# Constanze
|
||||
|
||||
A little cli tool to help you interact with akkoma instances.
|
||||
|
||||
Helpful when dealing with operations that are not yet supported by the web interface.
|
||||
|
||||
## Installation
|
||||
|
||||
download the latest release from the [releases page](./releases) and put it somewhere in your path.
|
||||
|
||||
## Usage
|
||||
|
||||
Use `./constanze --help` to get a list of all available commands.
|
||||
|
||||
### Configuration
|
||||
|
||||
A basic configuration file can be generated with:
|
||||
```bash
|
||||
./constanze configure
|
||||
```
|
||||
|
||||
### Creating a new oauth application
|
||||
|
||||
```bash
|
||||
./constanze token --client-app --scopes "read write" --client-name "My App"
|
||||
```
|
||||
|
||||
### Debug mode
|
||||
|
||||
If you want to see the requests and responses, you can enable debug mode with:
|
||||
```bash
|
||||
./constanze --debug <command>
|
||||
```
|
71
actions.go
71
actions.go
|
@ -1,9 +1,78 @@
|
|||
package main
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import (
|
||||
"github.com/pkg/browser"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func setDebugLogging(cCtx *cli.Context) {
|
||||
if cCtx.Bool("debug") {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log.Debug("Debug logging enabled")
|
||||
} else {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
}
|
||||
}
|
||||
|
||||
func Configure(cCtx *cli.Context) error {
|
||||
setDebugLogging(cCtx)
|
||||
instanceUrl := ReadLine("Instance URL:")
|
||||
config := Config{InstanceUrl: instanceUrl}
|
||||
return SaveConfigFromContext(cCtx, config)
|
||||
}
|
||||
|
||||
func Login(cCtx *cli.Context) error {
|
||||
setDebugLogging(cCtx)
|
||||
config, err := LoadConfigFromContext(cCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
scopes := cCtx.String("scopes")
|
||||
clientApp := cCtx.Bool("client-app")
|
||||
clientName := cCtx.String("client-name")
|
||||
|
||||
log.Info("Creating OAuth app")
|
||||
oauthApp, err := CreateOAuthApp(config.InstanceUrl, clientName, scopes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.OAuthApp = oauthApp
|
||||
if !clientApp {
|
||||
err = SaveConfigFromContext(cCtx, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Info("OAuth app created")
|
||||
|
||||
log.Info("Opening browser")
|
||||
authUrl, err := AuthorizeUrl(config.InstanceUrl, config.OAuthApp, cCtx.String("scopes"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Auth URL: ", authUrl)
|
||||
err = browser.OpenURL(authUrl)
|
||||
if err != nil {
|
||||
log.Infof("Could not open browser, please open the following URL in your browser: %s", authUrl)
|
||||
}
|
||||
authCode := ReadLine("Auth code:")
|
||||
oauthToken, err := RequestToken(config.InstanceUrl, config.OAuthApp, scopes, authCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.OAuthToken = oauthToken
|
||||
if !clientApp {
|
||||
err = SaveConfigFromContext(cCtx, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if clientApp {
|
||||
log.Info("Client app token:")
|
||||
log.Infof("%s %s", oauthToken.TokenType, oauthToken.AccessToken)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
InstanceUrl string `yaml:"instance_url"`
|
||||
InstanceUrl string `yaml:"instance_url"`
|
||||
OAuthApp OAuthApp `yaml:"oauth_app"`
|
||||
OAuthToken OAuthToken `yaml:"oauth_token"`
|
||||
}
|
||||
|
||||
func LoadConfig(configLocation string) (Config, error) {
|
||||
|
|
4
go.mod
4
go.mod
|
@ -4,6 +4,8 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/go-playground/validator/v10 v10.11.1
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/urfave/cli/v2 v2.23.7
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
@ -16,6 +18,6 @@ require (
|
|||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
)
|
||||
|
|
8
go.sum
8
go.sum
|
@ -22,6 +22,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -30,6 +32,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
|
|||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
|
@ -44,8 +48,10 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Post(url string, body interface{}) (*http.Response, error) {
|
||||
log.Debugf("POST %s", url)
|
||||
log.Debugf("Body: %v", body)
|
||||
jsonBody, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonBody))
|
||||
log.Debugf("Status: %d", resp.StatusCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
30
main.go
30
main.go
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
|
@ -17,6 +17,11 @@ func main() {
|
|||
Value: "config.yml",
|
||||
Usage: "Point to your config file",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Value: false,
|
||||
Usage: "Enable debug logging",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -32,5 +37,28 @@ func commands() []*cli.Command {
|
|||
Usage: "Set up costanze",
|
||||
Action: Configure,
|
||||
},
|
||||
{
|
||||
Name: "login",
|
||||
Usage: "Log in to your instance",
|
||||
Aliases: []string{"token"},
|
||||
Action: Login,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "scopes",
|
||||
Value: "read write follow push",
|
||||
Usage: "Scopes to request",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "client-app",
|
||||
Value: false,
|
||||
Usage: "Create a new oauth app, but just print out the credentials for later use",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "client-name",
|
||||
Value: "costanze",
|
||||
Usage: "Name of the client app",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type OAuthApp struct {
|
||||
ClientId string `json:"client_id" yaml:"client_id"`
|
||||
ClientSecret string `json:"client_secret" yaml:"client_secret"`
|
||||
}
|
||||
|
||||
type OAuthToken struct {
|
||||
AccessToken string `json:"access_token" yaml:"access_token"`
|
||||
Me string `json:"me" yaml:"me"`
|
||||
RefreshToken string `json:"refresh_token" yaml:"refresh_token"`
|
||||
Scope string `json:"scope" yaml:"scope"`
|
||||
TokenType string `json:"token_type" yaml:"token_type"`
|
||||
}
|
||||
|
||||
func CreateOAuthApp(instanceUrl string, clientName string, scopes string) (OAuthApp, error) {
|
||||
uri, err := url.JoinPath(instanceUrl, "/api/v1/apps")
|
||||
if err != nil {
|
||||
return OAuthApp{}, err
|
||||
}
|
||||
request := map[string]interface{}{
|
||||
"client_name": clientName,
|
||||
"redirect_uris": "urn:ietf:wg:oauth:2.0:oob",
|
||||
"scopes": scopes,
|
||||
}
|
||||
response, err := Post(uri, request)
|
||||
if err != nil {
|
||||
return OAuthApp{}, err
|
||||
}
|
||||
body, err := io.ReadAll(response.Body)
|
||||
app := &OAuthApp{}
|
||||
err = json.Unmarshal(body, app)
|
||||
if err != nil {
|
||||
return OAuthApp{}, err
|
||||
}
|
||||
return *app, nil
|
||||
}
|
||||
|
||||
func RequestToken(instanceUrl string, oauthApp OAuthApp, scopes string, code string) (OAuthToken, error) {
|
||||
log.Debug("Requesting token")
|
||||
args := map[string]interface{}{
|
||||
"client_id": oauthApp.ClientId,
|
||||
"client_secret": oauthApp.ClientSecret,
|
||||
"code": code,
|
||||
"grant_type": "authorization_code",
|
||||
"redirect_uri": "urn:ietf:wg:oauth:2.0:oob",
|
||||
"scope": scopes,
|
||||
}
|
||||
|
||||
tokenUrl, err := url.JoinPath(instanceUrl, "/oauth/token")
|
||||
if err != nil {
|
||||
return OAuthToken{}, err
|
||||
}
|
||||
response, err := Post(tokenUrl, args)
|
||||
if err != nil {
|
||||
return OAuthToken{}, err
|
||||
}
|
||||
body, err := io.ReadAll(response.Body)
|
||||
token := &OAuthToken{}
|
||||
err = json.Unmarshal(body, token)
|
||||
if err != nil {
|
||||
return OAuthToken{}, err
|
||||
}
|
||||
|
||||
return *token, nil
|
||||
}
|
||||
|
||||
func AuthorizeUrl(instanceUrl string, oauthApp OAuthApp, scopes string) (string, error) {
|
||||
args := url.Values{
|
||||
"client_id": {oauthApp.ClientId},
|
||||
"redirect_uri": {"urn:ietf:wg:oauth:2.0:oob"},
|
||||
"response_type": {"code"},
|
||||
"scope": {scopes},
|
||||
}.Encode()
|
||||
authUrl, err := url.JoinPath(instanceUrl, "/oauth/authorize")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return authUrl + "?" + args, nil
|
||||
}
|
Loading…
Reference in New Issue