Created
September 5, 2022 18:42
-
-
Save pcf000/54a65a224d18b3010fe733a465faea24 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python3 | |
# This script is a refinement of what oauth2token provides, specific to | |
# my work situation. Normally, one could start with oauth2create and then | |
# use oauth2get indefinitely, with the latter getting a token either | |
# directly or after using the refresh token. However, my workplace has | |
# it set up to not have a refresh token, so I have this script try "get" | |
# and call "create" automatically if it fails. | |
# | |
# Start with "pip install oauth2token". | |
import argparse | |
import os | |
import sys | |
from contextlib import contextmanager | |
from oauth2token.token_mgmt import get_token,create_user_credentials | |
from google.auth.exceptions import RefreshError | |
# Requirements. This is a fleshing-out of the instructions in oauth2token's | |
# PKG-INFO | |
# | |
# 1. pip install oauth2token | |
# | |
# 2. Create $XDG_CONFIG_HOME/.config/oauth2token/office365/config.json and | |
# $XDG_CONFIG_HOME/.config/oauth2token/office365/scopes.json. They're | |
# essentially boilerplate. The client_id and client_secret below are | |
# from Thunderbird; see | |
# https://github.com/mozilla/releases-comm-central/blob/master/mailnews/base/src/OAuth2Providers.jsm | |
# | |
# config.json: | |
# | |
# { | |
# "installed": { | |
# "client_id": "08162f7c-0fd2-4200-a84a-f25a4db0b584", | |
# "client_secret":"TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82", | |
# "redirect_uris": ["http://localhost", "urn:ietf:wg:oauth:2.0:oob"], | |
# "auth_uri": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", | |
# "token_uri": "https://login.microsoftonline.com/common/oauth2/v2.0/token" | |
# } | |
# } | |
# | |
# scopes.json | |
# | |
# ["https://outlook.office.com/POP.AccessAsUser.All", | |
# "https://outlook.office.com/SMTP.Send", | |
# "https://outlook.office.com/IMAP.AccessAsUser.All"] | |
# These two functions are used to redirect stdout to /dev/null, since work's | |
# arrangement makes me call create_user_credentials often, and that prints | |
# something, and I don't want that because I'm also printing the eventual | |
# token for capture by the caller. | |
def fileno(file_or_fd): | |
fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)() | |
if not isinstance(fd, int): | |
raise ValueError("Expected a file (`.fileno()`) or a file descriptor") | |
return fd | |
@contextmanager | |
def stdout_redirected(to=os.devnull, stdout=None): | |
if stdout is None: | |
stdout = sys.stdout | |
stdout_fd = fileno(stdout) | |
# copy stdout_fd before it is overwritten | |
#NOTE: `copied` is inheritable on Windows when duplicating a standard stream | |
with os.fdopen(os.dup(stdout_fd), 'wb') as copied: | |
stdout.flush() # flush library buffers that dup2 knows nothing about | |
try: | |
os.dup2(fileno(to), stdout_fd) # $ exec >&to | |
except ValueError: # filename | |
with open(to, 'wb') as to_file: | |
os.dup2(to_file.fileno(), stdout_fd) # $ exec > to | |
try: | |
yield stdout # allow code to be run with the redirected stdout | |
finally: | |
# restore stdout to its previous value | |
#NOTE: dup2 makes stdout_fd inheritable unconditionally | |
stdout.flush() | |
os.dup2(copied.fileno(), stdout_fd) # $ exec >&copied | |
# The actual work of getting or creating the token. I use it as | |
# | |
# PassCmd "/home/ME/bin/oauth2getcreate office365 [email protected]" | |
# AuthMechs XOAUTH2 | |
# | |
# where I arbitrarily named the app "office365" and our logins require | |
# user@host. oauth2getcreate prints the token and exits. | |
# | |
# I use mbsync, which unfortunately required me to build it from source, | |
# and also build cyrus-sasl-xoauth2 from source. | |
parser = argparse.ArgumentParser() | |
parser.add_argument('app') | |
parser.add_argument('user', default=os.getenv('USER')) | |
args = parser.parse_args() | |
try: | |
print (get_token(user=args.user, app=args.app)) | |
except (RefreshError, FileNotFoundError): | |
with stdout_redirected(os.devnull): | |
create_user_credentials(user=args.user, app=args.app) | |
print (get_token(user=args.user, app=args.app)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment