Session Management Basics
After a user has logged in, Ory creates a session cookie that your application can use to verify the user's authentication status. This guide shows how to work with sessions in your application.
Checking Session Status
You'll need to verify if a user is authenticated before allowing access to protected resources. Here's how to implement session verification:
Verify the session
Check if the user has a valid session cookie
Access identity information
Retrieve user details from the session
Handle unauthenticated users
Redirect to login if no valid session exists
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
Session Verification with Express.js
// Check if a user is authenticated
const requireAuth = async (req, res, next) => {
try {
// This verifies the session and throws an error if not authenticated
const { data: session } = await ory.toSession({
cookie: req.header("cookie"),
})
// Make session available to the route handler
req.session = session
next()
} catch (err) {
// Not authenticated, redirect to login
res.redirect("/login")
}
}
// Use the middleware for protected routes
app.get("/dashboard", requireAuth, (req, res) => {
// Access user data
const userId = req.session.identity.id
const email = req.session.identity.traits.email
res.render("dashboard", { user: req.session.identity })
})
Session Verification in React
import { useEffect, useState } from "react"
import { ory } from "../lib/ory"
export const Dashboard = () => {
const [session, setSession] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
// Check if the user is authenticated
ory
.toSession()
.then(({ data }) => {
setSession(data)
setLoading(false)
})
.catch(() => {
// Not authenticated, redirect to login
window.location.href = "/login?return_to=/dashboard"
})
}, [])
if (loading) {
return <div>Loading...</div>
}
return (
<div>
<h2>
Welcome, {session.identity.traits.name || session.identity.traits.email}
</h2>
<div>
<p>User ID: {session.identity.id}</p>
<p>Email: {session.identity.traits.email}</p>
</div>
<button onClick={() => (window.location.href = "/logout")}>
Log Out
</button>
</div>
)
}
Session Verification in Go
package main
import (
"context"
"fmt"
"net/http"
ory "github.com/ory/client-go"
)
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Initialize the Ory client
configuration := ory.NewConfiguration()
configuration.Servers = []ory.ServerConfiguration{
{
URL: "https://$PROJECT_SLUG.projects.oryapis.com",
},
}
client := ory.NewAPIClient(configuration)
// Check if user is authenticated
cookie := r.Header.Get("Cookie")
session, _, err := client.FrontendApi.ToSession(context.Background()).Cookie(cookie).Execute()
if err != nil {
// Not authenticated, redirect to login
http.Redirect(w, r, "/login", http.StatusFound)
return
}
// User is authenticated, add session to context
ctx := context.WithValue(r.Context(), "session", session)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
// Get session from context
session := r.Context().Value("session").(*ory.Session)
// Access user data
userId := session.Identity.Id
email := session.Identity.Traits["email"]
// Render dashboard with user data
fmt.Fprintf(w, "Welcome, %s", email)
}
Session Verification with cURL
# Check if the user is authenticated
curl -X GET \
'https://$PROJECT_SLUG.projects.oryapis.com/sessions/whoami' \
-H 'Accept: application/json' \
-H 'Cookie: ory_session_YOUR_PROJECT=YOUR_SESSION_COOKIE' \
--verbose
# If the user is authenticated, the response will include session details:
# {
# "id": "session_id",
# "active": true,
# "expires_at": "2023-01-01T00:00:00Z",
# "authenticated_at": "2022-01-01T00:00:00Z",
# "issued_at": "2022-01-01T00:00:00Z",
# "identity": {
# "id": "identity_id",
# "traits": {
# "email": "user@example.com"
# }
# }
# }
Protecting Routes
Common patterns for protecting routes in your application:
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
// Create an authentication middleware
const requireAuth = async (req, res, next) => {
try {
const { data: session } = await ory.toSession({
cookie: req.header("cookie"),
})
req.session = session
next()
} catch (err) {
res.redirect("/login?return_to=" + encodeURIComponent(req.originalUrl))
}
}
// Apply the middleware to routes that need protection
app.get("/dashboard", requireAuth, dashboardHandler)
app.get("/settings", requireAuth, settingsHandler)
app.get("/profile", requireAuth, profileHandler)
import { useEffect, useState } from "react"
import { ory } from "../lib/ory"
import { Navigate } from "react-router-dom"
// Create a protected route wrapper component
const ProtectedRoute = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false)
const [loading, setLoading] = useState(true)
useEffect(() => {
ory
.toSession()
.then(() => {
setIsAuthenticated(true)
setLoading(false)
})
.catch(() => {
setIsAuthenticated(false)
setLoading(false)
})
}, [])
if (loading) {
return <div>Loading...</div>
}
return isAuthenticated ? children : <Navigate to="/login" replace />
}
// Use the protected route component
;<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
// middleware.ts file at the project root
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"
export async function middleware(request: NextRequest) {
const url = new URL(request.url)
// List of protected paths
const protectedPaths = ["/dashboard", "/settings", "/profile"]
const isProtectedPath = protectedPaths.some((path) => url.pathname === path || url.pathname.startsWith(`${path}/`))
if (isProtectedPath) {
// Check for the session cookie
const cookie = request.cookies.get("ory_session_YOUR_PROJECT")
if (!cookie) {
// Redirect to login if no session cookie exists
return NextResponse.redirect(new URL(`/login?return_to=${encodeURIComponent(url.pathname)}`, request.url))
}
}
return NextResponse.next()
}
// Configure which paths the middleware runs on
export const config = {
matcher: ["/dashboard/:path*", "/settings/:path*", "/profile/:path*"],
}
func main() {
// Create a router
mux := http.NewServeMux()
// Public routes
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/login", loginHandler)
// Protected routes with auth middleware
mux.Handle("/dashboard", authMiddleware(http.HandlerFunc(dashboardHandler)))
mux.Handle("/settings", authMiddleware(http.HandlerFunc(settingsHandler)))
mux.Handle("/profile", authMiddleware(http.HandlerFunc(profileHandler)))
http.ListenAndServe(":8080", mux)
}
Session Lifespans
Ory sessions have several important time properties:
Property | Description |
---|---|
issued_at | When the session was created |
authenticated_at | When the user was authenticated |
expires_at | When the session will expire |
active | Whether the session is still active |
By default, sessions expire after 24 hours. This can be configured in your Ory project settings.
Refreshing Sessions
To extend a session's lifespan, you can use the session refresh flow:
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
app.get("/refresh-session", async (req, res) => {
try {
const { data: refreshedSession } = await ory.extendSession({
cookie: req.header("cookie"),
})
console.log("Session extended until:", refreshedSession.expires_at)
res.redirect("/dashboard")
} catch (err) {
console.error("Failed to refresh session:", err)
res.redirect("/login")
}
})
const refreshSession = async () => {
try {
const { data: refreshedSession } = await ory.extendSession()
console.log("Session extended until:", refreshedSession.expires_at)
return true
} catch (err) {
console.error("Failed to refresh session:", err)
window.location.href = "/login"
return false
}
}
// Call this function periodically or when the user performs actions
func refreshSessionHandler(w http.ResponseWriter, r *http.Request) {
// Initialize the Ory client
configuration := ory.NewConfiguration()
configuration.Servers = []ory.ServerConfiguration{
{
URL: "https://$PROJECT_SLUG.projects.oryapis.com",
},
}
client := ory.NewAPIClient(configuration)
// Refresh the session
cookie := r.Header.Get("Cookie")
refreshedSession, _, err := client.FrontendApi.ExtendSession(context.Background()).Cookie(cookie).Execute()
if err != nil {
http.Redirect(w, r, "/login", http.StatusFound)
return
}
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
# Refresh a session
curl -X POST \
'https://$PROJECT_SLUG.projects.oryapis.com/sessions/extend' \
-H 'Accept: application/json' \
-H 'Cookie: ory_session_YOUR_PROJECT=YOUR_SESSION_COOKIE' \
--verbose
Best Practices for Session Management
- Always Verify Sessions: Never assume a user is authenticated without verifying their session first
- Use Short Expiry Times: Set shorter session lifespans for sensitive applications
- Implement Session Refresh: Allow users to extend their sessions when they're active
- Secure Cookie Handling: Always pass cookies with credentials in API requests
- Provide Graceful Redirects: When a session expires, save the user's intended destination and redirect there after re-authentication
Next Steps
Now that you've learned how to manage user sessions, you can: