diff --git a/auth/auth.go b/auth/auth.go
index e9f09151b3d1da22557aedfa822e7a00bdbea564..c973b2fc34d0338b08d618eda620b6c0b6e2699c 100644
--- a/auth/auth.go
+++ b/auth/auth.go
@@ -27,6 +27,7 @@ package auth
 import (
 	"encoding/json"
 	"net/http"
+	"strings"
 
 	"github.com/gorilla/handlers"
 	"github.com/gorilla/mux"
@@ -61,32 +62,55 @@ func Init(c *Config) (err error) {
 
 func newSession() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		request := &NewSessionRequest{}
+		err := json.NewDecoder(r.Body).Decode(request)
+		if err != nil {
+			sendHTTPErrorResponse(w, http.StatusBadRequest, "Error decoding request: "+err.Error())
+			return
+		}
+
+		response := &NewSessionResponse{}
+		switch strings.ToLower(request.Backend) {
+		case "oidc":
+			if auth.oidc == nil {
+				sendHTTPErrorResponse(w, http.StatusBadRequest, "OIDC authentication is not configured")
+				return
+			}
+			if response.SessionID, err = NewOIDCSession(); err != nil {
+				sendHTTPErrorResponse(w, http.StatusBadRequest, "Error creating session: "+err.Error())
+				return
+			}
+		default:
+			sendHTTPErrorResponse(w, http.StatusBadRequest, "invalid authentication backend: "+request.Backend)
+			return
+		}
+		sendHTTPResponse(w, http.StatusOK, response)
 	})
 }
 
 func getSession() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		_, s := getSessionFromBearerToken(r)
-		if s == nil || s.Username == "" {
-			sendHTTPResponseInvalidSession(w)
+		sID := mux.Vars(r)["session-id"]
+		s := auth.sessions.get(sID)
+		if s == nil {
+			sendHTTPErrorResponse(w, http.StatusNotFound, "this session does not exist")
 			return
 		}
 
-		w.Header().Set("Content-Type", "application/json")
-		w.WriteHeader(http.StatusOK)
-		json.NewEncoder(w).Encode(s)
+		sendHTTPResponse(w, http.StatusOK, s)
 	})
 }
 
 func deleteSession() http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		sid, s := getSessionFromBearerToken(r)
+		sID := mux.Vars(r)["session-id"]
+		s := auth.sessions.get(sID)
 		if s == nil {
-			sendHTTPResponseInvalidSession(w)
+			sendHTTPErrorResponse(w, http.StatusNotFound, "this session does not exist")
 			return
 		}
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusOK, HTTPResponse{Message: "you are now logged out"})
+		auth.sessions.remove(sID)
+		sendHTTPResponse(w, http.StatusOK, "you are now logged out")
 	})
 }
 
@@ -110,7 +134,7 @@ func listBackends(w http.ResponseWriter, r *http.Request) {
 }
 
 func disabled(w http.ResponseWriter, r *http.Request) {
-	sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "authentication is disabled"})
+	sendHTTPErrorResponse(w, http.StatusBadRequest, "authentication is disabled")
 }
 
 func InstallHTTPHandler(r *mux.Router) {
@@ -119,11 +143,14 @@ func InstallHTTPHandler(r *mux.Router) {
 		return
 	}
 
+	sessionsHandler := make(handlers.MethodHandler)
+	sessionsHandler["POST"] = newSession()
+	r.Handle("/sessions", sessionsHandler)
+
 	sessionHandler := make(handlers.MethodHandler)
-	sessionHandler["POST"] = newSession()
 	sessionHandler["GET"] = getSession()
 	sessionHandler["DELETE"] = deleteSession()
-	r.Handle("/session", sessionHandler)
+	r.Handle("/sessions/{session-id}", sessionHandler)
 
 	r.HandleFunc("/backends", listBackends)
 	if auth.oidc != nil {
@@ -142,7 +169,7 @@ func Middleware(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		_, s := getSessionFromBearerToken(r)
 		if s == nil || s.Username == "" {
-			sendHTTPResponseInvalidSession(w)
+			sendHTTPInvalidSessionResponse(w)
 			return
 		}
 		next.ServeHTTP(w, attachSessionToRequest(r, s))
diff --git a/auth/oidc.go b/auth/oidc.go
index 2704037688ce9624a8c97a43339900a1cefffb5a..fab61af719227e4aed836ff2df55f4e062ca7435 100644
--- a/auth/oidc.go
+++ b/auth/oidc.go
@@ -39,12 +39,17 @@ type OIDCSession struct {
 	token *oauth2.Token
 }
 
-func NewOIDCSession() (s *OIDCSession, err error) {
-	s = &OIDCSession{}
-	if s.State, err = generateRandomString(32); err != nil {
+func NewOIDCSession() (sID string, err error) {
+	os := &OIDCSession{}
+	if os.State, err = generateRandomString(16); err != nil {
 		return
 	}
-	if s.Nonce, err = generateRandomString(32); err != nil {
+	if os.Nonce, err = generateRandomString(16); err != nil {
+		return
+	}
+
+	s := &Session{oidc: os}
+	if sID, err = auth.sessions.insert(s); err != nil {
 		return
 	}
 	return
@@ -100,24 +105,13 @@ func (h *oidcLoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	if s != nil {
 		msg := ""
 		if s.Username != "" {
-			msg = "You are still logged in, please logout first"
+			msg = "This session is already logged in."
 		} else if s.oidc == nil {
 			msg = "This is not an OIDC session."
 		} else {
-			msg = "OIDC login already in progress, retry later."
+			msg = "OIDC login already in progress."
 		}
-		sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: msg})
-		return
-	}
-
-	os, err := NewOIDCSession()
-	if err != nil {
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Failed to generate new OIDC session: " + err.Error()})
-		return
-	}
-	s = &Session{oidc: os, Expires: time.Now().Add(time.Minute)}
-	if _, err = auth.sessions.insert(s); err != nil {
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Failed to generate new session: " + err.Error()})
+		sendHTTPErrorResponse(w, http.StatusConflict, msg)
 		return
 	}
 	http.Redirect(w, r, h.backend.oauth2Config.AuthCodeURL(s.oidc.State, oidc.Nonce(s.oidc.Nonce)), http.StatusFound)
@@ -128,61 +122,62 @@ type oidcCallbackHandler struct {
 }
 
 func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	sid, s := getSessionFromBearerToken(r)
+	// TODO: this won't work, we need to use `state` to find the right session
+	sID, s := getSessionFromBearerToken(r)
 	if s == nil {
-		sendHTTPResponseInvalidSession(w)
+		sendHTTPInvalidSessionResponse(w)
 		return
 	}
 
 	if s.oidc == nil {
-		sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: "This is not an OIDC session."})
+		sendHTTPErrorResponse(w, http.StatusConflict, "This is not an OIDC session.")
 		return
 	}
 	if s.Username != "" {
-		sendHTTPResponse(w, http.StatusConflict, HTTPResponse{Error: "This session is already logged in."})
+		sendHTTPErrorResponse(w, http.StatusConflict, "This session is already logged in.")
 		return
 	}
 	if r.URL.Query().Get("state") != s.oidc.State {
-		sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OIDC verification failed: state did not match"})
+		sendHTTPErrorResponse(w, http.StatusBadRequest, "OIDC verification failed: state did not match")
 		return
 	}
 
 	oauth2Token, err := h.backend.oauth2Config.Exchange(r.Context(), r.URL.Query().Get("code"))
 	if err != nil {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OAuth2 token exchange failed: " + err.Error()})
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusBadRequest, "OAuth2 token exchange failed: "+err.Error())
 		return
 	}
 	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
 	if !ok {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusBadRequest, HTTPResponse{Error: "OIDC verification failed: no id_token field in oauth2 token."})
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusBadRequest, "OIDC verification failed: no id_token field in oauth2 token.")
 		return
 	}
 
 	// Verify the ID Token signature and nonce.
 	idToken, err := h.backend.verifier.Verify(r.Context(), rawIDToken)
 	if err != nil {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "OAuth2 ID token verification failed: " + err.Error()})
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusInternalServerError, "OAuth2 ID token verification failed: "+err.Error())
 		return
 	}
 	if idToken.Nonce != s.oidc.Nonce {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "OAuth2 ID token verification failed: invalid nonce"})
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusInternalServerError, "OAuth2 ID token verification failed: invalid nonce")
 		return
 	}
 
 	userInfo, err := h.backend.provider.UserInfo(r.Context(), oauth2.StaticTokenSource(oauth2Token))
 	if err != nil {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Fetching OIDC UserInfo failed: " + err.Error()})
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusInternalServerError, "Fetching OIDC UserInfo failed: "+err.Error())
 		return
 	}
 
 	newS := &Session{}
 	if err := userInfo.Claims(newS); err != nil {
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Parsing OIDC UserInfo failed: " + err.Error()})
+		sendHTTPErrorResponse(w, http.StatusInternalServerError, "Parsing OIDC UserInfo failed: "+err.Error())
 		return
 	}
 	maxAge := defaultAge
@@ -192,10 +187,10 @@ func (h *oidcCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	newS.Expires = time.Now().Add(maxAge)
 	newS.oidc = &OIDCSession{State: s.oidc.State, Nonce: s.oidc.Nonce}
 	newS.oidc.token = oauth2Token
-	if err = auth.sessions.update(sid, newS); err != nil {
-		auth.sessions.remove(sid)
-		sendHTTPResponse(w, http.StatusInternalServerError, HTTPResponse{Error: "Updating session failed: " + err.Error()})
+	if err = auth.sessions.update(sID, newS); err != nil {
+		auth.sessions.remove(sID)
+		sendHTTPErrorResponse(w, http.StatusInternalServerError, "Updating session failed: "+err.Error())
 		return
 	}
-	sendHTTPResponse(w, http.StatusOK, HTTPResponse{Message: "You are now logged in as: " + newS.Username})
+	sendHTTPResponse(w, http.StatusOK, "You are now logged in as: "+newS.Username)
 }
diff --git a/auth/sessions.go b/auth/sessions.go
index 92f345769d7ca3bb8738aa955a996b8d6bec0c92..7d75333c9332147ca4f8a8a47b62280a1bd31251 100644
--- a/auth/sessions.go
+++ b/auth/sessions.go
@@ -73,21 +73,21 @@ func getSessionFromBearerToken(r *http.Request) (string, *Session) {
 		return "", nil
 	}
 
-	sid, ok := parseBearerAuthHeader(authHeader)
+	sID, ok := parseBearerAuthHeader(authHeader)
 	if !ok {
 		return "", nil
 	}
 
-	s := auth.sessions.get(sid)
+	s := auth.sessions.get(sID)
 	if s == nil {
 		return "", nil
 	}
 	if s.Expired() {
-		auth.sessions.remove(sid)
+		auth.sessions.remove(sID)
 		return "", nil
 	}
 
-	return sid, s
+	return sID, s
 }
 
 func attachSessionToRequest(r *http.Request, s *Session) *http.Request {
@@ -125,6 +125,8 @@ func (sm *SessionManager) insert(s *Session) (id string, err error) {
 		return
 	}
 
+	// TODO: set session expiry
+
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
 	sm.sessions[id] = s
@@ -146,6 +148,8 @@ func (sm *SessionManager) update(id string, s *Session) error {
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
 
+	// TODO: set session expiry
+
 	if _, ok := sm.sessions[id]; !ok {
 		return errors.New("session not found.")
 	}
diff --git a/auth/utils.go b/auth/utils.go
index 35b77d1b8f2c2351c9452417134fe0a4b09f98f6..a639f032abb5f3521e86ccc60d8569dbac0bc1a4 100644
--- a/auth/utils.go
+++ b/auth/utils.go
@@ -45,18 +45,28 @@ func generateRandomString(len int) (string, error) {
 	return base64.RawURLEncoding.EncodeToString(b[:]), nil
 }
 
-type HTTPResponse struct {
-	Message string `json:"message,omitempty"`
-	Error   string `json:"error,omitempty"`
+type NewSessionRequest struct {
+	Backend string `json:"backend"`
 }
 
-func sendHTTPResponse(w http.ResponseWriter, status int, resp HTTPResponse) {
+type NewSessionResponse struct {
+	SessionID string `json:"session-id"`
+}
+
+func sendHTTPResponse(w http.ResponseWriter, status int, resp interface{}) {
 	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 token or session is already expired."}
-	sendHTTPResponse(w, http.StatusUnauthorized, resp)
+type HTTPErrorResponse struct {
+	Error string `json:"error,omitempty"`
+}
+
+func sendHTTPErrorResponse(w http.ResponseWriter, status int, error string) {
+	sendHTTPResponse(w, status, HTTPErrorResponse{Error: error})
+}
+
+func sendHTTPInvalidSessionResponse(w http.ResponseWriter) {
+	sendHTTPErrorResponse(w, http.StatusUnauthorized, "Request does not contain a valid token or session is already expired.")
 }