From 709353b46efa2bc5813d129414bbbb66f9fa0021 Mon Sep 17 00:00:00 2001 From: Gabriel Simmer Date: Wed, 6 Jul 2022 00:17:41 +0100 Subject: [PATCH] Store oauth session for redirect, invite un-authed Also added Dockerfile :) --- Dockerfile | 23 ++++++++++++++++++++++ README.md | 0 auth/auth.go | 4 ++-- main.go | 3 ++- store/database.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++ store/database.sql | 9 ++++++++- transport/http.go | 27 ++++++++++++++++++++++++-- 7 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 Dockerfile create mode 100644 README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f1c2cac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM golang:alpine as builder + +WORKDIR /build/wlm +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +RUN apk add --update gcc musl-dev +WORKDIR /build/wlm +RUN go build -o wlm -ldflags "-s -w" + + +FROM alpine + +WORKDIR /app +EXPOSE 8080 + +RUN apk add --no-cache ca-certificates sqlite + +COPY --from=builder /build/wlm/wlm wlm + +ENTRYPOINT ["/app/wlm"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/auth/auth.go b/auth/auth.go index 84c01b9..8dda493 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -49,14 +49,14 @@ type MinecraftAuth struct { IdentityToken string `json:"identityToken"` } -func LoginUrl() string { +func LoginUrl(stateId string) string { oaConfig := &oauth2.Config{ ClientID: os.Getenv("AZURE_OAUTH_CLIENT_ID"), ClientSecret: os.Getenv("AZURE_OAUTH_CLIENT_SECRET"), Endpoint: endpoints.Microsoft, Scopes: []string{"Xboxlive.signin", "Xboxlive.offline_access"}, } - return oaConfig.AuthCodeURL("foo") + return oaConfig.AuthCodeURL(stateId) } func Authenticate(code string) (store.User, error) { diff --git a/main.go b/main.go index 9d52ec1..82cfca6 100644 --- a/main.go +++ b/main.go @@ -24,11 +24,12 @@ func main() { // API endpoints mux.Group(func(mux *flow.Mux) { mux.Use(handlers.Cors) + mux.HandleFunc("/api/v1/invite/:id", handlers.GetInvite, "GET", "DELETE") + mux.Use(handlers.SessionAuth) mux.HandleFunc("/api/v1/me", handlers.CurrentUser, "GET") mux.HandleFunc("/api/v1/invites", handlers.CreateInvite, "POST") - mux.HandleFunc("/api/v1/invite/:id", handlers.GetInvite, "GET", "DELETE") mux.HandleFunc("/api/v1/invite/:id/accept", handlers.AcceptInvite, "POST") mux.HandleFunc("/api/v1/servers", handlers.Server, "GET", "POST") diff --git a/store/database.go b/store/database.go index 509b03f..2ed2176 100644 --- a/store/database.go +++ b/store/database.go @@ -32,6 +32,14 @@ type Storer interface { SaveUser(user User) error SaveSession(token string, user User) error SessionUser(token string) (User, error) + SaveOauthState(state OauthState) error + OauthState(id string) (OauthState, error) +} + +type OauthState struct { + Id string + Origin string + State string } type Invite struct { @@ -301,3 +309,43 @@ func (s *Store) SessionUser(token string) (User, error) { return s.GetUser(sess.UID) } + +func (s *Store) SaveOauthState(state OauthState) error { + _, err := s.OauthState(state.Id) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + q, err := s.database.Prepare("INSERT INTO oauth_states (state_id, origin, state) VALUES ($1, $2, $3)") + if err != nil { + return err + } + _, err = q.Exec(state.Id, state.Origin, state.State) + + if err != nil { + return err + } + return nil + } else { + return err + } + } + q, err := s.database.Prepare("UPDATE oauth_states SET state=$2 WHERE state_id=$1") + if err != nil { + return err + } + _, err = q.Exec(state.Id, state.State) + if err != nil { + return err + } + + return err +} + +func (s *Store) OauthState(id string) (OauthState, error) { + q := s.database.QueryRow("SELECT * FROM oauth_states WHERE state_id=$1", id) + var state OauthState + err := q.Scan(&state.Id, &state.Origin, &state.State) + if err != nil { + return OauthState{}, err + } + return state, nil +} diff --git a/store/database.sql b/store/database.sql index 8d0d77d..d5039a6 100644 --- a/store/database.sql +++ b/store/database.sql @@ -44,4 +44,11 @@ CREATE TABLE `invite_log` ( `user` TEXT NOT NULL, PRIMARY KEY (`entry_id`), FOREIGN KEY (`invite`) REFERENCES invites(`token`) -) \ No newline at end of file +) STRICT; + +CREATE TABLE `oauth_states` ( + `state_id` TEXT NOT NULL, + `origin` TEXT NOT NULL, + `state` TEXT NOT NULL, + PRIMARY KEY (`state_id`) +) STRICT; \ No newline at end of file diff --git a/transport/http.go b/transport/http.go index f91ff42..fcc297b 100644 --- a/transport/http.go +++ b/transport/http.go @@ -170,11 +170,28 @@ func (h *Handler) AcceptInvite(w http.ResponseWriter, r *http.Request) { } func (h *Handler) AuthRedirect(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, auth.LoginUrl(), http.StatusTemporaryRedirect) + state := store.OauthState{ + Id: uuid.New().String(), + Origin: r.Header.Get("Referer"), + State: "started", + } + err := h.store.SaveOauthState(state) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + http.Redirect(w, r, auth.LoginUrl(state.Id), http.StatusTemporaryRedirect) } func (h *Handler) AuthCallback(w http.ResponseWriter, r *http.Request) { code := r.URL.Query()["code"][0] + stateId := r.URL.Query()["state"][0] + state, err := h.store.OauthState(stateId) + if err != nil || state.State == "completed" { + http.Error(w, err.Error(), http.StatusForbidden) + return + } + user, err := auth.Authenticate(code) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -202,8 +219,14 @@ func (h *Handler) AuthCallback(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + state.State = "completed" + err = h.store.SaveOauthState(state) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - http.Redirect(w, r, "/", http.StatusTemporaryRedirect) + http.Redirect(w, r, state.Origin, http.StatusTemporaryRedirect) } func generateSessionToken() (string, error) {