-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsso.go
81 lines (68 loc) · 1.99 KB
/
sso.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package gotrix
import (
// Embedding success HTML to be displayed to the user.
_ "embed"
"net"
"net/http"
"strconv"
)
//go:embed sso_success.html
var defaultSuccessHTML []byte
// SuccessHTML is the HTML displayed when the user successfully logs in through SSO.
var SuccessHTML = defaultSuccessHTML
// handleSSOResult creates a function that handles the redirect from Matrix's SSO by forwarding it to the provided
// channel.
func handleSSOResult(src string, success chan string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.FormValue("loginToken") == "" {
http.Redirect(w, r, src, http.StatusFound)
return
}
_, _ = w.Write(SuccessHTML)
success <- r.FormValue("loginToken")
}
}
// LoginSSO returns a URL that can be used to log the user in through SSO and starts a HTTP server to listen to the
// response from the homeserver.
// It automatically cleans up the HTTP server when the context the client has expires or when the user logs in
// successfully.
//
// The returned function blocks until the login finishes or is canceled and returns an error if the login was
// unsuccessful.
func (c *Client) LoginSSO() (string, func() error, error) {
// Manually create a listener so we know what the port is.
listener, err := net.Listen("tcp", ":0")
if err != nil {
return "", nil, err
}
port := listener.Addr().(*net.TCPAddr).Port
url := "http://127.0.0.1:" + strconv.Itoa(port) + "/"
ssoURL := c.FullRoute(c.Client.Endpoints.SSOLogin(url))
success := make(chan string)
errChannel := make(chan error)
srv := http.Server{
Handler: handleSSOResult(ssoURL, success),
}
go func() {
_ = srv.Serve(listener)
}()
go func() {
defer srv.Close()
var token string
if c.ctx != nil {
select {
case <-c.ctx.Done():
errChannel <- c.ctx.Err()
return
case token = <-success:
}
} else {
token = <-success
}
errChannel <- c.LoginToken(token)
}()
listenResult := func() error {
return <-errChannel
}
return ssoURL, listenResult, nil
}