Overview
You have the option of customizing session handling using two functions: evalMaxLifetimeSE
and evalIdleTimeoutSE
.
Evaluate Max Session Lifetime (
evalMaxLifetimeSE
) determines how sessions reaching their max lifetime are handled. WhilemaxLifetimeSeconds
is still used for individually expiring attributes,evalMaxLifetimeSE
checks if the user has been logged in for too long (beyondMaxTimeout
), and logs them out if so.Evaluate Idle Session Timeout (
evalIdleTimeoutSE
) checks if the user has been inactive for too long and logs them out if so. If this Service Extension is defined, theidleTimeout
value is ignored.
Implementation
Service extensions allow complete flexibility over the process of controlling a user's session lifespan.
To create these service extensions, go to Service Extensions.
From the right sidebar, click the + icon next to Evaluate Max Session Lifetime or Evaluate Idle Session Timeout.
Enter a name and description for the service extension, and click Create.
The service extension code appears on the next page. From here, you can add assets, providers, and/or metadata. When you've finished adding this information, click Update.
You will receive a confirmation message in the lower right corner that the service extension file has been updated.
You can now use this service extension in an existing or new deployment.
Go to the Deployments screen and select an existing deployment from the list or click Create.
Under Orchestrator Settings, next to Session and Cookie, click Edit.
The service extensions you created can be selected from the dropdown menus under Evaluate Session Maximum Lifetime Service Extension and/or Evaluate Session Idle Timeout Service Extension.
Code example
The example below triggers single logout on a session expiry event. The max session timeout and idle session timeout are determined dynamically depending on the user's employee type (full-time employee vs contractor).
/etc/maverics/extensions/session.go
package main
import (
"net/http"
"time"
"github.com/strata-io/service-extension/orchestrator"
)
// These constants define the Single Logout (SLO) endpoint and timeout values
// based on the type of user (contractor or full-time employee).
const (
sloEndpoint = "https://example.com/single-logout"
// Contractor session timeout settings
contractorIdleTimeoutSeconds = 300 // 5 minutes of inactivity
contractorMaxTimeoutSeconds = 3600 // 1 hour total session duration
// Full-time employee session timeout settings
fteIdleTimeoutSeconds = 3600 // 1 hour of inactivity
fteMaxTimeoutSeconds = 43200 // 12 hours total session duration
)
// EvalMaxLifetime checks if a user's session has exceeded the maximum allowed duration,
// depending on whether the user is a full-time employee or a contractor.
func EvalMaxLifetime(
api orchestrator.Orchestrator,
rw http.ResponseWriter,
req *http.Request,
createdAt time.Time, // The time the session was created
) bool {
logger := api.Logger()
// Get the current session from the orchestrator
sess, err := api.Session()
if err != nil {
// Log an error and return false if we can't get the session
logger.Error("se", "failed to retrieve session", "err", err)
return false
}
// Retrieve the user's employee type from the session (e.g., "full_time" or "contractor")
employeeType, err := sess.GetString("okta.employeeType")
if err != nil {
// Log an error if we fail to retrieve the employee type
logger.Error("se", "failed to retrieve 'employeeType'", "err", err)
return false
}
// Set the default timeout to the contractor's max timeout
maxTimeout := contractorMaxTimeoutSeconds
// If the user is a full-time employee, override the timeout with the FTE value
if employeeType == "full_time" {
maxTimeout = fteMaxTimeoutSeconds
}
// Check if the session has passed the maximum lifetime
if sessionExpired(createdAt, maxTimeout) {
// Log the event and redirect the user to the logout (SLO) endpoint
logger.Info("se", "user has reached max session lifetime: redirecting to SLO endpoint")
http.Redirect(rw, req, sloEndpoint, http.StatusFound)
// Return false to allow SLO endpoint to perform proper logout steps.
// If we returned true, it might clear session data prematurely.
return false
}
// Session is still valid
return false
}
// EvalIdleTimeout checks if a user's session has been idle (inactive) for too long,
// using different timeouts for full-time employees and contractors.
func EvalIdleTimeout(
api orchestrator.Orchestrator,
rw http.ResponseWriter,
req *http.Request,
lastAccess time.Time, // The last time the user was active
) bool {
logger := api.Logger()
// Get the session from the orchestrator
sess, err := api.Session()
if err != nil {
// Log and return if we can't get the session
logger.Error("se", "failed to retrieve session", "err", err)
return false
}
// Retrieve the employee type from the session
employeeType, err := sess.GetString("okta.employeeType")
if err != nil {
logger.Error("se", "failed to retrieve 'employeeType'", "err", err)
return false
}
// Set the default idle timeout to contractor's idle time
idleTimeout := contractorIdleTimeoutSeconds
// Use a longer timeout for full-time employees
if employeeType == "full_time" {
idleTimeout = fteIdleTimeoutSeconds
}
// Check if the session has been idle for too long
if sessionExpired(lastAccess, idleTimeout) {
logger.Info("se", "user has reached idle session lifetime: redirecting to SLO endpoint")
http.Redirect(rw, req, sloEndpoint, http.StatusFound)
// Return false to signal that logout should be handled by SLO endpoint
return false
}
// Session is still active within the allowed idle time
return false
}
// sessionExpired is a helper function that checks whether a given time (like session start
// or last activity) is older than the allowed timeout period.
func sessionExpired(startPoint time.Time, timeout int) bool {
// Convert the timeout to a time duration and subtract it from the current time.
// If the start time is before that, the session has expired.
return startPoint.Before(time.Now().Add(-time.Duration(timeout) * time.Second))
}