Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulAsjes committed Aug 27, 2024
1 parent 778d5bc commit a066b18
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 84 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
source 'https://rubygems.org'

gemspec
gem 'jwt'
gem 'encryptor'
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ GEM
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.7)
crack (1.0.0)
bigdecimal
rexml
diff-lcs (1.5.1)
encryptor (3.0.0)
hashdiff (1.1.0)
jwt (2.8.2)
base64
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
Expand Down Expand Up @@ -61,6 +65,8 @@ PLATFORMS

DEPENDENCIES
bundler (>= 2.0.1)
encryptor
jwt
rspec (~> 3.9.0)
rubocop (~> 0.77)
vcr (~> 5.0.0)
Expand Down
2 changes: 1 addition & 1 deletion lib/workos/authentication_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def initialize(authentication_response_json, session = nil)
end
@authentication_method = json[:authentication_method]
@sealed_session =
if (session[:seal_session])
if session and session[:seal_session]
WorkOS::Session.seal_data({
access_token: access_token,
refresh_token: refresh_token,
Expand Down
2 changes: 1 addition & 1 deletion lib/workos/refresh_authentication_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def initialize(authentication_response_json, session = nil)
reason: impersonator_json[:reason],)
end
@sealed_session =
if (session[:seal_session])
if session and session[:seal_session]
WorkOS::Session.seal_data({
access_token: access_token,
refresh_token: refresh_token,
Expand Down
7 changes: 5 additions & 2 deletions lib/workos/session.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require_relative './user_management'
require 'jwt'
require 'uri'
require 'net/http'
Expand Down Expand Up @@ -62,7 +61,11 @@ def authenticate
def refresh(options = nil)
cookie_password = options.nil? || options[:cookie_password].nil? ? @cookie_password : options[:cookie_password]

session = Session::unseal_data(@session_data, cookie_password)
begin
session = Session::unseal_data(@session_data, cookie_password)
rescue StandardError => e
return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' }
end

return { authenticated: false, reason: 'INVALID_SESSION_COOKIE' } unless session[:refresh_token] && session[:user]

Expand Down
4 changes: 2 additions & 2 deletions lib/workos/user_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ def authenticate_with_code(
user_agent: nil,
session: nil
)
if session[:seal_session] == true and session[:cookie_password].nil?
if session and session[:seal_session] == true and session[:cookie_password].nil?
raise ArgumentError, 'cookie_password is required when sealing session'
end

Expand Down Expand Up @@ -354,7 +354,7 @@ def authenticate_with_refresh_token(
user_agent: nil,
session: nil
)
if session[:seal_session] == true and session[:cookie_password].nil?
if session and session[:seal_session] == true and session[:cookie_password].nil?
raise ArgumentError, 'cookie_password is required when sealing session'
end

Expand Down
144 changes: 144 additions & 0 deletions spec/lib/workos/session_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# frozen_string_literal: true

describe WorkOS::Session do

let(:user_management) { instance_double('UserManagement') }
let(:client_id) { 'test_client_id' }
let(:cookie_password) { 'test_very_long_cookie_password__' }
let(:session_data) { 'test_session_data' }
let(:jwks_url) { 'https://api.workos.com/sso/jwks/client_123' }
let(:jwks_hash) { '{"keys":[{"alg":"RS256","kty":"RSA","use":"sig","n":"test_n","e":"AQAB","kid":"sso_oidc_key_pair_123","x5c":["test"],"x5t#S256":"test"}]}' }
let(:jwk) { JWT::JWK.new(OpenSSL::PKey::RSA.new(2048), { kid: 'sso_oidc_key_pair_123', use: 'sig', alg: 'RS256' }) }

before do
allow(user_management).to receive(:get_jwks_url).with(client_id).and_return(jwks_url)
allow(Net::HTTP).to receive(:get).and_return(jwks_hash)
end

describe 'initialize' do
it 'raises an error if cookie_password is nil or empty' do
expect {
WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: nil)
}.to raise_error(ArgumentError, 'cookiePassword is required')

expect {
WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: '')
}.to raise_error(ArgumentError, 'cookiePassword is required')
end

it 'initializes with valid parameters' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: cookie_password)
expect(session.user_management).to eq(user_management)
expect(session.client_id).to eq(client_id)
expect(session.session_data).to eq(session_data)
expect(session.cookie_password).to eq(cookie_password)
expect(session.jwks.map(&:export)).to eq(JSON.parse(jwks_hash, symbolize_names: true)[:keys])
expect(session.jwks_algorithms).to eq(['RS256'])
end
end

describe '.authenticate' do
let(:valid_access_token) do
payload = { sid: 'session_id', org_id: 'org_id', role: 'role', permissions: ['read'], exp: Time.now.to_i + 3600 }
headers = { kid: jwk[:kid] }
JWT.encode(payload, jwk.signing_key, jwk[:alg], headers)
end
let(:session_data) { WorkOS::Session.seal_data({ access_token: valid_access_token, user: 'user', impersonator: 'impersonator' }, cookie_password) }

it 'returns NO_SESSION_COOKIE_PROVIDED if session_data is nil' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: nil, cookie_password: cookie_password)
result = session.authenticate
expect(result).to eq({ authenticated: false, reason: 'NO_SESSION_COOKIE_PROVIDED' })
end

it 'returns INVALID_SESSION_COOKIE if session_data is invalid' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: 'invalid_data', cookie_password: cookie_password)
result = session.authenticate
expect(result).to eq({ authenticated: false, reason: 'INVALID_SESSION_COOKIE' })
end

it 'returns INVALID_JWT if access_token is invalid' do
invalid_session_data = WorkOS::Session.seal_data({ access_token: 'invalid_token' }, cookie_password)
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: invalid_session_data, cookie_password: cookie_password)
result = session.authenticate
expect(result).to eq({ authenticated: false, reason: 'INVALID_JWT' })
end

it 'authenticates successfully with valid session_data' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: cookie_password)
allow(session).to receive(:is_valid_jwt).and_return(true)
allow(JWT).to receive(:decode).and_return([{ 'sid' => 'session_id', 'org_id' => 'org_id', 'role' => 'role', 'permissions' => ['read'] }])

result = session.authenticate
expect(result).to eq({
authenticated: true,
session_id: 'session_id',
organization_id: 'org_id',
role: 'role',
permissions: ['read'],
user: 'user',
impersonator: 'impersonator',
reason: nil
})
end
end

describe '.refresh' do
let(:refresh_token) { 'test_refresh_token' }
let(:session_data) { WorkOS::Session.seal_data({ refresh_token: refresh_token, user: 'user' }, cookie_password) }
let(:auth_response) { double('AuthResponse', sealed_session: 'new_sealed_session') }

before do
allow(user_management).to receive(:authenticate_with_refresh_token).and_return(auth_response)
end

it 'returns INVALID_SESSION_COOKIE if session_data is invalid' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: 'invalid_data', cookie_password: cookie_password)
result = session.refresh
expect(result).to eq({ authenticated: false, reason: 'INVALID_SESSION_COOKIE' })
end

it 'refreshes the session successfully with valid session_data' do
session = WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: cookie_password)
result = session.refresh
expect(result).to eq({
authenticated: true,
sealed_session: 'new_sealed_session',
session: auth_response,
reason: nil
})
end
end

describe '.get_logout_url' do
let(:session) { WorkOS::Session.new(user_management: user_management, client_id: client_id, session_data: session_data, cookie_password: cookie_password) }

context 'when authentication is successful' do
before do
allow(session).to receive(:authenticate).and_return({
authenticated: true,
session_id: 'session_id',
reason: nil
})
allow(user_management).to receive(:get_logout_url).with(session_id: 'session_id').and_return('https://example.com/logout')
end

it 'returns the logout URL' do
expect(session.get_logout_url).to eq('https://example.com/logout')
end
end

context 'when authentication fails' do
before do
allow(session).to receive(:authenticate).and_return({
authenticated: false,
reason: 'Invalid session'
})
end

it 'raises an error' do
expect { session.get_logout_url }.to raise_error(RuntimeError, 'Failed to extract session ID for logout URL: Invalid session')
end
end
end
end
1 change: 1 addition & 0 deletions spec/lib/workos/user_management_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@
)
expect(authentication_response.access_token).to eq('<ACCESS_TOKEN>')
expect(authentication_response.refresh_token).to eq('<REFRESH_TOKEN>')
expect(authentication_response.user.id).to eq('user_01H93WD0R0KWF8Q7BK02C0RPYJ')
end
end
end
Expand Down
Loading

0 comments on commit a066b18

Please sign in to comment.