Skip to content
Snippets Groups Projects
Commit 416e584b authored by Christian Pointner's avatar Christian Pointner
Browse files

make repsonses from auth subsystem more consistent

parent b973f62f
No related branches found
No related tags found
No related merge requests found
...@@ -110,6 +110,6 @@ func authorizeRequest(w http.ResponseWriter, r *http.Request, showID string) (bo ...@@ -110,6 +110,6 @@ func authorizeRequest(w http.ResponseWriter, r *http.Request, showID string) (bo
return true, s return true, s
} }
} }
http.Error(w, "you are not allowed to access show: "+showID, http.StatusForbidden) sendWebResponse(w, http.StatusForbidden, ErrorResponse{Error: "you are not allowed to access show: " + showID})
return false, s return false, s
} }
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package auth package auth
import ( import (
"encoding/json"
"net/http" "net/http"
"strings" "strings"
...@@ -45,6 +46,7 @@ func Init(c *Config) (err error) { ...@@ -45,6 +46,7 @@ func Init(c *Config) (err error) {
return return
} }
auth.config = c auth.config = c
if auth.sessions, err = NewSessionManager(); err != nil { if auth.sessions, err = NewSessionManager(); err != nil {
return return
} }
...@@ -54,7 +56,6 @@ func Init(c *Config) (err error) { ...@@ -54,7 +56,6 @@ func Init(c *Config) (err error) {
return return
} }
} }
return return
} }
...@@ -63,14 +64,14 @@ func login(w http.ResponseWriter, r *http.Request) { ...@@ -63,14 +64,14 @@ func login(w http.ResponseWriter, r *http.Request) {
switch strings.ToLower(method) { switch strings.ToLower(method) {
case "oidc": case "oidc":
if auth.oidc == nil { if auth.oidc == nil {
http.Error(w, "OIDC authentication is not configured", http.StatusBadRequest) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OIDC authentication is not configured"})
return return
} }
auth.oidc.HandleLogin(w, r) auth.oidc.HandleLogin(w, r)
case "": case "":
http.Error(w, "default/fallback authentication method has not been implemented yet!", http.StatusNotImplemented) sendHTTPResponse(w, http.StatusNotImplemented, HTTPResponse{Error: "default/fallback authentication method has not been implemented yet"})
default: default:
http.Error(w, "invalid authentication method: "+method, http.StatusBadRequest) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "invalid authentication method: " + method})
} }
} }
...@@ -78,15 +79,27 @@ func logout(w http.ResponseWriter, r *http.Request) { ...@@ -78,15 +79,27 @@ func logout(w http.ResponseWriter, r *http.Request) {
sid, s := getSessionFromCookie(r) sid, s := getSessionFromCookie(r)
invalidateSessionCookie(w) invalidateSessionCookie(w)
if s == nil { if s == nil {
http.Error(w, "Request does not contain a valid session cookie or session is already expired.", http.StatusUnauthorized) sendHTTPResponseInvalidSession(w)
return return
} }
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "You are now logged out.", http.StatusOK) sendHTTPResponse(w, http.StatusOK, HTTPResponse{Message: "you are now logged out"})
}
func whoami(w http.ResponseWriter, r *http.Request) {
_, s := getSessionFromCookie(r)
if s == nil {
sendHTTPResponseInvalidSession(w)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(s)
} }
func disabled(w http.ResponseWriter, r *http.Request) { func disabled(w http.ResponseWriter, r *http.Request) {
http.Error(w, "authentication is disabled", http.StatusNotAcceptable) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "authentication is disabled"})
} }
func InstallHTTPHandler(r *mux.Router) { func InstallHTTPHandler(r *mux.Router) {
...@@ -97,6 +110,7 @@ func InstallHTTPHandler(r *mux.Router) { ...@@ -97,6 +110,7 @@ func InstallHTTPHandler(r *mux.Router) {
r.HandleFunc("/login", login) r.HandleFunc("/login", login)
r.HandleFunc("/logout", logout) r.HandleFunc("/logout", logout)
r.HandleFunc("/whoami", whoami)
if auth.oidc != nil { if auth.oidc != nil {
r.Handle("/oidc/callback", auth.oidc.CallbackHandler()) r.Handle("/oidc/callback", auth.oidc.CallbackHandler())
} }
...@@ -112,7 +126,7 @@ func Middleware(next http.Handler) http.Handler { ...@@ -112,7 +126,7 @@ func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, s := getSessionFromCookie(r) _, s := getSessionFromCookie(r)
if s == nil { if s == nil {
http.Error(w, "Request does not contain a valid session cookie or session is already expired.", http.StatusUnauthorized) sendHTTPResponseInvalidSession(w)
return return
} }
next.ServeHTTP(w, attachSessionToRequest(r, s)) next.ServeHTTP(w, attachSessionToRequest(r, s))
......
...@@ -26,7 +26,6 @@ package auth ...@@ -26,7 +26,6 @@ package auth
import ( import (
"context" "context"
"encoding/json"
"net/http" "net/http"
"time" "time"
...@@ -95,24 +94,24 @@ func (b *OIDCBackend) HandleLogin(w http.ResponseWriter, r *http.Request) { ...@@ -95,24 +94,24 @@ func (b *OIDCBackend) HandleLogin(w http.ResponseWriter, r *http.Request) {
if s != nil { if s != nil {
msg := "" msg := ""
if s.Username != "" { if s.Username != "" {
msg = "You are still logged in, please logout first." msg = "You are still logged in, please logout first"
} else if s.oidc == nil { } else if s.oidc == nil {
msg = "This is not an OIDC session." msg = "This is not an OIDC session."
} else { } else {
msg = "OIDC login already in progress, retry later." msg = "OIDC login already in progress, retry later."
} }
http.Error(w, msg, http.StatusConflict) sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: msg})
return return
} }
os, err := NewOIDCSession() os, err := NewOIDCSession()
if err != nil { if err != nil {
http.Error(w, "Failed to generate new OIDC session: "+err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Failed to generate new OIDC session: " + err.Error()})
return return
} }
s = &Session{oidc: os, Expires: time.Now().Add(time.Minute)} s = &Session{oidc: os, Expires: time.Now().Add(time.Minute)}
if sid, err = auth.sessions.insert(s); err != nil { if sid, err = auth.sessions.insert(s); err != nil {
http.Error(w, "Failed to generate new session: "+err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Failed to generate new session: " + err.Error()})
return return
} }
setSessionCookie(w, sid) setSessionCookie(w, sid)
...@@ -127,20 +126,20 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) ...@@ -127,20 +126,20 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
sid, s := getSessionFromCookie(r) sid, s := getSessionFromCookie(r)
if s == nil { if s == nil {
invalidateSessionCookie(w) invalidateSessionCookie(w)
http.Error(w, "Request does not contain a valid session cookie or session is already expired.", http.StatusUnauthorized) sendHTTPResponseInvalidSession(w)
return return
} }
if s.oidc == nil { if s.oidc == nil {
http.Error(w, "This is not an OIDC session.", http.StatusConflict) sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: "This is not an OIDC session."})
return return
} }
if s.Username != "" { if s.Username != "" {
http.Error(w, "This session is already logged in.", http.StatusConflict) sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: "This session is already logged in."})
return return
} }
if r.URL.Query().Get("state") != s.oidc.State { if r.URL.Query().Get("state") != s.oidc.State {
http.Error(w, "OIDC verification failed: state did not match", http.StatusBadRequest) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OIDC verification failed: state did not match"})
return return
} }
...@@ -148,14 +147,14 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) ...@@ -148,14 +147,14 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
if err != nil { if err != nil {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "OAuth2 token exchange failed: "+err.Error(), http.StatusBadRequest) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OAuth2 token exchange failed: " + err.Error()})
return return
} }
rawIDToken, ok := oauth2Token.Extra("id_token").(string) rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok { if !ok {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "OIDC verification failed: no id_token field in oauth2 token.", http.StatusInternalServerError) sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OIDC verification failed: no id_token field in oauth2 token."})
return return
} }
...@@ -164,13 +163,13 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) ...@@ -164,13 +163,13 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
if err != nil { if err != nil {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "OAuth2 ID token verification failed: "+err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "OAuth2 ID token verification failed: " + err.Error()})
return return
} }
if idToken.Nonce != s.oidc.Nonce { if idToken.Nonce != s.oidc.Nonce {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "OAuth2 ID token verification failed: invalid nonce", http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "OAuth2 ID token verification failed: invalid nonce"})
return return
} }
...@@ -178,30 +177,29 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) ...@@ -178,30 +177,29 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
if err != nil { if err != nil {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "Fetching OIDC UserInfo failed: "+err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Fetching OIDC UserInfo failed: " + err.Error()})
return return
} }
newS := &Session{} newS := &Session{}
if err := userInfo.Claims(newS); err != nil { if err := userInfo.Claims(newS); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Parsing OIDC UserInfo failed: " + err.Error()})
return return
} }
maxAge := defaultAge maxAge := defaultAge
if auth.config.Sessions.MaxAge > 0 { if auth.config.Sessions.MaxAge > 0 {
maxAge = auth.config.Sessions.MaxAge maxAge = auth.config.Sessions.MaxAge
} }
newS.Expires = time.Now().Add(maxAge * time.Second) newS.Expires = time.Now().Add(maxAge)
newS.oidc = &OIDCSession{State: s.oidc.State, Nonce: s.oidc.Nonce} newS.oidc = &OIDCSession{State: s.oidc.State, Nonce: s.oidc.Nonce}
newS.oidc.token = oauth2Token newS.oidc.token = oauth2Token
if err = auth.sessions.update(sid, newS); err != nil { if err = auth.sessions.update(sid, newS); err != nil {
invalidateSessionCookie(w) invalidateSessionCookie(w)
auth.sessions.remove(sid) auth.sessions.remove(sid)
http.Error(w, "Updating session failed: "+err.Error(), http.StatusInternalServerError) sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Updating session failed: " + err.Error()})
return return
} }
setSessionCookie(w, sid) // reset the cookie to update max-age setSessionCookie(w, sid) // re-set the cookie to update max-age
data, _ := json.MarshalIndent(newS, "", " ") sendHTTPResponse(w, http.StatusOK, HTTPResponse{Message: "You are now logged in as: " + newS.Username})
w.Write(data)
} }
...@@ -27,6 +27,8 @@ package auth ...@@ -27,6 +27,8 @@ package auth
import ( import (
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"encoding/json"
"net/http"
) )
type contextKey int type contextKey int
...@@ -42,3 +44,19 @@ func generateRandomString(len int) (string, error) { ...@@ -42,3 +44,19 @@ func generateRandomString(len int) (string, error) {
} }
return base64.RawURLEncoding.EncodeToString(b[:]), nil return base64.RawURLEncoding.EncodeToString(b[:]), nil
} }
type HTTPResponse struct {
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
}
func sendHTTPResponse(w http.ResponseWriter, status int, resp HTTPResponse) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(resp) // TODO: Error Handling?
}
func sendHTTPResponseInvalidSession(w http.ResponseWriter) {
resp := HTTPResponse{Error: "Request does not contain a valid session cookie or session is already expired."}
sendHTTPResponse(w, http.StatusUnauthorized, resp)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment