How can I script the op signin via a node application?

eddiehedges
eddiehedges
Community Member
edited June 2019 in CLI

I'm working on some node scripts/apps that will simplify my life on multiple platforms when using the op CLI and have a question on stack overflow which I'd love for someone to take a look at and provide some feedback if possible: https://stackoverflow.com/q/56438183/1165441


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

Comments

  • Hey @eddiehedges

    We don't provide support on Stack Overflow; this forum is for 1Password support.

    Feel free to ask a question here for support, but note we cannot provide debugging assistance on your node app. We can answer questions on and around the CLI, such as how it receives and outputs data.

  • eddiehedges
    eddiehedges
    Community Member

    I'm simply using node to invoke an op command and am running into an error when the password prompt appears. Since the CLI is not open source and I haven't attempted to decompile/figure out how to access the source is there some information that you'd be able to provide to me so that I may continue in my efforts of scripting the signin process? Are there alternatives that take the master password as an argument or is this a feature that could be added? Or even more generally how does that prompt receive and output data?

  • @eddiehedges

    Here is the full CLI command documentation: https://support.1password.com/command-line/

    You can pass a signin domain, email, and secret key like so op signin <signinaddress> <emailaddress> <secretkey>.

    Putting a master password in a script or as an argument is something we strongly recommend against; it should only be in your head, never written down beyond on your recovery kit.

    The signin password prompt is set up to read from stdin or from /dev/tty.

  • eddiehedges
    eddiehedges
    Community Member

    Any idea as to why it's crashing the child_process spawn call? I can't even get the script to accept stdin since it crashes before that.

  • mickael
    mickael
    Community Member

    @eddiehedges I had some issues to launch the op cli in python as well. If I compare with what I did, try to spawn the command in a shell and pass the full environment.

  • eddiehedges
    eddiehedges
    Community Member

    Thanks @mickael I'll give that a shot.

  • cohix
    cohix
    1Password Alumni

    Let us know how it goes!

  • regsethu
    regsethu
    Community Member
    edited August 2019

    I am trying to do exactly the same thing. Trying to automate moving from lastpass to 1password with shared folders, etc. I am trying to invoke op from a node program and it doesn't work. I keep getting an error saying "[LOG] 2019/08/05 16:48:40 (ERROR) operation not supported on socket"

    Is op actively blocking being spawned from a node process somehow? All other programs seem to be working with the approach I am using.

    `
    const { spawn } = require('child_process')
    const {streamWrite, streamEnd, onExit} = require('@rauschma/stringio');

    const signInTo1Password = async (onePasswordCredentials) => {
      const subprocess = spawn(opPath, [
        'signin',
        '1password team url', 
        onePasswordCredentials.username,
        onePasswordCredentials.secretKey,
        '--output=raw'],
       {
          stdio: ['pipe', process.stdout, process.stderr]
        })
      writeToWritable(subprocess.stdin)  
      await onExit(subprocess)  
    }
    
    async function writeToWritable(writable) {
      await streamWrite(writable, 'my 1password secret password\n');
      await streamEnd(writable);
    }
    

    `

  • @regsethu

    We do not actively block our program from being used in any way. Ideally you should be able to use it every way!

    Generally, if you can reproduce this in a shell, I can help determine how the program is either being called incorrectly, or behaving incorrectly.

    As a person who is not familiar with Node and spawning binaries, what exactly are you are trying to do? I'm guessing this is just a shell wrapper for op.

    From what I see here, you spawn a subprocess off the main Node process containing op and the siginin parameters. The standard out of that subprocess is then piped into the standard out and standard error of the containing process. Then you write your master password to the standard in. (I hope that is not saved in the same place as the rest of your credentials :wink: )

    Where exactly is it failing? What are the surrounding errors? Can you reproduce it in a shell?

    Also, have you tried @mickael 's suggestion? This is to:

    try to spawn the command in a shell and pass the full environment

  • regsethu
    regsethu
    Community Member
    edited August 2019

    I had to use node-pty to simulate a shell to get around the problem. node-pty simulates a shell for you.. the code looks like this

    const os = require('os')
    const pty = require('node-pty')
    
    const TOKEN_REGEX = new RegExp('^(.{43})$', 'gm') // the token is always 43 chars
    const LOGIN_COMMAND = 'op signin https://myteam.1password.com myemail <my secret key> --output=raw'
    const PASSWORD = 'password'
    
    const SHELL = os.platform() === 'win32' ? 'cmd.exe' : 'bash'
    
    const ptyProcess = pty.spawn(SHELL, [], {
      name: 'xterm-color',
      cols: 80,
      rows: 30,
      cwd: process.env.HOME,
      env: process.env,
    })
    
    let result = ''
    
    ptyProcess.on('data', function(data) {
      result += data.toString()
    
      if (result.endsWith('at myteam.1password.com: ')) {
        console.log('entering password')
        ptyProcess.write(`${PASSWORD}\r`)
      }
    
      const tokenMatches = TOKEN_REGEX.exec(result)
      if (tokenMatches) {
        const token = tokenMatches[1]
        console.log('got token!', token)
        ptyProcess.kill()
      }
    })
    
    ptyProcess.write(`${LOGIN_COMMAND}\r`)
    

    This token works when I run the above program as is. For some reason, the token is not usable in my electron application when I subsequently use it later. To get past this, I had to force the user to open a terminal do a op signin (i give them the command they can copy paste) and obtain a token. They then paste the token into the app to proceed.

    (Edited by @graham_1P for code formatting)

  • Hey @regsethu

    I'm glad to hear you got it working.

    Let us know if you run into any other issues.

    Graham

  • daffl
    daffl
    Community Member

    We were running into the same problem in Node 13 and later (it does appear to work with version 12). Looks like it has something to do with the standard in and error setup which can be fixed by setting it to inherit when spawning the process in Node like this:

    const { spawn } = require('child_process');
    const cmd = 'op';
    const args = [
      'get',
      'item',
      'staging-payments-key'
    ];
    const proc = spawn(
      cmd, // the command you want to run, in my case `op`
      args, // arguments you want to use with the above cmd `signin`, etc.
      {
        stdio: [
          'inherit', // stdin: changed from the default `pipe`
          'pipe', // stdout
          'inherit' // stderr: changed from the default `pipe`
        ]
      });
    var result = '';
    proc.stdout.on('data', function (data) {
      result += data.toString();
    });
    proc.on('close', function (code) {
      console.log(result);
    });
    
  • askriz
    askriz
    Community Member

    @daffl high chances the error you were encountering was that in the inital code version, Node was spawning new process with non-tty stdin/out/err. Now that you inherit these fds, op can use them as tty.

  • felix_1p
    felix_1p
    1Password Alumni

    @daffl : @askriz is right that by default if you spawn a process in Node.js, Node.js creates new pipes/file descriptors for stdin,stdout and stderr for the new process. The new process won't be connected to the terminal. See https://nodejs.org/api/child_process.html#child_process_options_stdio for more information.

    Having said that, I can understand why op signin would throw an error (as mentioned in the original post) but where stdin and stderr point to shouldn't matter for other commands. I will investigate this to make sure we aren't doing anything weird in that regard.

    Thank you both!

This discussion has been closed.