|
|
|
@ -29,6 +29,7 @@ var (
|
|
|
|
|
type Instance struct {
|
|
|
|
|
Secret string `json:"client_secret"`
|
|
|
|
|
ID string `json:"client_id"`
|
|
|
|
|
Host string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type App struct {
|
|
|
|
@ -47,7 +48,14 @@ type AppConfig struct {
|
|
|
|
|
Website string `yaml:"website"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) authRequestWebSocket(c echo.Context) error {
|
|
|
|
|
type AuthToken struct {
|
|
|
|
|
RequestID string
|
|
|
|
|
Token string `json:"access_token"`
|
|
|
|
|
Error string `json:"error"`
|
|
|
|
|
ErrorDescription string `json:"error_description"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) AuthRequestWebSocket(c echo.Context) error {
|
|
|
|
|
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -71,11 +79,119 @@ func (a *App) authRequestWebSocket(c echo.Context) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) GetInstanceDataByRequest(request *AuthRequest) (*Instance, error) {
|
|
|
|
|
func (a *App) AuthRequestConfirm(c echo.Context) error {
|
|
|
|
|
code := c.QueryParam("code")
|
|
|
|
|
reqID := c.QueryParam("state")
|
|
|
|
|
|
|
|
|
|
if code == "" || reqID == "" {
|
|
|
|
|
return c.String(
|
|
|
|
|
http.StatusUnprocessableEntity,
|
|
|
|
|
"Could not process request, missing required parameters",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
authRequest := a.AuthMap.GetRequestByID(reqID)
|
|
|
|
|
|
|
|
|
|
if authRequest == nil {
|
|
|
|
|
(*a.Logger).Errorf("Could not find auth request by ID %s", reqID)
|
|
|
|
|
c.String(http.StatusNotFound, "Auth request with given ID not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance, err := a.GetCredentialsForInstanceHost(authRequest.Instance)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
(*a.Logger).Errorf(
|
|
|
|
|
"Was not able to retrieve instance credentials for host %s during confirm action: %s",
|
|
|
|
|
authRequest.Instance,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return c.String(
|
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
|
"Something has gone wrong, please note down the current time and contact the server admin",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token, err := a.GetAuthTokenForAuthorizationCode(code, instance)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
(*a.Logger).Errorf(
|
|
|
|
|
"Failed to retreive acces token from instance %s: %s",
|
|
|
|
|
instance.Host,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return c.String(
|
|
|
|
|
http.StatusOK,
|
|
|
|
|
"Something went wrong while retrieving the access token. Please contact the server admin",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = authRequest.FulFill(token)
|
|
|
|
|
a.AuthMap.Delete(authRequest.ID)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
(*a.Logger).Errorf(
|
|
|
|
|
"Unable to fulfill authentication request with ID %s : %s",
|
|
|
|
|
authRequest.ID,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
return c.String(
|
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
|
"Something went wrong with the connection to your client",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.String(http.StatusOK, "Athentication completed, you can close this window now")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) GetAuthTokenForAuthorizationCode(code string, instance *Instance) (*AuthToken, error) {
|
|
|
|
|
data := map[string]string{
|
|
|
|
|
"client_id": instance.ID,
|
|
|
|
|
"client_secret": instance.Secret,
|
|
|
|
|
"code": code,
|
|
|
|
|
"grant_type": "authorization_code",
|
|
|
|
|
"redirect_uri": a.Config.AppScheme + "://" + a.Config.AppHost + "/confirm",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonData, err := json.Marshal(data)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response, err := http.Post("https://"+instance.Host+"/oauth/token", "application/json", bytes.NewBuffer(jsonData))
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
responseData, err := ioutil.ReadAll(response.Body)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
authToken := &AuthToken{}
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(responseData, authToken)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if authToken.Error != "" {
|
|
|
|
|
return nil, errors.New(authToken.Error + ": " + authToken.ErrorDescription)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return authToken, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) GetCredentialsForInstanceHost(host string) (*Instance, error) {
|
|
|
|
|
var instance *Instance
|
|
|
|
|
|
|
|
|
|
err := a.DB.View(func(t *bbolt.Tx) error {
|
|
|
|
|
instanceJson := t.Bucket([]byte("instances")).Get([]byte(request.Instance))
|
|
|
|
|
instanceJson := t.Bucket([]byte("instances")).Get([]byte(host))
|
|
|
|
|
|
|
|
|
|
if instanceJson == nil {
|
|
|
|
|
instance = nil
|
|
|
|
@ -90,9 +206,9 @@ func (a *App) GetInstanceDataByRequest(request *AuthRequest) (*Instance, error)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if instance == nil && err == nil {
|
|
|
|
|
(*a.Logger).Infof("No instance data known for host '%s', requesting credentials from host\n", request.Instance)
|
|
|
|
|
(*a.Logger).Infof("No instance data known for host '%s', requesting credentials from host\n", host)
|
|
|
|
|
|
|
|
|
|
instance, err = a.GetCredentialsFromInstanceHost(request.Instance)
|
|
|
|
|
instance, err = a.GetCredentialsFromInstanceHost(host)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@ -105,18 +221,16 @@ func (a *App) GetInstanceDataByRequest(request *AuthRequest) (*Instance, error)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = t.Bucket([]byte("instances")).Put([]byte(request.Instance), instanceJson)
|
|
|
|
|
err = t.Bucket([]byte("instances")).Put([]byte(host), instanceJson)
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return instance, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
return instance, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) authRequestRedirect(c echo.Context) error {
|
|
|
|
|
func (a *App) AuthRequestRedirect(c echo.Context) error {
|
|
|
|
|
requestId := c.Param("request_id")
|
|
|
|
|
|
|
|
|
|
authRequest := a.AuthMap.GetRequestByID(requestId)
|
|
|
|
@ -125,10 +239,14 @@ func (a *App) authRequestRedirect(c echo.Context) error {
|
|
|
|
|
return c.String(http.StatusNotFound, "No authentication request found by this ID")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance, err := a.GetInstanceDataByRequest(authRequest)
|
|
|
|
|
instance, err := a.GetCredentialsForInstanceHost(authRequest.Instance)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
(*a.Logger).Error(err)
|
|
|
|
|
(*a.Logger).Errorf(
|
|
|
|
|
"Failed to get credentials for instance host %s: %s",
|
|
|
|
|
authRequest.Instance,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if instance == nil {
|
|
|
|
@ -143,6 +261,7 @@ func (a *App) authRequestRedirect(c echo.Context) error {
|
|
|
|
|
"redirect_uri": []string{a.Config.AppScheme + "://" + a.Config.AppHost + "/confirm"},
|
|
|
|
|
"scopes": []string{strings.Join(a.Config.AppScopes, " ")},
|
|
|
|
|
"response_type": []string{"code"},
|
|
|
|
|
"state": []string{authRequest.ID},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.Redirect(
|
|
|
|
@ -237,8 +356,9 @@ func main() {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e.GET("/ws", a.authRequestWebSocket)
|
|
|
|
|
e.GET("/auth/:request_id", a.authRequestRedirect)
|
|
|
|
|
e.GET("/ws", a.AuthRequestWebSocket)
|
|
|
|
|
e.GET("/auth/:request_id", a.AuthRequestRedirect)
|
|
|
|
|
e.GET("/confirm", a.AuthRequestConfirm)
|
|
|
|
|
e.Logger.Fatal(e.Start(":1323"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -268,7 +388,9 @@ func (a *App) GetCredentialsFromInstanceHost(host string) (*Instance, error) {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instance := &Instance{}
|
|
|
|
|
instance := &Instance{
|
|
|
|
|
Host: host,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(body, instance)
|
|
|
|
|
|
|
|
|
|