This document describes the cased-cli components along with the interaction between them.
- cased-cli: Command line tool installed on end user's machine, an ssh client, the actual command is just cased.
- cased-server: SSH server, handles cased-cli connections and provide a TUI (terminal user interface) for the user to interact with.
- cased-shell: Software that provide resources to the cased-server over a REST API (Python/Tornado). Resources include the list of prompts a user can connect to along with managing connection to those prompts over a websocket channel.
Notes on OAuth2 PKCE terminology:
. code_verifier
: A cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long. Once the client has generated the code verifier, it uses that to create the code challenge.
. code_challenge
: A computation of code_verifier
using code_challenge_method
.
. code_challenge_method
: Must be either plain
or s256
:
. `plain`: The plain method does not change the input, so the value of `code_verifier` and the resultant value of `code_challenge` are equal.
. `S256`: The `code_challenge` is the base64 encode of the sha256 hash of `code_verifier`, i.e. `code_challenge` = `base64encode(sha256(code_verifier))`.
- User types (in a shell):
cased auth instance.domain
. cased-cli
generates a randomcode_verifier
, then calculates thecode_challenge
usingS256
.cased-cli
sends a GET request toinstance.domain/v2/api/auth/
, thecode_challenge
andcode_challenge_method
are sent as query parameters usingcc
andcm
respectively, i.e.v2/api/auth?cc=f718...&cm=S256
.cased-shell
sends back a JSON with the login url to authenticate the user.cased-cli
launches the user web browser, the web browser connects to the login URL provided bycased-shell
.- User authenticates with the IDP.
- If authentication is successful proceed to 8, otherwise this process ends with an error message displayed to the user.
cased-shell
generates a randomauthorization_code
, then it stores the sessionJWT
,code_challenge
andcode_challenge_method
in a dictionary where the key is theauthorization_code
.cased-shell
sends back theauthorization_code
to cased-cli.cased-cli
obtains theauthorization_code
, then it sends a POST request tocased-shell
on/v2/api/token
, thecode_verifier
andauthorization_code
are sent as POST arguments (x-www-form-urlencoded). NOTE: One possible solution for allowing the web browser to send theauthorization_code
back to thecased-cli
app could be implemented as follows: . In step 3,cased-cli
starts a temporary http server and listens on a random port (RP). .cased-cli
sends a GET request to cased-shell on theinstance.domain/v2/api/auth/
URL, the RP is sent asrp
along with thecc
andcm
parameters. . After authenticating with the IDP, cased-shell sends back javascript code that creates a POST request to 127.0.0.1:(RP), and send theauthorization_code
as payload (JSON or whatever). .cased-cli
parses the POST request and retrieves theauthorization_code
.cased-shell
, retrives the JWT,code_challenge
andcode_challenge_method
associated with theauthorization_code
. If there is no associated data an error must be returned and the process ends.cased_shell
computes a code challenge using the thecode_verifier
provided and thecode_challenge_method
stored previously.- If the code challenge just computed matches the
code_challenge
stored initially, then the verification is ok,cased-shell
sends back the sessionJWT
tocased-cli
in the response, otherwise return an error status + message. cased-cli
connects tocased-server
using password authentication, where the password is the sessionJWT
provided bycased-shell
.cased-server
validates the JWT, if validation fails the user is disconnected fromcased-server
, otherwise the connection is succesfully established.
TODO: If cased-cli
fails to open a web browser to authenticate the user, another fallback solution to authentication must be provided.
- User selects a prompt in the TUI provided by cased-server then hits .
- cased-server sends a POST request to cased-shell on the /v2/ along with the user session JWT
- cased_server sends a POST request to cased-shell on the /v2/ws?id= with the fetched from cased-shell in step 2.
- If the websocket connection succeeds, cased-server forwards terminal input from cased-cli to cased-server websocket connection, and data from the cased-server websocket connection to the cased-cli session, effectively acting as a bridge.
This is sample code that could be used to send the authorization_code
back to the cased-cli.
NOTE: {{.Port}} and {{.AUTH}} are templates to be substituted by cased-shell.
<script type="text/javascript">
var xhr = new XMLHttpRequest();
var url = "http://127.0.0.1:" + {{.Port}} + "/";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// success!
// var json = JSON.parse(xhr.responseText);
// Show some status message to the user using the element with id=result
// document.getElementById("result").innerHTML = xhr.responseText;
} else {
// Error, maybe show authorization_code to the user and ask it to paste to the cased-cli?
}
};
var data = JSON.stringify({"authorization_code": "{{.AUTH}}"});
xhr.send(data);
</script>
sequenceDiagram
actor User
participant cased_cli
participant web_browser
participant cased_server
participant cased_shell
participant prompt
participant IDP
User->>cased_cli: cased auth instance.domain
cased_cli->>cased_shell: GET v2/api/auth?cc=...&cm=...&rp=...
cased_shell->>cased_cli: 200 OK, Body: '{ "auth_url": "..." }'
web_browser->>IDP: GET auth_url
User->web_browser: Fill in credentials, submit
IDP->>cased_shell: POST instance.domain/callback
cased_shell->>web_browser: 200 OK, Body: '{ "authorization_code": "..." }
web_browser->>cased_cli: POST 127.0.0.1:<RP> Body: '{ "authorization_code": "..." }
cased_cli->>cased_shell: POST instance.domain/token authorization_code=...&code_verifier=...
cased_shell->>cased_cli: 200 OK, Body: '{ "JWT": "..." }'
cased_cli->>cased_server: Connect using JWT as password
User->>cased_cli: Select a Prompt
cased_server->>cased_shell: Open websocket connection
cased_shell->>prompt: Open SSH connection (Paramiko)
loop WebsocketShell
cased_cli->>cased_server: Send input
cased_server->>cased_shell: forward input (WS)
cased_shell->>prompt: Foward input (SSH)
prompt->>cased_shell: Send response (SSH)
cased_shell->>cased_server: Forward response (WS)
cased_server->>cased_cli:Forward response (SSH)
end