|
1 | 1 | defmodule WraftDocWeb.Plug.ApiKeyAuth do |
2 | 2 | @moduledoc """ |
3 | 3 | Plug for API Key authentication. |
4 | | - |
| 4 | +
|
5 | 5 | This plug attempts to authenticate requests using an API key from the X-API-Key header. |
6 | 6 | If a valid API key is found: |
7 | 7 | - Sets conn.assigns.current_user to the user associated with the API key |
8 | 8 | - Sets conn.assigns.current_organisation to the organisation |
9 | 9 | - Sets conn.assigns.api_key to the API key struct (for audit purposes) |
10 | 10 | - Sets conn.assigns.authenticated_via to :api_key |
11 | | - |
| 11 | +
|
12 | 12 | If no API key is provided or invalid, the plug does nothing and lets the |
13 | 13 | request continue to the next authentication method (JWT). |
14 | 14 | """ |
15 | | - |
| 15 | + |
16 | 16 | import Plug.Conn |
17 | 17 | require Logger |
18 | 18 |
|
@@ -52,44 +52,56 @@ defmodule WraftDocWeb.Plug.ApiKeyAuth do |
52 | 52 |
|
53 | 53 | case ApiKeys.verify_api_key(api_key_string, remote_ip) do |
54 | 54 | {:ok, %{api_key: api_key, user: user, organisation: organisation}} -> |
55 | | - # Check rate limit |
56 | | - case ApiKeys.check_rate_limit(api_key) do |
57 | | - {:ok, _} -> |
58 | | - conn |
59 | | - |> assign(:current_user, user) |
60 | | - |> assign(:current_organisation, organisation) |
61 | | - |> assign(:api_key, api_key) |
62 | | - |> assign(:authenticated_via, :api_key) |
63 | | - |
64 | | - {:error, :rate_limit_exceeded} -> |
65 | | - Logger.warning("API key rate limit exceeded: #{api_key.id}") |
66 | | - send_error_response(conn, 429, "Rate limit exceeded") |
67 | | - end |
| 55 | + handle_rate_limit_check(conn, api_key, user, organisation) |
68 | 56 |
|
69 | | - {:error, :invalid_api_key} -> |
70 | | - Logger.warning("Invalid API key provided") |
71 | | - send_error_response(conn, 401, "Invalid API key") |
| 57 | + {:error, error_type} -> |
| 58 | + handle_authentication_error(conn, error_type) |
| 59 | + end |
| 60 | + end |
72 | 61 |
|
73 | | - {:error, :api_key_expired} -> |
74 | | - Logger.warning("Expired API key used") |
75 | | - send_error_response(conn, 401, "API key has expired") |
| 62 | + defp handle_rate_limit_check(conn, api_key, user, organisation) do |
| 63 | + case ApiKeys.check_rate_limit(api_key) do |
| 64 | + {:ok, _} -> |
| 65 | + conn |
| 66 | + |> assign(:current_user, user) |
| 67 | + |> assign(:current_organisation, organisation) |
| 68 | + |> assign(:api_key, api_key) |
| 69 | + |> assign(:authenticated_via, :api_key) |
| 70 | + |
| 71 | + {:error, :rate_limit_exceeded} -> |
| 72 | + Logger.warning("API key rate limit exceeded: #{api_key.id}") |
| 73 | + send_error_response(conn, 429, "Rate limit exceeded") |
| 74 | + end |
| 75 | + end |
76 | 76 |
|
77 | | - {:error, :api_key_inactive} -> |
78 | | - Logger.warning("Inactive API key used") |
79 | | - send_error_response(conn, 401, "API key is inactive") |
| 77 | + defp handle_authentication_error(conn, :invalid_api_key) do |
| 78 | + Logger.warning("Invalid API key provided") |
| 79 | + send_error_response(conn, 401, "Invalid API key") |
| 80 | + end |
80 | 81 |
|
81 | | - {:error, :ip_not_whitelisted} -> |
82 | | - Logger.warning("API key used from non-whitelisted IP") |
83 | | - send_error_response(conn, 403, "IP address not authorized for this API key") |
| 82 | + defp handle_authentication_error(conn, :api_key_expired) do |
| 83 | + Logger.warning("Expired API key used") |
| 84 | + send_error_response(conn, 401, "API key has expired") |
| 85 | + end |
84 | 86 |
|
85 | | - {:error, :user_not_found} -> |
86 | | - Logger.warning("API key user not found") |
87 | | - send_error_response(conn, 401, "User associated with API key not found") |
| 87 | + defp handle_authentication_error(conn, :api_key_inactive) do |
| 88 | + Logger.warning("Inactive API key used") |
| 89 | + send_error_response(conn, 401, "API key is inactive") |
| 90 | + end |
88 | 91 |
|
89 | | - {:error, reason} -> |
90 | | - Logger.warning("API key authentication failed: #{inspect(reason)}") |
91 | | - send_error_response(conn, 401, "API key authentication failed") |
92 | | - end |
| 92 | + defp handle_authentication_error(conn, :ip_not_whitelisted) do |
| 93 | + Logger.warning("API key used from non-whitelisted IP") |
| 94 | + send_error_response(conn, 403, "IP address not authorized for this API key") |
| 95 | + end |
| 96 | + |
| 97 | + defp handle_authentication_error(conn, :user_not_found) do |
| 98 | + Logger.warning("API key user not found") |
| 99 | + send_error_response(conn, 401, "User associated with API key not found") |
| 100 | + end |
| 101 | + |
| 102 | + defp handle_authentication_error(conn, reason) do |
| 103 | + Logger.warning("API key authentication failed: #{inspect(reason)}") |
| 104 | + send_error_response(conn, 401, "API key authentication failed") |
93 | 105 | end |
94 | 106 |
|
95 | 107 | defp get_remote_ip(conn) do |
@@ -119,4 +131,3 @@ defmodule WraftDocWeb.Plug.ApiKeyAuth do |
119 | 131 | |> halt() |
120 | 132 | end |
121 | 133 | end |
122 | | - |
|
0 commit comments