refactor: ensure session lifetime does not exceed access token lifetime

Co-Authored-By: Sindre Rødseth Hansen <sindre.rodseth.hansen@nav.no>
This commit is contained in:
Trong Huu Nguyen
2021-09-30 12:08:23 +02:00
co-authored by Sindre Rødseth Hansen
parent b2e89f32fa
commit dbc0a47a46
2 changed files with 58 additions and 24 deletions
+3 -24
View File
@@ -80,15 +80,6 @@ func (h *Handler) WithSecureCookie(enabled bool) *Handler {
return h
}
// localSessionID prefixes the given `sid` with the given client ID to prevent key collisions.
// `sid` is a key that refers to the user's unique SSO session at the Identity Provider, and the same key is present
// in all tokens acquired by any Relying Party (such as Wonderwall) during that session.
// Thus, we cannot assume that the value of `sid` to uniquely identify the pair of (user, application session)
// if using a shared session store.
func (h *Handler) localSessionID(sid string) string {
return fmt.Sprintf("%s-%s", h.Config.ClientID, sid)
}
type loginParams struct {
state string
codeVerifier string
@@ -300,22 +291,10 @@ func (h *Handler) Callback(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
return
}
sessionID := h.localSessionID(externalSessionID)
err = h.setEncryptedCookie(w, h.GetSessionCookieName(), sessionID, h.Config.SessionMaxLifetime)
err = h.createSession(w, r, externalSessionID, tokens, idToken)
if err != nil {
log.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
}
err = h.Sessions.Write(r.Context(), sessionID, &session.Data{
ExternalSessionID: externalSessionID,
OAuth2Token: tokens,
IDTokenSerialized: idToken.Raw,
}, h.Config.SessionMaxLifetime)
if err != nil {
log.Error(err)
log.Errorf("creating session: %+v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
@@ -323,7 +302,7 @@ func (h *Handler) Callback(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, loginCookie.Referer, http.StatusTemporaryRedirect)
}
// Proxy all requests upstream
// Default proxies all requests upstream
func (h *Handler) Default(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithCancel(r.Context())
defer cancel()
+55
View File
@@ -2,10 +2,23 @@ package router
import (
"fmt"
"github.com/lestrrat-go/jwx/jwt"
"github.com/nais/wonderwall/pkg/session"
"github.com/nais/wonderwall/pkg/token"
"golang.org/x/oauth2"
"net/http"
"time"
)
// localSessionID prefixes the given `sid` with the given client ID to prevent key collisions.
// `sid` is a key that refers to the user's unique SSO session at the Identity Provider, and the same key is present
// in all tokens acquired by any Relying Party (such as Wonderwall) during that session.
// Thus, we cannot assume that the value of `sid` to uniquely identify the pair of (user, application session)
// if using a shared session store.
func (h *Handler) localSessionID(sid string) string {
return fmt.Sprintf("%s-%s", h.Config.ClientID, sid)
}
func (h *Handler) getSessionFromCookie(r *http.Request) (*session.Data, error) {
sessionID, err := h.getEncryptedCookie(r, h.GetSessionCookieName())
if err != nil {
@@ -14,3 +27,45 @@ func (h *Handler) getSessionFromCookie(r *http.Request) (*session.Data, error) {
return h.Sessions.Read(r.Context(), sessionID)
}
func (h *Handler) getSessionLifetime(accessToken string) (time.Duration, error) {
defaultSessionLifetime := h.Config.SessionMaxLifetime
token, err := jwt.Parse([]byte(accessToken))
if err != nil {
return 0, err
}
tokenDuration := token.Expiration().Sub(time.Now())
if tokenDuration <= defaultSessionLifetime {
return tokenDuration, nil
}
return defaultSessionLifetime, nil
}
func (h *Handler) createSession(w http.ResponseWriter, r *http.Request, externalSessionID string, tokens *oauth2.Token, idToken *token.IDToken) error {
sessionID := h.localSessionID(externalSessionID)
sessionLifetime, err := h.getSessionLifetime(tokens.AccessToken)
if err != nil {
return fmt.Errorf("getting access token lifetime: %w", err)
}
err = h.setEncryptedCookie(w, h.GetSessionCookieName(), sessionID, sessionLifetime)
if err != nil {
return fmt.Errorf("setting session cookie: %w", err)
}
err = h.Sessions.Write(r.Context(), sessionID, &session.Data{
ExternalSessionID: externalSessionID,
OAuth2Token: tokens,
IDTokenSerialized: idToken.Raw,
}, sessionLifetime)
if err != nil {
return fmt.Errorf("writing session to store: %w", err)
}
return nil
}