title = "SIP 007 - Deployment Authentication and Authorisation" template = "main" date = "2022-09-30T01:01:01Z"
Summary: User experience and implementation design for authentication to deployment targets.
Owners: ivan.towlson@fermyon.com
Created: Sep 30, 2022
spin deploy
currently requires the user to authenticate on every deployment. For a "username-password" style of authentication, this is relatively painless as the user can set the HIPPO_USERNAME
and HIPPO_PASSWORD
environment variables and never need to worry about it. However, it's unsatisfactory for token-based schemes such as OAuth or device code login, which are modelled more around a login experience that grants an access token.
We want to provide a way for spin deploy
to work smoothly against environments configured with these schemes, as well as the existing "username-password" environments.
To recap, the current spin deploy
authentication experience is:
--bindle-server
or$BINDLE_URL
- required--bindle-username
or$BINDLE_USERNAME
- optional--bindle-password
or$BINDLE_PASSWORD
- optional--hippo-server
or$HIPPO_URL
- required--hippo-username
or$HIPPO_USERNAME
- required--hippo-password
or$HIPPO_PASSWORD
- required
At any given time, Spin is either be logged out (no active deployment target, no credentials stored for any target) or logged in to a deployment target (URL and credentials stored for that target). Logging in is a separate step from deployment. Deployment requires Spin to be logged in, and always deploys to the stored URL with the stored credentials
NOTE: This will apply to existing username-password environments as well. The current UI for deploying to those environments goes away.
FUTURE: In future Spin may be able to store multiple deployment targets, in a similar manner to Kubernetes contexts. A user would then be able to switch between these contexts without re-establishing credentials. It would be desirable to have some level of this very early on, so that users or Fermyon staff could easily test on deployment targets running stable and canary Spin versions.
We propose to add a spin login
command, which takes the desired Hippo server as an argument:
spin login --url https://example.com
This launches an interactive login experience as follows.
1. Determine auth type.
At the moment, Spin doesn't know whether a URL uses device flow or username-password, so spin login
will prompt. If we can identify particular URLs or families of URLs whose auth mode is known, those can skip this step.
What authentication method does this server support?
1. Sign in with GitHub
2. Sign in with a username and password
Enter a number:
Programmatic consumers need a way to suppress this prompt, as follows:
- If
--get-device-code
or--check-device-code
is present, use GitHub auth. - Otherwise, support a
--method=[username|github]
flag to specify the method. This flag can be hidden from the online help.
FUTURE: We would implement an API for Spin to ask a server which auth mode(s) it supports.
2a. Username-password prompt.
If the mode is username-password, Spin prompts for these values. If the environment variables currently defined for spin deploy
are set, these will be picked up and not be prompted for.
Hippo username:
Hippo password:
# etc
The URLs (Hippo and Bindle) and credentials are stored on the machine.
2b. GitHub device authorisation flow.
If the mode is GitHub device auth, Spin prompts the user to open their browser:
$ spin login --url https://example.com
Copy your one-time code: tWOJCFhY
Press Enter to open https://example.com/device-authorization in your browser
# ...user hits Enter...
Waiting for device authorization...
Waiting for device authorization...
Device authorized!
Login successful.
The URL and token generated by the device authorisation flow are stored on the machine.
spin logout
destroys the current stored credentials and puts Spin in the 'logged out' state.
The spin deploy
command will now act as follows:
- The Hippo and Bindle flags/environment variables are removed.
- If Spin is logged out,
spin deploy
prints a prompt to runspin login
, and exits.- FUTURE: It will launch the login flow.
- It would be good to have distinguished exit codes for "need to log in" vs "deployment failed," for programmatic consumption.
- If Spin is logged in,
spin deploy
performs the current deployment flow, using the stored URL and credentials.- If auth fails (e.g. wrong password or expired token),
spin deploy
prints a prompt to runspin login
, and exits. - FUTURE: It will launch the login flow.
- If auth fails (e.g. wrong password or expired token),
spin login
is a new command and a relatively complex user experience. This section describes the behaviour in more detail.
As part of device auth flow, the user needs to go to the verification URL in their browser and paste in the code.
We discussed:
- Opening the browser automatically
- Copying the user code to the clipboard
The current proposal follows the gh
flow, which seems appropriate for GitHub auth. That is:
- The user hits a key to open the browser. This seems like a good compromise between the convenience factor and the potential surprise (or "on which of my five monitors did it open") of doing it when the user isn't expecting it.
- A clickable URL is displayed in case automatic open doesn't work.
- The user is responsible for copying the code if they want to. This avoids the risk of blatting important clipboard contents. As the user has to hit a key to open the browser, they have plenty of time to copy the code, or memorise it, or jot it down on a scrap of parchment, whatever is suitable to their current state and workflow.
The device auth flow involves the client polling the server using the device code as the auth token. Each poll can have three outcomes:
- 200 and the response contains a token: user has authorised this device
- 200 and the response doesn't contain a token: user has not yet authorised this device
- Server error
- Network error
We propose to poll for up to 15 minutes (the expiry time of the code). If the user has not entered the code by then, spin login
fails and exits.
Errors do not cause polling to terminate. We do not want a transient network error or server hiccup to block login. That said, if we can identify server responses that are necessarily fatal, we can in future terminate early on those. spin login
will, however, report the error so that a user can cancel out if it's looking broken rather than gazing at a polling display for 15 minutes.
When the user logs into a server, we need to store:
- The server URL
- Any server connection options, such as the
--insecure
flag - The credentials (token, username-password, etc.)
A simple solution is to store these in a file under a non-roaming directory. This is not fantastically secure for tokens, but may be enough for the preview. (And hey, it's good enough for kubectl
.)
FUTURE: There will be either multiple files or multiple items in the file (a la kubeconfig
). We might want to plan for this in our implementation.
For environments like the VS Code extension, the standard interactive device auth flow is hard to manage. We therefore propose an alternative flow, controlled by flags that are hidden from interactive users, e.g.
$ spin login --url foo --get-device-code
{ "verificationUrl" : "...", "deviceCode" : "...", "userCode": " ..." }
$ spin login --url foo --check-device-code bar
{ "status" : "waiting" }
# user enters code into browser
$ spin login --url foo --check-device-code bar
{ "status" : "authorized", "token" : "..." }
Similarly, it should be possible to make username-password login non-interactive by passing suitable flags or environment variables.