Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/workos.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def self.key
autoload :Invitation, 'workos/invitation'
autoload :MagicAuth, 'workos/magic_auth'
autoload :MFA, 'workos/mfa'
autoload :OAuthTokens, 'workos/oauth_tokens'
autoload :Organization, 'workos/organization'
autoload :Organizations, 'workos/organizations'
autoload :OrganizationMembership, 'workos/organization_membership'
Expand Down
5 changes: 4 additions & 1 deletion lib/workos/authentication_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class AuthenticationResponse
:access_token,
:refresh_token,
:authentication_method,
:sealed_session
:sealed_session,
:oauth_tokens

# rubocop:disable Metrics/AbcSize
def initialize(authentication_response_json, session = nil)
Expand All @@ -27,6 +28,7 @@ def initialize(authentication_response_json, session = nil)
reason: impersonator_json[:reason],)
end
@authentication_method = json[:authentication_method]
@oauth_tokens = json[:oauth_tokens] ? WorkOS::OAuthTokens.new(json[:oauth_tokens].to_json) : nil
@sealed_session =
if session && session[:seal_session]
WorkOS::Session.seal_data({
Expand All @@ -49,6 +51,7 @@ def to_json(*)
refresh_token: refresh_token,
authentication_method: authentication_method,
sealed_session: sealed_session,
oauth_tokens: oauth_tokens&.to_json,
}
end
end
Expand Down
29 changes: 29 additions & 0 deletions lib/workos/oauth_tokens.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module WorkOS
# The OAuthTokens class represents the third party provider OAuth tokens returned in the authentication response.
# This class is not meant to be instantiated in user space, and is instantiated internally but exposed.
class OAuthTokens
include HashProvider

attr_accessor :access_token, :refresh_token, :scopes, :expires_at

def initialize(json)
hash = JSON.parse(json, symbolize_names: true)

@access_token = hash[:access_token]
@refresh_token = hash[:refresh_token]
@scopes = hash[:scopes]
@expires_at = hash[:expires_at]
end

def to_json(*)
{
access_token: access_token,
refresh_token: refresh_token,
scopes: scopes,
expires_at: expires_at,
}
end
end
end
5 changes: 4 additions & 1 deletion lib/workos/user_management.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def load_sealed_session(client_id:, session_data:, cookie_password:)
# field of the IdP sign-in page for the user, if you know their username ahead of time.
# @param [String] domain_hint Can be used to pre-fill the domain field when
# initiating authentication with Microsoft OAuth, or with a GoogleSAML connection type.
# @param [Array<String>] provider_scopes An array of additional OAuth scopes to request from the provider.
# @example
# WorkOS::UserManagement.authorization_url(
# connection_id: 'conn_123',
Expand All @@ -96,7 +97,8 @@ def authorization_url(
provider: nil,
connection_id: nil,
organization_id: nil,
state: ''
state: '',
provider_scopes: nil
)

validate_authorization_url_arguments(
Expand All @@ -115,6 +117,7 @@ def authorization_url(
provider: provider,
connection_id: connection_id,
organization_id: organization_id,
provider_scopes: provider_scopes,
}.compact)

"https://#{WorkOS.config.api_hostname}/user_management/authorize?#{query}"
Expand Down
60 changes: 60 additions & 0 deletions spec/lib/workos/user_management_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@
'edit%22%7D&provider=authkit',
)
end

context 'with provider_scopes' do
it 'returns a valid authorization URL that includes provider_scopes' do
url = WorkOS::UserManagement.authorization_url(
provider: 'GoogleOAuth',
provider_scopes: %w[custom-scope-1 custom-scope-2],
client_id: 'workos-proj-123',
redirect_uri: 'foo.com/auth/callback',
state: {
next_page: '/dashboard/edit',
}.to_s,
)

expect(url).to eq(
'https://api.workos.com/user_management/authorize?' \
'client_id=workos-proj-123' \
'&redirect_uri=foo.com%2Fauth%2Fcallback' \
'&response_type=code' \
'&state=%7B%3Anext_page%3D%3E%22%2Fdashboard%2F' \
'edit%22%7D' \
'&provider=GoogleOAuth' \
'&provider_scopes=custom-scope-1' \
'&provider_scopes=custom-scope-2',
)
end
end
end

context 'with a connection selector' do
Expand Down Expand Up @@ -453,6 +479,40 @@
end
end

context 'when oauth_tokens is present in the api response' do
it 'returns an oauth_tokens object' do
VCR.use_cassette('user_management/authenticate_with_code/valid_with_oauth_tokens') do
authentication_response = WorkOS::UserManagement.authenticate_with_code(
code: '01H93ZZHA0JBHFJH9RR11S83YN',
client_id: 'client_123',
ip_address: '200.240.210.16',
user_agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/108.0.0.0 Safari/537.36',
)

expect(authentication_response.oauth_tokens).to be_a(WorkOS::OAuthTokens)
expect(authentication_response.oauth_tokens.access_token).to eq('oauth_access_token')
expect(authentication_response.oauth_tokens.refresh_token).to eq('oauth_refresh_token')
expect(authentication_response.oauth_tokens.scopes).to eq(%w[read write])
expect(authentication_response.oauth_tokens.expires_at).to eq(1_234_567_890)
end
end
end

context 'when oauth_tokens is not present in the api response' do
it 'returns nil oauth_tokens' do
VCR.use_cassette('user_management/authenticate_with_code/valid') do
authentication_response = WorkOS::UserManagement.authenticate_with_code(
code: '01H93ZZHA0JBHFJH9RR11S83YN',
client_id: 'client_123',
ip_address: '200.240.210.16',
user_agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Chrome/108.0.0.0 Safari/537.36',
)

expect(authentication_response.oauth_tokens).to be_nil
end
end
end

context 'when the user is being impersonated' do
it 'contains the impersonator metadata' do
VCR.use_cassette('user_management/authenticate_with_code/valid_with_impersonator') do
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading