Initial commit

This commit is contained in:
2023-10-19 07:13:09 +02:00
commit 6acb0afcc3
4 changed files with 143 additions and 0 deletions

82
app/main.go Normal file
View File

@@ -0,0 +1,82 @@
package main
import (
"flag"
htemplate "html/template"
"log"
"net"
"net/http"
"net/http/fcgi"
"os"
"strings"
)
var (
domain = flag.String("domain", "thequux.com", "The base domain to enable SSO for")
listen = flag.String("listen", "0.0.0.0:80", "The address to listen on")
)
func main() {
flag.Parse()
l := log.New(os.Stderr, "ipaSSO: ", log.Ldate|log.Ltime|log.Lshortfile)
mux := http.NewServeMux()
mux.Handle("/env", http.HandlerFunc(dumpEnv))
l.Printf("Starting")
listener, err := net.Listen("tcp", *listen)
if err != nil {
log.Fatalln("Failed to listen: ", err)
}
l.Println("Listening on", listener.Addr())
log.Fatal(fcgi.Serve(listener, mux))
}
var envTemplate = htemplate.Must(htemplate.New("envdoc").Parse(`
<!DOCTYPE html>
<html>
<head>
<style>
pre { margin: 0 1ex; }
</style>
</head>
<body>
<h1>Headers</h1>
<table>
{{- range $k,$v := .Headers }}
<tr>
<td><pre>[{{ $k }}]</pre></td>
<td><pre>{{ $v }}</pre></td>
</tr>
{{- end }}
</table>
<h1>Process Environment</h1>
<table>
{{- range $k,$v := .ProcessEnv }}
<tr>
<td><pre>{{ $k }}</pre></td>
<td><pre>{{ $v }}</pre></td>
</tr>
{{- end }}
</table>
</body>
</html>
`))
type envdocArgs struct {
Headers map[string]string
ProcessEnv map[string]string
}
func dumpEnv(w http.ResponseWriter, r *http.Request) {
var headers = map[string]string{}
for k, v := range r.Header {
headers[k] = strings.Join(v, "\n")
}
var args = envdocArgs{ProcessEnv: fcgi.ProcessEnv(r), Headers: headers}
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
if err := envTemplate.Execute(w, args); err != nil {
panic(err)
}
}

58
docs/auth-flow.md Normal file
View File

@@ -0,0 +1,58 @@
# Authorization flow
Current authorization state is passwd around via a signed cookie containing authorization details in JSON.
Most pages do not have any form of authorization, and simply depend on the cookie.
However, in order to log in, more complex flow is necessary.
Login pages are all grouped under `/login`, which normally serves an HTML form with some additional javascript.
This form includes a hidden field with the page to redirect to.
The JS on `/login` begins by fetching `/login/status` via XHR GET.
`/login/status` returns a JSON response indicating the login state, which is one of:
* VALID: User is fully logged in. User details are attached
* EXPLICIT_LOGOUT: Cookie is set to indicate logged-out state.
* UNKNOWN: No cookie set
* INVALID: Invalid signature on cookie, cookie content invalid, or cookie expired. This also clears the cookie.
If the state is `UNKNOWN`, automatic login is performed.
If the state is `EXPLICIT_LOGOUT`, the process terminates here.
Otherwise, automatic login is performed.
If automatic login fails, the page simply displays a login form.
The user may log in using a username/password, at which point a cookie is set with their details.
The user may also submit the form with blank username/password, in which case automatic login is performed.
# Automatic login
These steps are performed in sequence, each returning the same data as `/login/status`.
The process completes when the state is `VALID` or `EXPLICIT_LOGOUT`.
1. Fetch `/login/spnego` via XHR GET.
2. Fetch `/login/x509` via XHR GET.
Upon successful login, a redirect is performed to the page in the hidden field.
# API
In addition to the `/login` endpoint, there exist several other useful endpoints:
* `/logout`: Set cookie to indicate logged-out state
* `/status`: Fetch a representation of user's info.
If `Accept` requests `application/json`, this is JSON.
Otherwise, this is an HTML page
* `/sigkey`: Fetch the current signing key. This is an Ed25519 key.
* `/refresh`: Refresh the user data cookie given the refresh token.
# Two cookies
In the above description, the login token was described as a single cookie.
It is, in fact, two cookies:
* A short-lived (~seconds) user data cookie
* A longer-lived (~8 hour) refresh token, tied to an auth server
If the user data cookie expires, the refresh token can be fed to `/refresh` to update the user data cookie.
The purpose of the user data cookie is solely to reduce the load on the SSO server; the resources fetched by a page will
generally be fetched within a few seconds of the initial request.
It is the reverse proxy's responsibility to refresh the user data cookie and translate it into headers or FCGI environment variables for the backend server.
This task can be assisted by ipasso-rpxagentd (to be specified).

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module git.thequux.com/thequux/ipasso
go 1.20

0
go.sum Normal file
View File