diff --git a/config/config.exs b/config/config.exs index e57137c..7c8d222 100644 --- a/config/config.exs +++ b/config/config.exs @@ -22,6 +22,15 @@ config :atlas, AtlasWeb.Endpoint, pubsub_server: Atlas.PubSub, live_view: [signing_salt: "Gt4Lm9lT"] +# Configures the Swagger +config :atlas, :phoenix_swagger, + swagger_files: %{ + "priv/static/swagger.json" => [ + router: AtlasWeb.Router, + endpoint: AtlasWeb.Endpoint + ] + } + # Configures the mailer # # By default it uses the "Local" adapter which stores the emails @@ -39,6 +48,9 @@ config :logger, :console, # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason +# Use Jason for JSON parsing in Phoenix Swagger +config :phoenix_swagger, json_library: Jason + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/lib/atlas_web.ex b/lib/atlas_web.ex index cf02bfb..80145d1 100644 --- a/lib/atlas_web.ex +++ b/lib/atlas_web.ex @@ -17,7 +17,7 @@ defmodule AtlasWeb do those modules here. """ - def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) + def static_paths, do: ~w(assets fonts images favicon.ico robots.txt swagger.json) def router do quote do diff --git a/lib/atlas_web/controllers/auth_controller.ex b/lib/atlas_web/controllers/auth_controller.ex index ce29715..7e40114 100644 --- a/lib/atlas_web/controllers/auth_controller.ex +++ b/lib/atlas_web/controllers/auth_controller.ex @@ -1,5 +1,6 @@ defmodule AtlasWeb.AuthController do use AtlasWeb, :controller + use PhoenixSwagger alias Atlas.Accounts alias Atlas.Accounts.{Guardian, User} @@ -9,6 +10,24 @@ defmodule AtlasWeb.AuthController do @refresh_token_days 7 @audience "astra" + swagger_path :sign_in do + post("/v1/auth/sign_in") + summary("Sign in a user") + description("Sign in a user. Returns an access token.") + produces("application/json") + tag("Authentication") + operation_id("sign_in") + + parameters do + email(:query, :string, "User email", required: true) + password(:query, :string, "User password", required: true) + end + + response(200, "Successful sign in", Schema.ref(:SignInResponse)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + response(500, "Failed to create user session", Schema.ref(:ErrorResponse)) + end + def sign_in(conn, %{"email" => email, "password" => password}) do case Accounts.get_user_by_email_and_password(email, password) do %User{} = user -> @@ -48,6 +67,18 @@ defmodule AtlasWeb.AuthController do end end + swagger_path :me do + get("/v1/auth/me") + summary("User in the current session") + description("Returns the user in the current session.") + produces("application/json") + tag("Authentication") + operation_id("me") + response(200, "User returned succesfully", Schema.ref(:User)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + security([%{Bearer: []}]) + end + def me(conn, _params) do {user, _session} = Guardian.Plug.current_resource(conn) @@ -62,6 +93,17 @@ defmodule AtlasWeb.AuthController do end end + swagger_path :refresh_token do + post("/v1/auth/refresh") + summary("Refresh access token") + description("Refresh access token with a refresh token cookie.") + produces("application/json") + tag("Authentication") + operation_id("refresh_token") + response(200, "Successful refresh", Schema.ref(:SuccessfulRefreshResponse)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + end + def refresh_token(conn, _params) do case fetch_refresh_token_cookie(conn) do {:ok, old_refresh_token} -> @@ -91,6 +133,19 @@ defmodule AtlasWeb.AuthController do end end + swagger_path :sign_out do + post("/v1/auth/sign_out") + summary("Sign out") + description("Signs out the user.") + produces("application/json") + tag("Authentication") + operation_id("sign_out") + response(204, "No content - Signed out successfully", Schema.ref(:SignOutResponse)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + response(500, "Failed to sign out", Schema.ref(:ErrorResponse)) + security([%{Bearer: []}]) + end + def sign_out(conn, _params) do {_user, session} = Guardian.Plug.current_resource(conn) @@ -114,6 +169,19 @@ defmodule AtlasWeb.AuthController do end end + swagger_path :sessions do + get("/v1/auth/sessions") + summary("User sessions") + description("Returns all the user sessions.") + produces("application/json") + tag("Authentication") + operation_id("sessions") + response(200, "Sessions succesfully returned", Schema.ref(:UserSessionsResponse)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + + security([%{Bearer: []}]) + end + def sessions(conn, _params) do {user, _session} = Guardian.Plug.current_resource(conn) @@ -130,6 +198,22 @@ defmodule AtlasWeb.AuthController do end end + swagger_path :forgot_password do + post("/v1/auth/forgot_password") + summary("Request password reset") + description("Sends password reset instructions to the user via email.") + produces("application/json") + tag("Authentication") + operation_id("forgot_password") + + parameters do + email(:query, :string, "User email", required: true) + end + + response(204, "No content", Schema.ref(:NoContentResponse)) + response(401, "Unauthorized", Schema.ref(:UnauthorizedResponse)) + end + def forgot_password(conn, %{"email" => email}) do if user = Accounts.get_user_by_email(email) do Accounts.deliver_user_reset_password_instructions(user, &"/auth/forgot_password/#{&1}") @@ -140,6 +224,24 @@ defmodule AtlasWeb.AuthController do |> send_resp(:no_content, "") end + swagger_path :reset_password do + post("/v1/auth/reset_password") + summary("Reset password") + description("Sends a request to reset user's password.") + produces("application/json") + tag("Authentication") + operation_id("reset_password") + + parameters do + token(:query, :string, "Access token", required: true) + password(:query, :string, "New password", required: true) + password_confirmation(:query, :string, "New password confirmation", required: true) + end + + response(200, "Password succesfully reset", Schema.ref(:ResetPasswordResponse)) + response(404, "Invalid or expired reset token", Schema.ref(:ErrorResponse)) + end + def reset_password(conn, %{ "token" => token, "password" => new_password, @@ -221,4 +323,150 @@ defmodule AtlasWeb.AuthController do agent: user_agent } end + + def swagger_definitions do + %{ + SignInResponse: + swagger_schema do + title("SignInResponse") + description("Response schema for successful sign in") + + properties do + session_id(:integer, "User session ID", required: true) + access_token(:string, "Access token", required: true) + end + + example(%{ + session_id: "e1387cae-ac1d-4aeb-8e13-ff1b3dd15ca4", + access_token: + "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhc3RyYSIsImV4cCI6MTc1MjYxMTYwOCwiaWF0IjoxNzUyNjEwNzA4LCJpc3MiOiJhdGxhcyIsImp0aSI6IjYyNjM2ZWFmLTVmZGQtNGU2My05ZmI1LWQyZjYwNmQzOGUzNSIsIm5iZiI6MTc1MjYxMDcwNywic3ViIjoiZTEzODdjYWUtYWMxZC00YWViLThlMTMtZmYxYjNkZDE1Y2E0IiwidHlwIjoiYWNjZXNzIn0.bAF6nLXPlHH80jhueetNyC5jZQ4rXXO1MO63izQ-7x98flalF6IGxc8v3HGLSRfF7s3cXYVOteeSvUUUqbx60A" + }) + end, + ErrorResponse: + swagger_schema do + title("ErrorResponse") + description("Error response schema") + + properties do + error(:string, "Error message", required: true) + end + end, + UnauthorizedResponse: + swagger_schema do + title("UnauthorizedResponse") + description("Unauthorized response schema") + + properties do + error(:string, "Unauthorized error message", required: true) + end + end, + User: + swagger_schema do + title("User") + description("User schema") + + properties do + id(:integer, "User ID", required: true) + name(:string, "User name", required: false) + inserted_at(:string, "Creation timestamp", format: "date-time", required: true) + email(:string, "User email", required: true) + updated_at(:string, "Last update timestamp", format: "date-time", required: true) + end + + example(%{ + user: %{ + id: "d18472e7-5251-4027-884f-58b8a3a6abe5", + name: "Leonardo Carvalho", + inserted_at: "2025-07-15T18:10:27Z", + email: "a114437@alunos.uminho.pt", + updated_at: "2025-07-15T18:10:27Z" + } + }) + end, + SuccessfulRefreshResponse: + swagger_schema do + title("SuccessfulRefreshResponse") + description("Response schema for successful token refresh") + + properties do + access_token(:string, "New access token", required: true) + end + + example(%{ + access_token: + "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhc3RyYSIsImV4cCI6MTc1MjYxMTY5NCwiaWF0IjoxNzUyNjEwNzk0LCJpc3MiOiJhdGxhcyIsImp0aSI6ImYwOTg4MDMzLTlhNDktNGUzZC04M2U5LWE3NDVkZDkwYmY5ZiIsIm5iZiI6MTc1MjYxMDc5Mywic3ViIjoiZTEzODdjYWUtYWMxZC00YWViLThlMTMtZmYxYjNkZDE1Y2E0IiwidHlwIjoiYWNjZXNzIn0.ztcw5nZ3cdI1v5iTU0ZHyx-xZgWukxeFpuMulhvar7iRfSubBztlggVxpVM8bD-ulmujuX1i3-ksbfSdpNYMTQ" + }) + end, + SignOutResponse: + swagger_schema do + title("SignOutResponse") + description("Response schema for successful sign out") + + properties do + message(:string, "Message indicating successful sign out", required: true) + end + + example(%{message: "Signed out successfully"}) + end, + UserSessionsResponse: + swagger_schema do + title("UserSessionsResponse") + description("Response schema for a list of user sessions") + + properties do + sessions(Schema.array(:UserSession), "List of user sessions", required: true) + end + + example(%{ + sessions: [ + %{ + id: "8fd2bef3-f1eb-4bf2-aade-f3ae80e0563d", + ip: "127.0.0.1", + user_agent: + "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0", + user_browser: "Firefox", + user_os: "Linux", + first_seen: "2025-07-15T20:13:41Z" + } + ] + }) + end, + UserSession: + swagger_schema do + title("User Session") + description("User session schema") + + properties do + id(:integer, "Session ID", required: true) + ip(:string, "IP address of the session", required: true) + user_agent(:string, "User agent string", required: true) + user_browser(:string, "Browser of the user agent", required: true) + user_os(:string, "Operating system of the user agent", required: true) + first_seen(:string, "First seen timestamp", format: "date-time", required: true) + end + end, + NoContentResponse: + swagger_schema do + title("NoContentResponse") + description("Response schema for no content") + + properties do + message(:string, "Message indicating no content", required: true) + end + + example(%{}) + end, + ResetPasswordResponse: + swagger_schema do + title("ResetPasswordResponse") + description("Response schema for successful password reset") + + properties do + message(:string, "Message indicating successful password reset", required: true) + end + + example(%{message: "Password reset successfully"}) + end + } + end end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index d78c354..9822a79 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -42,6 +42,10 @@ defmodule AtlasWeb.Router do end end + scope "/swagger" do + forward("/", PhoenixSwagger.Plug.SwaggerUI, otp_app: :atlas, swagger_file: "swagger.json") + end + # Enable LiveDashboard and Swoosh mailbox preview in development if Application.compile_env(:atlas, :dev_routes) do # If you want to use the LiveDashboard in production, you should put @@ -58,4 +62,22 @@ defmodule AtlasWeb.Router do forward "/mailbox", Plug.Swoosh.MailboxPreview end end + + # Usage for bearer token authorization: "Bearer " + + def swagger_info do + %{ + info: %{ + version: "0.1.0", + title: "Atlas" + }, + securityDefinitions: %{ + Bearer: %{ + type: "apiKey", + name: "Authorization", + in: "header" + } + } + } + end end diff --git a/mix.exs b/mix.exs index 68d7f42..50efc01 100644 --- a/mix.exs +++ b/mix.exs @@ -66,7 +66,10 @@ defmodule Atlas.MixProject do # utilities {:remote_ip, "~> 1.2"}, - {:ua_parser, "~> 1.8"} + {:ua_parser, "~> 1.8"}, + + # swagger + {:phoenix_swagger, "~> 0.8", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index 39c262a..bd38fa5 100644 --- a/mix.lock +++ b/mix.lock @@ -29,6 +29,7 @@ "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.17", "beeb16d83a7d3760f7ad463df94e83b087577665d2acc0bf2987cd7d9778068f", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a4ca05c1eb6922c4d07a508a75bfa12c45e5f4d8f77ae83283465f02c53741e1"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, + "phoenix_swagger": {:hex, :phoenix_swagger, "0.8.3", "298d6204802409d3b0b4fc1013873839478707cf3a62532a9e10fec0e26d0e37", [:mix], [{:ex_json_schema, "~> 0.7.1", [hex: :ex_json_schema, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.11", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "3bc0fa9f5b679b8a61b90a52b2c67dd932320e9a84a6f91a4af872a0ab367337"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.18.0", "d78df36c41f7e798f2edf1f33e1727eae438e9dd5d809a9997c463a108244042", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "819f9e176d51e44dc38132e132fe0accaf6767eab7f0303431e404da8476cfa2"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, diff --git a/priv/static/swagger.json b/priv/static/swagger.json new file mode 100644 index 0000000..0f6e0f9 --- /dev/null +++ b/priv/static/swagger.json @@ -0,0 +1,517 @@ +{ + "info": { + "version": "0.1.0", + "title": "Atlas" + }, + "host": "localhost:4000", + "definitions": { + "User": { + "description": "User schema", + "example": { + "user": { + "email": "a114437@alunos.uminho.pt", + "id": "d18472e7-5251-4027-884f-58b8a3a6abe5", + "inserted_at": "2025-07-15T18:10:27Z", + "name": "Leonardo Carvalho", + "updated_at": "2025-07-15T18:10:27Z" + } + }, + "properties": { + "email": { + "description": "User email", + "type": "string" + }, + "id": { + "description": "User ID", + "type": "integer" + }, + "inserted_at": { + "description": "Creation timestamp", + "format": "date-time", + "type": "string" + }, + "name": { + "description": "User name", + "type": "string" + }, + "updated_at": { + "description": "Last update timestamp", + "format": "date-time", + "type": "string" + } + }, + "required": [ + "updated_at", + "email", + "inserted_at", + "id" + ], + "title": "User", + "type": "object" + }, + "SignInResponse": { + "description": "Response schema for successful sign in", + "example": { + "access_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhc3RyYSIsImV4cCI6MTc1MjYxMTYwOCwiaWF0IjoxNzUyNjEwNzA4LCJpc3MiOiJhdGxhcyIsImp0aSI6IjYyNjM2ZWFmLTVmZGQtNGU2My05ZmI1LWQyZjYwNmQzOGUzNSIsIm5iZiI6MTc1MjYxMDcwNywic3ViIjoiZTEzODdjYWUtYWMxZC00YWViLThlMTMtZmYxYjNkZDE1Y2E0IiwidHlwIjoiYWNjZXNzIn0.bAF6nLXPlHH80jhueetNyC5jZQ4rXXO1MO63izQ-7x98flalF6IGxc8v3HGLSRfF7s3cXYVOteeSvUUUqbx60A", + "session_id": "e1387cae-ac1d-4aeb-8e13-ff1b3dd15ca4" + }, + "properties": { + "access_token": { + "description": "Access token", + "type": "string" + }, + "session_id": { + "description": "User session ID", + "type": "integer" + } + }, + "required": [ + "access_token", + "session_id" + ], + "title": "SignInResponse", + "type": "object" + }, + "UnauthorizedResponse": { + "description": "Unauthorized response schema", + "properties": { + "error": { + "description": "Unauthorized error message", + "type": "string" + } + }, + "required": [ + "error" + ], + "title": "UnauthorizedResponse", + "type": "object" + }, + "ErrorResponse": { + "description": "Error response schema", + "properties": { + "error": { + "description": "Error message", + "type": "string" + } + }, + "required": [ + "error" + ], + "title": "ErrorResponse", + "type": "object" + }, + "SuccessfulRefreshResponse": { + "description": "Response schema for successful token refresh", + "example": { + "access_token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhc3RyYSIsImV4cCI6MTc1MjYxMTY5NCwiaWF0IjoxNzUyNjEwNzk0LCJpc3MiOiJhdGxhcyIsImp0aSI6ImYwOTg4MDMzLTlhNDktNGUzZC04M2U5LWE3NDVkZDkwYmY5ZiIsIm5iZiI6MTc1MjYxMDc5Mywic3ViIjoiZTEzODdjYWUtYWMxZC00YWViLThlMTMtZmYxYjNkZDE1Y2E0IiwidHlwIjoiYWNjZXNzIn0.ztcw5nZ3cdI1v5iTU0ZHyx-xZgWukxeFpuMulhvar7iRfSubBztlggVxpVM8bD-ulmujuX1i3-ksbfSdpNYMTQ" + }, + "properties": { + "access_token": { + "description": "New access token", + "type": "string" + } + }, + "required": [ + "access_token" + ], + "title": "SuccessfulRefreshResponse", + "type": "object" + }, + "SignOutResponse": { + "description": "Response schema for successful sign out", + "example": { + "message": "Signed out successfully" + }, + "properties": { + "message": { + "description": "Message indicating successful sign out", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "SignOutResponse", + "type": "object" + }, + "UserSessionsResponse": { + "description": "Response schema for a list of user sessions", + "example": { + "sessions": [ + { + "first_seen": "2025-07-15T20:13:41Z", + "id": "8fd2bef3-f1eb-4bf2-aade-f3ae80e0563d", + "ip": "127.0.0.1", + "user_agent": "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0", + "user_browser": "Firefox", + "user_os": "Linux" + } + ] + }, + "properties": { + "sessions": { + "description": "List of user sessions", + "items": { + "$ref": "#/definitions/UserSession" + }, + "type": "array" + } + }, + "required": [ + "sessions" + ], + "title": "UserSessionsResponse", + "type": "object" + }, + "NoContentResponse": { + "description": "Response schema for no content", + "example": {}, + "properties": { + "message": { + "description": "Message indicating no content", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "NoContentResponse", + "type": "object" + }, + "ResetPasswordResponse": { + "description": "Response schema for successful password reset", + "example": { + "message": "Password reset successfully" + }, + "properties": { + "message": { + "description": "Message indicating successful password reset", + "type": "string" + } + }, + "required": [ + "message" + ], + "title": "ResetPasswordResponse", + "type": "object" + }, + "UserSession": { + "description": "User session schema", + "properties": { + "first_seen": { + "description": "First seen timestamp", + "format": "date-time", + "type": "string" + }, + "id": { + "description": "Session ID", + "type": "integer" + }, + "ip": { + "description": "IP address of the session", + "type": "string" + }, + "user_agent": { + "description": "User agent string", + "type": "string" + }, + "user_browser": { + "description": "Browser of the user agent", + "type": "string" + }, + "user_os": { + "description": "Operating system of the user agent", + "type": "string" + } + }, + "required": [ + "first_seen", + "user_os", + "user_browser", + "user_agent", + "ip", + "id" + ], + "title": "User Session", + "type": "object" + } + }, + "paths": { + "/v1/auth/forgot_password": { + "post": { + "description": "Sends password reset instructions to the user via email.", + "operationId": "forgot_password", + "parameters": [ + { + "description": "User email", + "in": "query", + "name": "email", + "required": true, + "type": "string" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "No content", + "schema": { + "$ref": "#/definitions/NoContentResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + } + }, + "summary": "Request password reset", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/me": { + "get": { + "description": "Returns the user in the current session.", + "operationId": "me", + "parameters": [], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "User returned succesfully", + "schema": { + "$ref": "#/definitions/User" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "User in the current session", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/refresh": { + "post": { + "description": "Refresh access token with a refresh token cookie.", + "operationId": "refresh_token", + "parameters": [], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful refresh", + "schema": { + "$ref": "#/definitions/SuccessfulRefreshResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + } + }, + "summary": "Refresh access token", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/reset_password": { + "post": { + "description": "Sends a request to reset user's password.", + "operationId": "reset_password", + "parameters": [ + { + "description": "Access token", + "in": "query", + "name": "token", + "required": true, + "type": "string" + }, + { + "description": "New password", + "in": "query", + "name": "password", + "required": true, + "type": "string" + }, + { + "description": "New password confirmation", + "in": "query", + "name": "password_confirmation", + "required": true, + "type": "string" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Password succesfully reset", + "schema": { + "$ref": "#/definitions/ResetPasswordResponse" + } + }, + "404": { + "description": "Invalid or expired reset token", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "summary": "Reset password", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/sessions": { + "get": { + "description": "Returns all the user sessions.", + "operationId": "sessions", + "parameters": [], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Sessions succesfully returned", + "schema": { + "$ref": "#/definitions/UserSessionsResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "User sessions", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/sign_in": { + "post": { + "description": "Sign in a user. Returns an access token.", + "operationId": "sign_in", + "parameters": [ + { + "description": "User email", + "in": "query", + "name": "email", + "required": true, + "type": "string" + }, + { + "description": "User password", + "in": "query", + "name": "password", + "required": true, + "type": "string" + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "Successful sign in", + "schema": { + "$ref": "#/definitions/SignInResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + }, + "500": { + "description": "Failed to create user session", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "summary": "Sign in a user", + "tags": [ + "Authentication" + ] + } + }, + "/v1/auth/sign_out": { + "post": { + "description": "Signs out the user.", + "operationId": "sign_out", + "parameters": [], + "produces": [ + "application/json" + ], + "responses": { + "204": { + "description": "No content - Signed out successfully", + "schema": { + "$ref": "#/definitions/SignOutResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/UnauthorizedResponse" + } + }, + "500": { + "description": "Failed to sign out", + "schema": { + "$ref": "#/definitions/ErrorResponse" + } + } + }, + "security": [ + { + "Bearer": [] + } + ], + "summary": "Sign out", + "tags": [ + "Authentication" + ] + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "Bearer": { + "in": "header", + "name": "Authorization", + "type": "apiKey" + } + } +} \ No newline at end of file