sshx icon indicating copy to clipboard operation
sshx copied to clipboard

Add authentication token support

Open bhcopeland opened this issue 4 months ago • 6 comments

Add --auth-token CLI option to include Bearer authentication in gRPC requests. Supports both command line argument and SSHX_AUTH_TOKEN environment variable.

This enables integration with authentication systems that require tokens to be passed in the Authorization header for both session creation and cleanup.

bhcopeland avatar Jul 28 '25 16:07 bhcopeland

I see this fails CI. Since it only touches client code, it's good in its current form. However, if you want me to include another commit, I can add the Controller::new calls to include the auth_token. Thanks!

I have included a diff of the proposed fix:

-    let controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     controller.close().await?;
     Ok(())
 }
@@ -23,7 +23,7 @@ async fn test_handshake() -> Result<()> {
 async fn test_command() -> Result<()> {
     let server = TestServer::new().await;
     let runner = Runner::Shell("/bin/bash".into());
-    let mut controller = Controller::new(&server.endpoint(), "", runner, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", runner, false, &None).await?;

     let session = server
         .state()
@@ -71,7 +71,7 @@ async fn test_ws_missing() -> Result<()> {
 async fn test_ws_basic() -> Result<()> {
     let server = TestServer::new().await;

-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     tokio::spawn(async move { controller.run().await });
@@ -103,7 +103,7 @@ async fn test_ws_basic() -> Result<()> {
 async fn test_ws_resize() -> Result<()> {
     let server = TestServer::new().await;

-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     tokio::spawn(async move { controller.run().await });
@@ -147,7 +147,7 @@ async fn test_ws_resize() -> Result<()> {
 async fn test_users_join() -> Result<()> {
     let server = TestServer::new().await;

-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     tokio::spawn(async move { controller.run().await });
@@ -176,7 +176,7 @@ async fn test_users_join() -> Result<()> {
 async fn test_users_metadata() -> Result<()> {
     let server = TestServer::new().await;

-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     tokio::spawn(async move { controller.run().await });
@@ -201,7 +201,7 @@ async fn test_users_metadata() -> Result<()> {
 async fn test_chat_messages() -> Result<()> {
     let server = TestServer::new().await;

-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, false, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     tokio::spawn(async move { controller.run().await });
@@ -234,7 +234,7 @@ async fn test_read_write_permissions() -> Result<()> {
     let server = TestServer::new().await;

     // create controller with read-only mode enabled
-    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, true).await?;
+    let mut controller = Controller::new(&server.endpoint(), "", Runner::Echo, true, &None).await?;
     let name = controller.name().to_owned();
     let key = controller.encryption_key().to_owned();
     let write_url = controller

bhcopeland avatar Jul 28 '25 16:07 bhcopeland

@ekzhang I am not sure if you have any comments on this. Let me know, be good to get this upstreamed, otherwise I'll have to build sshx locally :).

bhcopeland avatar Aug 18 '25 09:08 bhcopeland

Ah yes this makes sense, we could do this. Technically self-hosting is not supported, but it seems like the bare minimum sshx could offer to help. Thanks for implementing!

ekzhang avatar Aug 18 '25 13:08 ekzhang

I should change the API a bit so it doesn't break CI, and also add validation to the server

ekzhang avatar Aug 18 '25 13:08 ekzhang

@ekzhang, no problem. It would be awesome to have it upstreamed soon if possible! Yes, I understand. I'll leave it with you. Let me know if you want me to make any changes. Thanks for a great project!

bhcopeland avatar Aug 21 '25 08:08 bhcopeland

@ekzhang, can we get this through, do you think?

bhcopeland avatar Sep 23 '25 10:09 bhcopeland