Class: WorkOS::Session
- Inherits:
-
Object
- Object
- WorkOS::Session
- Defined in:
- lib/workos/session.rb
Overview
The Session class provides helper methods for working with WorkOS sessions This class is not meant to be instantiated in a user space, and is instantiated internally but exposed.
Instance Attribute Summary collapse
-
#client_id ⇒ Object
Returns the value of attribute client_id.
-
#cookie_password ⇒ Object
Returns the value of attribute cookie_password.
-
#jwks ⇒ Object
Returns the value of attribute jwks.
-
#jwks_algorithms ⇒ Object
Returns the value of attribute jwks_algorithms.
-
#session_data ⇒ Object
Returns the value of attribute session_data.
-
#user_management ⇒ Object
Returns the value of attribute user_management.
Class Method Summary collapse
-
.seal_data(data, key) ⇒ String
Encrypts and seals data using AES-256-GCM.
-
.unseal_data(sealed_data, key) ⇒ Hash
Decrypts and unseals data using AES-256-GCM.
Instance Method Summary collapse
-
#authenticate(include_expired: false) ⇒ Hash
Authenticates the user based on the session data rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity.
-
#get_logout_url(return_to: nil) ⇒ String
Returns a URL to redirect the user to for logging out.
-
#initialize(user_management:, client_id:, session_data:, cookie_password:) ⇒ Session
constructor
A new instance of Session.
-
#refresh(options = nil) ⇒ Hash
Refreshes the session data using the refresh token stored in the session data and a reason if the refresh failed.
Constructor Details
#initialize(user_management:, client_id:, session_data:, cookie_password:) ⇒ Session
Returns a new instance of Session.
17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/workos/session.rb', line 17 def initialize(user_management:, client_id:, session_data:, cookie_password:) raise ArgumentError, 'cookiePassword is required' if .nil? || .empty? @user_management = user_management @cookie_password = @session_data = session_data @client_id = client_id @jwks = Cache.fetch("jwks_#{client_id}", expires_in: 5 * 60) do create_remote_jwk_set(URI(@user_management.get_jwks_url(client_id))) end @jwks_algorithms = @jwks.map { |key| key[:alg] }.compact.uniq end |
Instance Attribute Details
#client_id ⇒ Object
Returns the value of attribute client_id.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def client_id @client_id end |
#cookie_password ⇒ Object
Returns the value of attribute cookie_password.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def @cookie_password end |
#jwks ⇒ Object
Returns the value of attribute jwks.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def jwks @jwks end |
#jwks_algorithms ⇒ Object
Returns the value of attribute jwks_algorithms.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def jwks_algorithms @jwks_algorithms end |
#session_data ⇒ Object
Returns the value of attribute session_data.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def session_data @session_data end |
#user_management ⇒ Object
Returns the value of attribute user_management.
15 16 17 |
# File 'lib/workos/session.rb', line 15 def user_management @user_management end |
Class Method Details
.seal_data(data, key) ⇒ String
Encrypts and seals data using AES-256-GCM
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/workos/session.rb', line 141 def self.seal_data(data, key) iv = SecureRandom.random_bytes(12) encrypted_data = Encryptor.encrypt( value: JSON.generate(data), key: key, iv: iv, algorithm: 'aes-256-gcm', ) Base64.encode64(iv + encrypted_data) # Combine IV with encrypted data and encode as base64 end |
.unseal_data(sealed_data, key) ⇒ Hash
Decrypts and unseals data using AES-256-GCM
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/workos/session.rb', line 157 def self.unseal_data(sealed_data, key) decoded_data = Base64.decode64(sealed_data) iv = decoded_data[0..11] # Extract the IV (first 12 bytes) encrypted_data = decoded_data[12..-1] # Extract the encrypted data decrypted_data = Encryptor.decrypt( value: encrypted_data, key: key, iv: iv, algorithm: 'aes-256-gcm', ) JSON.parse(decrypted_data, symbolize_names: true) # Parse the decrypted JSON string back to original data end |
Instance Method Details
#authenticate(include_expired: false) ⇒ Hash
Authenticates the user based on the session data rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/workos/session.rb', line 35 def authenticate(include_expired: false) return { authenticated: false, reason: 'NO_SESSION_COOKIE_PROVIDED' } if @session_data.nil? begin session = Session.unseal_data(@session_data, @cookie_password) rescue StandardError return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } end return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } unless session[:access_token] begin decoded = JWT.decode( session[:access_token], nil, true, algorithms: @jwks_algorithms, jwks: @jwks, verify_expiration: false, ).first expired = decoded['exp'] && decoded['exp'] < Time.now.to_i # Early return for expired tokens when not including expired data (backward compatible) return { authenticated: false, reason: 'INVALID_JWT' } if expired && !include_expired # Return full data for valid tokens or when include_expired is true { authenticated: !expired, session_id: decoded['sid'], organization_id: decoded['org_id'], role: decoded['role'], roles: decoded['roles'], permissions: decoded['permissions'], entitlements: decoded['entitlements'], feature_flags: decoded['feature_flags'], user: session[:user], impersonator: session[:impersonator], reason: expired ? 'INVALID_JWT' : nil, } rescue JWT::DecodeError { authenticated: false, reason: 'INVALID_JWT' } rescue StandardError => e { authenticated: false, reason: e. } end end |
#get_logout_url(return_to: nil) ⇒ String
Returns a URL to redirect the user to for logging out
127 128 129 130 131 132 133 134 135 |
# File 'lib/workos/session.rb', line 127 def get_logout_url(return_to: nil) auth_response = authenticate unless auth_response[:authenticated] raise "Failed to extract session ID for logout URL: #{auth_response[:reason]}" end @user_management.get_logout_url(session_id: auth_response[:session_id], return_to: return_to) end |
#refresh(options = nil) ⇒ Hash
Refreshes the session data using the refresh token stored in the session data and a reason if the refresh failed
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/workos/session.rb', line 88 def refresh( = nil) = .nil? || [:cookie_password].nil? ? @cookie_password : [:cookie_password] begin session = Session.unseal_data(@session_data, ) rescue StandardError return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } end return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } unless session[:refresh_token] && session[:user] begin auth_response = @user_management.authenticate_with_refresh_token( client_id: @client_id, refresh_token: session[:refresh_token], organization_id: .nil? || [:organization_id].nil? ? nil : [:organization_id], session: { seal_session: true, cookie_password: }, ) @session_data = auth_response.sealed_session @cookie_password = { authenticated: true, sealed_session: auth_response.sealed_session, session: auth_response, reason: nil, } rescue StandardError => e { authenticated: false, reason: e. } end end |