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
12 changes: 10 additions & 2 deletions lib/workos/action_context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ defmodule WorkOS.ActionContext do
`organization_membership`, `issuer`
* `"user_registration_action_context"` — `user_data`, `invitation`

`ip_address`, `user_agent`, and `device_fingerprint` are shared by both
context types; fields specific to the other variant are `nil`.
`authentication_method`, `ip_address`, `user_agent`, and
`device_fingerprint` are shared by both context types; fields specific to
the other variant are `nil`.
"""

defstruct [
:object,
:id,
:authentication_method,
:ip_address,
:user_agent,
:device_fingerprint,
Expand All @@ -32,6 +34,7 @@ defmodule WorkOS.ActionContext do
@type t :: %__MODULE__{
object: String.t() | nil,
id: String.t() | nil,
authentication_method: WorkOS.AuthenticateResponseAuthenticationMethod.t() | nil,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Include unknown enum strings

The reused caster preserves unrecognized authentication methods as raw strings, but this field typespec permits only known enum atoms or nil. This makes the declared contract inaccurate for forward-compatible API values and can mislead static analysis and callers into treating the atom variants as exhaustive.

Suggested change
authentication_method: WorkOS.AuthenticateResponseAuthenticationMethod.t() | nil,
authentication_method:
WorkOS.AuthenticateResponseAuthenticationMethod.t() | String.t() | nil,
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/workos/action_context.ex
Line: 36

Comment:
**Include unknown enum strings**

The reused caster preserves unrecognized authentication methods as raw strings, but this field typespec permits only known enum atoms or `nil`. This makes the declared contract inaccurate for forward-compatible API values and can mislead static analysis and callers into treating the atom variants as exhaustive.

```suggestion
          authentication_method:
            WorkOS.AuthenticateResponseAuthenticationMethod.t() | String.t() | nil,
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session restore failed HTTP 422: ENOSPC: no space left on device, write

ip_address: String.t() | nil,
user_agent: String.t() | nil,
device_fingerprint: String.t() | nil,
Expand All @@ -49,6 +52,11 @@ defmodule WorkOS.ActionContext do
%__MODULE__{
object: map["object"],
id: map["id"],
authentication_method:
WorkOS.Cast.enum(
map["authentication_method"],
&WorkOS.AuthenticateResponseAuthenticationMethod.cast/1
),
ip_address: map["ip_address"],
user_agent: map["user_agent"],
device_fingerprint: map["device_fingerprint"],
Expand Down
32 changes: 30 additions & 2 deletions test/workos/actions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule WorkOS.ActionsTest do
alias WorkOS.Webhooks.Signature

@secret "actions_secret_123"
@payload ~s({"object":"authentication_action_context","id":"action_01","user":{"object":"user","id":"user_01","email":"test@example.com"},"ip_address":"1.2.3.4","device_fingerprint":"fp_123","issuer":"https://auth.example.com"})
@payload ~s({"object":"authentication_action_context","id":"action_01","authentication_method":"Password","user":{"object":"user","id":"user_01","email":"test@example.com"},"ip_address":"1.2.3.4","device_fingerprint":"fp_123","issuer":"https://auth.example.com"})

defp signed_header(payload, now_ms) do
timestamp = Integer.to_string(now_ms)
Expand Down Expand Up @@ -44,6 +44,7 @@ defmodule WorkOS.ActionsTest do

assert action.object == "authentication_action_context"
assert action.id == "action_01"
assert action.authentication_method == :password
assert action.user.id == "user_01"
assert action.user.email == "test@example.com"
assert action.ip_address == "1.2.3.4"
Expand All @@ -52,19 +53,46 @@ defmodule WorkOS.ActionsTest do

test "construct_action deserializes a user_registration request" do
payload =
~s({"object":"user_registration_action_context","id":"action_02","user_data":{"object":"user_data","email":"new@example.com","first_name":"New","last_name":"User","name":null},"ip_address":"5.6.7.8","device_fingerprint":"fp_456"})
~s({"object":"user_registration_action_context","id":"action_02","authentication_method":"GoogleOAuth","user_data":{"object":"user_data","email":"new@example.com","first_name":"New","last_name":"User","name":null},"ip_address":"5.6.7.8","device_fingerprint":"fp_456"})

header = signed_header(payload, System.system_time(:millisecond))

assert {:ok, %WorkOS.ActionContext{} = action} =
WorkOS.Actions.construct_action(payload, header, @secret)

assert action.object == "user_registration_action_context"
assert action.authentication_method == :google_oauth
assert action.user_data.email == "new@example.com"
assert action.user_data.first_name == "New"
assert action.user == nil
end

test "construct_action leaves authentication_method nil when the field is absent" do
payload =
~s({"object":"authentication_action_context","id":"action_03","user":{"object":"user","id":"user_03","email":"absent@example.com"},"ip_address":"9.10.11.12"})

header = signed_header(payload, System.system_time(:millisecond))

assert {:ok, %WorkOS.ActionContext{} = action} =
WorkOS.Actions.construct_action(payload, header, @secret)

assert action.id == "action_03"
assert action.authentication_method == nil
end

test "construct_action passes an unknown authentication_method through unchanged" do
payload =
~s({"object":"authentication_action_context","id":"action_04","authentication_method":"FutureMethod","user":{"object":"user","id":"user_04","email":"future@example.com"},"ip_address":"13.14.15.16"})

header = signed_header(payload, System.system_time(:millisecond))

assert {:ok, %WorkOS.ActionContext{} = action} =
WorkOS.Actions.construct_action(payload, header, @secret)

assert action.id == "action_04"
assert action.authentication_method == "FutureMethod"
end

test "sign_response produces a workos-node-compatible Allow response" do
now_ms = 1_700_000_000_000

Expand Down
Loading