Create API Key without user input (ttn-lw-cli login in a script)

I am writing a script to configure a stack installation from scratch, with no user input. I’ve got the stack running, created an admin user, and registered the CLI and console for OAuth. However, I’m stuck trying to create an API token for my admin user.

ttn-lw-stack doesn’t have any command for creating an API key, so I can’t use that. ttn-lw-cli could do the job, but it requires me to log in via OAuth. How can I perform ttn-lw-cli login without requiring user interaction?

I created an issue on GitHub for adding a command to ttn-lw-stack that creates API keys for a user:

I also created an issue to make ttn-lw-cli login possible without user interaction:

In the meantime, you can use something like this to login to the CLI without user interaction:


curl -b cookie.jar -c cookie.jar -D headers.txt "$base_url/oauth/"
cat headers.txt | grep "X-Csrf-Token: " | tee headers.txt
echo "Content-Type: application/json" >> headers.txt
curl -XPOST -b cookie.jar -c cookie.jar --header @headers.txt --data "{\"user_id\":\"$user_id\",\"password\":\"$password\"}" "$base_url/oauth/api/auth/login"

ttn-lw-cli login &

sleep 5

curl -L -b cookie.jar -c cookie.jar "$base_url/oauth/authorize?client_id=cli&redirect_uri=local-callback&response_type=code"

The CLI may still try to open a browser window until that second issue is closed.

1 Like

Thanks for launching those issues. A resolution for either of them would be invaluable to me.

I’ve had a crack at using your short term script solution, and it almost works. I’m coming unstuck on the last command because I’m running in a docker container and the callback goes to instead of the address I’ve specified in ttn-lw-stack-docker.yaml:

~ $ curl -L -b cookie.jar -c cookie.jar "$base_url/oauth/authorize?client_id=cli&redirect_uri=local-callback&response_type=code"
ERROR Could not exchange OAuth access token    error=Post "https://localhost/oauth/token": dial tcp connect: connection refused
The CLI could not exchange the OAuth access token: Post "https://localhost/oauth/token": dial tcp connect: connection refused.

It looks like ttn-lw-cli is ignoring the value of is.oauth.ui.canonical-url. Both base_url and is.oauth.ui.canonical-url are set to the machine’s static IP address (http://10.0.x.x).

I’ve tried the second option in the configuration instructions (http://localhost:1885) but I get the same result.

In both cases I’ve been using http rather than https - could that be an issue? When I try using https the curl command fails:

curl -b cookie.jar -c cookie.jar -D headers.txt "$base_url/oauth/"
curl: (35) error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error

I’m intending to run this on a private air-gapped server, so https shouldn’t be necessary.

EDIT: I’ve also used the same base_url value when registering the console and cli for oauth
EDIT 2: the TLS error was due to incorrectly configured TLS (forgot to uncomment the TLS lines in the docker and ttn config files)

I’m still battling this, but http vs https was not the problem. After setting up TLS properly I’m still getting that OAuth exchange error where it tries posting to localhost instead of the static ip address that I’ve used in all the config.

Yep, I can’t see any way to progress with this. I tried using iptables to reroute port 443 to 8885 but I’m still seeing the same error on that last command. This is preventing me from logging in entirely when running in docker - even if I open up my browser using --callback=false to provide a token, that last step still fails.

For security reasons, the callback to localhost when logging in with the CLI is hardcoded, so that can not be changed.

I’m afraid your only option at this point is that command to create API keys ( We haven’t planned that issue yet, so it could take a while before it’s implemented. If this is important to you, you’re of course welcome to contribute this feature yourself.

Thanks for the explanation. Does this mean that it’s impossible to log in to the cli or console when running the open source stack in docker? That doesn’t seem right, because it essentially renders the stack unusable, but I can’t see a way around it, and docker is the recommended way to run the open source stack

No, it just means that it’s not possible to do a non-interactive (scripted) CLI login.

In order to use the CLI, you’ll need to use a web browser for the actual username+password login. This login can currently be done with cURL, but when we add CAPTCHA and 2FA that will no longer be possible.

The OAuth flow with callback (the default) is possible when your CLI can expose a port on localhost.

The OAuth flow without callback (with --callback=false is possible when you can paste the authorization code to the CLI interactively.

Thanks for clarifying. In my dev environment, I have tried logging in interactively (both with and without callback) but I’m still getting blocked by the localhost callback issue. As per the documentation, I’m running the things stack inside a docker container.

When I use the default oauth flow (callback), no browser window is launched, because I’ve run ttn-lw-cli inside my docker container. I’ve got the default port forwarding setup going into my docker container as per docker-compose.yaml. Since this is a dev environment I’m happy to try exposing a port on localhost but I’m not sure exactly what is needed there.

When I use the --callback=false flow, I can manually open my browser, point it at the ip address of my container, and get a login token. Pasting that token back into the container doesn’t work, though:

/ $ ttn-lw-cli login --callback=false
INFO Opening your browser on https://localhost/oauth/authorize?client_id=cli&redirect_uri=code&response_type=code
WARN Could not open your browser, you'll have to go there yourself error=exec: "xdg-open": executable file not found in $PATH
INFO After logging in and authorizing the CLI, we'll get an access token for future commands.
INFO Please paste the authorization code and press enter
ERROR Could not exchange OAuth access token    error=Post "https://localhost/oauth/token": dial tcp connect: connection refused
Post "https://localhost/oauth/token": dial tcp connect: connection refused

Note that instead of visiting https://localhost/oauth/authorize?client_id=cli&redirect_uri=code&response_type=code I had to visit https://<container_ip_address>/oauth/authorize?client_id=cli&redirect_uri=code&response_type=code

My question about token issues when logging in is a bit off topic, so I’ll stop talking about it here. The login-without-user-input question has been resolved so this thread can safely be left to close itself.

Further discussion of the token/login issue (including solutions) is taking place in this thread.

This topic was automatically closed after 30 days. New replies are no longer allowed.