Session management
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
// Using the Ory SDK instance from the sign-in guide
// Middleware to verify sessions
const requireAuth = async (req, res, next) => {
try {
const { data: session } = await ory.toSession({
cookie: req.header("cookie"),
})
// Store session in request for use in route handlers
req.session = session
next()
} catch (error) {
// No valid session, redirect to Ory login UI
res.redirect(`${basePath}/ui/login`)
}
}
Session verification in React
// Using the Ory SDK instance from the sign-in guide
// Component to verify session
function ProtectedComponent() {
const [session, setSession] = useState<Session | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
// Check if the user is authenticated
const checkSession = async () => {
try {
const session = await ory.toSession()
setSession(session)
setLoading(false)
} catch (error) {
// No valid session, redirect to Ory login UI
window.location.href = `${basePath}/ui/login`
}
}
checkSession()
}, [])
if (loading) {
return <div>Loading...</div>
}
return (
<div>
<h1>Welcome, {session?.identity.traits.email}</h1>
<pre>{JSON.stringify(session, null, 2)}</pre>
</div>
)
}
Session verification in Next.js
// Using the Ory SDK instance from the sign-in guide
function Dashboard() {
const [session, setSession] = useState<Session | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
// Check if the user is authenticated
const checkSession = async () => {
try {
const session = await ory.toSession()
setSession(session)
setLoading(false)
} catch (error) {
// No valid session, redirect to Ory login UI
window.location.href = `${basePath}/ui/login`
}
}
checkSession()
}, [])
if (loading) {
return <div>Loading...</div>
}
return (
<main>
<h1>Dashboard</h1>
<p>Welcome, {session?.identity.traits.email}</p>
<pre>{JSON.stringify(session, null, 2)}</pre>
</main>
)
}
Session verification in Go
// Using the App struct from the sign-in guide
// Session middleware to check authentication status
func (app *App) sessionMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
// Pass cookies to Ory's ToSession endpoint
cookies := request.Header.Get("Cookie")
// Verify session with Ory
session, _, err := app.ory.FrontendApi.ToSession(request.Context()).
Cookie(cookies).Execute()
if err != nil || (err == nil && !*session.Active) {
// No active session, redirect to Ory login UI
http.Redirect(writer, request, app.tunnelUrl+"/ui/login",
http.StatusSeeOther)
return
}
// Add session to context for the handler
ctx := withSession(request.Context(), session)
next.ServeHTTP(writer, request.WithContext(ctx))
}
}
// Helper functions to store/retrieve session from context
func withSession(ctx context.Context, v *ory.Session) context.Context {
return context.WithValue(ctx, "req.session", v)
}
func getSession(ctx context.Context) (*ory.Session, error) {
session, ok := ctx.Value("req.session").(*ory.Session)
if !ok || session == nil {
return nil, errors.New("session not found in context")
}
return session, nil
}
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
// Using the requireAuth middleware defined above
// Apply the middleware to routes that need protection
app.get("/dashboard", requireAuth, (req, res) => {
// Access user data from the session
const user = req.session.identity
res.render("dashboard", { user })
})
app.get("/settings", requireAuth, (req, res) => {
res.render("settings", { user: req.session.identity })
})
// Create a protected route wrapper component
function 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)
// Redirect to Ory login UI
window.location.href = `${basePath}/ui/login`
})
}, [])
if (loading) {
return <div>Loading...</div>
}
return isAuthenticated ? children : null
}
// 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)
const basePath = process.env.NEXT_PUBLIC_ORY_SDK_URL || "http://localhost:4000"
// 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 Ory login UI with return URL
return NextResponse.redirect(`${basePath}/ui/login?return_to=${encodeURIComponent(url.pathname)}`)
}
}
return NextResponse.next()
}
// Configure which paths the middleware runs on
export const config = {
matcher: ["/dashboard/:path*", "/settings/:path*", "/profile/:path*"],
}
// Using the App struct and middleware from above
func main() {
// Assuming app is already initialized as in the sign-in guide
// Create router
mux := http.NewServeMux()
// Routes that use the session middleware
mux.Handle("/dashboard", app.sessionMiddleware(app.dashboardHandler))
mux.Handle("/profile", app.sessionMiddleware(app.profileHandler))
// Start server
http.ListenAndServe(":3000", mux)
}
// Example protected route handler
func (app *App) dashboardHandler(w http.ResponseWriter, r *http.Request) {
// Get session from context (set by middleware)
session, err := getSession(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Use session data
fmt.Fprintf(w, "Welcome, %s!", session.Identity.Traits["email"])
}
Refresh sessions
You can refresh user sessions to extend their expiration time:
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
// Force session refresh by prompting re-authentication
app.get("/refresh-session", async (req, res) => {
const basePath = process.env.ORY_SDK_URL || "http://localhost:4000"
// Redirect to login with refresh=true parameter
res.redirect(`${basePath}/ui/login?refresh=true&return_to=/dashboard`)
})
// Refresh a session programmatically
async function refreshSession(req, res, next) {
try {
const { data: session } = await ory.refreshSession({
cookie: req.header("cookie"),
})
// Session is now refreshed
req.session = session
next()
} catch (error) {
// Error handling
res.redirect(`${basePath}/ui/login`)
}
}
// Force session refresh by prompting re-authentication
function refreshUserSession() {
const basePath = process.env.REACT_APP_ORY_SDK_URL || "http://localhost:4000"
// Redirect to login with refresh=true parameter
window.location.href = `${basePath}/ui/login?refresh=true&return_to=${window.location.pathname}`
}
// Button component example
function RefreshSessionButton() {
return <button onClick={refreshUserSession}>Refresh Session</button>
}
// Force session refresh by prompting re-authentication
function refreshUserSession() {
const basePath = process.env.NEXT_PUBLIC_ORY_SDK_URL || "http://localhost:4000"
// Redirect to login with refresh=true parameter
window.location.href = `${basePath}/ui/login?refresh=true&return_to=${window.location.pathname}`
}
// API route to refresh session programmatically (pages/api/refresh-session.js)
export default async function handler(req, res) {
try {
const { data: session } = await ory.refreshSession({
cookie: req.headers.cookie,
})
res.status(200).json({ success: true, session })
} catch (error) {
res.status(401).json({ success: false, error: "Unauthorized" })
}
}
// Force session refresh by prompting re-authentication
func refreshSessionHandler(w http.ResponseWriter, r *http.Request) {
// Redirect to login with refresh=true parameter
http.Redirect(w, r, app.tunnelUrl+"/ui/login?refresh=true&return_to=/dashboard",
http.StatusSeeOther)
}
// Refresh a session programmatically
func (app *App) refreshSession(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("Cookie")
// Refresh the session
session, _, err := app.ory.FrontendApi.RefreshSession(r.Context()).
Cookie(cookie).Execute()
if err != nil {
http.Redirect(w, r, app.tunnelUrl+"/ui/login", http.StatusSeeOther)
return
}
// Session is now refreshed
ctx := withSession(r.Context(), session)
// Continue with the refreshed session
}
# Force session refresh by prompting re-authentication
# Redirect the user to this URL:
https://$PROJECT_SLUG.projects.oryapis.com/self-service/login/browser?refresh=true
# Refresh a session programmatically
curl -X GET \
'https://$PROJECT_SLUG.projects.oryapis.com/self-service/login/refresh?refresh=aAaAfAEAAdAFECQAAJMAbCcDdFEBaGcG' \
-H 'Accept: application/json' \
-H 'Cookie: ory_session_YOUR_PROJECT=YOUR_SESSION_COOKIE' \
--verbose
Revoke sessions
There are times when you need to terminate user sessions, such as after a password change or for security reasons:
- JavaScript/Node.js
- React
- Next.js
- Go
- cURL
// Revoke all sessions except the current one
app.post("/revoke-other-sessions", requireAuth, async (req, res) => {
try {
await ory.revokeOtherSessions({
cookie: req.header("cookie"),
})
res.redirect("/settings")
} catch (error) {
res.status(500).send("Failed to revoke sessions")
}
})
// Revoke a specific session
app.post("/revoke-session/:id", requireAuth, async (req, res) => {
try {
await ory.revokeSession({
id: req.params.id,
})
res.redirect("/settings")
} catch (error) {
res.status(500).send("Failed to revoke session")
}
})
// Revoke current session (logout)
app.post("/logout", async (req, res) => {
try {
const { data } = await ory.createBrowserLogoutFlow({
cookie: req.header("cookie"),
})
res.redirect(data.logout_url)
} catch (error) {
res.redirect("/")
}
})
// Revoke all sessions except the current one
async function revokeOtherSessions() {
try {
await ory.revokeOtherSessions()
alert("All other sessions revoked")
} catch (error) {
console.error("Failed to revoke sessions:", error)
}
}
// Revoke a specific session
async function revokeSession(sessionId) {
try {
await ory.revokeSession({
id: sessionId,
})
alert("Session revoked")
} catch (error) {
console.error("Failed to revoke session:", error)
}
}
// Logout (revoke current session)
async function logout() {
try {
const { data } = await ory.createBrowserLogoutFlow()
window.location.href = data.logout_url
} catch (error) {
console.error("Logout failed:", error)
}
}
// Revoke all sessions except the current one
async function revokeOtherSessions() {
try {
await ory.revokeOtherSessions()
alert("All other sessions revoked")
} catch (error) {
console.error("Failed to revoke sessions:", error)
}
}
// Revoke a specific session
async function revokeSession(sessionId) {
try {
await ory.revokeSession({
id: sessionId,
})
alert("Session revoked")
} catch (error) {
console.error("Failed to revoke session:", error)
}
}
// API route to revoke sessions (pages/api/revoke-sessions.js)
export default async function handler(req, res) {
if (req.method === "POST") {
try {
await ory.revokeOtherSessions({
cookie: req.headers.cookie,
})
res.status(200).json({ success: true })
} catch (error) {
res.status(500).json({ success: false })
}
} else {
res.status(405).json({ error: "Method not allowed" })
}
}
// Revoke all sessions except the current one
func (app *App) revokeOtherSessionsHandler(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("Cookie")
// Revoke all other sessions
_, _, err := app.ory.FrontendApi.RevokeOtherSessions(r.Context()).
Cookie(cookie).Execute()
if err != nil {
http.Error(w, "Failed to revoke sessions", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
// Revoke a specific session
func (app *App) revokeSessionHandler(w http.ResponseWriter, r *http.Request) {
sessionID := r.URL.Query().Get("id")
// Revoke the specific session
_, err := app.ory.FrontendApi.RevokeSession(r.Context()).
Id(sessionID).Execute()
if err != nil {
http.Error(w, "Failed to revoke session", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
# Revoke all sessions except the current one
curl -X DELETE \
'https://$PROJECT_SLUG.projects.oryapis.com/sessions' \
-H 'Accept: application/json' \
-H 'Cookie: ory_session_YOUR_PROJECT=YOUR_SESSION_COOKIE' \
--verbose
# Revoke a specific session
curl -X DELETE \
'https://$PROJECT_SLUG.projects.oryapis.com/sessions/SESSION_ID' \
-H 'Accept: application/json' \
--verbose
Configure automatic session revocation
You can configure Ory to automatically revoke all active sessions when a user changes their password:
- Ory Console
- Ory CLI
- Go to Authentication → Sessions
- Configure Revoke sessions after password change
- Click Save
- Download your current identity configuration:
ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml
- Edit the configuration file to add the
revoke_active_sessions
hook:
selfservice:
flows:
settings:
after:
password:
hooks:
- hook: revoke_active_sessions
- Update your configuration:
ory update identity-config --project <project-id> --workspace <workspace-id> --file identity-config.yaml
Configuring session settings in Ory Console
You can configure various session-related settings through the Ory Console. Learn how to:
- Configure Session lifespan
- Allow users to change sensitive settings (like passwords, adding a second factor, or changing email) in their profile.
Next steps
Now that you've learned how to manage user sessions, you can: