openapi: 3.0.3
info:
  title: PressMatrix Authorization API
  version: 2.0.0
  description: |
    Customer-implemented authorization API for PressMatrix.

    This API is not provided or operated by PressMatrix. The customer must implement and expose the described endpoints in their own infrastructure.

    This API does not handle end-user login. Authentication happens upstream against the customer's Identity Provider (IdP).
    PressMatrix currently supports the internal `PX-User` provider, `Auth0`, and `Keycloak` for this OIDC integration.
    Additional providers are in preparation and may be added for customer projects on request.

    PressMatrix sends:
    - an access token in the `Authorization: Bearer <access_token>` header
    - an optional OpenID Connect ID Token in the `OIDC-ID-Token` header, if available and required by the customer integration
    - business-specific resource metadata in the JSON request body

    The customer implementation validates the access token according to its own setup and decides whether access is granted.

    Important response semantics:
    - `200` + `{ "granted": true }` = authenticated request, access allowed
    - `200` + `{ "granted": false }` = authenticated request, access denied by business rules
    - `401` = missing, malformed, expired, or otherwise invalid bearer token
    - `400` = malformed request payload

    Profile token:
    Every API call requires a `profile_token` in the URL. This token uniquely identifies the publication or tenant.

    Example browser URL:
    `https://bc.pressmatrix.com/de/profiles/<profile_token>/editions`
  contact:
    name: PressMatrix API Doc
    url: https://www.docs.pressmatrix.com/authentication-api
servers:
  - url: https://{auth-server-url}/pmx-api/v2
    description: Customer authorization server
    variables:
      auth-server-url:
        default: api.your-domain.com
        description: Customer authorization server URL
      profile_token:
        default: a1b2c3d4e5f6
        description: Token uniquely identifying the publication / tenant
security:
  - bearerAuth: []
paths:
  /{profile_token}/authorize:
    post:
      tags:
        - Authorization
      summary: Authorize access to an issue
      description: |
        Checks whether the authenticated user may access a specific issue.

        The bearer token represents the upstream-authenticated user.
        The request body contains the business metadata needed for the authorization decision.
      parameters:
        - $ref: '#/components/parameters/ProfileToken'
        - $ref: '#/components/parameters/OidcIdToken'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthorizeIssueRequest'
            example:
              issue_name: Sample Issue May 2014
              issue_date: '2014-05-01'
              category_name: Sample Category
              category_ids: 20924,20925
              product_id_apple: sample_issue_2014_05
              product_id_google: sample_issue_2014_05
              product_id_amazon: sample_issue_2014_05
              product_id_external: sample_id_1,sample_id_2
      responses:
        '200':
          description: Authorization decision completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthorizeResponse'
              examples:
                granted:
                  summary: Access granted
                  value:
                    granted: true
                denied:
                  summary: Access denied by business rules
                  value:
                    granted: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{profile_token}/authorize_download:
    post:
      tags:
        - Authorization (Optional)
      summary: Authorize access to a download
      description: |
        Checks whether the authenticated user may access a specific download.
      parameters:
        - $ref: '#/components/parameters/ProfileToken'
        - $ref: '#/components/parameters/OidcIdToken'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthorizeDownloadRequest'
            example:
              name: Sample Download May 2014
              date: '2014-05-01'
              category_name: Sample Category
              category_ids: 20924,20925
              product_id_external: sample_id_1,sample_id_2
      responses:
        '200':
          description: Authorization decision completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthorizeResponse'
              examples:
                granted:
                  summary: Access granted
                  value:
                    granted: true
                denied:
                  summary: Access denied by business rules
                  value:
                    granted: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{profile_token}/authorize_chatbot:
    post:
      tags:
        - Authorization (Optional)
      summary: Authorize access to a chatbot
      description: |
        Checks whether the authenticated user may access a specific chatbot.
      parameters:
        - $ref: '#/components/parameters/ProfileToken'
        - $ref: '#/components/parameters/OidcIdToken'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthorizeChatbotRequest'
            example:
              name: Chatty
              uuid: 4ea94fb1-7d9d-4e6d-ab57-90d7e7b31b2e
              product_id_external: com.pressmatrix.staging.chatbot.001
      responses:
        '200':
          description: Authorization decision completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthorizeResponse'
              examples:
                granted:
                  summary: Access granted
                  value:
                    granted: true
                denied:
                  summary: Access denied by business rules
                  value:
                    granted: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{profile_token}/authorize_article:
    post:
      tags:
        - Authorization (Optional)
      summary: Authorize access to an article
      description: |
        Checks whether the authenticated user may access a specific article.
      parameters:
        - $ref: '#/components/parameters/ProfileToken'
        - $ref: '#/components/parameters/OidcIdToken'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AuthorizeArticleRequest'
            example:
              name: Article name
              date: '2014-05-01'
              category_name: Sample Category
              category_ids: 20924,20925
              product_id_apple: sample_issue_2014_05
              product_id_google: sample_issue_2014_05
              product_id_amazon: sample_issue_2014_05
              product_id_external: sample_id_1,sample_id_2
      responses:
        '200':
          description: Authorization decision completed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthorizeResponse'
              examples:
                granted:
                  summary: Access granted
                  value:
                    granted: true
                denied:
                  summary: Access denied by business rules
                  value:
                    granted: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /{profile_token}/issues:
    post:
      tags:
        - Optional Features
      summary: Get accessible issues
      description: |
        Optional feature.

        Returns a list of issue product IDs accessible to the authenticated user.
        This feature must be explicitly activated by PressMatrix.
      parameters:
        - $ref: '#/components/parameters/ProfileToken'
        - $ref: '#/components/parameters/OidcIdToken'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IssuesRequest'
            example: {}
      responses:
        '200':
          description: List of accessible issues returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IssuesResponse'
              example:
                issues:
                  - sample_issue_2014_01
                  - sample_issue_2014_02
                  - sample_issue_2014_03
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: access token
      description: |
        Access token issued by the configured OIDC provider.

        PressMatrix sends the token in the `Authorization` header:
        `Authorization: Bearer <access_token>`

        If the customer integration requires the original OpenID Connect ID Token, PressMatrix may additionally forward it in the optional `OIDC-ID-Token` header.

        `Bearer` is the HTTP authorization scheme, not the token format.
        Token format is intentionally not fixed by this contract.
        The customer may use a JWT access token or an opaque access token.
  parameters:
    ProfileToken:
      name: profile_token
      in: path
      required: true
      description: |
        Unique profile token for the publication / tenant.
        This token is provided during onboarding by PressMatrix.
      schema:
        type: string
        pattern: '^[A-Za-z0-9_-]+$'
        example: a1b2c3d4e5f6
    OidcIdToken:
      name: OIDC-ID-Token
      in: header
      required: false
      description: |
        Optional OpenID Connect ID Token issued by the customer's Identity Provider.
        PressMatrix forwards this token without modification when available and required by the customer integration.
        The access token in the Authorization header remains the primary authorization credential.
      schema:
        type: string
  responses:
    BadRequest:
      description: Malformed request body or missing required business fields
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: invalid_request
            message: Missing required field issue_name.
    Unauthorized:
      description: Missing, malformed, expired, or otherwise invalid bearer token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: unauthorized
            message: Missing or invalid bearer token.
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: internal_error
            message: An unexpected error occurred.
  schemas:
    AuthorizeIssueRequest:
      type: object
      required:
        - issue_name
        - issue_date
      properties:
        issue_name:
          type: string
          description: Display name of the issue to access
          example: Sample Issue May 2014
        issue_date:
          type: string
          format: date
          description: Publication date of the issue (YYYY-MM-DD)
          example: '2014-05-01'
        category_name:
          type: string
          description: Display name of the category the issue belongs to
          example: Sample Category
        category_ids:
          type: string
          description: Comma-separated list of all category IDs
          example: 20924,20925
        product_id_apple:
          type: string
          description: Apple product ID
          example: sample_issue_2014_05
        product_id_google:
          type: string
          description: Google product ID
          example: sample_issue_2014_05
        product_id_amazon:
          type: string
          description: Amazon product ID
          example: sample_issue_2014_05
        product_id_external:
          type: string
          description: External (custom) product ID, optionally comma-separated
          example: sample_id_1,sample_id_2
      additionalProperties: false

    AuthorizeDownloadRequest:
      type: object
      required:
        - name
        - date
      properties:
        name:
          type: string
          description: Display name of the download to access
          example: Sample Download May 2014
        date:
          type: string
          format: date
          description: Publication date (YYYY-MM-DD)
          example: '2014-05-01'
        category_name:
          type: string
          description: Display name of the category
          example: Sample Category
        category_ids:
          type: string
          description: Comma-separated list of all category IDs
          example: 20924,20925
        product_id_external:
          type: string
          description: External (custom) product ID, optionally comma-separated
          example: sample_id_1,sample_id_2
      additionalProperties: false

    AuthorizeChatbotRequest:
      type: object
      required:
        - name
        - uuid
      properties:
        name:
          type: string
          description: Display name of the chatbot
          example: Chatty
        uuid:
          type: string
          format: uuid
          description: Chatbot UUID
          example: 4ea94fb1-7d9d-4e6d-ab57-90d7e7b31b2e
        product_id_external:
          type: string
          description: External (custom) product ID
          example: com.pressmatrix.staging.chatbot.001
      additionalProperties: false

    AuthorizeArticleRequest:
      type: object
      required:
        - name
        - date
      properties:
        name:
          type: string
          description: Display name of the article to access
          example: Article name
        date:
          type: string
          format: date
          description: Publication date (YYYY-MM-DD)
          example: '2014-05-01'
        category_name:
          type: string
          description: Display name of the category
          example: Sample Category
        category_ids:
          type: string
          description: Comma-separated list of all category IDs
          example: 20924,20925
        product_id_apple:
          type: string
          description: Apple product ID
          example: sample_issue_2014_05
        product_id_google:
          type: string
          description: Google product ID
          example: sample_issue_2014_05
        product_id_amazon:
          type: string
          description: Amazon product ID
          example: sample_issue_2014_05
        product_id_external:
          type: string
          description: External (custom) product ID, optionally comma-separated
          example: sample_id_1,sample_id_2
      additionalProperties: false

    IssuesRequest:
      type: object
      description: |
        No business fields are required. The authenticated user is derived from the bearer token.
      additionalProperties: false

    AuthorizeResponse:
      type: object
      required:
        - granted
      properties:
        granted:
          type: boolean
          description: Whether access is granted
          example: true
      additionalProperties: false

    IssuesResponse:
      type: object
      required:
        - issues
      properties:
        issues:
          type: array
          description: Product IDs of accessible issues
          items:
            type: string
          example:
            - sample_issue_2014_01
            - sample_issue_2014_02
            - sample_issue_2014_03
      additionalProperties: false

    ErrorResponse:
      type: object
      required:
        - error
        - message
      properties:
        error:
          type: string
          example: unauthorized
        message:
          type: string
          example: Missing or invalid bearer token.
      additionalProperties: false
tags:
  - name: Authorization
    description: Endpoints for content access authorization
  - name: Authorization (Optional)
    description: Optional endpoints for content access authorization
  - name: Optional Features
    description: Optional features that must be explicitly activated
