Check if signed in with op

danielcompton
danielcompton
Community Member

It would be handy if there was a way to check if a user was signed into an account without actually requiring them to sign in. op signin myproj will always prompt for the password, even if there is an existing session in the shell.


1Password Version: Not Provided
Extension Version: Not Provided
OS Version: Not Provided
Sync Type: Not Provided

Comments

  • CraigSiemens
    CraigSiemens
    Community Member

    :+1:
    My use case is that I'm making a script to generate some files that contain keys stored in 1Password. It would be great if I could check whether the use is already signed into our work subdomain before prompting them again.

  • cohix
    cohix
    1Password Alumni

    @danielcompton @CraigSiemens A fairly easy way would be to run op get account and check that the exit code is 0. We are considering adding something more specific for this use-case, though.

  • janedenone
    janedenone
    Community Member

    How would I call the login from within a (Python) script and provide the password interactively?

     signincheck = subprocess.Popen(['op', 'get', 'account', item_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     if signincheck.returncode is None:
         signin = subprocess.Popen(['eval', '$(op', 'signin', '<subdomain>)]'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    
  • felix_1p
    felix_1p
    1Password Alumni

    @janedenone What does your script currently do? Since you are asking, I assume it does not work as you intend but it would knowing what currently happens.

  • janedenone
    janedenone
    Community Member

    @felix_1p There are two problems: signincheck.returncode always returns None (whether I am logged in or not), and the password dialog for the op signin action is never displayed. I also tried to provide the password via pexpect (unsuccessfully).

  • felix_1p
    felix_1p
    1Password Alumni

    Thank you @janedenone ! I wouldn't be surprised if there is an issue related to how we write to and read from stdout/stdin. I will play around a bit with Python myself and report back with what I found out.

  • felix_1p
    felix_1p
    1Password Alumni
    edited January 2020

    @janedenone I tested it myself and luckily it doesn't look like an issue with op itself. Here is what I came up with:

    import subprocess
    import os
    import sys
    
    ignore = open(os.devnull,"w")
    
    domain = '<domain>'
    envname = 'OP_SESSION_'+domain
    # Use existing environment variable if possible
    token = os.environ[envname]
    
    # Check if we are currently signed in
    signincheck = subprocess.call(['op', 'get', 'account', '--account='+domain], stdout=ignore, stderr=ignore)
    if signincheck == 1:
        signin = subprocess.Popen(['op', 'signin', domain, '--raw'], stdout=subprocess.PIPE)
        token = str(signin.communicate()[0], 'utf-8').strip()
        if signin.returncode != 0:
            print('unable to login')
            sys.exit(1)
    
    account = subprocess.Popen(['op', 'get', 'account', '--account='+domain], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={envname: token})
    print(account.communicate()[0])
    

    signincheck.returncode always returns None

    I believe that is because you didn't wait for the process to terminate (but I could be wrong). Adding singincheck.wait() or switching to subprocess.call as shown above works for me.

    the password dialog for the op signin action is never displayed

    That's because you specified stderr=subprocess.PIPE. This causes Python to create a new file descriptor for stderr and makes it available via signin.stderr. If you want the process to use the same file/destination as the parent process, then just leave it unspecified.

    Now, as you can see, I'm not using eval in my example. By default op signin returns something like export OP_SESSION_<domain>=.... which is evaled to set the environment variable. Setting the environment variable this way only affects other subprocesses of that process though. But that process terminates immediately after that.

    In order to make other processes started via subprocess "see" the environment variable, you have to explicitly set it. This is where --raw comes in. It allows us to get the raw session token on stdout. We then store it in a variable and set the proper env variable in the next call.

    I also needed to specify --account=.... explicitly otherwise the next command wouldn't consider the environment variable for whatever reason.

    The ignore file descriptor just redirects output I'm not interested in to /dev/null. Alternatively just specifying subprocess.PIPE would works as well, but I thought it would express the intent better.

    This is of course only one way to do it. I don't know how you plan to use op through Python, this is just what I came up with. There might also be better ways to call commands and use their output, my Python is a bit rusty.

    Please let me know if you have any questions about this!

  • janedenone
    janedenone
    Community Member

    Thanks, Felix (both for the code and the explanation). Unfortunately, I get an KeyError message regarding os.environ[envname], stating that there is no key OP_SESSION_. I tried specifying the subdomain via the domain variable both in caps and and in lower case letters.

  • felix_1p
    felix_1p
    1Password Alumni

    That's one of the cases where my Python knowledge is rusty. You will have to check first whether the key exists (if key in os.environ maybe)? It worked for me because that environment variable existed.

This discussion has been closed.