Skip to main content
Sessions in Zitadel represent authenticated user states and can be used for authentication across multiple applications. This guide covers creating, managing, and validating sessions.

Prerequisites

  • Zitadel Ruby SDK installed and configured
  • Authentication configured
  • Required permissions: session.read, session.write, session.delete

Understanding Sessions

A session in Zitadel:
  • Represents an authenticated user state
  • Can include multiple authentication factors
  • Returns a session token that can be used as an OAuth2 access token
  • Can have metadata and expiration settings
  • Can be used to authenticate OIDC and SAML requests

Creating Sessions

Create a Basic Session

Create a session with a user check:
require 'zitadel-client'

client = Zitadel::Client::Zitadel.with_access_token(
  "https://example.zitadel.cloud",
  "your_access_token"
)

request = Zitadel::Client::SessionServiceCreateSessionRequest.new(
  checks: {
    user: {
      login_name: "[email protected]"
    }
  },
  lifetime: "18000s" # 5 hours
)

begin
  response = client.sessions.create_session(request)
  puts "Session created!"
  puts "  Session ID: #{response.session_id}"
  puts "  Session Token: #{response.session_token}"
  puts "  Expires in: #{response.details.lifetime}"
rescue Zitadel::Client::ApiError => e
  puts "Error creating session: #{e.message}"
end
Store the session token securely. It can be used to authenticate API requests and should be treated like a password.

Create Session with Password Check

request = Zitadel::Client::SessionServiceCreateSessionRequest.new(
  checks: {
    user: {
      login_name: "[email protected]"
    },
    password: {
      password: "user_password_here"
    }
  },
  lifetime: "3600s" # 1 hour
)

response = client.sessions.create_session(request)
puts "Authenticated session created: #{response.session_id}"

Create Session with Metadata

require 'base64'

metadata = {
  ip_address: "192.168.1.1",
  device: "mobile",
  app_version: "1.2.3"
}.to_json

request = Zitadel::Client::SessionServiceCreateSessionRequest.new(
  checks: {
    user: {
      login_name: "[email protected]"
    }
  },
  metadata: {
    "client_info" => Base64.strict_encode64(metadata)
  },
  lifetime: "7200s"
)

response = client.sessions.create_session(request)
puts "Session with metadata created: #{response.session_id}"
Session metadata values must be base64 encoded.

Retrieving Sessions

Get Session by ID

request = Zitadel::Client::SessionServiceGetSessionRequest.new(
  session_id: "session_id_here",
  session_token: "session_token_here" # Optional if you have permission
)

response = client.sessions.get_session(request)

puts "Session Details:"
puts "  ID: #{response.session.id}"
puts "  User ID: #{response.session.factors.user.id}"
puts "  Login Name: #{response.session.factors.user.login_name}"
puts "  Created: #{response.session.creation_date}"
puts "  Expires: #{response.session.expiration_date}"

if response.session.metadata
  puts "  Metadata:"
  response.session.metadata.each do |key, value|
    decoded = Base64.strict_decode64(value)
    puts "    #{key}: #{decoded}"
  end
end
You can retrieve a session without the session token if:
  • You created the session
  • You’re requesting your own session
  • You have session.read permission

List Sessions

request = Zitadel::Client::SessionServiceListSessionsRequest.new(
  queries: []
)

response = client.sessions.list_sessions(request)

response.sessions.each do |session|
  puts "Session: #{session.id}"
  puts "  User: #{session.factors.user.login_name}"
  puts "  Created: #{session.creation_date}"
  puts "  Expires: #{session.expiration_date || 'Never'}"
end

Search Sessions by User

request = Zitadel::Client::SessionServiceListSessionsRequest.new(
  queries: [
    {
      user_id_query: {
        user_id: "user_id_here"
      }
    }
  ]
)

response = client.sessions.list_sessions(request)
puts "Found #{response.sessions.length} sessions for user"

Updating Sessions

Update Session Lifetime

request = Zitadel::Client::SessionServiceSetSessionRequest.new(
  session_id: "session_id_here",
  session_token: "current_session_token",
  lifetime: "36000s" # 10 hours
)

response = client.sessions.set_session(request)
puts "Session updated!"
puts "New session token: #{response.session_token}"
Updating a session invalidates the previous token. You must use the new token returned in the response.

Add Metadata to Existing Session

metadata = { last_activity: Time.now.to_s }.to_json

request = Zitadel::Client::SessionServiceSetSessionRequest.new(
  session_id: "session_id_here",
  session_token: "current_session_token",
  metadata: {
    "activity" => Base64.strict_encode64(metadata)
  }
)

response = client.sessions.set_session(request)
puts "Metadata updated, new token: #{response.session_token}"

Add Additional Authentication Checks

request = Zitadel::Client::SessionServiceSetSessionRequest.new(
  session_id: "session_id_here",
  session_token: "current_session_token",
  checks: {
    totp: {
      code: "123456" # OTP code from authenticator app
    }
  }
)

response = client.sessions.set_session(request)
puts "MFA verified, new token: #{response.session_token}"

Deleting Sessions

Delete a Single Session

request = Zitadel::Client::SessionServiceDeleteSessionRequest.new(
  session_id: "session_id_here",
  session_token: "session_token_here" # Optional if you have permission
)

response = client.sessions.delete_session(request)
puts "Session terminated"
You can delete your own sessions without the session.delete permission. The session token is sufficient.

Delete All User Sessions (Logout Everywhere)

# List all sessions for a user
list_request = Zitadel::Client::SessionServiceListSessionsRequest.new(
  queries: [
    {
      user_id_query: {
        user_id: "user_id_here"
      }
    }
  ]
)

list_response = client.sessions.list_sessions(list_request)

# Delete each session
list_response.sessions.each do |session|
  delete_request = Zitadel::Client::SessionServiceDeleteSessionRequest.new(
    session_id: session.id
  )
  client.sessions.delete_session(delete_request)
  puts "Deleted session: #{session.id}"
end

puts "All sessions terminated"

Complete Workflow Examples

User Login with Session

require 'zitadel-client'

client = Zitadel::Client::Zitadel.with_private_key(
  "https://example.zitadel.cloud",
  "path/to/service-account.json"
)

def login(client, username, password)
  # Create session with password verification
  request = Zitadel::Client::SessionServiceCreateSessionRequest.new(
    checks: {
      user: {
        login_name: username
      },
      password: {
        password: password
      }
    },
    lifetime: "3600s", # 1 hour
    metadata: {
      "login_time" => Base64.strict_encode64(Time.now.to_s)
    }
  )

  begin
    response = client.sessions.create_session(request)
    {
      success: true,
      session_id: response.session_id,
      session_token: response.session_token,
      message: "Login successful"
    }
  rescue Zitadel::Client::ApiError => e
    {
      success: false,
      message: "Login failed: #{e.message}"
    }
  end
end

# Usage
result = login(client, "[email protected]", "password123")

if result[:success]
  puts "Logged in successfully!"
  puts "Session Token: #{result[:session_token]}"
else
  puts result[:message]
end

Session Validation and Refresh

def validate_and_refresh_session(client, session_id, session_token)
  begin
    # Get current session details
    get_request = Zitadel::Client::SessionServiceGetSessionRequest.new(
      session_id: session_id,
      session_token: session_token
    )
    
    session = client.sessions.get_session(get_request).session
    
    # Check if session is expired
    if session.expiration_date
      expiration = Time.parse(session.expiration_date)
      if expiration < Time.now
        return { valid: false, message: "Session expired" }
      end
    end
    
    # Refresh session if it expires in less than 5 minutes
    if session.expiration_date
      expiration = Time.parse(session.expiration_date)
      if expiration < Time.now + 300 # 5 minutes
        set_request = Zitadel::Client::SessionServiceSetSessionRequest.new(
          session_id: session_id,
          session_token: session_token,
          lifetime: "3600s" # Extend by 1 hour
        )
        
        response = client.sessions.set_session(set_request)
        return {
          valid: true,
          refreshed: true,
          new_token: response.session_token,
          message: "Session refreshed"
        }
      end
    end
    
    { valid: true, refreshed: false, message: "Session valid" }
    
  rescue Zitadel::Client::ApiError => e
    { valid: false, message: "Session invalid: #{e.message}" }
  end
end

# Usage
result = validate_and_refresh_session(client, session_id, session_token)

if result[:valid]
  puts result[:message]
  if result[:refreshed]
    puts "New token: #{result[:new_token]}"
    # Update stored token
  end
else
  puts "Session invalid: #{result[:message]}"
  # Redirect to login
end

Session-Based API Authentication

# Use session token as OAuth2 access token
require 'net/http'
require 'json'

def call_api_with_session(session_token, endpoint)
  uri = URI(endpoint)
  
  request = Net::HTTP::Get.new(uri)
  request['Authorization'] = "Bearer #{session_token}"
  request['Content-Type'] = 'application/json'
  
  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
    http.request(request)
  end
  
  if response.is_a?(Net::HTTPSuccess)
    JSON.parse(response.body)
  else
    { error: "API call failed: #{response.code} #{response.message}" }
  end
end

# Create session
create_request = Zitadel::Client::SessionServiceCreateSessionRequest.new(
  checks: {
    user: { login_name: "[email protected]" },
    password: { password: "secure_password" }
  },
  lifetime: "3600s"
)

session_response = client.sessions.create_session(create_request)

# Use session token to call Zitadel API
user_data = call_api_with_session(
  session_response.session_token,
  "https://example.zitadel.cloud/v2/users/me"
)

puts "User data: #{user_data}"

Best Practices

1
Set appropriate lifetimes
2
Use shorter lifetimes (1-2 hours) for sensitive applications and longer lifetimes for less critical apps.
3
Secure token storage
4
Store session tokens securely (HttpOnly cookies for web, secure storage for mobile).
5
Implement token refresh
6
Refresh sessions before they expire to provide seamless user experience.
7
Validate sessions
8
Always validate sessions before performing sensitive operations.
9
Clean up sessions
10
Delete sessions on logout and implement automatic cleanup of expired sessions.
11
Use metadata wisely
12
Store relevant context (IP address, device info) but avoid sensitive data.
13
Handle errors gracefully
14
Implement proper error handling for expired or invalid sessions.

Security Considerations

  • Never expose session tokens in URLs or logs
  • Use HTTPS for all session-related communications
  • Implement CSRF protection for web applications
  • Rotate session tokens after privilege escalation
  • Delete sessions immediately on logout

Next Steps

Build docs developers (and LLMs) love