Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for oauth2 with preregistered token #955

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ A user of Arch-based distros can also install the current mutt-wizard release fr

### Optional Dependencies

- `cyrus-sasl-xoauth2-git` - Support for OAUTH2.
- `pam-gnupg` - Automatically logs you into your GPG key on login so you will
never need to input your password once logged on to your system. Check the
repo and directions out [here](https://github.com/cruegge/pam-gnupg).
Expand Down
64 changes: 52 additions & 12 deletions bin/mw
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ prepmutt() {
}

getprofiles() {
# TODO: oauth2 only for mbsync right now
safename="$(echo $fulladdr | sed 's/@/_/g')"
case "$type" in
online)
Expand Down Expand Up @@ -173,11 +174,30 @@ askinfo() {
[ -z "$passprefix" ] && passprefix=""
hostname="${fulladdr#*@}"
login="${login:-$fulladdr}"
if [ -n "${password+x}" ]; then
insertpass
else
getpass
fi
[ -f "$oauthtokenfile" ] ||
printf "If you want to use OAUTH2 (for Microsoft or Google), input path to pre-created token file (see help). Otherwise, leave empty: " &&
read -r oauthtokenfile

if [ -f "$oauthtokenfile" ]; then
authtype_msmtp=xoauth2
authtype_mbsync=XOAUTH2
printf "Token will be moved to '%s', do you want to remove the original token file [y/N]? " "$PASSWORD_STORE_DIR/$passprefix$fulladdr.tokens"
read -r prompt
case "$prompt" in
y|Y) mv "$oauthtokenfile" "$PASSWORD_STORE_DIR/$passprefix$fulladdr.tokens" ;;
*) cp "$oauthtokenfile" "$PASSWORD_STORE_DIR/$passprefix$fulladdr.tokens" ;;
esac
else
[ -n "$oauthtokenfile" ] && echo "Token file not found"
authtype_msmtp=on
authtype_mbsync=LOGIN
if [ -n "${password+x}" ]; then
insertpass
else
getpass
fi
fi
pass_cmdline="$(pass_cmdline)"
}

insertpass() {
Expand All @@ -187,10 +207,6 @@ insertpass() {
errorexit() {
echo "Log-on not successful."
case "$imap" in
imap.gmail.com)
echo "This account with $service is using Google's Gmail servers, which disable all third-party applications without an application-specific password.
Please be sure you are using OAUTH with your Gmail account, or better yet, stop using Gmail."
;;
imap.mail.me.com)
echo "This account with $service is using Apple's iCloud servers, which disable all non-Apple applications by default.
Please be sure you either enable third-party applications, or create an app-specific password, or best of all, stop using Apple."
Expand All @@ -199,16 +215,35 @@ Please be sure you either enable third-party applications, or create an app-spec
exit 1
}

pass_cmdline() {
if [ -f "$oauthtokenfile" ]; then
# do not use pass insert to not clutter pass git history with token updates
encrypt_pipe="$GPG -qe $(printf -- " -r %s" $(cat "$PASSWORD_STORE_DIR/.gpg-id"))"
printf '%s ' /usr/share/neomutt/oauth2/mutt_oauth2.py --encryption-pipe "$encrypt_pipe" "$passprefix$fulladdr.tokens"
else
printf '%s ' pass "$passprefix$fulladdr"
fi
}

getpass() { while :; do
pass rm -f "$passprefix$fulladdr" >/dev/null 2>&1
pass insert -f "$passprefix$fulladdr" && break
done; }

getboxes() {
if [ -n "${force+x}" ]; then
# TODO: add oauth2 curl
# in the meantime, get box names after syncing from folder structure:
#for d in "$maildir"/*
#do
# echo "$(basename "$d"):"
# mailboxes="$(find "$d" -mindepth 1 -type d -not -name 'cur' -not -name 'new' -not -name 'tmp' -printf '="%P" ')"
# printf "\tmailboxes %s\n\n" "$mailboxes"
#done

if [ -f "$oauthtokenfile" ] || [ -n "${force+x}" ]; then
mailboxes="$(printf "INBOX\\nDrafts\\nJunk\\nTrash\\nSent\\nArchive")"
else
info="$(curl --location-trusted -s -m 5 --user "$login:$(pass "$passprefix$fulladdr")" --url "${protocol:-imaps}://$imap:${iport:-993}")"
info="$(curl --location-trusted -s -m 5 --user "$login:$(pass show "$passprefix$fulladdr")" --url "${protocol:-imaps}://$imap:${iport:-993}")"
[ -z "$info" ] && errorexit
mailboxes="$(echo "$info" | grep -v HasChildren | sed "s/.*\" //;s/\"//g" | tr -d '\r')"
fi
Expand Down Expand Up @@ -283,6 +318,7 @@ Options allowed with -a:
-s SMTP server address
-S SMTP server port
-x Password for account (recommended to be in double quotes)
-o Registered OAUTH2 token file path. See mw(1) for more info.
-p Add for a POP server instead of IMAP.
-P Pass Prefix (prefix of the file where password is stored)
-X Delete an account's local email too when deleting.
Expand Down Expand Up @@ -320,7 +356,7 @@ reorder() {
' "$tempfile" >>"$muttrc"
}

while getopts "rfpXlhodTYD:y:i:I:s:S:u:a:n:P:x:m:t:" o; do case "${o}" in
while getopts "rfpXlhodTYD:y:i:I:s:S:u:a:n:P:x:O:m:t:" o; do case "${o}" in
l) setact list ;;
r) setact reorder ;;
d) setact delete ;;
Expand Down Expand Up @@ -387,6 +423,10 @@ while getopts "rfpXlhodTYD:y:i:I:s:S:u:a:n:P:x:m:t:" o; do case "${o}" in
setact add
password="$OPTARG"
;;
O)
setact add
oauthtokenfile="$OPTARG"
;;
X)
setact delete
purge=True
Expand Down
29 changes: 29 additions & 0 deletions mw.1
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,35 @@ SMTP server address
.TP
.B -S
SMTP server port (assumed to be 465 if not specified)
.TP
.B -O path
Path to registered OAUTH2 token file. The file has to be first created using external scripts, see for example
.I https://wiki.archlinux.org/title/Isync#mutt_oauth2.py
for more information. If you already have
.B neomutt
installed, the script should be located in
.I /usr/share/neomutt/oauth2/mutt_oauth2.py
and you need to first create the token by specifying your authorization credentials. If your organization does not permit any new registrations, use Mozilla Thunderbird Client ID, for example, from
.I https://hg.mozilla.org/comm-central/file/tip/mailnews/base/src/OAuth2Providers.jsm
and pick your provider (Google or Microsoft).
Then you can use the script as
.IP
.EX
\&$ /usr/share/neomutt/oauth2/mutt_oauth2.py -v -t \\
\& --authorize --client-id "9e5f94bc-e8a4-4e73-b8be-63364c29d753" --client-secret "" \\
\& --email "<your email>" --provider microsoft \\
\& --encryption-pipe "gpg --encrypt --recipient <your_gpg_key>" \\
\& token_path
.EE
.
.XP

The token file is then decrypted by
.B mw
and saved to password store as
.I <passprefix><email>.tokens
encrypted using gpg. The file will not be tracked by git history by default.

.TP
.B -x
Account password. You will be prompted for the password interactively if this option is not given.
Expand Down
4 changes: 2 additions & 2 deletions share/mbsync-temp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ IMAPStore $fulladdr-remote
Host $imap
Port $iport
User $login
PassCmd "pass $passprefix$fulladdr"
AuthMechs LOGIN
PassCmd "$pass_cmdline"
AuthMechs $authtype_mbsync
SSLType $imapssl
CertificateFile $sslcert

Expand Down
4 changes: 2 additions & 2 deletions share/msmtp-temp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ host $smtp
port $sport
from $fulladdr
user $login
passwordeval "pass $passprefix$fulladdr"
auth on
passwordeval "$pass_cmdline"
auth $authtype_msmtp
tls on
tls_trust_file $sslcert
logfile $msmtplog
Expand Down