Initial commit
This commit is contained in:
82
app/main.go
Normal file
82
app/main.go
Normal 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
58
docs/auth-flow.md
Normal 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` or `INVALID`, automatic login is performed.
|
||||||
|
If the state is `EXPLICIT_LOGOUT`, the process terminates here.
|
||||||
|
Otherwise, a login form is displayed.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user