workos 0.6.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +5 -0
- data/Gemfile.lock +2 -2
- data/README.md +108 -44
- data/lib/workos.rb +1 -0
- data/lib/workos/audit_trail.rb +2 -2
- data/lib/workos/client.rb +13 -7
- data/lib/workos/passwordless.rb +84 -0
- data/lib/workos/portal.rb +5 -1
- data/lib/workos/profile.rb +8 -4
- data/lib/workos/types.rb +1 -0
- data/lib/workos/types/passwordless_session_struct.rb +15 -0
- data/lib/workos/types/profile_struct.rb +1 -0
- data/lib/workos/version.rb +1 -1
- data/spec/lib/workos/passwordless_spec.rb +83 -0
- data/spec/lib/workos/sso_spec.rb +1 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml +73 -0
- data/spec/support/profile.txt +1 -1
- metadata +15 -3
- data/CODEOWNERS +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa4a3df1bd438866ffdd87bea0cc2b4c0155a1a72073f22265335845711e0b07
|
4
|
+
data.tar.gz: e0330ab1c70938e2092f76102aa62559574a20dce5e9b6d3443c1c80792c82b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1e6953bd43faa02ba0f0a7e8e6fe192a05b68d6a5694f6a448a6da7d419a9adb409a7512b3a7a95caf8ba630cb56d9eb0a52cb5d32f7afc0205dcc4e7b80434
|
7
|
+
data.tar.gz: 3ff44f67cbe7f1fa8151d08579ee1e997d30d222b0c72d666a5d0cca21817a2201c97a1780aefe2965dcb20f42640cce71827e6092375eff87fbb114eed2cc0e
|
data/.github/CODEOWNERS
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
workos (0.
|
4
|
+
workos (0.9.1)
|
5
5
|
sorbet-runtime (~> 0.5)
|
6
6
|
|
7
7
|
GEM
|
@@ -54,7 +54,7 @@ GEM
|
|
54
54
|
simplecov-html (0.12.2)
|
55
55
|
sorbet (0.5.5560)
|
56
56
|
sorbet-static (= 0.5.5560)
|
57
|
-
sorbet-runtime (0.5.
|
57
|
+
sorbet-runtime (0.5.5943)
|
58
58
|
sorbet-static (0.5.5560-universal-darwin-14)
|
59
59
|
unicode-display_width (1.6.0)
|
60
60
|
vcr (5.0.0)
|
data/README.md
CHANGED
@@ -42,50 +42,9 @@ Alternatively, you may set the key yourself, such as in an initializer in your a
|
|
42
42
|
WorkOS.key = '[your api key]'
|
43
43
|
```
|
44
44
|
|
45
|
-
## The Audit Trail Module
|
46
|
-
|
47
|
-
The Audit Trail Module provides methods for creating Audit Trail events on
|
48
|
-
WorkOS.
|
49
|
-
|
50
|
-
See our [Audit Trail
|
51
|
-
Overview](https://docs.workos.com/audit-trail/overview) for
|
52
|
-
more information.
|
53
|
-
|
54
|
-
```ruby
|
55
|
-
payload = {
|
56
|
-
group: 'Foo Corp',
|
57
|
-
location: '127.0.0.1',
|
58
|
-
action: 'user.created',
|
59
|
-
action_type: 'C',
|
60
|
-
actor_name: 'Foo',
|
61
|
-
actor_id: 'user_12345',
|
62
|
-
target_name: 'Bar',
|
63
|
-
target_id: 'user_67890',
|
64
|
-
occurred_at: '2020-01-10T15:30:00-05:00',
|
65
|
-
metadata: {
|
66
|
-
source: 'Email',
|
67
|
-
}
|
68
|
-
}
|
69
|
-
|
70
|
-
WorkOS::AuditTrail.create_event(event: payload)
|
71
|
-
```
|
72
|
-
|
73
|
-
### Idempotency
|
74
|
-
|
75
|
-
To perform an idempotent request, provide an additional idempotency_key
|
76
|
-
parameter to the `create_event` options.
|
77
|
-
|
78
|
-
```ruby
|
79
|
-
WorkOS::AuditTrail.create_event(event: payload, idempotency_key: 'key123456')
|
80
|
-
```
|
81
|
-
|
82
|
-
See our [API
|
83
|
-
Reference](https://docs.workos.com/audit-trail/api-reference#idempotency)
|
84
|
-
for more information on idempotency keys.
|
85
|
-
|
86
45
|
## The SSO Module
|
87
46
|
|
88
|
-
The SSO Module provides
|
47
|
+
The SSO Module provides convenient methods for authenticating a Single Sign On (SSO) user via WorkOS. WorkOS SSO follows the OAuth 2.0 specification.
|
89
48
|
|
90
49
|
First, you'll direct your SSO users to an `authorization_url`. They will sign in to their SSO account with their Identity Provider, and be redirected to a
|
91
50
|
callback URL that you set in your WorkOS Dashboard. The user will be redirected with a `code` URL parameter, which you can then exchange for a WorkOS::Profile
|
@@ -137,7 +96,7 @@ the user to your callback URL with a `code` parameter. You'll use `WorkOS::SSO.p
|
|
137
96
|
code for a `WorkOS::Profile`.
|
138
97
|
|
139
98
|
```ruby
|
140
|
-
WorkOS::SSO.profile(code:, project_id:)
|
99
|
+
WorkOS::SSO.profile(code:, project_id:)
|
141
100
|
```
|
142
101
|
|
143
102
|
> Fetch a WorkOS::Profile for an authorized user.
|
@@ -154,6 +113,7 @@ This method will return an instance of a `WorkOS::Profile` with the following at
|
|
154
113
|
@id="prof_01DRA1XNSJDZ19A31F183ECQW5",
|
155
114
|
@email="demo@workos-okta.com",
|
156
115
|
@first_name="WorkOS",
|
116
|
+
@connection_id="conn_01EMH8WAK20T42N2NBMNBCYHAG",
|
157
117
|
@connection_type="OktaSAML",
|
158
118
|
@last_name="Demo",
|
159
119
|
@idp_id="00u1klkowm8EGah2H357",
|
@@ -167,7 +127,7 @@ This method will return an instance of a `WorkOS::Profile` with the following at
|
|
167
127
|
>
|
168
128
|
```
|
169
129
|
|
170
|
-
Our
|
130
|
+
Our Sinatra app can be extended to use this method:
|
171
131
|
|
172
132
|
```ruby
|
173
133
|
DOMAIN = 'example.com'
|
@@ -197,3 +157,107 @@ end
|
|
197
157
|
```
|
198
158
|
|
199
159
|
Given the `WorkOS::Profile`, you can now sign the user in according to your own authentication setup.
|
160
|
+
|
161
|
+
## The Magic Link Module
|
162
|
+
|
163
|
+
The Magic Link Module provides methods for authenticating a Passwordless user via WorkOS.
|
164
|
+
|
165
|
+
First, you'll create a Passwordless Session for a Magic Link connection.
|
166
|
+
Then, using the session ID, you'll email a user the Magic Link confirmation URL.
|
167
|
+
The user can then click on that link to be authenticated to your application.
|
168
|
+
|
169
|
+
> Create a Passwordless Session for a Magic Link Connection.
|
170
|
+
|
171
|
+
`WorkOS::Passwordless.create_session` accepts four arguments:
|
172
|
+
|
173
|
+
- `email` (string) - the email of the user to authenticate.
|
174
|
+
- `type` (string) - The type of Passwordless Session to create. Currently, the only supported value is `MagicLink`.
|
175
|
+
- `state` (optional, string) - Optional parameter that a Developer can choose to include in their authorization URL. If included, then the redirect URI received from WorkOS will contain the exact `state` that was passed in the authorization URL.
|
176
|
+
- `redirect_uri` (string) - a callback URL where your application redirects the user-agent after an authorization code is granted (ex. `workos.dev/callback`). This must match one of your configured callback URLs for the associated project on your WorkOS dashboard.
|
177
|
+
|
178
|
+
This method will return a Passwordless Session object, containing the following attributes:
|
179
|
+
|
180
|
+
- `id` (string) - the unique ID of the session.
|
181
|
+
- `email` (string) - the email address of the user for the session.
|
182
|
+
- `expires_at` (date) - the ISO-8601 datetime at which the session expires.
|
183
|
+
- `link` (string) - the link for the user to authenticate with. You can use this link to send a custom email to the user, or send an email using the `WorkOS::Passwordless.send_session` method, described below.
|
184
|
+
|
185
|
+
> Email a user the Magic Link confirmation URL.
|
186
|
+
|
187
|
+
`WorkOS::Passwordless.send_session` accepts one argument:
|
188
|
+
|
189
|
+
- `id` (string) - the unique identifier of the Passwordless Session to send an email for.
|
190
|
+
|
191
|
+
This method will return a boolean confirming the Magic Link was sent.
|
192
|
+
|
193
|
+
> Example with Sinatra application
|
194
|
+
|
195
|
+
Our Sinatra app can be altered to use Magic Link:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
PROJECT_ID = '{projectId}'
|
199
|
+
REDIRECT_URI = 'http://localhost:4567/callback'
|
200
|
+
|
201
|
+
post '/passwordless-auth' do
|
202
|
+
session = WorkOS::Passwordless.create_session(
|
203
|
+
email: params[:email],
|
204
|
+
type: 'MagicLink',
|
205
|
+
redirect_uri: REDIRECT_URI
|
206
|
+
)
|
207
|
+
WorkOS::Passwordless.send_session(session.id)
|
208
|
+
|
209
|
+
redirect '/check-email'
|
210
|
+
end
|
211
|
+
|
212
|
+
get '/callback' do
|
213
|
+
profile = WorkOS::SSO.profile(
|
214
|
+
code: params['code'],
|
215
|
+
project_id: PROJECT_ID,
|
216
|
+
)
|
217
|
+
|
218
|
+
session[:user] = profile.to_json
|
219
|
+
|
220
|
+
redirect '/'
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
## The Audit Trail Module
|
225
|
+
|
226
|
+
The Audit Trail Module provides methods for creating Audit Trail events on
|
227
|
+
WorkOS.
|
228
|
+
|
229
|
+
See our [Audit Trail
|
230
|
+
Overview](https://docs.workos.com/audit-trail/overview) for
|
231
|
+
more information.
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
payload = {
|
235
|
+
group: 'Foo Corp',
|
236
|
+
location: '127.0.0.1',
|
237
|
+
action: 'user.created',
|
238
|
+
action_type: 'C',
|
239
|
+
actor_name: 'Foo',
|
240
|
+
actor_id: 'user_12345',
|
241
|
+
target_name: 'Bar',
|
242
|
+
target_id: 'user_67890',
|
243
|
+
occurred_at: '2020-01-10T15:30:00-05:00',
|
244
|
+
metadata: {
|
245
|
+
source: 'Email',
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
WorkOS::AuditTrail.create_event(event: payload)
|
250
|
+
```
|
251
|
+
|
252
|
+
### Idempotency
|
253
|
+
|
254
|
+
To perform an idempotent request, provide an additional idempotency_key
|
255
|
+
parameter to the `create_event` options.
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
WorkOS::AuditTrail.create_event(event: payload, idempotency_key: 'key123456')
|
259
|
+
```
|
260
|
+
|
261
|
+
See our [API
|
262
|
+
Reference](https://docs.workos.com/audit-trail/api-reference#idempotency)
|
263
|
+
for more information on idempotency keys.
|
data/lib/workos.rb
CHANGED
@@ -32,6 +32,7 @@ module WorkOS
|
|
32
32
|
autoload :Connection, 'workos/connection'
|
33
33
|
autoload :DirectorySync, 'workos/directory_sync'
|
34
34
|
autoload :Organization, 'workos/organization'
|
35
|
+
autoload :Passwordless, 'workos/passwordless'
|
35
36
|
autoload :Portal, 'workos/portal'
|
36
37
|
autoload :Profile, 'workos/profile'
|
37
38
|
autoload :SSO, 'workos/sso'
|
data/lib/workos/audit_trail.rb
CHANGED
@@ -83,8 +83,8 @@ module WorkOS
|
|
83
83
|
# event occurred at or after
|
84
84
|
# @option options [String] occurred_at_lt ISO-8601 datetime of when an
|
85
85
|
# event occurred before
|
86
|
-
# @option options [String] ISO-8601 datetime of when an
|
87
|
-
# or before
|
86
|
+
# @option options [String] occurred_at_lte ISO-8601 datetime of when an
|
87
|
+
# event occured at or before
|
88
88
|
# @option options [String] search Keyword search
|
89
89
|
#
|
90
90
|
# @return [Array<Hash>]
|
data/lib/workos/client.rb
CHANGED
@@ -81,8 +81,7 @@ module WorkOS
|
|
81
81
|
].join('; ')
|
82
82
|
end
|
83
83
|
|
84
|
-
|
85
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
84
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
86
85
|
sig { params(response: ::T.untyped).void }
|
87
86
|
def handle_error_response(response:)
|
88
87
|
http_status = response.code.to_i
|
@@ -108,11 +107,10 @@ module WorkOS
|
|
108
107
|
request_id: response['x-request-id'],
|
109
108
|
)
|
110
109
|
when 422
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
message = json['message']
|
111
|
+
errors = extract_error(json['errors']) if json['errors']
|
112
|
+
message += " (#{errors})" if errors
|
114
113
|
|
115
|
-
message = "#{json['message']} (#{errors})"
|
116
114
|
raise InvalidRequestError.new(
|
117
115
|
message: message,
|
118
116
|
http_status: http_status,
|
@@ -120,6 +118,14 @@ module WorkOS
|
|
120
118
|
)
|
121
119
|
end
|
122
120
|
end
|
123
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
121
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def extract_error(errors)
|
126
|
+
errors.map do |error|
|
127
|
+
"#{error['field']}: #{error['code']}"
|
128
|
+
end.join('; ')
|
129
|
+
end
|
124
130
|
end
|
125
131
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module WorkOS
|
7
|
+
# The Passwordless module provides convenience methods for working with
|
8
|
+
# passwordless sessions including the WorkOS Magic Link. You'll need a valid
|
9
|
+
# API key.
|
10
|
+
#
|
11
|
+
# @see https://workos.com/docs/sso/configuring-magic-link
|
12
|
+
module Passwordless
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
include Base
|
16
|
+
include Client
|
17
|
+
|
18
|
+
# Create a Passwordless Session.
|
19
|
+
#
|
20
|
+
# @param [Hash] options A hash with options for the session
|
21
|
+
# @option options [String] email The email of the user to authenticate.
|
22
|
+
# @option options [String] state Optional parameter that the redirect URI
|
23
|
+
# received from WorkOS will contain. The state parameter can be used to
|
24
|
+
# encode arbitrary information to help restore application state between
|
25
|
+
# redirects.
|
26
|
+
# @option options [String] type The type of Passwordless Session to
|
27
|
+
# create. Currently, the only supported value is 'MagicLink'.
|
28
|
+
# @option options [String] redirect_uri The URI where users are directed
|
29
|
+
# after completing the authentication step. Must match a
|
30
|
+
# configured redirect URI on your WorkOS dashboard.
|
31
|
+
#
|
32
|
+
# @return Hash
|
33
|
+
sig do
|
34
|
+
params(
|
35
|
+
options: Hash,
|
36
|
+
).returns(WorkOS::Types::PasswordlessSessionStruct)
|
37
|
+
end
|
38
|
+
|
39
|
+
# rubocop:disable Metrics/MethodLength
|
40
|
+
def create_session(options)
|
41
|
+
response = execute_request(
|
42
|
+
request: post_request(
|
43
|
+
path: '/passwordless/sessions',
|
44
|
+
auth: true,
|
45
|
+
body: options,
|
46
|
+
),
|
47
|
+
)
|
48
|
+
|
49
|
+
hash = JSON.parse(response.body)
|
50
|
+
|
51
|
+
WorkOS::Types::PasswordlessSessionStruct.new(
|
52
|
+
id: hash['id'],
|
53
|
+
email: hash['email'],
|
54
|
+
expires_at: Date.parse(hash['expires_at']),
|
55
|
+
link: hash['link'],
|
56
|
+
)
|
57
|
+
end
|
58
|
+
# rubocop:enable Metrics/MethodLength
|
59
|
+
|
60
|
+
# Send a Passwordless Session via email.
|
61
|
+
#
|
62
|
+
# @param [String] session_id The unique identifier of the Passwordless
|
63
|
+
# Session to send an email for.
|
64
|
+
#
|
65
|
+
# @return Hash
|
66
|
+
sig do
|
67
|
+
params(
|
68
|
+
session_id: String,
|
69
|
+
).returns(T::Hash[String, T::Boolean])
|
70
|
+
end
|
71
|
+
|
72
|
+
def send_session(session_id)
|
73
|
+
response = execute_request(
|
74
|
+
request: post_request(
|
75
|
+
path: "/passwordless/sessions/#{session_id}/send",
|
76
|
+
auth: true,
|
77
|
+
),
|
78
|
+
)
|
79
|
+
|
80
|
+
JSON.parse(response.body)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/workos/portal.rb
CHANGED
@@ -103,8 +103,12 @@ module WorkOS
|
|
103
103
|
|
104
104
|
parsed_response = JSON.parse(response.body)
|
105
105
|
|
106
|
+
organizations = parsed_response['data'].map do |organization|
|
107
|
+
::WorkOS::Organization.new(organization.to_json)
|
108
|
+
end
|
109
|
+
|
106
110
|
WorkOS::Types::ListStruct.new(
|
107
|
-
data:
|
111
|
+
data: organizations,
|
108
112
|
list_metadata: parsed_response['listMetadata'],
|
109
113
|
)
|
110
114
|
end
|
data/lib/workos/profile.rb
CHANGED
@@ -13,9 +13,9 @@ module WorkOS
|
|
13
13
|
extend T::Sig
|
14
14
|
|
15
15
|
sig { returns(String) }
|
16
|
-
attr_accessor :id, :email, :first_name, :last_name,
|
16
|
+
attr_accessor :id, :email, :first_name, :last_name, :connection_id,
|
17
17
|
:connection_type, :idp_id, :raw_attributes
|
18
|
-
|
18
|
+
# rubocop:disable Metrics/AbcSize
|
19
19
|
sig { params(profile_json: String).void }
|
20
20
|
def initialize(profile_json)
|
21
21
|
raw = parse_json(profile_json)
|
@@ -24,10 +24,12 @@ module WorkOS
|
|
24
24
|
@email = T.let(raw.email, String)
|
25
25
|
@first_name = raw.first_name
|
26
26
|
@last_name = raw.last_name
|
27
|
+
@connection_id = T.let(raw.connection_id, String)
|
27
28
|
@connection_type = T.let(raw.connection_type, String)
|
28
29
|
@idp_id = raw.idp_id
|
29
30
|
@raw_attributes = raw.raw_attributes
|
30
31
|
end
|
32
|
+
# rubocop:enable Metrics/AbcSize
|
31
33
|
|
32
34
|
sig { returns(String) }
|
33
35
|
def full_name
|
@@ -40,6 +42,7 @@ module WorkOS
|
|
40
42
|
email: email,
|
41
43
|
first_name: first_name,
|
42
44
|
last_name: last_name,
|
45
|
+
connection_id: connection_id,
|
43
46
|
connection_type: connection_type,
|
44
47
|
idp_id: idp_id,
|
45
48
|
raw_attributes: raw_attributes,
|
@@ -48,7 +51,7 @@ module WorkOS
|
|
48
51
|
|
49
52
|
private
|
50
53
|
|
51
|
-
# rubocop:disable Metrics/AbcSize
|
54
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
52
55
|
sig { params(json_string: String).returns(WorkOS::Types::ProfileStruct) }
|
53
56
|
def parse_json(json_string)
|
54
57
|
hash = JSON.parse(json_string, symbolize_names: true)
|
@@ -58,11 +61,12 @@ module WorkOS
|
|
58
61
|
email: hash[:profile][:email],
|
59
62
|
first_name: hash[:profile][:first_name],
|
60
63
|
last_name: hash[:profile][:last_name],
|
64
|
+
connection_id: hash[:profile][:connection_id],
|
61
65
|
connection_type: hash[:profile][:connection_type],
|
62
66
|
idp_id: hash[:profile][:idp_id],
|
63
67
|
raw_attributes: hash[:profile][:raw_attributes],
|
64
68
|
)
|
65
69
|
end
|
66
|
-
# rubocop:enable Metrics/AbcSize
|
70
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
67
71
|
end
|
68
72
|
end
|
data/lib/workos/types.rb
CHANGED
@@ -9,6 +9,7 @@ module WorkOS
|
|
9
9
|
require_relative 'types/intent_enum'
|
10
10
|
require_relative 'types/list_struct'
|
11
11
|
require_relative 'types/organization_struct'
|
12
|
+
require_relative 'types/passwordless_session_struct'
|
12
13
|
require_relative 'types/profile_struct'
|
13
14
|
require_relative 'types/provider_enum'
|
14
15
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: strict
|
3
|
+
|
4
|
+
module WorkOS
|
5
|
+
module Types
|
6
|
+
# This PasswordlessSessionStruct acts as a typed interface
|
7
|
+
# for the Passwordless class
|
8
|
+
class PasswordlessSessionStruct < T::Struct
|
9
|
+
const :id, String
|
10
|
+
const :email, String
|
11
|
+
const :expires_at, Date
|
12
|
+
const :link, String
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -10,6 +10,7 @@ module WorkOS
|
|
10
10
|
const :email, String
|
11
11
|
const :first_name, T.nilable(String)
|
12
12
|
const :last_name, T.nilable(String)
|
13
|
+
const :connection_id, String
|
13
14
|
const :connection_type, String
|
14
15
|
const :idp_id, T.nilable(String)
|
15
16
|
const :raw_attributes, T::Hash[Symbol, Object]
|
data/lib/workos/version.rb
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
describe WorkOS::Passwordless do
|
5
|
+
before(:all) do
|
6
|
+
WorkOS.key = 'key'
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:all) do
|
10
|
+
WorkOS.key = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '.create_session' do
|
14
|
+
context 'with valid options payload' do
|
15
|
+
let(:valid_options) do
|
16
|
+
{
|
17
|
+
email: 'demo@workos-okta.com',
|
18
|
+
type: 'MagicLink',
|
19
|
+
redirect_uri: 'foo.com/auth/callback',
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'creates a session' do
|
24
|
+
VCR.use_cassette('passwordless/create_session') do
|
25
|
+
response = described_class.create_session(valid_options)
|
26
|
+
|
27
|
+
expect(response.email).to eq 'demo@workos-okta.com'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with invalid event payload' do
|
33
|
+
let(:invalid_options) do
|
34
|
+
{}
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'raises an error' do
|
38
|
+
VCR.use_cassette('passwordless/create_session_invalid') do
|
39
|
+
expect do
|
40
|
+
described_class.create_session(invalid_options)
|
41
|
+
end.to raise_error(
|
42
|
+
WorkOS::InvalidRequestError,
|
43
|
+
/Status 422, Validation failed \(email: email must be a string; type: type must be a valid enum value\)/,
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '.send_session' do
|
51
|
+
context 'with valid session id' do
|
52
|
+
let(:valid_options) do
|
53
|
+
{
|
54
|
+
email: 'demo@workos-okta.com',
|
55
|
+
type: 'MagicLink',
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'send a session' do
|
60
|
+
VCR.use_cassette('passwordless/send_session') do
|
61
|
+
response = described_class.send_session(
|
62
|
+
'passwordless_session_01EJC0F4KH42T11Y2DHPEB09BM',
|
63
|
+
)
|
64
|
+
|
65
|
+
expect(response['success']).to eq true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'with invalid session id' do
|
71
|
+
it 'raises an error' do
|
72
|
+
VCR.use_cassette('passwordless/send_session_invalid') do
|
73
|
+
expect do
|
74
|
+
described_class.send_session('session_123')
|
75
|
+
end.to raise_error(
|
76
|
+
WorkOS::InvalidRequestError,
|
77
|
+
/Status 422, The passwordless session 'session_123' has expired or is invalid./,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/spec/lib/workos/sso_spec.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.workos.com/passwordless/sessions
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"email":"demo@workos-okta.com","type":"MagicLink"}'
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
User-Agent:
|
17
|
+
- WorkOS; ruby/2.7.1; x86_64-darwin19; v0.7.0
|
18
|
+
Authorization:
|
19
|
+
- Bearer <API_KEY>
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 201
|
23
|
+
message: Created
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- Cowboy
|
27
|
+
Connection:
|
28
|
+
- keep-alive
|
29
|
+
Vary:
|
30
|
+
- Origin, Accept-Encoding
|
31
|
+
Access-Control-Allow-Credentials:
|
32
|
+
- 'true'
|
33
|
+
Content-Security-Policy:
|
34
|
+
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
35
|
+
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
36
|
+
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
37
|
+
X-Dns-Prefetch-Control:
|
38
|
+
- 'off'
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=0
|
41
|
+
X-Frame-Options:
|
42
|
+
- SAMEORIGIN
|
43
|
+
Strict-Transport-Security:
|
44
|
+
- max-age=15552000; includeSubDomains
|
45
|
+
X-Download-Options:
|
46
|
+
- noopen
|
47
|
+
X-Content-Type-Options:
|
48
|
+
- nosniff
|
49
|
+
X-Permitted-Cross-Domain-Policies:
|
50
|
+
- none
|
51
|
+
Referrer-Policy:
|
52
|
+
- no-referrer
|
53
|
+
X-Xss-Protection:
|
54
|
+
- '0'
|
55
|
+
X-Request-Id:
|
56
|
+
- 611c2a85-84e1-4bad-a2ec-43cf8371f134
|
57
|
+
Content-Type:
|
58
|
+
- application/json; charset=utf-8
|
59
|
+
Content-Length:
|
60
|
+
- '238'
|
61
|
+
Etag:
|
62
|
+
- W/"ee-6KkIusxSXraxKqTLP+31C0PeHDU"
|
63
|
+
Date:
|
64
|
+
- Wed, 16 Sep 2020 15:39:08 GMT
|
65
|
+
Via:
|
66
|
+
- 1.1 vegur
|
67
|
+
body:
|
68
|
+
encoding: UTF-8
|
69
|
+
string: '{"object":"passwordless_session","id":"passwordless_session_01EJBS3JSXFE2DP6JC6ZVBZ095","email":"demo@workos-okta.com","expires_at":"2020-09-16T15:44:08.475Z","link":"https://api.workos.com/passwordless/ZBxkn2ZTUYqa82ky6QEYecemI/confirm"}'
|
70
|
+
http_version:
|
71
|
+
recorded_at: Wed, 16 Sep 2020 15:39:08 GMT
|
72
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,73 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.workos.com/passwordless/sessions
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: "{}"
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
User-Agent:
|
17
|
+
- WorkOS; ruby/2.7.1; x86_64-darwin19; v0.7.0
|
18
|
+
Authorization:
|
19
|
+
- Bearer sk_4q5ka3d9bx0XJiZhkKmUIOG87
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 422
|
23
|
+
message: Unprocessable Entity
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- Cowboy
|
27
|
+
Connection:
|
28
|
+
- keep-alive
|
29
|
+
Vary:
|
30
|
+
- Origin, Accept-Encoding
|
31
|
+
Access-Control-Allow-Credentials:
|
32
|
+
- 'true'
|
33
|
+
Content-Security-Policy:
|
34
|
+
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
35
|
+
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
36
|
+
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
37
|
+
X-Dns-Prefetch-Control:
|
38
|
+
- 'off'
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=0
|
41
|
+
X-Frame-Options:
|
42
|
+
- SAMEORIGIN
|
43
|
+
Strict-Transport-Security:
|
44
|
+
- max-age=15552000; includeSubDomains
|
45
|
+
X-Download-Options:
|
46
|
+
- noopen
|
47
|
+
X-Content-Type-Options:
|
48
|
+
- nosniff
|
49
|
+
X-Permitted-Cross-Domain-Policies:
|
50
|
+
- none
|
51
|
+
Referrer-Policy:
|
52
|
+
- no-referrer
|
53
|
+
X-Xss-Protection:
|
54
|
+
- '0'
|
55
|
+
X-Request-Id:
|
56
|
+
- e3ca7215-2b2d-45cf-a04f-90279225f27e
|
57
|
+
Content-Type:
|
58
|
+
- application/json; charset=utf-8
|
59
|
+
Content-Length:
|
60
|
+
- '150'
|
61
|
+
Etag:
|
62
|
+
- W/"96-O5ltHaJ3rEQ8+dqFwhN+Lhmgdb0"
|
63
|
+
Date:
|
64
|
+
- Wed, 16 Sep 2020 17:34:07 GMT
|
65
|
+
Via:
|
66
|
+
- 1.1 vegur
|
67
|
+
body:
|
68
|
+
encoding: UTF-8
|
69
|
+
string: '{"message":"Validation failed","errors":[{"field":"email","code":"email
|
70
|
+
must be a string"},{"field":"type","code":"type must be a valid enum value"}]}'
|
71
|
+
http_version:
|
72
|
+
recorded_at: Wed, 16 Sep 2020 17:34:07 GMT
|
73
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,72 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.workos.com/passwordless/sessions/passwordless_session_01EJC0F4KH42T11Y2DHPEB09BM/send
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
User-Agent:
|
17
|
+
- WorkOS; ruby/2.7.1; x86_64-darwin19; v0.7.0
|
18
|
+
Authorization:
|
19
|
+
- Bearer sk_4q5ka3d9bx0XJiZhkKmUIOG87
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 201
|
23
|
+
message: Created
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- Cowboy
|
27
|
+
Connection:
|
28
|
+
- keep-alive
|
29
|
+
Vary:
|
30
|
+
- Origin, Accept-Encoding
|
31
|
+
Access-Control-Allow-Credentials:
|
32
|
+
- 'true'
|
33
|
+
Content-Security-Policy:
|
34
|
+
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
35
|
+
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
36
|
+
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
37
|
+
X-Dns-Prefetch-Control:
|
38
|
+
- 'off'
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=0
|
41
|
+
X-Frame-Options:
|
42
|
+
- SAMEORIGIN
|
43
|
+
Strict-Transport-Security:
|
44
|
+
- max-age=15552000; includeSubDomains
|
45
|
+
X-Download-Options:
|
46
|
+
- noopen
|
47
|
+
X-Content-Type-Options:
|
48
|
+
- nosniff
|
49
|
+
X-Permitted-Cross-Domain-Policies:
|
50
|
+
- none
|
51
|
+
Referrer-Policy:
|
52
|
+
- no-referrer
|
53
|
+
X-Xss-Protection:
|
54
|
+
- '0'
|
55
|
+
X-Request-Id:
|
56
|
+
- d7d72520-9223-4145-b34e-df5e80a776d6
|
57
|
+
Content-Type:
|
58
|
+
- application/json; charset=utf-8
|
59
|
+
Content-Length:
|
60
|
+
- '16'
|
61
|
+
Etag:
|
62
|
+
- W/"10-oV4hJxRVSENxc/wX8+mA4/Pe4tA"
|
63
|
+
Date:
|
64
|
+
- Wed, 16 Sep 2020 17:47:47 GMT
|
65
|
+
Via:
|
66
|
+
- 1.1 vegur
|
67
|
+
body:
|
68
|
+
encoding: UTF-8
|
69
|
+
string: '{"success":true}'
|
70
|
+
http_version:
|
71
|
+
recorded_at: Wed, 16 Sep 2020 17:47:47 GMT
|
72
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,73 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://api.workos.com/passwordless/sessions/session_123/send
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
14
|
+
Accept:
|
15
|
+
- "*/*"
|
16
|
+
User-Agent:
|
17
|
+
- WorkOS; ruby/2.7.1; x86_64-darwin19; v0.7.0
|
18
|
+
Authorization:
|
19
|
+
- Bearer sk_4q5ka3d9bx0XJiZhkKmUIOG87
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 422
|
23
|
+
message: Unprocessable Entity
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- Cowboy
|
27
|
+
Connection:
|
28
|
+
- keep-alive
|
29
|
+
Vary:
|
30
|
+
- Origin, Accept-Encoding
|
31
|
+
Access-Control-Allow-Credentials:
|
32
|
+
- 'true'
|
33
|
+
Content-Security-Policy:
|
34
|
+
- 'default-src ''self'';base-uri ''self'';block-all-mixed-content;font-src ''self''
|
35
|
+
https: data:;frame-ancestors ''self'';img-src ''self'' data:;object-src ''none'';script-src
|
36
|
+
''self'';script-src-attr ''none'';style-src ''self'' https: ''unsafe-inline'';upgrade-insecure-requests'
|
37
|
+
X-Dns-Prefetch-Control:
|
38
|
+
- 'off'
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=0
|
41
|
+
X-Frame-Options:
|
42
|
+
- SAMEORIGIN
|
43
|
+
Strict-Transport-Security:
|
44
|
+
- max-age=15552000; includeSubDomains
|
45
|
+
X-Download-Options:
|
46
|
+
- noopen
|
47
|
+
X-Content-Type-Options:
|
48
|
+
- nosniff
|
49
|
+
X-Permitted-Cross-Domain-Policies:
|
50
|
+
- none
|
51
|
+
Referrer-Policy:
|
52
|
+
- no-referrer
|
53
|
+
X-Xss-Protection:
|
54
|
+
- '0'
|
55
|
+
X-Request-Id:
|
56
|
+
- 6c22578f-9d49-4118-a7bd-18014d447aad
|
57
|
+
Content-Type:
|
58
|
+
- application/json; charset=utf-8
|
59
|
+
Content-Length:
|
60
|
+
- '79'
|
61
|
+
Etag:
|
62
|
+
- W/"4f-NjqaLicbRDM9SfS5gYKHlSgozt0"
|
63
|
+
Date:
|
64
|
+
- Wed, 16 Sep 2020 17:52:24 GMT
|
65
|
+
Via:
|
66
|
+
- 1.1 vegur
|
67
|
+
body:
|
68
|
+
encoding: UTF-8
|
69
|
+
string: '{"message":"The passwordless session ''session_123'' has expired or
|
70
|
+
is invalid."}'
|
71
|
+
http_version:
|
72
|
+
recorded_at: Wed, 16 Sep 2020 17:52:24 GMT
|
73
|
+
recorded_with: VCR 5.0.0
|
data/spec/support/profile.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"profile":{"object":"profile","id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","connection_type":"OktaSAML","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357","raw_attributes":{"id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357"}},"access_token":"01DVX6QBS3EG6FHY2ESAA5Q65X"}
|
1
|
+
{"profile":{"object":"profile","id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","connection_id":"conn_01EMH8WAK20T42N2NBMNBCYHAG","connection_type":"OktaSAML","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357","raw_attributes":{"id":"prof_01DRA1XNSJDZ19A31F183ECQW5","email":"demo@workos-okta.com","first_name":"WorkOS","last_name":"Demo","idp_id":"00u1klkowm8EGah2H357"}},"access_token":"01DVX6QBS3EG6FHY2ESAA5Q65X"}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- WorkOS
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-runtime
|
@@ -161,6 +161,7 @@ executables:
|
|
161
161
|
extensions: []
|
162
162
|
extra_rdoc_files: []
|
163
163
|
files:
|
164
|
+
- ".github/CODEOWNERS"
|
164
165
|
- ".github/renovate.json"
|
165
166
|
- ".gitignore"
|
166
167
|
- ".rspec"
|
@@ -168,7 +169,6 @@ files:
|
|
168
169
|
- ".ruby-version"
|
169
170
|
- ".semaphore/rubygems.yml"
|
170
171
|
- ".semaphore/semaphore.yml"
|
171
|
-
- CODEOWNERS
|
172
172
|
- Gemfile
|
173
173
|
- Gemfile.lock
|
174
174
|
- LICENSE
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/workos/directory_sync.rb
|
215
215
|
- lib/workos/errors.rb
|
216
216
|
- lib/workos/organization.rb
|
217
|
+
- lib/workos/passwordless.rb
|
217
218
|
- lib/workos/portal.rb
|
218
219
|
- lib/workos/profile.rb
|
219
220
|
- lib/workos/sso.rb
|
@@ -222,6 +223,7 @@ files:
|
|
222
223
|
- lib/workos/types/intent_enum.rb
|
223
224
|
- lib/workos/types/list_struct.rb
|
224
225
|
- lib/workos/types/organization_struct.rb
|
226
|
+
- lib/workos/types/passwordless_session_struct.rb
|
225
227
|
- lib/workos/types/profile_struct.rb
|
226
228
|
- lib/workos/types/provider_enum.rb
|
227
229
|
- lib/workos/version.rb
|
@@ -237,6 +239,7 @@ files:
|
|
237
239
|
- spec/lib/workos/audit_trail_spec.rb
|
238
240
|
- spec/lib/workos/base_spec.rb
|
239
241
|
- spec/lib/workos/directory_sync_spec.rb
|
242
|
+
- spec/lib/workos/passwordless_spec.rb
|
240
243
|
- spec/lib/workos/portal_spec.rb
|
241
244
|
- spec/lib/workos/sso_spec.rb
|
242
245
|
- spec/spec_helper.rb
|
@@ -260,6 +263,10 @@ files:
|
|
260
263
|
- spec/support/fixtures/vcr_cassettes/organization/create.yml
|
261
264
|
- spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml
|
262
265
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
266
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
267
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|
268
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml
|
269
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml
|
263
270
|
- spec/support/fixtures/vcr_cassettes/portal/generate_link.yml
|
264
271
|
- spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml
|
265
272
|
- spec/support/fixtures/vcr_cassettes/sso/create_connection_with_invalid_source.yml
|
@@ -294,6 +301,7 @@ test_files:
|
|
294
301
|
- spec/lib/workos/audit_trail_spec.rb
|
295
302
|
- spec/lib/workos/base_spec.rb
|
296
303
|
- spec/lib/workos/directory_sync_spec.rb
|
304
|
+
- spec/lib/workos/passwordless_spec.rb
|
297
305
|
- spec/lib/workos/portal_spec.rb
|
298
306
|
- spec/lib/workos/sso_spec.rb
|
299
307
|
- spec/spec_helper.rb
|
@@ -317,6 +325,10 @@ test_files:
|
|
317
325
|
- spec/support/fixtures/vcr_cassettes/organization/create.yml
|
318
326
|
- spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml
|
319
327
|
- spec/support/fixtures/vcr_cassettes/organization/list.yml
|
328
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml
|
329
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml
|
330
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml
|
331
|
+
- spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml
|
320
332
|
- spec/support/fixtures/vcr_cassettes/portal/generate_link.yml
|
321
333
|
- spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml
|
322
334
|
- spec/support/fixtures/vcr_cassettes/sso/create_connection_with_invalid_source.yml
|
data/CODEOWNERS
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
@marktran
|