How Does op signin Work?

(filed in terminal / 1pass)

If you try to sign into your teams account with their op command-line utility, you may find it a little reluctant to oblige:

→  op signin
[ERROR] 2022/04/09 15:01:52 Output of 'op signin' is meant to be
executed by your terminal. Please run 'eval $(op signin)'. You can
use the '-f' flag to override this warning.

While you can use -f (as the error hints), you may wonder how it is that op signin knows that you haven't run it in an evaluating subshell. If I had to hazard a guess, it's looking at something like isatty(3), a POSIX-mandated function that checks if a given file descriptor is a terminal (or teletype) devices. If you you call isatty(0), you can tell if the input channel (stdin) is attached to an interactive terminal.

In bash, you can use the test -t (or [ -t) predicate to determine if a file descriptor (by number) is hooked up to a tty device:


if test -t 0; then
  echo "Hello, Interactive User!"
  echo "Hello, Batch User!"

0 is the numeric index of the first open file descriptor for a process; by POSIX semantics, the first three of these are (in order): standard input, standard output, and standard error.

This test -t approach is easily fooled however; consider these two shell sessions:

$ ./is-a-tty
Hello, Interactive User!

$ echo 'something' | ./is-a-tty
Hello, Batch User!

In the second invocation, echoing something into the process causes that process' standard input to be reopened to the output of the echo command (its standard output). At that point, it ceases to be a terminal device and we follow the else branch.

With this knowledge, we can now workaround op signin's little trick:

$ op signin | cat
export OP_SESSION_ABCDEFGHIJKLMNOPQRSTUVWXYZ="... secret redacted ..."
# This command is meant to be used with your shell's eval function.
# Run 'eval $(op signin account)' to sign in to your 1Password account.
# Use the --raw flag to only output the session token.

(The cat UNIX utility, when called with no arguments, prints its standard input to its standard output. It's a bit of an I/O no-op. Crucially, it robs op signin of its tty output device, sidestepping the issue we first ran into)