From a9c95814651b2b880aec0531a125eea33ed7e7de Mon Sep 17 00:00:00 2001
From: Juliusz Chroboczek <jch@irif.fr>
Date: Sat, 19 Feb 2022 23:58:31 +0100
Subject: [PATCH] Implement authPortal.

---
 README           | 18 ++++++++++++++----
 group/group.go   | 11 ++++++++---
 static/galene.js |  2 ++
 3 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/README b/README
index 5e890ff..70d8dcf 100644
--- a/README
+++ b/README
@@ -94,7 +94,7 @@ following fields are allowed:
    definitions (see *Authorisation* below) and specifies the users allowed
    to connect respectively with operator privileges, with presenter
    privileges, and as passive listeners;
- - `authServer` and `authKeys`: see *Authorisation* below;
+ - `authKeys`, `authServer` and `authPortal`: see *Authorisation* below;
  - `public`: if true, then the group is visible on the landing page;
  - `displayName`: a human-friendly version of the group name;
  - `description`: a human-readable description of the group; this is
@@ -201,11 +201,10 @@ existing authentication and authorisation infrastructure, such as LDAP,
 OAuth2 or even Unix passwords.
 
 When an authorisation server is used, the group configuration file
-specifies the URL of the authorisation server and one or more public keys
-in JWK format:
+specifies one or more public keys in JWK format.  In addition, it may
+specify either an authorisation server or an authorisation portal.
 
     {
-        "authServer": "https://auth.example.org",
         "authKeys": [{
             "kty": "oct",
             "alg": "HS256",
@@ -219,12 +218,23 @@ in JWK format:
             "y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
             "kid": "20211101"
         }]
+        "authServer": "https://auth.example.org",
     }
 
 The `kid` field serves to distinguish among multiple keys, and must match
 the value provided by the authorisation server.  If the server doesn't
 provide a `kid`, the first key with a matching `alg` field will be used.
 
+If an authorisation server is specified, then the default client, after it
+prompts for a password, will request a token from the authorisation server
+and will join the group using token authentication.  The password is never
+communicated to the server.
+
+If an authorisation portal is specified, then the default client will
+redirect initial client connections to the authorisation portal.  The
+authorisation portal is expected to authorise the client and then redirect
+it to Galene with the `username` and `token` query parameters set.
+
 
 # Further information
 
diff --git a/group/group.go b/group/group.go
index f46eb73..724c8a8 100644
--- a/group/group.go
+++ b/group/group.go
@@ -972,11 +972,14 @@ type Description struct {
 	// A list of logins for non-presenting users.
 	Other []ClientPattern `json:"other,omitempty"`
 
-	// The URL of the authentication server.
+	// The (public) keys used for token authentication.
+	AuthKeys []map[string]interface{} `json:"authKeys"`
+
+	// The URL of the authentication server, if any.
 	AuthServer string `json:"authServer"`
 
-	// The (public) keys of the authentication server
-	AuthKeys []map[string]interface{} `json:"authKeys"`
+	// The URL of the authentication portal, if any.
+	AuthPortal string `json:"authPortal"`
 
 	// Codec preferences.  If empty, a suitable default is chosen in
 	// the APIFromNames function.
@@ -1152,6 +1155,7 @@ type Status struct {
 	DisplayName string `json:"displayName,omitempty"`
 	Description string `json:"description,omitempty"`
 	AuthServer  string `json:"authServer,omitempty"`
+	AuthPortal  string `json:"authPortal,omitempty"`
 	Locked      bool   `json:"locked,omitempty"`
 	ClientCount *int   `json:"clientCount,omitempty"`
 }
@@ -1162,6 +1166,7 @@ func (g *Group) Status (authentified bool) Status {
 		Name:        g.name,
 		DisplayName: desc.DisplayName,
 		AuthServer:  desc.AuthServer,
+		AuthPortal:  desc.AuthPortal,
 		Description: desc.Description,
 	}
 
diff --git a/static/galene.js b/static/galene.js
index 0ae5e80..9b1fca0 100644
--- a/static/galene.js
+++ b/static/galene.js
@@ -3779,6 +3779,8 @@ async function start() {
         username = parms.get('username');
         token = parms.get('token');
         await serverConnect();
+    } else if(groupStatus.authPortal) {
+        window.location.href = groupStatus.authPortal;
     } else {
         let container = document.getElementById("login-container");
         container.classList.remove('invisible');
