feat: authsession service

This commit is contained in:
Ilia Denisov
2026-04-08 16:23:07 +02:00
committed by GitHub
parent 28f04916af
commit 86a68ed9d0
174 changed files with 31732 additions and 112 deletions
+456
View File
@@ -0,0 +1,456 @@
openapi: 3.0.3
info:
title: Galaxy Auth / Session Service Internal API
version: v1
description: |
This specification documents the implemented `galaxy/authsession` v1
trusted internal REST contract.
Contract rules:
- the internal surface lives under `/api/v1/internal`;
- all request and response bodies are JSON only;
- read operations return canonical session DTO wrappers;
- mutating operations return explicit `200` JSON acknowledgements;
- mutation requests carry audit metadata as `reason_code` and `actor`;
- `BlockUser` accepts exactly one of `user_id` or `email`;
- `ListUserSessions` is newest-first and unpaginated in v1.
tags:
- name: InternalAuthSession
description: Trusted internal session read, revoke, and block operations.
paths:
/api/v1/internal/sessions/{device_session_id}:
get:
tags:
- InternalAuthSession
operationId: getSession
summary: Read one device session
parameters:
- $ref: "#/components/parameters/DeviceSessionID"
responses:
"200":
description: The requested device session.
content:
application/json:
schema:
$ref: "#/components/schemas/GetSessionResponse"
"404":
$ref: "#/components/responses/SessionNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/users/{user_id}/sessions:
get:
tags:
- InternalAuthSession
operationId: listUserSessions
summary: List all active and revoked sessions of one user
description: |
Returns the full v1 session list for one user. Results are ordered from
newest to oldest and are intentionally unpaginated in v1.
parameters:
- $ref: "#/components/parameters/UserID"
responses:
"200":
description: |
Sessions belonging to the requested user. Returns an empty array
when the user has no stored sessions, including unknown `user_id`
values.
content:
application/json:
schema:
$ref: "#/components/schemas/ListUserSessionsResponse"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/sessions/{device_session_id}/revoke:
post:
tags:
- InternalAuthSession
operationId: revokeDeviceSession
summary: Revoke one device session
parameters:
- $ref: "#/components/parameters/DeviceSessionID"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RevokeDeviceSessionRequest"
responses:
"200":
description: Explicit idempotent acknowledgement of the revoke result.
content:
application/json:
schema:
$ref: "#/components/schemas/RevokeDeviceSessionResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/SessionNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/users/{user_id}/sessions/revoke-all:
post:
tags:
- InternalAuthSession
operationId: revokeAllUserSessions
summary: Revoke all sessions of one user
parameters:
- $ref: "#/components/parameters/UserID"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/RevokeAllUserSessionsRequest"
responses:
"200":
description: Explicit idempotent acknowledgement of the bulk revoke result.
content:
application/json:
schema:
$ref: "#/components/schemas/RevokeAllUserSessionsResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/SubjectNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/internal/user-blocks:
post:
tags:
- InternalAuthSession
operationId: blockUser
summary: Block future auth flow for one subject and revoke active sessions
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/BlockUserRequest"
responses:
"200":
description: Explicit idempotent acknowledgement of the block result.
content:
application/json:
schema:
$ref: "#/components/schemas/BlockUserResponse"
"400":
$ref: "#/components/responses/InvalidRequestError"
"404":
$ref: "#/components/responses/SubjectNotFoundError"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
components:
parameters:
DeviceSessionID:
name: device_session_id
in: path
required: true
description: Stable identifier of one device session.
schema:
type: string
UserID:
name: user_id
in: path
required: true
description: Stable identifier of one user.
schema:
type: string
schemas:
Actor:
type: object
additionalProperties: false
required:
- type
properties:
type:
type: string
description: Machine-readable actor type such as `system`, `service`, or `admin`.
id:
type: string
description: Optional stable identifier of the initiating actor.
ErrorResponse:
type: object
additionalProperties: false
required:
- error
properties:
error:
$ref: "#/components/schemas/ErrorBody"
ErrorBody:
type: object
additionalProperties: false
required:
- code
- message
properties:
code:
type: string
description: Stable internal API error code.
message:
type: string
description: Human-readable error description safe for trusted internal callers.
Session:
type: object
additionalProperties: false
required:
- device_session_id
- user_id
- client_public_key
- status
- created_at
properties:
device_session_id:
type: string
user_id:
type: string
client_public_key:
type: string
description: Standard base64-encoded raw 32-byte Ed25519 public key of the device session.
status:
type: string
enum:
- active
- revoked
created_at:
type: string
format: date-time
description: RFC3339 UTC timestamp when the session was created.
revoked_at:
type: string
format: date-time
nullable: true
description: RFC3339 UTC timestamp when the session was revoked.
revoke_reason_code:
type: string
nullable: true
description: Machine-readable revoke reason code when the session is revoked.
revoke_actor_type:
type: string
nullable: true
description: Actor type that initiated the revoke.
revoke_actor_id:
type: string
nullable: true
description: Optional stable actor identifier that initiated the revoke.
GetSessionResponse:
type: object
additionalProperties: false
required:
- session
properties:
session:
$ref: "#/components/schemas/Session"
ListUserSessionsResponse:
type: object
additionalProperties: false
required:
- sessions
properties:
sessions:
type: array
description: Full newest-first session list for the requested user.
items:
$ref: "#/components/schemas/Session"
RevokeDeviceSessionRequest:
type: object
additionalProperties: false
required:
- reason_code
- actor
properties:
reason_code:
type: string
description: Machine-readable revoke reason code.
actor:
$ref: "#/components/schemas/Actor"
RevokeDeviceSessionResponse:
type: object
additionalProperties: false
required:
- outcome
- device_session_id
- affected_session_count
properties:
outcome:
type: string
enum:
- revoked
- already_revoked
device_session_id:
type: string
affected_session_count:
type: integer
format: int64
minimum: 0
RevokeAllUserSessionsRequest:
type: object
additionalProperties: false
required:
- reason_code
- actor
properties:
reason_code:
type: string
description: Machine-readable bulk revoke reason code.
actor:
$ref: "#/components/schemas/Actor"
RevokeAllUserSessionsResponse:
type: object
additionalProperties: false
required:
- outcome
- user_id
- affected_session_count
- affected_device_session_ids
properties:
outcome:
type: string
enum:
- revoked
- no_active_sessions
user_id:
type: string
affected_session_count:
type: integer
format: int64
minimum: 0
affected_device_session_ids:
type: array
items:
type: string
BlockUserRequest:
oneOf:
- $ref: "#/components/schemas/BlockUserByUserIDRequest"
- $ref: "#/components/schemas/BlockUserByEmailRequest"
BlockUserByUserIDRequest:
type: object
additionalProperties: false
required:
- user_id
- reason_code
- actor
properties:
user_id:
type: string
reason_code:
type: string
description: Machine-readable block reason code.
actor:
$ref: "#/components/schemas/Actor"
BlockUserByEmailRequest:
type: object
additionalProperties: false
required:
- email
- reason_code
- actor
properties:
email:
type: string
format: email
reason_code:
type: string
description: Machine-readable block reason code.
actor:
$ref: "#/components/schemas/Actor"
BlockUserResponse:
type: object
additionalProperties: false
required:
- outcome
- subject_kind
- subject_value
- affected_session_count
- affected_device_session_ids
properties:
outcome:
type: string
enum:
- blocked
- already_blocked
subject_kind:
type: string
enum:
- user_id
- email
subject_value:
type: string
affected_session_count:
type: integer
format: int64
minimum: 0
affected_device_session_ids:
type: array
items:
type: string
responses:
InvalidRequestError:
description: Request path, parameters, or body fields are invalid.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
invalidRequest:
value:
error:
code: invalid_request
message: reason_code must not be empty
SessionNotFoundError:
description: The referenced device session does not exist.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
sessionNotFound:
value:
error:
code: session_not_found
message: session not found
SubjectNotFoundError:
description: The referenced internal block or bulk-revoke subject does not exist.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
subjectNotFound:
value:
error:
code: subject_not_found
message: subject not found
ServiceUnavailableError:
description: A required dependency is temporarily unavailable.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
unavailable:
value:
error:
code: service_unavailable
message: service is unavailable
InternalError:
description: Unexpected internal failure while processing the request.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
internal:
value:
error:
code: internal_error
message: internal server error
+284
View File
@@ -0,0 +1,284 @@
openapi: 3.0.3
info:
title: Galaxy Auth / Session Service Public API
version: v1
description: |
This specification documents the implemented `galaxy/authsession` v1
public REST contract for the e-mail-code flow consumed by
`galaxy/gateway`.
Implemented public operations:
- `POST /api/v1/public/auth/send-email-code`
- `POST /api/v1/public/auth/confirm-email-code`
Contract rules:
- requests and responses are JSON only;
- request schemas reject unknown fields via `additionalProperties: false`;
- empty bodies, malformed JSON, multiple JSON objects, and unknown fields
are rejected as `400 invalid_request`;
- surrounding ASCII/Unicode whitespace is trimmed from input string fields
before validation;
- `send-email-code` remains success-shaped for existing, new, and blocked
e-mail addresses;
- `confirm-email-code` returns a ready `device_session_id` synchronously on
success.
tags:
- name: PublicAuth
description: Public unauthenticated e-mail-code authentication endpoints.
paths:
/api/v1/public/auth/send-email-code:
post:
tags:
- PublicAuth
operationId: sendEmailCode
summary: Start a public e-mail login challenge
description: |
Accepts one client e-mail address and starts the public challenge flow.
The outward result remains success-shaped even when the underlying
policy suppresses mail delivery for anti-enumeration purposes.
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendEmailCodeRequest"
examples:
default:
value:
email: pilot@example.com
responses:
"200":
description: The login challenge was accepted.
content:
application/json:
schema:
$ref: "#/components/schemas/SendEmailCodeResponse"
examples:
accepted:
value:
challenge_id: challenge-123
"400":
$ref: "#/components/responses/SendEmailCodeBadRequestError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
/api/v1/public/auth/confirm-email-code:
post:
tags:
- PublicAuth
operationId: confirmEmailCode
summary: Confirm a public e-mail login challenge
description: |
Completes a previously issued `challenge_id`, validates the submitted
verification code, registers the standard base64-encoded raw 32-byte
Ed25519 `client_public_key`, and returns the created
`device_session_id`.
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ConfirmEmailCodeRequest"
examples:
default:
value:
challenge_id: challenge-123
code: "123456"
client_public_key: 11qYAYdk8v3K6Yw8QK6ZlQ2nP4Wm8Cq5g1H0K8vT9no=
responses:
"200":
description: The device session was created and is ready for use.
content:
application/json:
schema:
$ref: "#/components/schemas/ConfirmEmailCodeResponse"
examples:
accepted:
value:
device_session_id: device-session-123
"400":
$ref: "#/components/responses/ConfirmEmailCodeBadRequestError"
"403":
$ref: "#/components/responses/BlockedByPolicyError"
"404":
$ref: "#/components/responses/ChallengeNotFoundError"
"409":
$ref: "#/components/responses/SessionLimitExceededError"
"410":
$ref: "#/components/responses/ChallengeExpiredError"
"503":
$ref: "#/components/responses/ServiceUnavailableError"
components:
schemas:
SendEmailCodeRequest:
type: object
additionalProperties: false
required:
- email
properties:
email:
type: string
description: Single client e-mail address that should receive the login code.
format: email
SendEmailCodeResponse:
type: object
additionalProperties: false
required:
- challenge_id
properties:
challenge_id:
type: string
description: Opaque challenge identifier returned by the Auth / Session Service.
ConfirmEmailCodeRequest:
type: object
additionalProperties: false
required:
- challenge_id
- code
- client_public_key
properties:
challenge_id:
type: string
description: Opaque challenge identifier previously returned by send-email-code.
code:
type: string
description: Verification code delivered to the client.
client_public_key:
type: string
description: Standard base64-encoded raw 32-byte Ed25519 public key registered for the new device session.
ConfirmEmailCodeResponse:
type: object
additionalProperties: false
required:
- device_session_id
properties:
device_session_id:
type: string
description: Stable identifier of the created device session.
ErrorResponse:
type: object
additionalProperties: false
required:
- error
properties:
error:
$ref: "#/components/schemas/ErrorBody"
ErrorBody:
type: object
additionalProperties: false
required:
- code
- message
properties:
code:
type: string
description: |
Stable gateway-generated or client-safe auth-adapter-projected
error code. Gateway-generated values include `invalid_request`,
`not_found`, `method_not_allowed`, `request_too_large`,
`rate_limited`, `internal_error`, and `service_unavailable`.
message:
type: string
description: Human-readable client-safe error description.
responses:
SendEmailCodeBadRequestError:
description: |
Request body or field values are invalid. This includes empty bodies,
malformed JSON, multiple JSON objects, unknown fields, and invalid
`email`.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
invalidRequest:
value:
error:
code: invalid_request
message: email must be a single valid email address
ConfirmEmailCodeBadRequestError:
description: |
Request body or field values are invalid. This includes malformed
request payloads, invalid confirmation codes, and malformed
`client_public_key` values.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
invalidRequest:
value:
error:
code: invalid_request
message: challenge_id must not be empty
invalidCode:
value:
error:
code: invalid_code
message: confirmation code is invalid
invalidClientPublicKey:
value:
error:
code: invalid_client_public_key
message: client_public_key is not a valid base64-encoded raw 32-byte Ed25519 public key
ChallengeNotFoundError:
description: The referenced challenge does not exist.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
notFound:
value:
error:
code: challenge_not_found
message: challenge not found
ChallengeExpiredError:
description: The referenced challenge has expired and can no longer be confirmed.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
expired:
value:
error:
code: challenge_expired
message: challenge expired
BlockedByPolicyError:
description: The auth flow is denied by account or registration policy.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
blocked:
value:
error:
code: blocked_by_policy
message: authentication is blocked by policy
SessionLimitExceededError:
description: Creating another active device session would exceed the configured limit.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
limitExceeded:
value:
error:
code: session_limit_exceeded
message: active session limit would be exceeded
ServiceUnavailableError:
description: The service is temporarily unable to serve the request safely.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
examples:
unavailable:
value:
error:
code: service_unavailable
message: service is unavailable