Extend the GraphQL API and socket to accept mobile session tokens alongside existing API tokens. Add OrganizationScope middleware that extracts organization_id from query args for mobile users. Add my_organizations query for mobile app org listing. Reviewed-on: graham/towerops-web#48
137 lines
3.6 KiB
Elixir
137 lines
3.6 KiB
Elixir
defmodule ToweropsWeb.Plugs.GraphQLAuthTest do
|
|
use ToweropsWeb.ConnCase, async: true
|
|
|
|
import Towerops.AccountsFixtures
|
|
import Towerops.OrganizationsFixtures
|
|
|
|
alias Towerops.ApiTokens
|
|
alias Towerops.MobileSessions
|
|
alias ToweropsWeb.Plugs.GraphQLAuth
|
|
|
|
describe "init/1" do
|
|
test "returns options unchanged" do
|
|
assert GraphQLAuth.init([]) == []
|
|
end
|
|
end
|
|
|
|
describe "call/2 with API token (towerops_ prefix)" do
|
|
setup do
|
|
user = user_fixture()
|
|
organization = organization_fixture(user.id)
|
|
|
|
{:ok, {_token, raw_token}} =
|
|
ApiTokens.create_api_token(%{
|
|
organization_id: organization.id,
|
|
user_id: user.id,
|
|
name: "GraphQL Auth Test Token"
|
|
})
|
|
|
|
%{user: user, organization: organization, raw_token: raw_token}
|
|
end
|
|
|
|
test "authenticates and sets org_id + user", %{
|
|
conn: conn,
|
|
raw_token: raw_token,
|
|
organization: organization,
|
|
user: user
|
|
} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer #{raw_token}")
|
|
|> GraphQLAuth.call([])
|
|
|
|
refute conn.halted
|
|
assert conn.assigns.current_organization_id == organization.id
|
|
assert conn.assigns.current_user.id == user.id
|
|
end
|
|
|
|
test "returns 401 for expired API token", %{conn: conn, organization: organization, user: user} do
|
|
expires_at = DateTime.add(DateTime.utc_now(), -1, :day)
|
|
|
|
{:ok, {_token, raw_token}} =
|
|
ApiTokens.create_api_token(%{
|
|
organization_id: organization.id,
|
|
user_id: user.id,
|
|
name: "Expired Token",
|
|
expires_at: expires_at
|
|
})
|
|
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer #{raw_token}")
|
|
|> GraphQLAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
end
|
|
end
|
|
|
|
describe "call/2 with mobile session token" do
|
|
setup do
|
|
user = user_fixture()
|
|
|
|
{:ok, session} =
|
|
MobileSessions.create_mobile_session(%{
|
|
user_id: user.id,
|
|
device_name: "Test iPhone"
|
|
})
|
|
|
|
%{user: user, session: session}
|
|
end
|
|
|
|
test "authenticates and sets user but not org_id", %{
|
|
conn: conn,
|
|
user: user,
|
|
session: session
|
|
} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer #{session.raw_token}")
|
|
|> GraphQLAuth.call([])
|
|
|
|
refute conn.halted
|
|
assert conn.assigns.current_user.id == user.id
|
|
refute Map.has_key?(conn.assigns, :current_organization_id)
|
|
end
|
|
|
|
test "returns 401 for expired mobile session", %{conn: conn} do
|
|
user = user_fixture()
|
|
expires_at = DateTime.add(DateTime.utc_now(), -1, :day)
|
|
|
|
{:ok, session} =
|
|
MobileSessions.create_mobile_session(%{
|
|
user_id: user.id,
|
|
device_name: "Expired iPhone",
|
|
expires_at: expires_at
|
|
})
|
|
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer #{session.raw_token}")
|
|
|> GraphQLAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
end
|
|
end
|
|
|
|
describe "call/2 with missing or invalid auth" do
|
|
test "returns 401 when Authorization header is missing", %{conn: conn} do
|
|
conn = GraphQLAuth.call(conn, [])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
assert json_response(conn, 401) == %{"error" => "Missing or invalid Authorization header"}
|
|
end
|
|
|
|
test "returns 401 when token is completely invalid", %{conn: conn} do
|
|
conn =
|
|
conn
|
|
|> put_req_header("authorization", "Bearer totally_invalid_token_xyz")
|
|
|> GraphQLAuth.call([])
|
|
|
|
assert conn.halted
|
|
assert conn.status == 401
|
|
end
|
|
end
|
|
end
|