Files
sure/spec/requests/api/v1/auth_spec.rb
Juan José Mata 9d61216518 Move AI enable endpoint to user scope
Replace /api/v1/auth/enable_ai with /api/v1/user/enable_ai by moving the action into Api::V1::UsersController, updating Flutter client calls, tests, and OpenAPI docs.
2026-02-16 20:18:56 +01:00

221 lines
7.4 KiB
Ruby

# frozen_string_literal: true
require 'swagger_helper'
RSpec.describe 'API V1 Auth', type: :request do
path '/api/v1/auth/signup' do
post 'Sign up a new user' do
tags 'Auth'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, required: true, schema: {
type: :object,
properties: {
user: {
type: :object,
properties: {
email: { type: :string, format: :email, description: 'User email address' },
password: { type: :string, description: 'Password (min 8 chars, mixed case, number, special char)' },
first_name: { type: :string },
last_name: { type: :string }
},
required: %w[email password]
},
device: {
type: :object,
properties: {
device_id: { type: :string, description: 'Unique device identifier' },
device_name: { type: :string, description: 'Human-readable device name' },
device_type: { type: :string, description: 'Device type (e.g. ios, android)' },
os_version: { type: :string },
app_version: { type: :string }
},
required: %w[device_id device_name device_type os_version app_version]
},
invite_code: { type: :string, nullable: true, description: 'Invite code (required when invites are enforced)' }
},
required: %w[user device]
}
response '201', 'user created' do
schema type: :object,
properties: {
access_token: { type: :string },
refresh_token: { type: :string },
token_type: { type: :string },
expires_in: { type: :integer },
created_at: { type: :integer },
user: {
type: :object,
properties: {
id: { type: :string, format: :uuid },
email: { type: :string },
first_name: { type: :string },
last_name: { type: :string },
ui_layout: { type: :string, enum: %w[dashboard intro] },
ai_enabled: { type: :boolean }
}
}
}
run_test!
end
response '422', 'validation error' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
response '403', 'invite code required or invalid' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
end
end
path '/api/v1/auth/login' do
post 'Log in with email and password' do
tags 'Auth'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, required: true, schema: {
type: :object,
properties: {
email: { type: :string, format: :email },
password: { type: :string },
otp_code: { type: :string, nullable: true, description: 'TOTP code if MFA is enabled' },
device: {
type: :object,
properties: {
device_id: { type: :string },
device_name: { type: :string },
device_type: { type: :string },
os_version: { type: :string },
app_version: { type: :string }
},
required: %w[device_id device_name device_type os_version app_version]
}
},
required: %w[email password device]
}
response '200', 'login successful' do
schema type: :object,
properties: {
access_token: { type: :string },
refresh_token: { type: :string },
token_type: { type: :string },
expires_in: { type: :integer },
created_at: { type: :integer },
user: {
type: :object,
properties: {
id: { type: :string, format: :uuid },
email: { type: :string },
first_name: { type: :string },
last_name: { type: :string },
ui_layout: { type: :string, enum: %w[dashboard intro] },
ai_enabled: { type: :boolean }
}
}
}
run_test!
end
response '401', 'invalid credentials or MFA required' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
end
end
path '/api/v1/auth/sso_exchange' do
post 'Exchange mobile SSO authorization code for tokens' do
tags 'Auth'
consumes 'application/json'
produces 'application/json'
description 'Exchanges a one-time authorization code (received via deep link after mobile SSO) for OAuth tokens. The code is single-use and expires after 5 minutes.'
parameter name: :body, in: :body, required: true, schema: {
type: :object,
properties: {
code: { type: :string, description: 'One-time authorization code from mobile SSO callback' }
},
required: %w[code]
}
response '200', 'tokens issued' do
schema type: :object,
properties: {
access_token: { type: :string },
refresh_token: { type: :string },
token_type: { type: :string },
expires_in: { type: :integer },
created_at: { type: :integer },
user: {
type: :object,
properties: {
id: { type: :string, format: :uuid },
email: { type: :string },
first_name: { type: :string },
last_name: { type: :string },
ui_layout: { type: :string, enum: %w[dashboard intro] },
ai_enabled: { type: :boolean }
}
}
}
run_test!
end
response '401', 'invalid or expired code' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
end
end
path '/api/v1/auth/refresh' do
post 'Refresh an access token' do
tags 'Auth'
consumes 'application/json'
produces 'application/json'
parameter name: :body, in: :body, required: true, schema: {
type: :object,
properties: {
refresh_token: { type: :string, description: 'The refresh token from a previous login or refresh' },
device: {
type: :object,
properties: {
device_id: { type: :string }
},
required: %w[device_id]
}
},
required: %w[refresh_token device]
}
response '200', 'token refreshed' do
schema type: :object,
properties: {
access_token: { type: :string },
refresh_token: { type: :string },
token_type: { type: :string },
expires_in: { type: :integer },
created_at: { type: :integer }
}
run_test!
end
response '401', 'invalid refresh token' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
response '400', 'missing refresh token' do
schema '$ref' => '#/components/schemas/ErrorResponse'
run_test!
end
end
end
end