sftpgo/examples/OTP/authy/keyint/main.go
Nicola Murino ca3e15578e
Use new methods in the io and os packages instead of ioutil ones
ioutil is deprecated in Go 1.16 and SFTPGo is an application, not
a library, we have no reason to keep compatibility with old Go
versions.

Go 1.16 fix some cifs related issues too.
2021-02-25 21:53:04 +01:00

137 lines
3 KiB
Go

package main
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
type userMapping struct {
SFTPGoUsername string
AuthyID int64
AuthyAPIKey string
}
type keyboardAuthHookResponse struct {
Instruction string `json:"instruction,omitempty"`
Questions []string `json:"questions,omitempty"`
Echos []bool `json:"echos,omitempty"`
AuthResult int `json:"auth_result"`
CheckPwd int `json:"check_password,omitempty"`
}
var (
mapping []userMapping
)
func init() {
// this is for demo only, you probably want to get this mapping dynamically, for example using a database query
mapping = append(mapping, userMapping{
SFTPGoUsername: "<SFTPGo username>",
AuthyID: 1234567,
AuthyAPIKey: "<your api key>",
})
}
func printAuthResponse(result int) {
resp, _ := json.Marshal(keyboardAuthHookResponse{
AuthResult: result,
})
fmt.Printf("%v\n", string(resp))
if result == 1 {
os.Exit(0)
} else {
os.Exit(1)
}
}
func main() {
// get credentials from env vars
username := os.Getenv("SFTPGO_AUTHD_USERNAME")
var userMap userMapping
for _, m := range mapping {
if m.SFTPGoUsername == username {
userMap = m
break
}
}
if userMap.SFTPGoUsername != username {
// no mapping found
os.Exit(1)
}
checkPwdQuestion := keyboardAuthHookResponse{
Instruction: "This is a sample keyboard authentication program that ask for your password + Authy token",
Questions: []string{"Your password: "},
Echos: []bool{false},
CheckPwd: 1,
AuthResult: 0,
}
q, _ := json.Marshal(checkPwdQuestion)
fmt.Printf("%v\n", string(q))
// in a real world app you probably want to use a read timeout
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
if scanner.Err() != nil {
printAuthResponse(-1)
}
response := scanner.Text()
if response != "OK" {
printAuthResponse(-1)
}
checkTokenQuestion := keyboardAuthHookResponse{
Instruction: "",
Questions: []string{"Authy token: "},
Echos: []bool{false},
CheckPwd: 0,
AuthResult: 0,
}
q, _ = json.Marshal(checkTokenQuestion)
fmt.Printf("%v\n", string(q))
scanner.Scan()
if scanner.Err() != nil {
printAuthResponse(-1)
}
authyToken := scanner.Text()
url := fmt.Sprintf("https://api.authy.com/protected/json/verify/%v/%v", authyToken, userMap.AuthyID)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
printAuthResponse(-1)
}
req.Header.Set("X-Authy-API-Key", userMap.AuthyAPIKey)
httpClient := &http.Client{
Timeout: 10 * time.Second,
}
resp, err := httpClient.Do(req)
if err != nil {
printAuthResponse(-1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
// status code 200 is expected
printAuthResponse(-1)
}
var authyResponse map[string]interface{}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
printAuthResponse(-1)
}
err = json.Unmarshal(respBody, &authyResponse)
if err != nil {
printAuthResponse(-1)
}
if authyResponse["success"].(string) == "true" {
printAuthResponse(1)
}
printAuthResponse(-1)
}