Skip to content

Commit

Permalink
Add support for writing code on file
Browse files Browse the repository at this point in the history
  • Loading branch information
ua741 committed Oct 14, 2023
1 parent c48a3ec commit b3937cf
Showing 1 changed file with 91 additions and 79 deletions.
170 changes: 91 additions & 79 deletions cmd/authy-export/authy-export.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,96 +28,86 @@ type deviceRegistration struct {
}

func main() {
savePtr := flag.String("save", "", "Save encrypted tokens to this JSON file")
loadPtr := flag.String("load", "", "Load tokens from this JSON file instead of the server")
//savePtr := flag.String("save", "", "Save encrypted tokens to this JSON file")
flag.Parse()

if len(os.Args) != 2 {
fmt.Println("Usage: ./authy-export-darwin-amd64 <export_file> ")
return
}
exportFile, err := resolvePath(os.Args[1])
if err != nil {
fmt.Println("Error resolving exportFile path:", err)
return
}
f, err := os.OpenFile(exportFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Println(err)
}
defer f.Close()

var resp struct {
Tokens authy.AuthenticatorTokensResponse `json:"tokens"`
Apps authy.AuthenticatorAppsResponse `json:"apps"`
}
if *loadPtr != "" {
// Get tokens from the json file
f, err := os.Open(*loadPtr)
if err != nil {
log.Fatalf("Failed to read the file: %v", err)
}
defer f.Close()

err = json.NewDecoder(f).Decode(&resp)
// Get tokens from the server
// If we don't already have a registered device, prompt the user for one
regr, err := loadExistingDeviceRegistration()
if err == nil {
fmt.Println("Found existing device registration")
} else if os.IsNotExist(err) {
fmt.Println("No existing device registration found, will perform registration now")
regr, err = newInteractiveDeviceRegistration()
if err != nil {
log.Fatalf("Failed to decode the file: %v", err)
}
} else {
// Get tokens from the server
// If we don't already have a registered device, prompt the user for one
regr, err := loadExistingDeviceRegistration()
if err == nil {
log.Println("Found existing device registration")
} else if os.IsNotExist(err) {
log.Println("No existing device registration found, will perform registration now")
regr, err = newInteractiveDeviceRegistration()
if err != nil {
log.Fatalf("Device registration failed: %v", err)
}
} else if err != nil {
log.Fatalf("Could not check for existing device registration: %v", err)
log.Fatalf("Device registration failed: %v", err)
}
} else if err != nil {
log.Fatalf("Could not check for existing device registration: %v", err)
}

// By now we have a valid user and device ID
log.Printf("Authy User ID %d, Device ID %d", regr.UserID, regr.DeviceID)
// By now we have a valid user and device ID
fmt.Printf("Authy User ID %d, Device ID %d\n", regr.UserID, regr.DeviceID)

cl, err := authy.NewClient()
if err != nil {
log.Fatalf("Couldn't create API client: %v", err)
}
cl, err := authy.NewClient()
if err != nil {
log.Fatalf("Couldn't create API client: %v", err)
}

// Fetch the apps
resp.Apps, err = cl.QueryAuthenticatorApps(nil, regr.UserID, regr.DeviceID, regr.Seed)
if err != nil {
log.Fatalf("Could not fetch authenticator apps: %v", err)
}
if !resp.Apps.Success {
log.Fatalf("Failed to fetch authenticator apps: %+v", resp.Apps)
}
// Fetch the apps
resp.Apps, err = cl.QueryAuthenticatorApps(nil, regr.UserID, regr.DeviceID, regr.Seed)
if err != nil {
log.Fatalf("Could not fetch authenticator apps: %v", err)
}
if !resp.Apps.Success {
log.Fatalf("Failed to fetch authenticator apps: %+v", resp.Apps)
}

// Fetch the actual tokens now
resp.Tokens, err = cl.QueryAuthenticatorTokens(nil, regr.UserID, regr.DeviceID, regr.Seed)
if err != nil {
log.Fatalf("Could not fetch authenticator tokens: %v", err)
}
if !resp.Tokens.Success {
log.Fatalf("Failed to fetch authenticator tokens: %+v", resp.Tokens)
}
// Fetch the actual tokens now
resp.Tokens, err = cl.QueryAuthenticatorTokens(nil, regr.UserID, regr.DeviceID, regr.Seed)
if err != nil {
log.Fatalf("Could not fetch authenticator tokens: %v", err)
}
if !resp.Tokens.Success {
log.Fatalf("Failed to fetch authenticator tokens: %+v", resp.Tokens)
}

if *savePtr != "" {
// Save encrypted tokens to json file
f, err := os.OpenFile(*savePtr, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
// Display decrypted tokens to the terminal
// We'll need the prompt the user to give the decryption password
pp := []byte(os.Getenv("AUTHY_EXPORT_PASSWORD"))
if len(pp) == 0 {
fmt.Printf("Please provide your Authy TOTP backup password: ")
var err error
pp, err = terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Fatalf("Creating backup file failed: %v", err)
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", "\t")
if err := enc.Encode(resp); err != nil {
log.Fatalf("Encoding backup file failed: %v", err)
}
} else {
// Display decrypted tokens to the terminal
// We'll need the prompt the user to give the decryption password
pp := []byte(os.Getenv("AUTHY_EXPORT_PASSWORD"))
if len(pp) == 0 {
log.Printf("Please provide your Authy TOTP backup password: ")
var err error
pp, err = terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Fatalf("Failed to read the password: %v", err)
}
log.Fatalf("Failed to read the password: %v", err)
}
}

// Print out in https://github.com/google/google-authenticator/wiki/Key-Uri-Format format
if len(resp.Tokens.AuthenticatorTokens) > 0 {
fmt.Printf("Found %d Regular Auth tokens\n", len(resp.Tokens.AuthenticatorTokens))

// Print out in https://github.com/google/google-authenticator/wiki/Key-Uri-Format format
log.Println("Here are your authenticator tokens:\n")
for _, tok := range resp.Tokens.AuthenticatorTokens {
decrypted, err := tok.Decrypt(string(pp))
if err != nil {
Expand All @@ -134,8 +124,16 @@ func main() {
Path: tok.Description(),
RawQuery: params.Encode(),
}
fmt.Println(u.String())
//fmt.Println(u.String())
// write u.String() to exportFile
if _, err := f.WriteString(u.String() + "\n"); err != nil {
log.Println(err)

}
}
}
if len(resp.Apps.AuthenticatorApps) > 0 {
fmt.Printf("Found %d Authy Specific tokens\n", len(resp.Apps.AuthenticatorApps))
for _, app := range resp.Apps.AuthenticatorApps {
tok, err := app.Token()
if err != nil {
Expand All @@ -152,7 +150,11 @@ func main() {
Path: app.Name,
RawQuery: params.Encode(),
}
fmt.Println(u.String())
//fmt.Println(u.String())
if _, err := f.WriteString(u.String() + "\n"); err != nil {
log.Println(err)

}
}
}
}
Expand All @@ -168,14 +170,14 @@ func newInteractiveDeviceRegistration() (deviceRegistration, error) {
sc := bufio.NewScanner(os.Stdin)
fmt.Print("\nWhat is your phone number's country code? (digits only): ")
if !sc.Scan() {
return regr, errors.New("Please provide a phone country code, e.g. 61")
return regr, errors.New("please provide a phone country code, e.g. 61")
}
if phoneCC, err = strconv.Atoi(strings.TrimSpace(sc.Text())); err != nil {
return regr, err
}
fmt.Print("\nWhat is your phone number? (digits only): ")
if !sc.Scan() {
return regr, errors.New("Please provide a phone country code, e.g. 12341234")
return regr, errors.New("please provide a phone country code, e.g. 12341234")
}
phoneNumber = strings.TrimSpace(sc.Text())
if err := sc.Err(); err != nil {
Expand Down Expand Up @@ -209,7 +211,7 @@ func newInteractiveDeviceRegistration() (deviceRegistration, error) {
timeout := time.Now().Add(5 * time.Minute)
for {
if timeout.Before(time.Now()) {
return regr, errors.New("Gave up waiting for user to respond to Authy device registration request")
return regr, errors.New("gave up waiting for user to respond to Authy device registration request")
}

log.Printf("Checking device registration status (%s until we give up)", time.Until(timeout).Truncate(time.Second))
Expand All @@ -222,7 +224,7 @@ func newInteractiveDeviceRegistration() (deviceRegistration, error) {
regPIN = regStatus.PIN
break
} else if regStatus.Status != "pending" {
return regr, fmt.Errorf("Invalid status while waiting for device registration: %s", regStatus.Status)
return regr, fmt.Errorf("invalid status while waiting for device registration: %s", regStatus.Status)
}

time.Sleep(5 * time.Second)
Expand Down Expand Up @@ -276,10 +278,20 @@ func loadExistingDeviceRegistration() (deviceRegistration, error) {
}

func configPath() (string, error) {
// TODO: In Go 1.13, use os.UserConfigDir()
regrPath, err := os.UserConfigDir()
if err != nil {
return "", err
}
return filepath.Join(regrPath, "authy-go.json"), nil
}

func resolvePath(path string) (string, error) {
if path[:2] != "~/" {
return path, nil
}
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return home + path[1:], nil
}

0 comments on commit b3937cf

Please sign in to comment.