diff --git a/info.plist b/info.plist
index e1f0502..35a3eea 100644
--- a/info.plist
+++ b/info.plist
@@ -6,11 +6,11 @@
com.iamthefij.alfred-yubico-auth
connections
- 2D69982A-0DB6-4ABA-812F-C7F2A73650AE
+ 0149DE47-0232-434D-BB9E-B2C0B419A2E3
destinationuid
- 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD
+ 55DFD4B6-922A-4259-8625-EC14889FBACD
modifiers
0
modifiersubtext
@@ -18,6 +18,22 @@
vitoclose
+
+ 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7
+
+
+ destinationuid
+ 2D69982A-0DB6-4ABA-812F-C7F2A73650AE
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ 2D69982A-0DB6-4ABA-812F-C7F2A73650AE
+
destinationuid
A8D2CCAC-5CA4-495E-BB62-5C7F596FA157
@@ -30,19 +46,44 @@
vitoclose
-
- 8486DCAA-AFB7-407D-A0E9-E57E09997B24
-
destinationuid
- FB0DDF80-FF90-439A-BF3F-6EC58C2AA870
+ 0149DE47-0232-434D-BB9E-B2C0B419A2E3
modifiers
- 1048576
+ 0
modifiersubtext
- Paste token
+
vitoclose
+
+ 55DFD4B6-922A-4259-8625-EC14889FBACD
+
+
+ destinationuid
+ AD82ED59-033E-4860-B371-8128574E2FBC
+ modifiers
+ 0
+ modifiersubtext
+
+ sourceoutputuid
+ 94F60406-01FF-4991-A697-2C83147293EB
+ vitoclose
+
+
+
+ destinationuid
+ 506787F2-9A61-492C-8C49-30EE04FB70BC
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ 8486DCAA-AFB7-407D-A0E9-E57E09997B24
+
destinationuid
2D69982A-0DB6-4ABA-812F-C7F2A73650AE
@@ -53,6 +94,16 @@
vitoclose
+
+ destinationuid
+ 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7
+ modifiers
+ 1048576
+ modifiersubtext
+ Paste code
+ vitoclose
+
+
9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD
@@ -71,7 +122,32 @@
destinationuid
- C252A5EC-1AEE-4EF4-864F-67483EAADCFA
+ DA99BA2E-7234-491D-BD0F-044151FA98E2
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ AD82ED59-033E-4860-B371-8128574E2FBC
+
+
+ destinationuid
+ FB0DDF80-FF90-439A-BF3F-6EC58C2AA870
+ modifiers
+ 0
+ modifiersubtext
+
+ sourceoutputuid
+ 94F60406-01FF-4991-A697-2C83147293EB
+ vitoclose
+
+
+
+ destinationuid
+ 9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD
modifiers
0
modifiersubtext
@@ -106,6 +182,31 @@
+ DA99BA2E-7234-491D-BD0F-044151FA98E2
+
+
+ destinationuid
+ C252A5EC-1AEE-4EF4-864F-67483EAADCFA
+ modifiers
+ 0
+ modifiersubtext
+
+ sourceoutputuid
+ 94F60406-01FF-4991-A697-2C83147293EB
+ vitoclose
+
+
+
+ destinationuid
+ 506787F2-9A61-492C-8C49-30EE04FB70BC
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E
@@ -146,21 +247,46 @@
config
- autopaste
-
- clipboardtext
- {query}
- ignoredynamicplaceholders
+ lastpathcomponent
- transient
-
+ onlyshowifquerypopulated
+
+ removeextension
+
+ text
+ Password key is now stored in your keychain
+ title
+ Password saved
type
- alfred.workflow.output.clipboard
+ alfred.workflow.output.notification
uid
- FB0DDF80-FF90-439A-BF3F-6EC58C2AA870
+ C252A5EC-1AEE-4EF4-864F-67483EAADCFA
version
- 3
+ 1
+
+
+ config
+
+ concurrently
+
+ escaping
+ 102
+ script
+ ./alfred-yubico-auth -run-script set-password
+ scriptargtype
+ 1
+ scriptfile
+
+ type
+ 0
+
+ type
+ alfred.workflow.action.script
+ uid
+ A8D2CCAC-5CA4-495E-BB62-5C7F596FA157
+ version
+ 2
config
@@ -190,7 +316,7 @@
runningsubtext
script
- ./alfred-yubico-auth
+ ./alfred-yubico-auth list
scriptargtype
1
scriptfile
@@ -211,6 +337,105 @@
version
3
+
+ config
+
+ conditions
+
+
+ inputstring
+ {var:result}
+ matchcasesensitive
+
+ matchmode
+ 0
+ matchstring
+ success
+ outputlabel
+ success
+ uid
+ 94F60406-01FF-4991-A697-2C83147293EB
+
+
+ elselabel
+ else
+
+ type
+ alfred.workflow.utility.conditional
+ uid
+ DA99BA2E-7234-491D-BD0F-044151FA98E2
+ version
+ 1
+
+
+ config
+
+ conditions
+
+
+ inputstring
+ {var:action}
+ matchcasesensitive
+
+ matchmode
+ 0
+ matchstring
+ set-password
+ outputlabel
+ Set password
+ uid
+ 94F60406-01FF-4991-A697-2C83147293EB
+
+
+ elselabel
+ else
+
+ type
+ alfred.workflow.utility.conditional
+ uid
+ 2D69982A-0DB6-4ABA-812F-C7F2A73650AE
+ version
+ 1
+
+
+ config
+
+ argument
+ {query}
+ passthroughargument
+
+ variables
+
+ result_action
+ paste
+
+
+ type
+ alfred.workflow.utility.argument
+ uid
+ 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7
+ version
+ 1
+
+
+ config
+
+ autopaste
+
+ clipboardtext
+ {query}
+ ignoredynamicplaceholders
+
+ transient
+
+
+ type
+ alfred.workflow.output.clipboard
+ uid
+ FB0DDF80-FF90-439A-BF3F-6EC58C2AA870
+ version
+ 3
+
config
@@ -264,6 +489,61 @@
version
1
+
+ config
+
+ conditions
+
+
+ inputstring
+ {var:result_action}
+ matchcasesensitive
+
+ matchmode
+ 0
+ matchstring
+ paste
+ outputlabel
+ paste
+ uid
+ 94F60406-01FF-4991-A697-2C83147293EB
+
+
+ elselabel
+ copy
+
+ type
+ alfred.workflow.utility.conditional
+ uid
+ AD82ED59-033E-4860-B371-8128574E2FBC
+ version
+ 1
+
+
+ config
+
+ concurrently
+
+ escaping
+ 102
+ script
+ query=$1
+
+./alfred-yubico-auth -run-script "$query"
+ scriptargtype
+ 1
+ scriptfile
+
+ type
+ 0
+
+ type
+ alfred.workflow.action.script
+ uid
+ 0149DE47-0232-434D-BB9E-B2C0B419A2E3
+ version
+ 2
+
config
@@ -311,15 +591,15 @@
inputstring
- {var:action}
+ {var:result}
matchcasesensitive
matchmode
0
matchstring
- set-password
+ success
outputlabel
- Set password
+ success
uid
94F60406-01FF-4991-A697-2C83147293EB
@@ -330,51 +610,28 @@
type
alfred.workflow.utility.conditional
uid
- 2D69982A-0DB6-4ABA-812F-C7F2A73650AE
+ 55DFD4B6-922A-4259-8625-EC14889FBACD
version
1
-
- config
-
- concurrently
-
- escaping
- 102
- script
- ./alfred-yubico-auth set-password
- scriptargtype
- 1
- scriptfile
-
- type
- 0
-
- type
- alfred.workflow.action.script
- uid
- A8D2CCAC-5CA4-495E-BB62-5C7F596FA157
- version
- 2
-
config
lastpathcomponent
onlyshowifquerypopulated
-
+
removeextension
text
- Password key is now stored in your keychain
+ {query}
title
- Password saved
+ Alfred Yubikey Error
type
alfred.workflow.output.notification
uid
- C252A5EC-1AEE-4EF4-864F-67483EAADCFA
+ 506787F2-9A61-492C-8C49-30EE04FB70BC
version
1
@@ -383,75 +640,117 @@
uidata
+ 0149DE47-0232-434D-BB9E-B2C0B419A2E3
+
+ xpos
+ 180
+ ypos
+ 305
+
0718204D-3398-4AEF-A621-DDDE1FC6ED75
xpos
- 990
+ 1245
ypos
- 145
+ 225
+
+ 27BC434A-1663-44A7-85AF-C4AC6E1BEFB7
+
+ xpos
+ 260
+ ypos
+ 155
2D69982A-0DB6-4ABA-812F-C7F2A73650AE
xpos
- 270
+ 365
ypos
- 245
+ 90
+
+ 506787F2-9A61-492C-8C49-30EE04FB70BC
+
+ xpos
+ 540
+ ypos
+ 455
+
+ 55DFD4B6-922A-4259-8625-EC14889FBACD
+
+ xpos
+ 365
+ ypos
+ 330
8486DCAA-AFB7-407D-A0E9-E57E09997B24
xpos
- 120
+ 70
ypos
- 140
+ 65
9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD
xpos
- 415
+ 605
ypos
- 235
+ 320
A8D2CCAC-5CA4-495E-BB62-5C7F596FA157
xpos
- 390
+ 515
ypos
- 415
+ 55
+
+ AD82ED59-033E-4860-B371-8128574E2FBC
+
+ xpos
+ 485
+ ypos
+ 285
BFB3A122-52BB-4FF1-B5B3-CECD42A730DB
xpos
- 810
+ 1065
ypos
- 145
+ 225
C252A5EC-1AEE-4EF4-864F-67483EAADCFA
xpos
- 595
+ 800
ypos
- 420
+ 45
DA3E9CE8-7C4F-4B09-BFA5-F8CA83297968
xpos
- 590
+ 780
ypos
- 235
+ 320
+
+ DA99BA2E-7234-491D-BD0F-044151FA98E2
+
+ xpos
+ 670
+ ypos
+ 75
E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E
xpos
- 730
+ 985
ypos
- 175
+ 255
FB0DDF80-FF90-439A-BF3F-6EC58C2AA870
xpos
- 455
+ 605
ypos
- 60
+ 185
version
diff --git a/main.go b/main.go
index ec32dad..7fdccbb 100644
--- a/main.go
+++ b/main.go
@@ -1,20 +1,23 @@
package main
import (
+ "bytes"
+ "errors"
"flag"
+ "fmt"
"git.iamthefij.com/iamthefij/slog"
aw "github.com/deanishe/awgo"
"github.com/deanishe/awgo/util"
"github.com/yawn/ykoath"
-
- "bytes"
)
var (
wf *aw.Workflow
oath *ykoath.OATH
keychainAccount = "yubico-auth-creds"
+
+ errIncorrectPassword = errors.New("incorrect password")
)
func init() {
@@ -28,102 +31,149 @@ func main() {
func promptPassword() (string, error) {
out, err := util.Run("./password-prompt.js")
if err != nil {
- return "", err
+ return "", fmt.Errorf("error reading password from prompt: %w", err)
}
+
out = bytes.TrimRight(out, "\n")
+
return string(out), nil
}
func setPassword(s *ykoath.Select) error {
passphrase, err := promptPassword()
if err != nil {
- slog.Error("failed reading passphrase")
- return err
+ return fmt.Errorf("failed reading passphrase: %w", err)
}
- key := s.DeriveKey(passphrase)
- // TODO: test key before storing
- err = wf.Keychain.Set(keychainAccount, string(key))
+
+ err = validatePassphrase(s, passphrase)
if err != nil {
- slog.Error("failed storing passphrase key in keychain")
- return err
+ return fmt.Errorf("failed validating passphrase: %w", err)
}
+
+ err = wf.Keychain.Set(keychainAccount, passphrase)
+ if err != nil {
+ return fmt.Errorf("failed storing passphrase in keychain: %w", err)
+ }
+
+ return nil
+}
+
+func sendResult(result string, args ...string) error {
+ results := aw.NewArgVars()
+
+ results.Arg(args...)
+ results.Var("result", result)
+
+ return results.Send()
+}
+
+func validatePassphrase(s *ykoath.Select, passphrase string) error {
+ key := s.DeriveKey(passphrase)
+
+ // verify password is correct with a validate call
+ ok, err := oath.Validate(s, key)
+ if err != nil {
+ return fmt.Errorf("error in validate: %w", err)
+ }
+
+ if !ok {
+ return errIncorrectPassword
+ }
+
return nil
}
func run() {
+ runScript := flag.Bool("run-script", false, "change output to script output")
+
wf.Args()
flag.Parse()
+ if *runScript {
+ wf.Configure(aw.TextErrors(true))
+ }
+
var err error
+
oath, err = ykoath.New()
if err != nil {
- slog.Error("failed to iniatialize new oath: %v", err)
- wf.FatalError(err)
+ wf.FatalError(fmt.Errorf("failed to iniatialize new oath: %w", err))
}
+
defer oath.Close()
oath.Debug = slog.Debug
// Select oath to begin
s, err := oath.Select()
if err != nil {
- slog.Error("failed to select oath: %v", err)
- wf.FatalError(err)
+ wf.FatalError(fmt.Errorf("failed to select oath: %w", err))
}
// Check to see if we are trying to set a password
if flag.Arg(0) == "set-password" {
err = setPassword(s)
if err != nil {
- wf.FatalError(err)
+ wf.FatalError(fmt.Errorf("failed to set password: %w", err))
}
+
+ if err = sendResult("success"); err != nil {
+ wf.FatalError(fmt.Errorf("failed to send password set result: %w", err))
+ }
+
return
}
// If required, authenticate with password from keychain
if s.Challenge != nil {
- key, err := wf.Keychain.Get(keychainAccount)
+ passphrase, err := wf.Keychain.Get(keychainAccount)
if err != nil {
slog.Error("no key found in keychain but password is required")
wf.NewWarningItem("No password set", "↵ to set password").
Var("action", "set-password").
Valid(true)
wf.SendFeedback()
+
return
}
- ok, err := oath.Validate(s, []byte(key))
- slog.FatalOnErr(err, "validation failed")
- if !ok {
- panic("could not validate")
+ err = validatePassphrase(s, passphrase)
+ if err != nil {
+ wf.FatalError(fmt.Errorf("passphrase failed: %w", err))
}
}
if flag.Arg(0) == "list" {
// List names only
names, err := oath.List()
- slog.FatalOnErr(err, "failed to list names")
+ if err != nil {
+ wf.FatalError(fmt.Errorf("failed to list names: %w", err))
+ }
+
for _, name := range names {
slog.Log(name.Name)
wf.NewItem(name.Name).
+ Icon(aw.IconAccount).
+ Subtitle("Copy to clipboard").
+ Arg(name.Name).
Valid(true)
}
} else {
- // Default execution is to calculate all codes and return them in list
- creds, err := oath.CalculateAll()
+ name := flag.Arg(0)
+
+ code, err := oath.CalculateOne(name)
if err != nil {
- slog.Error("failed to calculate all")
- wf.FatalError(err)
+ // TODO: Check for error "requires-auth" and notify touch
+ wf.FatalError(fmt.Errorf("failed to generate code: %w", err))
}
- for cred, code := range creds {
- slog.Log(cred)
- wf.NewItem(cred).
- Icon(aw.IconAccount).
- Subtitle("Copy to clipboard").
- Arg(code).
- Copytext(code).
- Valid(true)
+ slog.Log(code)
+
+ if err = sendResult("success", code); err != nil {
+ wf.FatalError(fmt.Errorf("failed to send code: %w", err))
}
}
- wf.SendFeedback()
+
+ if !*runScript {
+ wf.SendFeedback()
+ }
}
diff --git a/ykoath b/ykoath
index 0bb0699..fd081cb 160000
--- a/ykoath
+++ b/ykoath
@@ -1 +1 @@
-Subproject commit 0bb0699e51d3672572cc7869397eeff871bdf45e
+Subproject commit fd081cb213d030585bfdd03305e03bff4d6e7a09