workos 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +49 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +10 -0
  5. data/.ruby-version +1 -0
  6. data/.semaphore/rubygems.yml +19 -0
  7. data/.semaphore/semaphore.yml +63 -0
  8. data/CODEOWNERS +1 -0
  9. data/Gemfile +5 -0
  10. data/Gemfile.lock +84 -0
  11. data/LICENSE +21 -0
  12. data/README.md +21 -0
  13. data/bin/build +3 -0
  14. data/bin/console +3 -0
  15. data/bin/docs +5 -0
  16. data/bin/publish +3 -0
  17. data/codecov.yml +11 -0
  18. data/docs/WorkOS.html +328 -0
  19. data/docs/WorkOS/Base.html +281 -0
  20. data/docs/WorkOS/Profile.html +725 -0
  21. data/docs/WorkOS/RequestError.html +135 -0
  22. data/docs/WorkOS/SSO.html +511 -0
  23. data/docs/WorkOS/Types.html +129 -0
  24. data/docs/WorkOS/Types/ProfileStruct.html +135 -0
  25. data/docs/class_list.html +51 -0
  26. data/docs/css/common.css +1 -0
  27. data/docs/css/full_list.css +58 -0
  28. data/docs/css/style.css +496 -0
  29. data/docs/file.README.html +92 -0
  30. data/docs/file_list.html +56 -0
  31. data/docs/frames.html +17 -0
  32. data/docs/index.html +189 -0
  33. data/docs/js/app.js +303 -0
  34. data/docs/js/full_list.js +216 -0
  35. data/docs/js/jquery.js +4 -0
  36. data/docs/method_list.html +171 -0
  37. data/docs/top-level-namespace.html +110 -0
  38. data/lib/workos.rb +36 -0
  39. data/lib/workos/base.rb +18 -0
  40. data/lib/workos/profile.rb +53 -0
  41. data/lib/workos/request_error.rb +5 -0
  42. data/lib/workos/sso.rb +142 -0
  43. data/lib/workos/types.rb +11 -0
  44. data/lib/workos/types/profile_struct.rb +18 -0
  45. data/lib/workos/version.rb +7 -0
  46. data/sorbet/config +2 -0
  47. data/sorbet/rbi/gems/addressable.rbi +198 -0
  48. data/sorbet/rbi/gems/ast.rbi +47 -0
  49. data/sorbet/rbi/gems/codecov.rbi +19 -0
  50. data/sorbet/rbi/gems/crack.rbi +47 -0
  51. data/sorbet/rbi/gems/docile.rbi +31 -0
  52. data/sorbet/rbi/gems/hashdiff.rbi +65 -0
  53. data/sorbet/rbi/gems/jaro_winkler.rbi +14 -0
  54. data/sorbet/rbi/gems/parallel.rbi +81 -0
  55. data/sorbet/rbi/gems/parser.rbi +856 -0
  56. data/sorbet/rbi/gems/public_suffix.rbi +102 -0
  57. data/sorbet/rbi/gems/rack.rbi +103 -0
  58. data/sorbet/rbi/gems/rainbow.rbi +117 -0
  59. data/sorbet/rbi/gems/rake.rbi +632 -0
  60. data/sorbet/rbi/gems/rspec-core.rbi +1661 -0
  61. data/sorbet/rbi/gems/rspec-expectations.rbi +388 -0
  62. data/sorbet/rbi/gems/rspec-mocks.rbi +823 -0
  63. data/sorbet/rbi/gems/rspec-support.rbi +266 -0
  64. data/sorbet/rbi/gems/rspec.rbi +14 -0
  65. data/sorbet/rbi/gems/rubocop.rbi +7083 -0
  66. data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
  67. data/sorbet/rbi/gems/simplecov-html.rbi +30 -0
  68. data/sorbet/rbi/gems/simplecov.rbi +225 -0
  69. data/sorbet/rbi/gems/unicode-display_width.rbi +16 -0
  70. data/sorbet/rbi/gems/webmock.rbi +526 -0
  71. data/sorbet/rbi/hidden-definitions/errors.txt +6061 -0
  72. data/sorbet/rbi/hidden-definitions/hidden.rbi +10087 -0
  73. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
  74. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +254 -0
  75. data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
  76. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
  77. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
  78. data/sorbet/rbi/todo.rbi +7 -0
  79. data/spec/lib/workos/sso_spec.rb +95 -0
  80. data/spec/spec_helper.rb +32 -0
  81. data/spec/support/profile.txt +1 -0
  82. data/workos.gemspec +32 -0
  83. metadata +261 -0
data/lib/workos.rb ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ require 'workos/version'
6
+ require 'sorbet-runtime'
7
+
8
+ # Use the WorkOS module to authenticate your
9
+ # requests to the WorkOS API. The gem will read
10
+ # your API key automatically from the ENV var `WORKOS_KEY`.
11
+ # Alternatively, you can set the key yourself with
12
+ # `WorkOS.key = [your api key]` somewhere in the load path of
13
+ # your application, such as an initializer.
14
+ module WorkOS
15
+ API_HOSTNAME = 'api.workos.com'
16
+
17
+ def self.key=(value)
18
+ Base.key = value
19
+ end
20
+
21
+ def self.key
22
+ Base.key
23
+ end
24
+
25
+ def self.key!
26
+ key || raise('WorkOS.key not set')
27
+ end
28
+
29
+ autoload :Types, 'workos/types'
30
+ autoload :Base, 'workos/base'
31
+ autoload :Profile, 'workos/profile'
32
+ autoload :RequestError, 'workos/request_error'
33
+ autoload :SSO, 'workos/sso'
34
+
35
+ WorkOS.key = ENV['WORKOS_KEY'] unless ENV['WORKOS_KEY'].nil?
36
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+ # rubocop:disable Style/Documentation
4
+
5
+
6
+ module WorkOS
7
+ class Base
8
+ attr_accessor :key
9
+ class << self
10
+ attr_writer :key
11
+ end
12
+
13
+ class << self
14
+ attr_reader :key
15
+ end
16
+ end
17
+ end
18
+ # rubocop:enable Style/Documentation
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ require 'json'
6
+
7
+ module WorkOS
8
+ # The Profile class provides a lighweight wrapper around
9
+ # a normalized response from the various IDPs WorkOS
10
+ # supports as part of the SSO integration. This class
11
+ # is not meant ot be instantiated in user space, and
12
+ # is instantiated internally but exposed.
13
+ class Profile
14
+ extend T::Sig
15
+
16
+ sig { returns(String) }
17
+ attr_accessor :id, :email, :first_name, :last_name,
18
+ :connection_type, :idp_id
19
+
20
+ sig { params(profile_json: String).void }
21
+ def initialize(profile_json)
22
+ raw = parse_json(profile_json)
23
+
24
+ @id = T.let(raw.id, String)
25
+ @email = T.let(raw.email, String)
26
+ @first_name = T.let(raw.first_name, String)
27
+ @last_name = T.let(raw.last_name, String)
28
+ @connection_type = T.let(raw.connection_type, String)
29
+ @idp_id = T.let(raw.idp_id, String)
30
+ end
31
+
32
+ sig { returns(String) }
33
+ def full_name
34
+ [first_name, last_name].compact.join(' ')
35
+ end
36
+
37
+ private
38
+
39
+ sig { params(json_string: String).returns(WorkOS::Types::ProfileStruct) }
40
+ def parse_json(json_string)
41
+ hash = JSON.parse(json_string, symbolize_names: true)
42
+
43
+ WorkOS::Types::ProfileStruct.new(
44
+ id: hash[:profile][:id],
45
+ email: hash[:profile][:email],
46
+ first_name: hash[:profile][:first_name],
47
+ last_name: hash[:profile][:last_name],
48
+ connection_type: hash[:profile][:connection_type],
49
+ idp_id: hash[:profile][:idp_id],
50
+ )
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ module WorkOS
3
+ # Raised when an API request is unsuccessful
4
+ class RequestError < StandardError; end
5
+ end
data/lib/workos/sso.rb ADDED
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ require 'net/http'
6
+ require 'rack/utils'
7
+ require 'uri'
8
+
9
+ module WorkOS
10
+ # The SSO module provides convenience methods for working with the WorkOS
11
+ # SSO service. You'll need a valid API key, a project ID, and to have
12
+ # created an SSO connection on your WorkOS dashboard.
13
+ #
14
+ # @see https://dashboard.workos.com/docs/sso/what-is-sso
15
+ module SSO
16
+ class << self
17
+ extend T::Sig
18
+
19
+ sig do
20
+ params(
21
+ domain: String,
22
+ project_id: String,
23
+ redirect_uri: String,
24
+ state: Hash,
25
+ ).returns(String)
26
+ end
27
+
28
+ # Generate an Oauth2 authorization URL where your users will
29
+ # authenticate using the configured SSO Identity Provider.
30
+ #
31
+ # @param [String] domain The domain for the relevant SSO Connection
32
+ # configured on your WorkOS dashboard
33
+ # @param [String] project_id The WorkOS project ID for the project
34
+ # where you've configured your SSO connection
35
+ # @param [String] redirect_uri The URI where users are directed
36
+ # after completing the authentication step. Must match a
37
+ # configured redirect URI on your WorkOS dashboard
38
+ # @param [Hash] state An aribtrary state object
39
+ # that is preserved and available to the client in the response.
40
+ # @example
41
+ # WorkOS::SSO.authorization_url(
42
+ # domain: 'acme.com',
43
+ # project_id: 'project_01DG5TGK363GRVXP3ZS40WNGEZ',
44
+ # redirect_uri: 'https://workos.com/callback',
45
+ # state: {
46
+ # next_page: '/docs'
47
+ # }
48
+ # )
49
+ #
50
+ # => "https://api.workos.com/sso/authorize?domain=acme.com" \
51
+ # "&client_id=project_01DG5TGK363GRVXP3ZS40WNGEZ" \
52
+ # "&redirect_uri=https%3A%2F%2Fworkos.com%2Fcallback&" \
53
+ # "response_type=code&state=%7B%3Anext_page%3D%3E%22%2Fdocs%22%7D"
54
+ #
55
+ # @return [String]
56
+ def authorization_url(domain:, project_id:, redirect_uri:, state: {})
57
+ query = Rack::Utils.build_query(
58
+ domain: domain,
59
+ client_id: project_id,
60
+ redirect_uri: redirect_uri,
61
+ response_type: 'code',
62
+ state: state,
63
+ )
64
+
65
+ "https://#{WorkOS::API_HOSTNAME}/sso/authorize?#{query}"
66
+ end
67
+
68
+ sig do
69
+ params(
70
+ code: String,
71
+ project_id: String,
72
+ redirect_uri: String,
73
+ ).returns(WorkOS::Profile)
74
+ end
75
+
76
+ # Fetch the profile details for the authenticated SSO user.
77
+ #
78
+ # @param [String] code The authorization code provided in the callback URL
79
+ # @param [String] project_id The WorkOS project ID for the project
80
+ # where you've configured your SSO connection
81
+ # @param [String] redirect_uri The URI where the user was directed
82
+ # after completing the authentication step.
83
+ #
84
+ # @example
85
+ # WorkOS::SSO.profile(
86
+ # code: 'acme.com',
87
+ # project_id: 'project_01DG5TGK363GRVXP3ZS40WNGEZ',
88
+ # redirect_uri: 'https://workos.com/callback',
89
+ # )
90
+ # => #<WorkOS::Profile:0x00007fb6e4193d20
91
+ # @id="prof_01DRA1XNSJDZ19A31F183ECQW5",
92
+ # @email="demo@workos-okta.com",
93
+ # @first_name="WorkOS",
94
+ # @connection_type="OktaSAML",
95
+ # @last_name="Demo",
96
+ # @idp_id="00u1klkowm8EGah2H357",
97
+ # @access_token="01DVX6QBS3EG6FHY2ESAA5Q65X"
98
+ # >
99
+ #
100
+ # @return [WorkOS::Profile]
101
+ def profile(code:, project_id:, redirect_uri:)
102
+ query = Rack::Utils.build_query(
103
+ client_id: project_id,
104
+ client_secret: WorkOS.key!,
105
+ redirect_uri: redirect_uri,
106
+ grant_type: 'authorization_code',
107
+ code: code,
108
+ )
109
+
110
+ response = client.request(Net::HTTP::Post.new("/sso/token?#{query}"))
111
+ check_and_raise_error(response: response)
112
+
113
+ WorkOS::Profile.new(response.body)
114
+ end
115
+
116
+ private
117
+
118
+ sig { returns(Net::HTTP) }
119
+ def client
120
+ return @client if defined?(@client)
121
+
122
+ @client = Net::HTTP.new(WorkOS::API_HOSTNAME, 443)
123
+ @client.use_ssl = true
124
+
125
+ @client
126
+ end
127
+
128
+ sig { params(response: Net::HTTPResponse).void }
129
+ def check_and_raise_error(response:)
130
+ return if response.is_a? Net::HTTPOK
131
+
132
+ begin
133
+ message = JSON.parse(response.body)['message']
134
+ rescue StandardError
135
+ message = 'Something went wrong'
136
+ end
137
+
138
+ raise WorkOS::RequestError, message
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ module WorkOS
6
+ # WorkOS believes strongly in typed languages,
7
+ # so we're using Sorbet throughout this Ruby gem.
8
+ module Types
9
+ require_relative 'types/profile_struct'
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+
5
+ module WorkOS
6
+ module Types
7
+ # The ProfileStruct acts as a typed interface
8
+ # for the Profile class
9
+ class ProfileStruct < T::Struct
10
+ const :id, String
11
+ const :email, String
12
+ const :first_name, String
13
+ const :last_name, String
14
+ const :connection_type, String
15
+ const :idp_id, String
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+
5
+ module WorkOS
6
+ VERSION = '0.0.1'
7
+ end
data/sorbet/config ADDED
@@ -0,0 +1,2 @@
1
+ --dir
2
+ .
@@ -0,0 +1,198 @@
1
+ # This file is autogenerated. Do not edit it by hand. Regenerate it with:
2
+ # srb rbi gems
3
+
4
+ # typed: strong
5
+ #
6
+ # If you would like to make changes to this file, great! Please create the gem's shim here:
7
+ #
8
+ # https://github.com/sorbet/sorbet-typed/new/master?filename=lib/addressable/all/addressable.rbi
9
+ #
10
+ # addressable-2.7.0
11
+ module Addressable
12
+ end
13
+ module Addressable::VERSION
14
+ end
15
+ module Addressable::IDNA
16
+ def self.lookup_unicode_combining_class(codepoint); end
17
+ def self.lookup_unicode_compatibility(codepoint); end
18
+ def self.lookup_unicode_composition(unpacked); end
19
+ def self.lookup_unicode_lowercase(codepoint); end
20
+ def self.punycode_adapt(delta, numpoints, firsttime); end
21
+ def self.punycode_basic?(codepoint); end
22
+ def self.punycode_decode(punycode); end
23
+ def self.punycode_decode_digit(codepoint); end
24
+ def self.punycode_delimiter?(codepoint); end
25
+ def self.punycode_encode(unicode); end
26
+ def self.punycode_encode_digit(d); end
27
+ def self.to_ascii(input); end
28
+ def self.to_unicode(input); end
29
+ def self.unicode_compose(unpacked); end
30
+ def self.unicode_compose_pair(ch_one, ch_two); end
31
+ def self.unicode_decompose(unpacked); end
32
+ def self.unicode_decompose_hangul(codepoint); end
33
+ def self.unicode_downcase(input); end
34
+ def self.unicode_normalize_kc(input); end
35
+ def self.unicode_sort_canonical(unpacked); end
36
+ end
37
+ class Addressable::IDNA::PunycodeBadInput < StandardError
38
+ end
39
+ class Addressable::IDNA::PunycodeBigOutput < StandardError
40
+ end
41
+ class Addressable::IDNA::PunycodeOverflow < StandardError
42
+ end
43
+ class Addressable::URI
44
+ def +(uri); end
45
+ def ==(uri); end
46
+ def ===(uri); end
47
+ def absolute?; end
48
+ def authority; end
49
+ def authority=(new_authority); end
50
+ def basename; end
51
+ def default_port; end
52
+ def defer_validation; end
53
+ def display_uri; end
54
+ def domain; end
55
+ def dup; end
56
+ def empty?; end
57
+ def eql?(uri); end
58
+ def extname; end
59
+ def fragment; end
60
+ def fragment=(new_fragment); end
61
+ def freeze; end
62
+ def hash; end
63
+ def host; end
64
+ def host=(new_host); end
65
+ def hostname; end
66
+ def hostname=(new_hostname); end
67
+ def inferred_port; end
68
+ def initialize(options = nil); end
69
+ def inspect; end
70
+ def ip_based?; end
71
+ def join!(uri); end
72
+ def join(uri); end
73
+ def merge!(uri); end
74
+ def merge(hash); end
75
+ def normalize!; end
76
+ def normalize; end
77
+ def normalized_authority; end
78
+ def normalized_fragment; end
79
+ def normalized_host; end
80
+ def normalized_password; end
81
+ def normalized_path; end
82
+ def normalized_port; end
83
+ def normalized_query(*flags); end
84
+ def normalized_scheme; end
85
+ def normalized_site; end
86
+ def normalized_user; end
87
+ def normalized_userinfo; end
88
+ def omit!(*components); end
89
+ def omit(*components); end
90
+ def origin; end
91
+ def origin=(new_origin); end
92
+ def password; end
93
+ def password=(new_password); end
94
+ def path; end
95
+ def path=(new_path); end
96
+ def port; end
97
+ def port=(new_port); end
98
+ def query; end
99
+ def query=(new_query); end
100
+ def query_values(return_type = nil); end
101
+ def query_values=(new_query_values); end
102
+ def relative?; end
103
+ def remove_composite_values; end
104
+ def replace_self(uri); end
105
+ def request_uri; end
106
+ def request_uri=(new_request_uri); end
107
+ def route_from(uri); end
108
+ def route_to(uri); end
109
+ def scheme; end
110
+ def scheme=(new_scheme); end
111
+ def self.convert_path(path); end
112
+ def self.encode(uri, return_type = nil); end
113
+ def self.encode_component(component, character_class = nil, upcase_encoded = nil); end
114
+ def self.escape(uri, return_type = nil); end
115
+ def self.form_encode(form_values, sort = nil); end
116
+ def self.form_unencode(encoded_value); end
117
+ def self.heuristic_parse(uri, hints = nil); end
118
+ def self.ip_based_schemes; end
119
+ def self.join(*uris); end
120
+ def self.normalize_component(component, character_class = nil, leave_encoded = nil); end
121
+ def self.normalize_path(path); end
122
+ def self.normalized_encode(uri, return_type = nil); end
123
+ def self.parse(uri); end
124
+ def self.port_mapping; end
125
+ def self.unencode(uri, return_type = nil, leave_encoded = nil); end
126
+ def self.unencode_component(uri, return_type = nil, leave_encoded = nil); end
127
+ def self.unescape(uri, return_type = nil, leave_encoded = nil); end
128
+ def self.unescape_component(uri, return_type = nil, leave_encoded = nil); end
129
+ def site; end
130
+ def site=(new_site); end
131
+ def split_path(path); end
132
+ def tld; end
133
+ def tld=(new_tld); end
134
+ def to_hash; end
135
+ def to_s; end
136
+ def to_str; end
137
+ def user; end
138
+ def user=(new_user); end
139
+ def userinfo; end
140
+ def userinfo=(new_userinfo); end
141
+ def validate; end
142
+ end
143
+ class Addressable::URI::InvalidURIError < StandardError
144
+ end
145
+ module Addressable::URI::CharacterClasses
146
+ end
147
+ class Addressable::Template
148
+ def ==(template); end
149
+ def eql?(template); end
150
+ def expand(mapping, processor = nil, normalize_values = nil); end
151
+ def extract(uri, processor = nil); end
152
+ def freeze; end
153
+ def generate(params = nil, recall = nil, options = nil); end
154
+ def initialize(pattern); end
155
+ def inspect; end
156
+ def join_values(operator, return_value); end
157
+ def keys; end
158
+ def match(uri, processor = nil); end
159
+ def named_captures; end
160
+ def names; end
161
+ def normalize_keys(mapping); end
162
+ def normalize_value(value); end
163
+ def ordered_variable_defaults; end
164
+ def parse_template_pattern(pattern, processor = nil); end
165
+ def partial_expand(mapping, processor = nil, normalize_values = nil); end
166
+ def pattern; end
167
+ def source; end
168
+ def to_regexp; end
169
+ def transform_capture(mapping, capture, processor = nil, normalize_values = nil); end
170
+ def transform_partial_capture(mapping, capture, processor = nil, normalize_values = nil); end
171
+ def variable_defaults; end
172
+ def variables; end
173
+ end
174
+ class Addressable::Template::InvalidTemplateValueError < StandardError
175
+ end
176
+ class Addressable::Template::InvalidTemplateOperatorError < StandardError
177
+ end
178
+ class Addressable::Template::TemplateOperatorAbortedError < StandardError
179
+ end
180
+ class Addressable::Template::MatchData
181
+ def [](key, len = nil); end
182
+ def captures; end
183
+ def initialize(uri, template, mapping); end
184
+ def inspect; end
185
+ def keys; end
186
+ def mapping; end
187
+ def names; end
188
+ def post_match; end
189
+ def pre_match; end
190
+ def string; end
191
+ def template; end
192
+ def to_a; end
193
+ def to_s; end
194
+ def uri; end
195
+ def values; end
196
+ def values_at(*indexes); end
197
+ def variables; end
198
+ end