Repository navigation
Releases: PerfectlySoft/Perfect-Session
Releases · PerfectlySoft/Perfect-Session
Release list
Perfect-Session 4.0.0
Perfect-Session 4.0.0 is the first tagged release of the Swift 6 rewrite. It replaces 3.1.5 (2018). It consolidates the old per-backend repositories into one package with a product per backend: PerfectSessionCore, PerfectSessionMySQL, PerfectSessionPostgreSQL, PerfectSessionRedis, PerfectSessionSQLite and PerfectSessionMongoDB.
.package(url: "https://github.com/PerfectlySoft/Perfect-Session.git", from: "4.0.0"),Requirements
- Swift 6.2 or later (
swift-tools-version: 6.2, Swift 6 language mode). - macOS 12 or later, or Linux. CI covers Swift 6.2 and 6.4 on Ubuntu 24.04, in debug and release, with every backend running against a live server. It also covers Xcode 26.0.1 and 26.6 on macOS.
- Dependencies: Perfect-MySQL 4.0.0, Perfect-PostgreSQL 5.0.0, Perfect-Redis 4.0.0, Perfect-SQLite 6.0.0 and Perfect-MongoDB 4.0.3 (all
from:), plus swift-log (the core now uses it too). Each backend needs its own client library: libmysqlclient, libpq, libsqlite3 on Linux, or libmongoc 2. Redis needs none. Only the backends you depend on get built.
Highlights
- One package, five backends, all behind an
async,SendableSessionDriverprotocol. The core has no database or HTTP-framework dependency. - Server-side expiry. Every driver's
resumenow refuses an expired session (a deliberate change, see below), and every driver'ssaverenews the idle time. A new optionalSessionConfig.absoluteTimeoutcaps a session's lifetime from creation;clean(), the MongoDB TTL index and Redis TTLs honour it too. regenerate(_:)on every driver moves a session to a new token and a new CSRF token, keeping its creation time. Call it at sign-in to prevent session fixation.- CSRF and authentication helpers:
CSRFSecurity.tokenMatches(_:_:)(a timing-independent token comparison), a stricterCSRFSecurity.isValid(scheme, default ports, case), andAuthFilter.requiresAuthentication(_:using:), which matches by path segment and ignores case. The README has a Perfect-NIO middleware example that uses all three. - Logs never contain session tokens: the drivers log
PerfectSession.logID(_:), a 12-digit SHA-256 prefix. SessionCookiebuilds theSet-Cookieheader values fromSessionConfig. It enforcesSecureforSameSite=Noneand for the__Secure-/__Host-prefixes (in any case), and refuses values that would inject attributes.- 256-bit tokens from the system CSPRNG (
PerfectSession.newToken()). - Works with Perfect-NIO 0.1.0. The README has a session-filter example. A scratch app built against Perfect-NIO 0.1.0 with warnings as errors on Swift 6.2.4 and 6.4, and ran end to end: cookie set, resume, regenerate on sign-in, and the old token refused. A NIO 0.1.0 server gated by
requiresAuthentication("/admin")answered 401 for/Admin,/ADMIN,/Admin/usersand the other case and slash variants; with the oldshouldWeAccept, the review got 200 and the protected content for those. - MongoDB backend (new since 3.x):
datais stored as a real subdocument, with a TTL index.savedoesn't resurrect logged-out sessions,setup()purges legacy Perfect-Session-MongoDB documents, and awkward keys are escaped.
Breaking and behaviour changes
- The 3.x module and its HTTP filters are gone. That covers
PerfectSessionwith Perfect-HTTPServer'sSessionMemoryDriverrequest and response filters,CSRFFilterandisValid(_ request:). Upgrade: depend onPerfectSessionCoreplus a backend product, and write a small middleware that calls aSessionDriver,SessionCookie,CSRFSecurity,CORSSecurityandAuthFilter(see the README's Perfect-NIO example). - Drivers are
async.resumethrows, andsave(session:)becamesave(_:). Upgrade:try await driver.resume(token:),await driver.save(s)andawait driver.destroy(token:). - The separate Perfect-Session-MySQL, -PostgreSQL, -Redis, -SQLite and -MongoDB packages are superseded. Upgrade: switch to this package's products. Sessions stored by 3.x or by Perfect-Session-MongoDB aren't migrated, so users sign in again. The MongoDB backend's
setup()deletes legacy documents from a shared collection. resumethrowsInvalidSessionErrorfor an expired session. This is deliberate (Tim's decision). It used to return the session and leave the check toisValid(). Upgrade: nothing for most apps. If you relied on resuming expired sessions, read them from storage directly.- Every driver's
saverenews the idle time (touch()); only Redis did, so elsewhere a session expiredidleseconds after creation however active it was. 3.1.5's memory driver also touched on save. Upgrade: none; if you relied on a fixed lifetime from creation, setSessionConfig.absoluteTimeout. - Redis calls time out after 5 seconds by default (
RedisSessionConnector.commandTimeout, was no limit; Tim's decision). A Redis command that takes longer now throws and its connection is closed:create/save/destroylog it, andresumethrowsInvalidSessionError. Upgrade: setcommandTimeoutto a larger value, ornilfor no limit. PerfectSessionManageris nowSessionDriver,asyncandSendable, and also requirescreate(ipaddress:useragent:)(setup,cleanandregeneratehave default implementations). Upgrade: conform custom stores toSessionDriverand implementcreate.SessionConfig.couchDatabaseandmongoCollectionare removed. Upgrade: use the backend's connector (MongoDBSessionConnector.collection).SessionConfig.cookieSameSiteis aCookieSameSite(was Perfect-HTTP'sHTTPCookie.SameSite), andCORS.methodsis[String](was[HTTPMethod]). Upgrade:.strict/.lax/.nonekeep working; write methods as strings (["GET", "POST"]).- Settings with no effect in 4.0 are deprecated:
SessionConfig.CSRF.failAction,checkState,checkHeadersandrequireToken;SessionConfig.CORS.methods,customHeaders,withCredentialsandmaxAge;SessionConfig.purgeInterval,healthCheckRouteandisOAuth2;PerfectSession._isOAuth2;AuthenticationConfig.denied. Nothing reads them, so setting them (in 3.x,requireTokenenforced the CSRF token) does nothing. Upgrade: do it in your middleware:CSRFSecurity.tokenMatchesandisValidfor CSRF, your ownAccess-Control-*headers for CORS,clean()on your own schedule. AuthFilter.shouldWeAcceptis deprecated forrequiresAuthentication(_:using:)(same meaning: true means authentication is required), and matching changed: paths are compared by/segment ignoring empty and.segments and, by default, case;"/admin"covers everything below it but not/administrator;*is a wildcard only as a whole final segment (an inclusion that uses it otherwise requires authentication everywhere; such an exclusion matches nothing); when anything is protected, paths with..,%,;or\require authentication. Upgrade: check your patterns; setAuthenticationConfig.caseInsensitive = falseonly if your router is case-sensitive.CSRFSecurity.isValidcompares the scheme (anhttp://origin no longer passes for anhttps://host), treats:443/:80like no port, ignores case, and rejects anullorigin. Upgrade: list exact origins inacceptableHostnames, with a scheme if you want to restrict it.- A session-data value JSON can't hold drops only its own key (logged by name), not all of
data; a non-JSONcsrfis replaced.resumeadds a CSRF token to a stored session that lacks one. Upgrade: none. - Tokens are 64 lowercase hex characters, not UUIDs. Upgrade: if anything validated the UUID format, accept 64 hex characters instead. Every backend's column or key already fits.
- Redis keys now have a prefix:
RedisSessionConnector.keyPrefix + token, defaultperfect-session:. Upgrade: sessions written by the pre-releasemainaren't found, so users sign in again. SettingkeyPrefix = ""keeps the old keys but brings back the key-space exposure described under Fixes. - Redis TTL follows the session's own
idle, notSessionConfig.idle. A session withidle≤ 0 is now deleted instead of being stored forever. Upgrade: setidleon the session or inSessionConfig.idleas before. Don't use 0 to mean "never"; use a large value. MemorySessionDriver.saveonly updates a stored session, like the other drivers. Upgrade: create sessions throughcreate(), not by constructingPerfectSessionwith your own token.- New SQL tables use 64-bit
created/updated/idlecolumns (MySQLbigint, PostgreSQLint8). Tables thatsetup()created before 4.0 keep their 32-bit columns, and still work (verified on MySQL 8.4 and PostgreSQL 17). Upgrade: run the README'sALTER TABLEto support anidlebeyond about 68 years and times after 2038. isValid,CSRFSecurity.isValidandCORSSecurity.isAcceptablegained optional parameters for explicit lock and hostname config, defaulting toSessionConfig. Upgrade: none; existing calls compile unchanged.SessionDrivergained aregenerate(_:)requirement, with a default implementation. Upgrade: none for custom drivers, unless one already had aregeneratemethod with a different meaning.- CORS wildcard patterns now have to match on both sides of the
*(see Fixes). Upgrade: if you relied onhttps://*matching any https origin, list your origins, or use"*"deliberately.
Fixes
- AuthFilter: behind Perfect-NIO 0.1.0, whose router ignores case,
/Admin,/ADMINand/Admin/usersreached/adminhandlers without authentication (the review got 200s from a live app);//admin,/./admin,/%61dminand/admin;x=1weren't matched;"*.json"never matched and"/admin*"covered/administrator. (#17) - CSRF token loss: one non-JSON value in
session.datamade every backend but memory save the session with no data...
Fixing obsolete url.
3.1.1 Fixing obsolete url.