Skip to content

Commit

Permalink
refactor: Move auth flow to javascript Alfred object (stuartcryan#27)
Browse files Browse the repository at this point in the history
* Move the auth flow to javascript and out of go.
  • Loading branch information
blacs30 authored Aug 9, 2020
1 parent 9f06591 commit 5ca4c22
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 41 deletions.
8 changes: 4 additions & 4 deletions alfred/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,12 @@ func GetEmail(wf *aw.Workflow) string {
return getEmail
}

func GetSfa(wf *aw.Workflow) string {
return wf.Config.GetString(sfa)
func GetSfa(wf *aw.Workflow) bool {
return wf.Config.GetBool(sfa, true)
}

func GetSfaMode(wf *aw.Workflow) string {
return wf.Config.GetString(sfaMode)
func GetSfaMode(wf *aw.Workflow) int {
return wf.Config.GetInt(sfaMode, 0)
}

//// Modifiers
Expand Down
4 changes: 2 additions & 2 deletions bitwarden.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ func runLogin() {
password = strings.TrimRight(password, "\r\n")

args := fmt.Sprintf("%s login %s %s", BwExec, email, password)
if sfa == "true" {
if sfa {
display2faMode := map2faMode(sfaMode)
inputScript2faCode := fmt.Sprintf("osascript bitwarden-js-pw-promot.js Login %s %s false", email, display2faMode)
message := "Failed to get 2FA code to Login."
Expand All @@ -427,7 +427,7 @@ func runLogin() {
if err != nil {
wf.Fatalf("Error reading password, %s", err)
}
args = fmt.Sprintf("%s login %s %s --raw --method %s --code %s", BwExec, email, password, sfaMode, sfaCode)
args = fmt.Sprintf("%s login %s %s --raw --method %d --code %s", BwExec, email, password, sfaMode, sfaCode)
}

message = "Login to Bitwarden failed."
Expand Down
32 changes: 25 additions & 7 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"log"
"os"
"os/exec"
"strconv"
)

var (
Expand Down Expand Up @@ -293,6 +294,10 @@ func runAuth() {
if opts.Query == "" {
wf.Configure(aw.SuppressUIDs(true))
}
email, sfa, sfaMode, _ := getConfigs(wf)
if !sfa {
sfaMode = -1
}

log.Printf("filtering auth config %q ...", opts.Query)

Expand All @@ -301,7 +306,8 @@ func runAuth() {
Valid(true).
UID("login").
Icon(iconOn).
Var("action", "-login")
Var("action", "-login").
Arg("login", email, fmt.Sprintf("%d", sfaMode), map2faMode(sfaMode))

wf.NewItem("Logout").
Subtitle("Logout from Bitwarden").
Expand All @@ -315,7 +321,8 @@ func runAuth() {
UID("unlock").
Valid(true).
Icon(iconOn).
Var("action", "-unlock")
Var("action", "-unlock").
Arg("unlock", email)

wf.NewItem("Lock").
Subtitle("Lock Bitwarden").
Expand Down Expand Up @@ -371,7 +378,11 @@ func runSetConfigs() {
if err != nil {
wf.FatalError(err)
}
sfamode := map2faMode(value)
sfaModeValue, err := strconv.Atoi(value)
if err != nil {
log.Println(err)
}
sfamode := map2faMode(sfaModeValue)
fmt.Printf("DONE: Set %s to \n%s", mode, sfamode)
searchAlfred(BWCONF_KEYWORD)
return
Expand Down Expand Up @@ -443,7 +454,7 @@ func runSfa() {
Arg("0")
} else if opts.Id == "ON/OFF" {
wf.NewItem("ON/OFF: Enable 2FA for Bitwarden").
Subtitle(fmt.Sprintf("Currently set to: %q", sfa)).
Subtitle(fmt.Sprintf("Currently set to: %t", sfa)).
UID("sfaon").
Valid(true).
Icon(iconOn).
Expand All @@ -453,7 +464,7 @@ func runSfa() {
Arg("true")

wf.NewItem("ON/OFF: Disable 2FA for Bitwarden").
Subtitle(fmt.Sprintf("Currently set to: %q", sfa)).
Subtitle(fmt.Sprintf("Currently set to: %t", sfa)).
UID("sfaoff").
Valid(true).
Icon(iconOff).
Expand Down Expand Up @@ -525,6 +536,11 @@ func runSearch(folderSearch bool, itemId string) {
if wf.Cache.Expired(CACHE_NAME, maxCacheAge) || wf.Cache.Expired(FOLDER_CACHE_NAME, maxCacheAge) {
wf.Rerun(0.3)
if !wf.IsRunning("cache") {
email, sfa, sfaMode, _ := getConfigs(wf)
if !sfa {
sfaMode = -1
}

// check if logged in or locked first
loginErr, unlockErr := BitwardenAuthChecks()
if loginErr != nil {
Expand All @@ -534,7 +550,8 @@ func runSearch(folderSearch bool, itemId string) {
Valid(true).
UID("login").
Icon(iconOn).
Var("action", "-login")
Var("action", "-login").
Arg("login", email, fmt.Sprintf("%d", sfaMode), map2faMode(sfaMode))
wf.NewWarningItem("Not logged in to Bitwarden.", "Need to login first.")
wf.SendFeedback()
return
Expand All @@ -546,7 +563,8 @@ func runSearch(folderSearch bool, itemId string) {
Valid(true).
UID("unlock").
Icon(iconOn).
Var("action", "-unlock")
Var("action", "-unlock").
Arg("unlock", email)
wf.NewWarningItem("Bitwarden is locked.", "Need to unlock first.")
wf.SendFeedback()
return
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func run() {
//}

email, sfaEnabled, sfaMode, server := getConfigs(wf)
allConfigs := []string{"email:", email, "2FA enabled:", sfaEnabled, "2FA Mode:", map2faMode(sfaMode), "Server:", server}
allConfigs := []string{"email:", email, "2FA enabled:", fmt.Sprintf("%t",sfaEnabled), "2FA Mode:", map2faMode(sfaMode), "Server:", server}
log.Printf("%#v", opts)
if wf.Debug() {
log.Printf("args=%#v => %#v", wf.Args(), cli.Args())
Expand Down
14 changes: 7 additions & 7 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func searchAlfred(search string) {
}
}

func getConfigs(wf *aw.Workflow) (email string, sfa string, sfamode string, server string) {
func getConfigs(wf *aw.Workflow) (email string, sfa bool, sfamode int, server string) {
email = alfred.GetEmail(wf)
sfa = alfred.GetSfa(wf)
sfamode = alfred.GetSfaMode(wf)
Expand All @@ -111,17 +111,17 @@ func commandExists(cmd string) bool {
return err == nil
}

func map2faMode(mode string) string {
func map2faMode(mode int) string {
switch mode {
case "0":
case 0:
return "Authenticator-app"
case "1":
case 1:
return "Email"
case "2":
case 2:
return "Duo"
case "3":
case 3:
return "YubiKey"
case "4":
case 4:
return "U2F"
}
return " "
Expand Down
151 changes: 151 additions & 0 deletions workflow/bitwarden-auth-flow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#!/usr/bin/osascript
ObjC.import('stdlib')
var BW_EXEC = $.getenv('BW_EXEC');
var PATH = $.getenv('PATH');

function run(arg) {

var app = Application.currentApplication()
app.includeStandardAdditions = true

var mode = arg[0]
var modeSet = "unlock"
var email = arg[1]
if (mode.localeCompare(modeSet) == 0) {
var text = `Unlock Bitwarden for user ${email}.\nPlease enter your password:`
var response = app.displayDialog(text, {
defaultAnswer: "",
withIcon: "caution",
buttons: ["Cancel", "OK"],
defaultButton: "OK",
cancelButton: "Cancel",
givingUpAfter: 120,
hiddenAnswer: "true"
})

var password = response.textReturned
return unlock(password)
} else {
var totpInt = arg[2]
var totpStr = arg[3]

var text = `Login to Bitwarden for user ${email}.\nPlease enter your password:`
var response = app.displayDialog(text, {
defaultAnswer: "",
withIcon: "caution",
buttons: ["Cancel", "OK"],
defaultButton: "OK",
cancelButton: "Cancel",
givingUpAfter: 120,
hiddenAnswer: "true"
})
var password = response.textReturned

if (totpStr.localeCompare(" ") != 0) {
if (totpInt.localeCompare("1") != 0) {
var text = `2FA authentication for Bitwarden user ${email}.\nPlease enter your 2FA code for ${totpStr}:`
var response = app.displayDialog(text, {
defaultAnswer: "",
withIcon: "caution",
buttons: ["Cancel", "OK"],
defaultButton: "OK",
cancelButton: "Cancel",
givingUpAfter: 120,
hiddenAnswer: "false"
})

var totpCode = response.textReturned
return login(email, password, totpCode, totpInt)
} else {
return login(email, password, "", totpInt)
}
} else {
return login(email, password, "", "")
}
}
}

function login(email, password, totp, totpMode) {
var app = Application.currentApplication()
app.includeStandardAdditions = true

if (totpMode.localeCompare("1") == 0) {
var cmd = `PATH=${PATH}; ${BW_EXEC} login ${email} --method ${totpMode} "${password}"`
try{
var result = app.doShellScript(cmd);
}catch(e) {
var res = e.toString()
res = res.includes("Two-step login code")
if (!res) {
console.log(e.toString())
return e.toString()
}
}
var text = `Email authentication for Bitwarden user ${email}.\nPlease enter your 2FA code sent via email:`
var response = app.displayDialog(text, {
defaultAnswer: "",
withIcon: "caution",
buttons: ["Cancel", "OK"],
defaultButton: "OK",
cancelButton: "Cancel",
givingUpAfter: 120,
hiddenAnswer: "false"
})
var totpCode = response.textReturned

var cmd = `PATH=${PATH}; ${BW_EXEC} login ${email} --method ${totpMode} --code ${totpCode} "${password}" --raw`
try {
var result = app.doShellScript(cmd);
}catch(e) {
console.log(e.toString())
return e.toString()
}
} else {
if (totp.localeCompare("") != 0) {
var cmd = `PATH=${PATH}; ${BW_EXEC} login ${email} --method ${totpMode} --code ${totp} "${password}" --raw`
} else {
var cmd = `PATH=${PATH}; ${BW_EXEC} login ${email} "${password}" --raw`
}
try {
var result = app.doShellScript(cmd);
}catch(e) {
console.log(e.toString())
return e.toString()
}
}
var res = setToken(result)
if (res.localeCompare("") == 0) {
return "Logged in."
}
}

function unlock(password) {
var app = Application.currentApplication()
app.includeStandardAdditions = true
var cmd = `PATH=${PATH}; ${BW_EXEC} unlock "${password}" --raw`
try {
var result = app.doShellScript(cmd);
var res = setToken(result)
if (res.localeCompare("") == 0) {
return "Unlocked"
}
}catch(e) {
console.log(e)
return e.toString()
}
}

function setToken(password) {
var bundleId = "com.lisowski-development.alfred.bitwarden"
var app = Application.currentApplication()
app.includeStandardAdditions = true

var cmd = `PATH=${PATH}; /usr/bin/security add-generic-password -s ${bundleId} -a token -w ${password} -U`
try {
app.doShellScript(cmd);
return ""
}catch(e) {
console.log(e.toString())
return e.toString()
}
}
Loading

0 comments on commit 5ca4c22

Please sign in to comment.