You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
3.8 KiB
148 lines
3.8 KiB
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"p83.nl/go/indieauth"
|
|
)
|
|
|
|
const ClientID = "https://p83.nl/track-me"
|
|
|
|
type IndieAuthHandler struct {
|
|
}
|
|
|
|
func performIndieauthCallback(state, code string, sess *Session) (bool, *indieauth.AuthResponse, error) {
|
|
if state != sess.State {
|
|
return false, &indieauth.AuthResponse{}, fmt.Errorf("mismatched state %q != %q", state, sess.State)
|
|
}
|
|
return indieauth.VerifyAuthCode(ClientID, code, sess.RedirectURI, sess.AuthorizationEndpoint)
|
|
}
|
|
|
|
func (h *IndieAuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
sess, err := NewSession(w, r)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
defer sess.Flush()
|
|
|
|
if sess.LoggedIn {
|
|
http.Redirect(w, r, "/", 302)
|
|
return
|
|
}
|
|
|
|
if r.Method == http.MethodGet {
|
|
if r.URL.Path == "" {
|
|
fmt.Fprint(w, `<!doctype html>
|
|
<html>
|
|
<head>
|
|
<title>Login - Track Me</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport"
|
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"
|
|
integrity="sha256-zIG416V1ynj3Wgju/scU80KAEWOsO5rRLfVyRDuOv7Q=" crossorigin="anonymous"/>
|
|
</head>
|
|
<body>
|
|
<div class="section">
|
|
<div class="container">
|
|
<form action="/auth/login" method="post">
|
|
<h1 class="title">Login in with your own website</h1>
|
|
<div class="field">
|
|
<label class="label">Web Sign In:</label>
|
|
<div class="control">
|
|
<input type="url" name="url" class="input" placeholder="url" />
|
|
</div>
|
|
</div>
|
|
<div class="field">
|
|
<div class="control">
|
|
<button class="button is-primary" type="submit">Sign in</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`)
|
|
return
|
|
} else if r.URL.Path == "callback" {
|
|
state := r.URL.Query().Get("state")
|
|
code := r.URL.Query().Get("code")
|
|
verified, authResponse, err := performIndieauthCallback(state, code, sess)
|
|
if err != nil {
|
|
fmt.Fprintf(w, "ERROR: %q\n", err)
|
|
return
|
|
}
|
|
if verified {
|
|
sess.Me = authResponse.Me
|
|
sess.LoggedIn = true
|
|
|
|
nextURI := "/"
|
|
if sess.NextURI != "" {
|
|
nextURI = sess.NextURI
|
|
}
|
|
http.Redirect(w, r, nextURI, 302)
|
|
return
|
|
}
|
|
return
|
|
} else if r.URL.Path == "logout" {
|
|
cleanupSession(sess)
|
|
http.Redirect(w, r, "/", 302)
|
|
return
|
|
}
|
|
} else if r.Method == http.MethodPost {
|
|
r.ParseForm()
|
|
if r.URL.Path == "login" {
|
|
// redirect to endpoint
|
|
me := r.Form.Get("url")
|
|
meURL, err := url.Parse(me)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("Bad Request: %s, %s", err.Error(), me), 400)
|
|
return
|
|
}
|
|
endpoints, err := indieauth.GetEndpoints(meURL)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("Bad Request: while getting endpoints for %s: %s", me, err.Error()), 400)
|
|
return
|
|
}
|
|
log.Println(endpoints)
|
|
|
|
authURL, err := url.Parse(endpoints.AuthorizationEndpoint)
|
|
if err != nil {
|
|
http.Error(w, fmt.Sprintf("Bad Request: %s %s", err.Error(), me), 400)
|
|
return
|
|
}
|
|
log.Println(authURL)
|
|
|
|
state := RandStringBytes(16)
|
|
redirectURI := fmt.Sprintf("%s/auth/callback", os.Getenv("TRACKME_HOST"))
|
|
|
|
sess.AuthorizationEndpoint = endpoints.AuthorizationEndpoint
|
|
sess.Me = meURL.String()
|
|
sess.State = state
|
|
sess.RedirectURI = redirectURI
|
|
sess.LoggedIn = false
|
|
|
|
authenticationURL := indieauth.CreateAuthenticationURL(*authURL, meURL.String(), ClientID, redirectURI, state)
|
|
|
|
http.Redirect(w, r, authenticationURL, 302)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func cleanupSession(sess *Session) {
|
|
sess.LoggedIn = false
|
|
sess.State = ""
|
|
sess.Me = ""
|
|
sess.AuthorizationEndpoint = ""
|
|
sess.NextURI = ""
|
|
sess.RedirectURI = ""
|
|
}
|