{"openapi":"3.1.0","info":{"title":"Auth Service API","description":"Multi-tenant authentication and authorization microservice for SaaS applications","contact":{"name":"Ivan Hernandez","url":"https://ivanhernandez.dev"},"license":{"name":"CC BY-NC 4.0","url":"https://creativecommons.org/licenses/by-nc/4.0/"},"version":"1.0.0"},"servers":[{"url":"http://auth-service.ivanhernandez.dev","description":"Generated server url"}],"tags":[{"name":"Users","description":"User management endpoints"},{"name":"Authentication","description":"Authentication and authorization endpoints"},{"name":"Tenants","description":"Tenant management endpoints"}],"paths":{"/api/v1/users/me":{"get":{"tags":["Users"],"summary":"Get current user profile","description":"Returns the profile of the authenticated user","operationId":"getProfile","responses":{"200":{"description":"User profile retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"401":{"description":"Not authenticated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Access denied","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearerAuth":[]}]},"put":{"tags":["Users"],"summary":"Update current user profile","description":"Updates the profile of the authenticated user","operationId":"updateProfile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserProfileRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Not authenticated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Access denied","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"User profile updated successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/users/me/password":{"put":{"tags":["Users"],"summary":"Change password","description":"Changes the password of the authenticated user","operationId":"changePassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"User not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Access denied","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"Password changed successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"401":{"description":"Not authenticated or wrong current password","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/tenants":{"post":{"tags":["Tenants"],"summary":"Create a new tenant","description":"Creates a new tenant organization","operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTenantRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"409":{"description":"Tenant with this slug already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"201":{"description":"Tenant created successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/TenantResponse"}}}},"429":{"description":"Too many requests","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/verify-email":{"get":{"tags":["Authentication"],"summary":"Verify email via link","description":"Verifies the user's email address using the verification token from URL","operationId":"verifyEmailViaLink","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Email verified successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Invalid token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Token expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"tags":["Authentication"],"summary":"Verify email","description":"Verifies the user's email address using the verification token","operationId":"verifyEmail","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"}}},"required":true},"responses":{"200":{"description":"Email verified successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Invalid token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Token expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/register":{"post":{"tags":["Authentication"],"summary":"Register a new user","description":"Creates a new user account in the specified tenant","operationId":"register","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"404":{"description":"Tenant not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"201":{"description":"User registered successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"409":{"description":"User already exists","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many requests","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh token","description":"Generates a new access token using a valid refresh token","operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"200":{"description":"Token refreshed successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid or expired refresh token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/password/reset":{"post":{"tags":["Authentication"],"summary":"Reset password","description":"Resets the user's password using the reset token","operationId":"resetPassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Password reset successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Invalid request data or token","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Token expired","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/password/reset-request":{"post":{"tags":["Authentication"],"summary":"Request password reset","description":"Sends a password reset email to the user","operationId":"requestPasswordReset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestPasswordResetRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"200":{"description":"Password reset email sent (if user exists)","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"429":{"description":"Too many requests","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["Authentication"],"summary":"Logout","description":"Revokes the current session's refresh token only","operationId":"logout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Not authenticated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Access denied","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"Logged out successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/auth/logout-all":{"post":{"tags":["Authentication"],"summary":"Logout from all devices","description":"Revokes all refresh tokens for the current user across all devices","operationId":"logoutAll","responses":{"401":{"description":"Not authenticated","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"Logged out from all devices successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"403":{"description":"Access denied","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"bearerAuth":[]}]}},"/api/v1/auth/login":{"post":{"tags":["Authentication"],"summary":"Login","description":"Authenticates a user and returns access and refresh tokens","operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"400":{"description":"Invalid request data","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"403":{"description":"Email not verified or user/tenant disabled","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid credentials","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"Login successful","content":{"*/*":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"429":{"description":"Too many requests","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"User or tenant not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/introspect":{"post":{"tags":["Authentication"],"summary":"Introspect token","description":"Validates a token and returns user information if valid","operationId":"introspect","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntrospectRequest"}}},"required":true},"responses":{"200":{"description":"Token introspection result","content":{"*/*":{"schema":{"$ref":"#/components/schemas/IntrospectResponse"}}}}}}},"/api/v1/tenants/{slug}":{"get":{"tags":["Tenants"],"summary":"Get tenant by slug","description":"Returns tenant information by its slug","operationId":"getBySlug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"404":{"description":"Tenant not found","content":{"*/*":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"200":{"description":"Tenant retrieved successfully","content":{"*/*":{"schema":{"$ref":"#/components/schemas/TenantResponse"}}}}}}}},"components":{"schemas":{"ErrorResponse":{"description":"Standard error response","properties":{"status":{"description":"HTTP status code","example":404},"message":{"description":"Error message","example":"Resource not found"},"timestamp":{"format":"date-time","description":"Error timestamp"}}},"ValidationErrorResponse":{"description":"Validation error response with field-level errors","properties":{"status":{"description":"HTTP status code","example":400},"message":{"description":"Error message","example":"Validation failed"},"errors":{"type":"object","additionalProperties":{"type":"string"},"description":"Map of field names to error messages","example":{"email":"must be a valid email address","password":"must be at least 8 characters"}},"timestamp":{"format":"date-time","description":"Error timestamp"}}},"UpdateUserProfileRequest":{"type":"object","properties":{"firstName":{"type":"string","maxLength":100,"minLength":0},"lastName":{"type":"string","maxLength":100,"minLength":0}},"required":["firstName","lastName"]},"UserProfileResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"tenantSlug":{"type":"string"},"tenantName":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"emailVerified":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"ChangePasswordRequest":{"type":"object","properties":{"currentPassword":{"type":"string"},"newPassword":{"type":"string","maxLength":2147483647,"minLength":8,"pattern":"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$"}},"required":["currentPassword","newPassword"]},"MessageResponse":{"type":"object","properties":{"status":{"type":"integer","format":"int32"},"message":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}},"CreateTenantRequest":{"type":"object","properties":{"name":{"type":"string","maxLength":100,"minLength":0},"slug":{"type":"string","maxLength":50,"minLength":3,"pattern":"^[a-z0-9-]+$"}},"required":["name","slug"]},"TenantResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"enabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"VerifyEmailRequest":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]},"RegisterRequest":{"type":"object","properties":{"tenantSlug":{"type":"string"},"email":{"type":"string"},"password":{"type":"string","maxLength":2147483647,"minLength":8,"pattern":"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$"},"firstName":{"type":"string","maxLength":100,"minLength":0},"lastName":{"type":"string","maxLength":100,"minLength":0}},"required":["email","firstName","lastName","password","tenantSlug"]},"RefreshTokenRequest":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"TokenResponse":{"type":"object","properties":{"accessToken":{"type":"string"},"tokenType":{"type":"string"},"expiresIn":{"type":"integer","format":"int64"}}},"ResetPasswordRequest":{"type":"object","properties":{"token":{"type":"string"},"newPassword":{"type":"string","maxLength":2147483647,"minLength":8,"pattern":"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$"}},"required":["newPassword","token"]},"RequestPasswordResetRequest":{"type":"object","properties":{"tenantSlug":{"type":"string"},"email":{"type":"string"}},"required":["email","tenantSlug"]},"LogoutRequest":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"LoginRequest":{"type":"object","properties":{"tenantSlug":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"}},"required":["email","password","tenantSlug"]},"AuthResponse":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"tokenType":{"type":"string"},"expiresIn":{"type":"integer","format":"int64"},"user":{"$ref":"#/components/schemas/UserProfileResponse"}}},"IntrospectRequest":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]},"IntrospectResponse":{"type":"object","properties":{"active":{"type":"boolean"},"userId":{"type":"string","format":"uuid"},"tenantId":{"type":"string","format":"uuid"},"tenantSlug":{"type":"string"},"email":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}}}}},"securitySchemes":{"bearerAuth":{"type":"http","description":"Enter your JWT token","scheme":"bearer","bearerFormat":"JWT"}}}}