Implement retrieval and storage of oauth_{client,secret} +http redirect

master
Hugo Thunnissen 4 years ago
parent 4d767c4970
commit 33d4b54eef

@ -4,12 +4,16 @@ go 1.13
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dougm/goflymake v0.0.0-20140731161037-3b9634ef394a // indirect
github.com/gorilla/websocket v1.4.1
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.0
github.com/nsf/gocode v0.0.0-20190302080247-5bee97b48836 // indirect
github.com/oklog/ulid v1.3.1
github.com/rogpeppe/godef v1.1.1 // indirect
github.com/valyala/fasttemplate v1.1.0 // indirect
go.etcd.io/bbolt v1.3.3
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769 // indirect
gopkg.in/yaml.v2 v2.2.2
)

@ -1,7 +1,11 @@
9fans.net/go v0.0.0-20181112161441-237454027057 h1:OcHlKWkAMJEF1ndWLGxp5dnJQkYM/YImUOvsBoz6h5E=
9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dougm/goflymake v0.0.0-20140731161037-3b9634ef394a h1:S4+OSVOtl5a40p2ZgV7rA6YLl0wcjehR2jQVyWK1/UE=
github.com/dougm/goflymake v0.0.0-20140731161037-3b9634ef394a/go.mod h1:5K1XZIXX56t7gg4jDBoVXBH9ABugPa830vLJiD/XPHI=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
@ -13,10 +17,14 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/nsf/gocode v0.0.0-20190302080247-5bee97b48836 h1:oc3CL18CoGhyOQJ7HDa9gJAde33bwI8Vi28zLdIzJVc=
github.com/nsf/gocode v0.0.0-20190302080247-5bee97b48836/go.mod h1:6Q8/OMaaKAgTX7/jt2bOXVDrm1eJhoNd+iwzghR7jvs=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/godef v1.1.1 h1:NujOtt9q9vIClRTB3sCZpavac+NMRaIayzrcz1h4fSE=
github.com/rogpeppe/godef v1.1.1/go.mod h1:oEo1eMy1VUEHUzUIX4F7IqvMJRiz9UId44mvnR8oPlQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -34,6 +42,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2eP
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -41,6 +51,11 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181130195746-895048a75ecf/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769 h1:nIPDpirk90v9eLG0L8usrehSoJ1rWd6wX7BdjAKhZ4I=
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

@ -4,16 +4,22 @@ package main
// from mastodon instances.
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/gorilla/websocket"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
"github.com/labstack/gommon/log"
"go.etcd.io/bbolt"
"gopkg.in/yaml.v2"
)
var (
@ -29,6 +35,16 @@ type App struct {
AuthMap *AuthenticationMap
Logger *echo.Logger
DB *bbolt.DB
Config *AppConfig
}
type AppConfig struct {
AppName string `yaml:"app_name"`
AppHost string `yaml:"app_host"`
AppScheme string `yaml:"app_scheme"`
DBPath string `yaml:"db_path"`
AppScopes []string `yaml:"app_scopes"`
Website string `yaml:"website"`
}
func (a *App) authRequestWebSocket(c echo.Context) error {
@ -55,18 +71,11 @@ func (a *App) authRequestWebSocket(c echo.Context) error {
return nil
}
func (a *App) authRequestRedirect(c echo.Context) error {
requestId := c.Param("request_id")
authRequest := a.AuthMap.GetRequestByID(requestId)
if authRequest == nil {
return c.String(http.StatusNotFound, "No authentication request found by this ID")
}
func (a *App) GetInstanceDataByRequest(request *AuthRequest) (*Instance, error) {
var instance *Instance
a.DB.View(func(t *bbolt.Tx) error {
instanceJson := t.Bucket([]byte("instances")).Get([]byte(authRequest.Instance))
err := a.DB.View(func(t *bbolt.Tx) error {
instanceJson := t.Bucket([]byte("instances")).Get([]byte(request.Instance))
if instanceJson == nil {
instance = nil
@ -80,34 +89,128 @@ func (a *App) authRequestRedirect(c echo.Context) error {
return err
})
return c.String(http.StatusOK, "BLAAT")
if instance == nil && err == nil {
(*a.Logger).Infof("No instance data known for host '%s', requesting credentials from host\n", request.Instance)
instance, err = a.GetCredentialsFromInstanceHost(request.Instance)
//return c.Redirect(http.StatusTemporaryRedirect, )
if err != nil {
return nil, err
}
err = a.DB.Update(func(t *bbolt.Tx) error {
instanceJson, err := json.Marshal(instance)
if err != nil {
return err
}
err = t.Bucket([]byte("instances")).Put([]byte(request.Instance), instanceJson)
return err
})
return instance, err
}
return nil, err
}
func main() {
arguments := os.Args[1:]
func (a *App) authRequestRedirect(c echo.Context) error {
requestId := c.Param("request_id")
if len(arguments) != 2 {
fmt.Fprintln(os.Stderr, "Expected 2 command line argumens, but got : ", len(arguments))
fmt.Fprintln(os.Stderr, "Usage: generic-mastodon-authenticator APP_NAME DB_PATH")
os.Exit(1)
authRequest := a.AuthMap.GetRequestByID(requestId)
if authRequest == nil {
return c.String(http.StatusNotFound, "No authentication request found by this ID")
}
instance, err := a.GetInstanceDataByRequest(authRequest)
if err != nil {
(*a.Logger).Error(err)
}
if instance == nil {
return c.String(
http.StatusInternalServerError,
"Something has gone wrong, please note down the current time and contact the server admin",
)
}
values := &url.Values{
"client_id": []string{instance.ID},
"redirect_uri": []string{a.Config.AppScheme + "://" + a.Config.AppHost + "/confirm"},
"scopes": []string{strings.Join(a.Config.AppScopes, " ")},
"response_type": []string{"code"},
}
database := arguments[1]
return c.Redirect(
http.StatusTemporaryRedirect,
"https://"+authRequest.Instance+"/oauth/authorize?"+values.Encode(),
)
}
db, err := bbolt.Open(database, 0600, nil)
func (c *AppConfig) FromFile(filePath string) error {
yamlfile, err := ioutil.ReadFile(filePath)
if err != nil {
log.Fatal(err)
return err
}
err = yaml.Unmarshal(yamlfile, c)
if err != nil {
return err
}
db.Update(func(t *bbolt.Tx) error {
if c.AppName == "" || c.AppHost == "" || c.DBPath == "" || c.Website == "" {
return errors.New("app_name, app_host, db_path and website are required config parameters")
}
if c.AppScheme == "" {
c.AppScheme = "https"
}
return nil
}
func (a *App) InitializeDB() error {
(*a.Logger).Info("Initializing DB from file: ", a.Config.DBPath)
db, err := bbolt.Open(a.Config.DBPath, 0600, nil)
if err != nil {
return err
}
err = db.Update(func(t *bbolt.Tx) error {
_, err := t.CreateBucketIfNotExists([]byte("instances"))
return err
})
a.DB = db
return nil
}
func main() {
arguments := os.Args[1:]
if len(arguments) != 1 {
fmt.Fprintln(os.Stderr, "Expected 1 command line argument, but got : ", len(arguments))
fmt.Fprintln(os.Stderr, "Usage: generic-mastodon-authenticator CONFIG_PATH")
os.Exit(1)
}
config := &AppConfig{}
err := config.FromFile(arguments[0])
if err != nil {
log.Fatal(err)
}
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
@ -117,7 +220,7 @@ func main() {
a := &App{
Logger: &e.Logger,
DB: db,
Config: config,
}
m := &AuthenticationMap{
@ -128,7 +231,46 @@ func main() {
a.AuthMap = m
err = a.InitializeDB()
if err != nil {
log.Fatal(err)
}
e.GET("/ws", a.authRequestWebSocket)
e.GET("/auth/:request_id", a.authRequestRedirect)
e.Logger.Fatal(e.Start(":1323"))
}
func (a *App) GetCredentialsFromInstanceHost(host string) (*Instance, error) {
postData := &map[string]string{
"client_name": a.Config.AppName,
"redirect_uris": a.Config.AppScheme + "://" + a.Config.AppHost + "/confirm",
"scopes": strings.Join(a.Config.AppScopes, " "),
"website": a.Config.Website,
}
data, err := json.Marshal(postData)
if err != nil {
return nil, err
}
response, err := http.Post("https://"+host+"/api/v1/apps", "application/json", bytes.NewBuffer(data))
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
instance := &Instance{}
err = json.Unmarshal(body, instance)
return instance, err
}

Loading…
Cancel
Save