mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-01 13:17:21 +02:00
170 lines
5.0 KiB
Go
170 lines
5.0 KiB
Go
package authentik
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"imuslab.com/zoraxy/mod/database"
|
|
"imuslab.com/zoraxy/mod/info/logger"
|
|
"imuslab.com/zoraxy/mod/utils"
|
|
)
|
|
|
|
type AuthentikRouterOptions struct {
|
|
UseHTTPS bool //If the Authentik server is using HTTPS
|
|
AuthentikURL string //The URL of the Authentik server
|
|
Logger *logger.Logger
|
|
Database *database.Database
|
|
}
|
|
|
|
type AuthentikRouter struct {
|
|
options *AuthentikRouterOptions
|
|
}
|
|
|
|
// NewAuthentikRouter creates a new AuthentikRouter object
|
|
func NewAuthentikRouter(options *AuthentikRouterOptions) *AuthentikRouter {
|
|
options.Database.NewTable("authentik")
|
|
|
|
//Read settings from database, if exists
|
|
options.Database.Read("authentik", "authentikURL", &options.AuthentikURL)
|
|
options.Database.Read("authentik", "useHTTPS", &options.UseHTTPS)
|
|
|
|
return &AuthentikRouter{
|
|
options: options,
|
|
}
|
|
}
|
|
|
|
// HandleSetAuthentikURLAndHTTPS is the internal handler for setting the Authentik URL and HTTPS
|
|
func (ar *AuthentikRouter) HandleSetAuthentikURLAndHTTPS(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodGet {
|
|
//Return the current settings
|
|
js, _ := json.Marshal(map[string]interface{}{
|
|
"useHTTPS": ar.options.UseHTTPS,
|
|
"authentikURL": ar.options.AuthentikURL,
|
|
})
|
|
|
|
utils.SendJSONResponse(w, string(js))
|
|
return
|
|
} else if r.Method == http.MethodPost {
|
|
//Update the settings
|
|
AuthentikURL, err := utils.PostPara(r, "authentikURL")
|
|
if err != nil {
|
|
utils.SendErrorResponse(w, "authentikURL not found")
|
|
return
|
|
}
|
|
|
|
useHTTPS, err := utils.PostBool(r, "useHTTPS")
|
|
if err != nil {
|
|
useHTTPS = false
|
|
}
|
|
|
|
//Write changes to runtime
|
|
ar.options.AuthentikURL = AuthentikURL
|
|
ar.options.UseHTTPS = useHTTPS
|
|
|
|
//Write changes to database
|
|
ar.options.Database.Write("authentik", "authentikURL", AuthentikURL)
|
|
ar.options.Database.Write("authentik", "useHTTPS", useHTTPS)
|
|
|
|
utils.SendOK(w)
|
|
} else {
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
// HandleAuthentikAuth is the internal handler for Authentik authentication
|
|
// Set useHTTPS to true if your Authentik server is using HTTPS
|
|
// Set AuthentikURL to the URL of the Authentik server, e.g. Authentik.example.com
|
|
func (ar *AuthentikRouter) HandleAuthentikAuth(w http.ResponseWriter, r *http.Request) error {
|
|
const outpostPrefix = "outpost.goauthentik.io"
|
|
client := &http.Client{}
|
|
|
|
if ar.options.AuthentikURL == "" {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Authentik URL not set", nil)
|
|
w.WriteHeader(500)
|
|
w.Write([]byte("500 - Internal Server Error"))
|
|
return errors.New("authentik URL not set")
|
|
}
|
|
protocol := "http"
|
|
if ar.options.UseHTTPS {
|
|
protocol = "https"
|
|
}
|
|
|
|
authentikBaseURL := protocol + "://" + ar.options.AuthentikURL
|
|
//Remove tailing slash if any
|
|
authentikBaseURL = strings.TrimSuffix(authentikBaseURL, "/")
|
|
|
|
scheme := "http"
|
|
if r.TLS != nil {
|
|
scheme = "https"
|
|
}
|
|
reqUrl := scheme + "://" + r.Host + r.RequestURI
|
|
// Pass request to outpost if path matches outpost prefix
|
|
if reqPath := strings.TrimPrefix(r.URL.Path, "/"); strings.HasPrefix(reqPath, outpostPrefix) {
|
|
req, err := http.NewRequest(r.Method, authentikBaseURL+r.RequestURI, r.Body)
|
|
if err != nil {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Unable to create request", err)
|
|
w.WriteHeader(401)
|
|
return errors.New("unauthorized")
|
|
}
|
|
req.Header.Set("X-Original-URL", reqUrl)
|
|
req.Header.Set("Host", r.Host)
|
|
for _, cookie := range r.Cookies() {
|
|
req.AddCookie(cookie)
|
|
}
|
|
if resp, err := client.Do(req); err != nil {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Unable to pass request to Authentik outpost", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return errors.New("internal server error")
|
|
} else {
|
|
defer resp.Body.Close()
|
|
for k := range resp.Header {
|
|
w.Header().Set(k, resp.Header.Get(k))
|
|
}
|
|
w.WriteHeader(resp.StatusCode)
|
|
if _, err = io.Copy(w, resp.Body); err != nil {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Unable to pass Authentik outpost response to client", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
return errors.New("internal server error")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//Make a request to Authentik to verify the request
|
|
req, err := http.NewRequest(http.MethodGet, authentikBaseURL+"/"+outpostPrefix+"/auth/nginx", nil)
|
|
if err != nil {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Unable to create request", err)
|
|
w.WriteHeader(401)
|
|
return errors.New("unauthorized")
|
|
}
|
|
|
|
req.Header.Set("X-Original-URL", reqUrl)
|
|
|
|
// Copy cookies from the incoming request
|
|
for _, cookie := range r.Cookies() {
|
|
req.AddCookie(cookie)
|
|
}
|
|
|
|
// Making the verification request
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
ar.options.Logger.PrintAndLog("Authentik", "Unable to verify", err)
|
|
w.WriteHeader(401)
|
|
return errors.New("unauthorized")
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
redirectURL := authentikBaseURL + "/" + outpostPrefix + "/start?rd=" + url.QueryEscape(scheme+"://"+r.Host+r.URL.String())
|
|
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
|
|
return errors.New("unauthorized")
|
|
}
|
|
|
|
return nil
|
|
}
|