mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 23:00:55 +03:00
add some examples hooks for one time password logins
The examples use Twillo Authy since I use it for my GitHub account. You can easily use other multi factor authentication software in a similar way.
This commit is contained in:
137
examples/OTP/authy/keyint/main.go
Normal file
137
examples/OTP/authy/keyint/main.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"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 := ioutil.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)
|
||||
}
|
||||
Reference in New Issue
Block a user