fix: IDEAS.md quickfixes — idle-timeout app config, id_token out of Claims, FormEvent import
session.idle_timeout (minutes, default 15) joins the app-config registry and drives the onboarding Garmin session eviction, distinct from session.duration (the login cookie lifetime in hours). The raw Keycloak ID token no longer rides in auth.Claims through every request context: it's minted into the session cookie separately and read back only by the logout handler via IDTokenFromSessionCookie. OnboardingWizard uses the type-imported FormEvent<HTMLFormElement> instead of the React.FormEvent namespace alias. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,10 @@
|
||||
// tokens are never stored or refreshed -- once HandleCallback verifies the
|
||||
// ID token and role, only this package's own cookie matters for subsequent
|
||||
// requests. The one exception is the raw ID token itself, carried opaquely
|
||||
// inside the signed session cookie (Claims.IDToken) solely so a later
|
||||
// logout can pass it back to Keycloak as id_token_hint -- letting Keycloak
|
||||
// inside the signed session cookie (apart from Claims -- see
|
||||
// MintSessionCookie's idToken parameter and IDTokenFromSessionCookie)
|
||||
// solely so a later logout can pass it back to Keycloak as
|
||||
// id_token_hint -- letting Keycloak
|
||||
// skip its own logout-confirmation prompt for a session it can positively
|
||||
// identify, rather than leaving the user a chance to cancel out of it after
|
||||
// their geniusrun account (and profile) is already gone.
|
||||
@@ -32,14 +34,15 @@ const (
|
||||
)
|
||||
|
||||
// Claims identifies the authenticated user, carried in the signed session
|
||||
// cookie. IDToken is the raw Keycloak ID token JWT from the OIDC callback,
|
||||
// carried opaquely so a later logout can pass it back to Keycloak as
|
||||
// id_token_hint (see EndSessionURL) -- geniusrun never inspects it itself.
|
||||
// cookie and stashed in every request's context. Deliberately does NOT
|
||||
// carry the raw Keycloak ID token: that JWT lives in the cookie payload
|
||||
// separately (see MintSessionCookie/IDTokenFromSessionCookie) because it's
|
||||
// only ever needed once more, at logout, and has no business riding
|
||||
// through every handler's context.
|
||||
type Claims struct {
|
||||
Sub string
|
||||
Name string
|
||||
Email string
|
||||
IDToken string
|
||||
Sub string
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
type sessionClaims struct {
|
||||
@@ -65,13 +68,15 @@ type txnClaims struct {
|
||||
|
||||
// MintSessionCookie signs claims into a JWT valid for duration and wraps it
|
||||
// in a cookie. secure should be true whenever the app is served over HTTPS.
|
||||
func MintSessionCookie(claims Claims, secret []byte, duration time.Duration, secure bool) (*http.Cookie, error) {
|
||||
// idToken is the raw Keycloak ID token to carry for the eventual logout's
|
||||
// id_token_hint (empty is fine, e.g. in tests -- the hint is optional).
|
||||
func MintSessionCookie(claims Claims, idToken string, secret []byte, duration time.Duration, secure bool) (*http.Cookie, error) {
|
||||
now := time.Now()
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, sessionClaims{
|
||||
Sub: claims.Sub,
|
||||
Name: claims.Name,
|
||||
Email: claims.Email,
|
||||
IDToken: claims.IDToken,
|
||||
IDToken: idToken,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(duration)),
|
||||
@@ -102,7 +107,21 @@ func ParseSessionCookie(cookie *http.Cookie, secret []byte) (Claims, error) {
|
||||
if _, err := jwt.ParseWithClaims(cookie.Value, &sc, keyfunc(secret), jwt.WithValidMethods([]string{"HS256"})); err != nil {
|
||||
return Claims{}, fmt.Errorf("parse session token: %w", err)
|
||||
}
|
||||
return Claims{Sub: sc.Sub, Name: sc.Name, Email: sc.Email, IDToken: sc.IDToken}, nil
|
||||
return Claims{Sub: sc.Sub, Name: sc.Name, Email: sc.Email}, nil
|
||||
}
|
||||
|
||||
// IDTokenFromSessionCookie verifies the cookie and returns the raw Keycloak
|
||||
// ID token it carries, for logout's id_token_hint. Only the logout handler
|
||||
// needs this -- everything else uses ParseSessionCookie's Claims.
|
||||
func IDTokenFromSessionCookie(cookie *http.Cookie, secret []byte) (string, error) {
|
||||
if cookie == nil {
|
||||
return "", fmt.Errorf("no session cookie")
|
||||
}
|
||||
var sc sessionClaims
|
||||
if _, err := jwt.ParseWithClaims(cookie.Value, &sc, keyfunc(secret), jwt.WithValidMethods([]string{"HS256"})); err != nil {
|
||||
return "", fmt.Errorf("parse session token: %w", err)
|
||||
}
|
||||
return sc.IDToken, nil
|
||||
}
|
||||
|
||||
// MintTxnCookie signs an OIDC login transaction into a short-lived cookie.
|
||||
|
||||
Reference in New Issue
Block a user