How to Troubleshoot Gitea Authentication Issues: A Complete Technical Guide

To troubleshoot Gitea authentication issues, enable trace logging to capture the authPathDetector evaluation, verify credential flows through the pluggable auth methods in services/auth/auth.go, and inspect token parsing logic in oauth2.go to isolate failures in path detection, validation, or session persistence.

Gitea’s authentication system relies on a pluggable "auth method" framework that processes requests through a structured detection and validation pipeline. When users report login failures or API access denials, understanding the internal flow—from path detection in services/auth/auth.go to token parsing in oauth2.go—is essential for rapid diagnosis. This guide provides a source-code-backed approach to identify and resolve authentication failures in the go-gitea/gitea repository.

Understanding Gitea's Authentication Architecture

Path Detection and Protected Routes

In services/auth/auth.go, the newAuthPathDetector function examines incoming HTTP requests to determine if authentication is required. The detector returns true for protected paths including API endpoints, Git raw content, archives, attachments, and feeds (lines 63-80). If the detector misidentifies a path as public, the request bypasses authentication entirely.

Auth Method Selection and Verification

Once a path is flagged for protection, Gitea iterates through registered auth.Method implementations until one returns a valid user. These methods include password-based, LDAP, SMTP, OAuth2, WebAuthn, and token-based API authentication. Each method implements its own credential validation logic in the services/auth/source/ directory.

Session Management and Regeneration

Successful authentication triggers handleSignIn (lines 23-64 in auth.go), which regenerates the session ID to prevent fixation attacks. This function stores the user ID, username, and language preferences, while clearing stale WebAuthn assertion data. Failures here typically manifest as "Error regenerating session" messages in the logs.

Common Authentication Failure Points

  • Invalid credentials (password, LDAP, SMTP): Occurs when passwords mismatch, accounts are disabled, or external sources fail to connect. Check services/auth/source/ldap/source_authenticate.go and corresponding SMTP authentication handlers to verify bind DN settings and TLS configurations.

  • API token rejected: Results from missing tokens, expired grants, or DISABLE_QUERY_AUTH_TOKEN being set to true while clients still transmit tokens via query strings. Inspect the parseToken function in services/auth/oauth2.go (lines 84-98) to confirm token extraction logic.

  • OAuth2 login failures: Stems from provider misconfiguration (client ID/secret mismatches), invalid redirect URLs, or JWT signature verification failures. The GetOAuthAccessTokenScopeAndUserID function (lines 40-55 in oauth2.go) validates token signatures and expiration claims.

  • WebAuthn/2FA failures: Caused by missing assertions, incorrect FIDO2 device registration, or lost session cookies. Note that handleSignIn explicitly clears webauthnAssertion data during sign-in, requiring clients to maintain persistent session cookies.

  • Git raw/attachment 401 errors: Happens when authPathDetector fails to recognize paths as authenticated resources. Verify isGitRawOrAttachPath and isArchivePath methods (lines 96-112 in auth.go) if protected assets return unexpected 401 status codes.

  • Session regeneration errors: Indicate underlying storage failures in Redis, memory, or file-based session stores when session.RegenerateSession cannot create new session IDs.

  • Feed authentication disabled: Occurs when setting.Other.EnableFeed is false but clients attempt to access RSS/Atom endpoints. Check authPathDetector.isFeedRequest (lines 82-90 in auth.go).

Step-by-Step Troubleshooting Checklist

  1. Enable detailed logging: Set APP_LOG_LEVEL=trace in app.ini and restart Gitea. Monitor output from modules/log for Trace, Warn, and Error entries during authentication attempts.

  2. Verify auth source configuration: Review [auth] sections in app.ini (e.g., [ldap], [smtp]). Confirm host, port, bind DN, TLS settings, and network connectivity between Gitea and external authentication servers.

  3. Validate OAuth2 settings: Inspect the [oauth2] section for CLIENT_ID, CLIENT_SECRET, and JWT_SECRET mismatches. Ensure signing keys align with external provider configurations.

  4. Inspect token contents: Decode JWT tokens using standard JWT debuggers to verify exp, iat, and grant_id claims without requiring the signing secret.

  5. Query the database directly:

    • Check access_token table for API tokens: SELECT * FROM access_token WHERE sha256 = ...
    • Verify OAuth grants in oauth2_grant: SELECT * FROM oauth2_grant WHERE id = ...
    • Confirm user status in user table: is_active, login_type, and login_source columns
  6. Verify session persistence: Ensure the i_like_gitea cookie (or the name specified in setting.CookieUsername) accompanies every request. Validate that the session store (memory, Redis, or file) has write permissions and adequate storage.

  7. Test request paths manually: Use curl -v to examine endpoints, observing returned status codes and WWW-Authenticate headers for detailed rejection reasons.

  8. Run integration tests: Execute make test-integration to exercise LDAP, OAuth2, and token authentication paths via tests/integration/auth_ldap_test.go and oauth_test.go.

Debugging with Code Examples

Manually Parsing Tokens from HTTP Requests

When debugging API authentication, you can replicate Gitea's internal token extraction logic:

// Re-use Gitea's internal helper – it respects DISABLE_QUERY_AUTH_TOKEN.
token, ok := parseToken(req)
if !ok {
    // No token found – authentication will be skipped for this request.
}

Reference: parseToken in services/auth/oauth2.go (lines 84-100).

Forcing Session Regeneration After Password Changes

To manually regenerate sessions during custom authentication workflows:

// Regenerate the session (same logic as handleSignIn)
newSess, err := session.RegenerateSession(w, r)
if err != nil {
    log.Error("Failed to regenerate session: %v", err)
}
sess = newSess
sess.Set("uid", user.ID)
sess.Set("uname", user.Name)

Reference: handleSignIn in services/auth/auth.go (lines 23-45).

Decoding OAuth2 JWT Access Tokens for Debugging

For troubleshooting OAuth2 token validation:

import "code.gitea.io/gitea/services/oauth2_provider"

func decodeAccessToken(tokenStr string) {
    token, err := oauth2_provider.ParseToken(tokenStr, oauth2_provider.DefaultSigningKey)
    if err != nil {
        log.Error("Invalid JWT: %v", err)
        return
    }
    log.Info("GrantID=%d, Owner=%d, Scope=%s", token.GrantID, token.OwnerID, token.Scope)
}

Reference: Token parsing in GetOAuthAccessTokenScopeAndUserID (lines 40-55 in oauth2.go).

Checking Whether a Request Requires Authentication

To verify if a specific endpoint triggers authentication checks:

detector := newAuthPathDetector(req)
if detector.isAPIPath() || detector.isGitRawOrAttachPath() ||
   detector.isArchivePath() || detector.isAttachmentDownload() {
    // Authentication required – invoke the appropriate auth method.
}

Reference: authPathDetector methods in services/auth/auth.go (lines 63-112).

Key Source Files for Authentication Debugging

Understanding these core files accelerates troubleshooting:

Summary

  • Gitea uses a pluggable auth framework with authPathDetector determining which requests require authentication based on URL patterns in services/auth/auth.go.
  • Token validation occurs in oauth2.go, where parseToken extracts credentials from headers or query strings, respecting the DISABLE_QUERY_AUTH_TOKEN setting.
  • Session regeneration happens in handleSignIn, which clears WebAuthn data and creates new session IDs to prevent fixation attacks.
  • LDAP and SMTP failures typically stem from connection issues or bind DN misconfigurations in services/auth/source/ directories.
  • Database verification of access_token, oauth2_grant, and user tables often reveals account status or token expiration issues.
  • Trace logging via APP_LOG_LEVEL=trace provides visibility into the authentication decision chain.

Frequently Asked Questions

Why does my API token work in the header but not in the query string?

Gitea respects the DISABLE_QUERY_AUTH_TOKEN setting in app.ini. When enabled, the parseToken function in services/auth/oauth2.go (lines 84-98) ignores tokens passed as query parameters for security reasons. Use the Authorization: Bearer <token> header instead, which is always evaluated regardless of this setting.

How does Gitea decide which requests need authentication?

The newAuthPathDetector function in services/auth/auth.go (lines 63-80) evaluates URL paths against patterns for API endpoints, Git raw content, archives, attachments, and feeds. If a path matches protected patterns, Gitea iterates through registered auth methods until one validates the request, otherwise returning 401 or 403 responses.

What causes "Error regenerating session" during login?

This error originates from handleSignIn in services/auth/auth.go when session.RegenerateSession fails. Common causes include full disk space (for file-based sessions), Redis connection failures, or permission issues on session storage directories. Verify that your configured session store (memory, file, or Redis) is accessible and writable by the Gitea process.

Why does OAuth2 authentication fail with valid credentials?

OAuth2 failures typically occur in GetOAuthAccessTokenScopeAndUserID (lines 40-55 in oauth2.go) due to JWT signature mismatches, expired tokens, or incorrect JWT_SECRET configurations in app.ini. Verify that the [oauth2] section's JWT_SECRET matches between Gitea and your OAuth2 provider, and decode the token to confirm the exp claim has not passed.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →