CLI Authentication with Auth0
Authentication with an Identity as a Service provider, such as Auth0, is pretty straight forward in a web application, but we also want to provide the same convenient SSO experience for our CLI users.
In this article, we’ll take a look at the traditional way to do authentication in the terminal and how we can improve the experience for our users.
TL;DR
Skip to the Implementation section to see the steps and code samples.
The code in this article was simplified for a better reading experience. We also published an open source library that implements the solution and can save you some work.
The library sources are available at https://github.com/altostra/altostra-cli-login-auth0 and an NPM package at https://www.npmjs.com/package/@altostra/cli-login-auth0
The traditional solution: using secrets
One way to approach this challenge is to generate a secret for users to store in a file manually (e.g. AWS access keys, GitHub SSH Keys, and NPM Auth Tokens).
There are a few drawbacks to this approach:
- The secrets don’t rotate — an attacker may use a stolen secret until the user manually revokes it
- Users must log in and create a secret for each machine they use
- Users must manually delete the files that hold the secrets or revoke them to log out
Making users manually manage their secrets works, but it is not the best experience for them. If you care to provide your users with a better experience, there is another way.
The better solution
After reading the Auth0 documentation and blog, it is clear how we can obtain an access token for the user from the terminal and still enjoy a web authentication experience with SSO:
- Start an HTTP server on the localhost address
- Open a parameterized Auth0 authorization URL in a browser
- Let the user login the same as they would in a web application
- Handle a redirect request from Auth0 to the localhost server, obtain the authentication code, and stop the server
- Call the Auth0 Management API to obtain an access token for the user
Once we obtain the access token, we can store it in a file, accessible only to the user, just as we would store secrets. Later, any of our locally running apps can use this token to authenticate. This process automates the manual work done by the user in the common solution while providing a web login experience with SSO.
The benefits
- We can enforce expiration on the access token and have the user re-authenticate, thus rotating credentials periodically.
- The login process is simple and doesn’t take much time.
- The process of setting up a new machine is quick and simple.
- We can provide the user with a log-out command that deletes the stored token and revokes it with Auth0.
Implementation
Step 1: Start a local HTTP server
Start an HTTP server on the localhost
address with the custom port 4242
that will handle authentication responses from Auth0.
Step 2: Open an Auth0 authentication URL
Using data from an Auth0 account and application, we construct an authentication URL:
https://domain.auth0.com/authorize
?response_type=code
&code_challenge_method=S256
&code_challenge=<code-challenge>
&client_id=<client-id>
&redirect_uri=http://localhost:4242
&scope=<token-scope>
&audience=<token-audience>
&state=<state>
The parameters are:
domain.auth0.com
is your Auth0 domain, you can find it in the settings page of your Auth0 applications in the Auth0 management console.response_type
must be set tocode
.code_challenge_method
should be set toS256
. The other possible value isclean
, but that would be a bad idea, security-wise.code_challenge
must be a string generated for each request, we use it to validate the response.client_id
is the Auth0 application Client ID. It's located in the settings of the application in the Auth0 management console.redirect_uri
is the URI to which the request with an authorization code should redirect. We must set it to thelocalhost
address with the same port number that we used to start the HTTP server.state
is an "opaque" value used as a CSRF-token to prevent CSRF attacks.
The full API documentation is available here.
Step 3: Handle the response
After the user completes the authentication in the browser, which either succeeds or fails, the login page redirects the user to the URL we provide in redirect_uri
. The query string of the URL contains either a code
value, when successful or an error
and error_description
values when unsuccessful.
Keep in mind that the response to the redirected request must be an HTML page to inform the user about the authentication process result.
Step 4: Get the access token
Using the code we obtained in step 3, we call the Auth0 authentication API to obtain an access token for our user. We provide the code_verifier
and the authentication code
values. The request looks like this:
URL : https://domain.auth0.com/oauth/token
Method : POST
Headers : ContentType: application/x-www-form-urlencoded
Body : grant_type=authorization_code&client_id=<client-id>&code_verifier=<code-verifier>&code=<autnentication-code>&redirect_uri=<uri>
domain.auth0.com
is your Auth0 domain, you can find it in the settings page of your Auth0 applications in the Auth0 management console.grant_type
must be set toauthorization_code
, indicating that we're also passing theauthentication-code
parameter.client_id
is required and must be the Auth0 application Client ID.code_verifier
is required and must be thecode_verifier
value generated at the beginning of the process.code
is required and must be the authentication code we got from the login page redirect.redirect_uri
is required and must be the same as theredirect_uri
value used in the authorization URL.
The full API documentation is available here.
Next Steps: Use the authentication token
Next, we store the access token in a file accessible only by the user, and then use it to authenticate to our services.