Skip to content

API Key authentication not supported in private mode (LoginRequired=true) #1508

@william-p

Description

@william-p

Describe the bug

When Apache Answer is configured in private mode (LoginRequired=true), API requests authenticated with a valid API Key are rejected with 401 Unauthorized. The middleware EjectUserBySiteInfo() currently only checks for user session cookies and does not verify API Keys, which prevents programmatic access to private instances.

To Reproduce

  1. Configure Apache Answer with LoginRequired=true (private mode)
  2. Create a valid API Key via Admin Panel → API Keys
  3. Make an API request to a public endpoint (e.g., /answer/api/v1/question/page) with the API Key in the Authorization header:
    curl -H "Authorization: sk_xxxxxxxxxxxxx" \
         "http://localhost:9080/answer/api/v1/question/page?page=1&page_size=10"
  4. Observe the response: {"code":401,"reason":"base.unauthorized_error","msg":"Unauthorized.","data":null}

Expected behavior

API requests with a valid API Key should be allowed to access public endpoints, even when the site is in private mode. API Keys are specifically designed for programmatic access and should bypass the user session requirement.

Actual Behavior

All API requests without a user session cookie are rejected with 401 Unauthorized, regardless of whether a valid API Key is provided.

Root Cause

File: internal/base/middleware/auth.go (lines 79-108)

The EjectUserBySiteInfo() middleware checks if the site is in private mode and then verifies only user sessions via GetUserInfoFromContext(ctx). It does not check for API Keys before rejecting the request.

func (am *AuthUserMiddleware) EjectUserBySiteInfo() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        mustLogin := false
        siteInfo, _ := am.siteInfoCommonService.GetSiteSecurity(ctx)
        if siteInfo != nil {
            mustLogin = siteInfo.LoginRequired
        }
        if !mustLogin {
            ctx.Next()
            return
        }

        // If site in private mode, user must login.
        userInfo := GetUserInfoFromContext(ctx)  // ❌ Only checks user session
        if userInfo == nil {
            handler.HandleResponse(ctx, errors.Unauthorized(reason.UnauthorizedError), nil)
            ctx.Abort()
            return
        }
        // ...
    }
}

Impact

This limitation currently prevents some valuable use cases for private Answer instances:

  • Automated data synchronization with external systems
  • Integration with third-party tools and services
  • Headless/API-only clients for programmatic access
  • CI/CD pipelines and automated testing

Supporting API Keys in private mode would enable these scenarios while maintaining the security benefits of restricted access.

Environment

  • Apache Answer version: [latest from main branch]
  • Configuration: LoginRequired=true (private mode)
  • API Key scope: read-only or read-write

Workaround

Currently, the only workaround is to disable private mode (LoginRequired=false), which is not ideal for instances that need to restrict public access.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions