# How Gitea Handles User Authentication Flows: A Deep Dive into the go-gitea/gitea Source Code

> Explore how Gitea handles user authentication using a three-phase pipeline. Dive into the go-gitea/gitea source code for detailed insights into request entry, credential validation, and session management.

- Repository: [Gitea/gitea](https://github.com/go-gitea/gitea)
- Tags: deep-dive
- Published: 2026-03-07

---

**Gitea processes every authentication request through a three-phase pipeline—request entry, credential validation against pluggable auth sources, and session establishment with optional two-factor enforcement—implemented across [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go), [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go), and [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go).**

Gitea's authentication system provides a flexible, pluggable architecture that supports everything from local database credentials to LDAP and OAuth2 providers. Understanding how Gitea handles user authentication flows requires examining the interplay between HTTP routers, authentication methods, and session management in the go-gitea/gitea codebase.

## The Three-Phase Authentication Architecture

Every incoming request passes through a standardized authentication pipeline divided into three distinct phases. This architecture ensures consistent security enforcement regardless of whether the user is accessing the web UI, API endpoints, or static resources.

| Phase | Description | Key Source Files |
|-------|-------------|------------------|
| **Request Entry** | HTTP router directs traffic to authentication-aware handlers | [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go) – `SignIn`, `autoSignIn` |
| **Credential Validation** | Auth methods extract and verify credentials (cookies, headers, tokens) | [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go), [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go), `services/auth/oauth2` |
| **Session & Post-Login** | Session creation, 2FA/WebAuthn enforcement, and redirection | [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go) – `handleSignInFull`, `updateSession` |

## Phase 1: Request Entry and Auto-Login

When a request first hits the Gitea server, the routing layer determines whether the user already has valid credentials or must authenticate.

### Automatic "Remember Me" Validation

Before presenting a login page, Gitea attempts silent authentication via the `autoSignIn` function. This mechanism checks for the presence of a `remember_token` cookie established during previous sessions.

```go
t, err := auth_service.CheckAuthToken(ctx, ctx.GetSiteCookie(setting.CookieRememberName))

```

If the token validates successfully, `autoSignIn` generates a fresh token, populates the session with the user's identity (`uid`, `uname`, `userHasTwoFactorAuth`), and treats the request as authenticated. This logic resides in [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go) and allows returning users to bypass the login screen entirely while maintaining security through rotating tokens.

## Phase 2: Credential Validation

When automatic login fails or the user explicitly submits credentials, Gitea enters the validation phase. The system supports multiple authentication methods that extract credentials from different transport mechanisms.

### Standard Username/Password Authentication

Web UI login requests POST to `/user/login`, which invokes `SignInPost` in [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go). This handler delegates credential verification to `UserSignIn` in [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go):

```go
u, source, err := auth_service.UserSignIn(ctx, form.UserName, form.Password)

```

The `UserSignIn` function implements intelligent identifier resolution:

- **Email detection**: If the supplied identifier contains "@", Gitea queries the `email_address` table
- **Username normalization**: Otherwise, the identifier is converted to lowercase for the username lookup
- **Source resolution**: The retrieved `user_model.User` contains a `LoginSource` field pointing to an `auth.Source` (LDAP, SMTP, PAM, or local database)
- **Password verification**: The source's `PasswordAuthenticator` implementation validates the credential against the respective backend

If the authentication source is inactive, the function returns `oauth2.ErrAuthSourceNotActivated`, preventing login through disabled providers.

### HTTP Basic Auth and API Tokens

API requests utilize `Basic.Verify` in [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go) to extract the `Authorization` header. This method implements a cascading validation strategy:

```go
uid := GetOAuthAccessTokenScopeAndUserID(req.Context(), authToken)
token, err := auth_model.GetAccessTokenBySHA(req.Context(), authToken)

```

First, the verifier attempts to interpret the header value as an **OAuth2 token** or **personal access token**. If these checks fail and Basic Auth is enabled, the system falls back to standard password verification via `UserSignIn`.

Notably, `Basic.Verify` enforces security policies by refusing Basic Auth when the user has enrolled WebAuthn devices, returning `ErrUserAuthMessage` to prevent credential stuffing against accounts with hardware-based two-factor authentication.

### OAuth2 and External Providers

For OAuth2 flows (GitHub, GitLab, OpenID Connect), the system bypasses `UserSignIn` entirely. After the provider redirects back to the callback endpoint in [`routers/web/auth/oauth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/oauth.go), the OAuth2 source creates or links a Gitea user directly:

```go
func OAuth2Callback(ctx *context.Context) {
    // Exchange code for token, fetch user info
    u := oauth2Source.LoginUser(gothUser) // creates/links Gitea user
    handleSignIn(ctx, u, false)
}

```

## Phase 3: Session Management and Two-Factor Enforcement

Once credentials validate successfully, Gitea establishes a session and enforces additional security controls before granting access.

### Two-Factor Authentication and WebAuthn Checks

After `UserSignIn` returns a valid user object, `SignInPost` inspects the user's security configuration:

```go
hasTOTPtwofa, err := auth.HasTwoFactorByUID(ctx, u.ID)
hasWebAuthnTwofa, err := auth.HasWebAuthnRegistrationsByUID(ctx, u.ID)

```

The authentication flow branches based on these checks:

- **No 2FA**: Immediate login via `handleSignIn`
- **TOTP enabled**: Redirect to `/user/two_factor` for time-based code verification
- **WebAuthn enrolled**: Redirect to `/user/webauthn` for hardware key authentication

This enforcement ensures that even with valid primary credentials, users must complete secondary verification before accessing protected resources.

### Session Creation and Locale Handling

The `handleSignInFull` function in [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go) orchestrates session establishment:

```go
if err := updateSession(ctx, nil, map[string]any{
    session.KeyUID:                  u.ID,
    session.KeyUname:                u.Name,
    session.KeyUserHasTwoFactorAuth: userHasTwoFactorAuth,
}); err != nil {
    ctx.ServerError("UserSignIn", err)
    return
}

```

This routine performs several critical tasks:

1. **Session regeneration**: Creates a new session to prevent session fixation attacks
2. **Remember me handling**: Optionally writes a long-lived authentication token if the user selected "remember me"
3. **Locale synchronization**: Updates the user's language preference if unset
4. **Audit logging**: Registers the last-login timestamp for security monitoring

## Pluggable Authentication Sources

Gitea's architecture supports enterprise authentication through a modular source system. All external backends implement the `PasswordAuthenticator` interface defined in [`services/auth/interface.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/interface.go).

The built-in sources auto-register via blank imports in [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go):

```go
_ "code.gitea.io/gitea/services/auth/source/db"
_ "code.gitea.io/gitea/services/auth/source/ldap"
_ "code.gitea.io/gitea/services/auth/source/pam"
_ "code.gitea.io/gitea/services/auth/source/sspi"
_ "code.gitea.io/gitea/services/auth/source/smtp"

```

Each source resides under `services/auth/source/<type>/` and provides both authentication and user synchronization capabilities. For example, LDAP sources implement [`source_authenticate.go`](https://github.com/go-gitea/gitea/blob/main/source_authenticate.go) to bind against directory servers, while SMTP sources verify credentials against mail servers.

## Code Examples in Action

### Web UI Login Flow

The standard web authentication flow begins with form submission to the `SignInPost` handler:

```go
// POST /user/login → SignInPost
func SignInPost(ctx *context.Context) {
    // Form validation omitted for brevity
    u, source, err := auth_service.UserSignIn(ctx,
        ctx.FormString("user_name"),
        ctx.FormString("password"))
    
    // u contains the authenticated user
    // source indicates the authentication backend (local, LDAP, etc.)
    // 2FA redirect logic follows...
}

```

### API Access with Personal Tokens

For programmatic access, clients can authenticate using personal access tokens:

```bash
curl -H "Authorization: token 0123456789abcdef" \
    https://gitea.example.com/api/v1/user

```

`Basic.VerifyAuthToken` processes this header by looking up the SHA hash via `auth_model.GetAccessTokenBySHA`, setting `LoginMethod = "access_token"` in the request store, and returning the associated user object.

### Command-Line Basic Authentication

For scripts that require password authentication:

```bash
curl -u myuser:mypassword "https://gitea.example.com/api/v1/user"

```

The `Basic.Verify` handler extracts these credentials, attempts token interpretation first, then falls back to `UserSignIn` for password validation against the configured authentication sources.

## Summary

- **Three-phase architecture**: Gitea authentication moves sequentially through request entry (cookie/token detection), credential validation (source-specific verification), and session establishment (2FA enforcement and cookie generation)
- **Pluggable sources**: The `PasswordAuthenticator` interface allows seamless integration of LDAP, SMTP, PAM, SSPI, and local database backends through auto-registered packages in `services/auth/source/`
- **Security layering**: WebAuthn enrollment blocks Basic Auth attempts, while `handleSignInFull` regenerates sessions and handles "remember me" tokens securely
- **Dual-path validation**: [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go) handles web UI passwords with email/username flexibility, while [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go) manages API authentication with OAuth2, personal tokens, and fallback Basic Auth

## Frequently Asked Questions

### How does Gitea's "remember me" feature work?

The "remember me" functionality uses rotating authentication tokens stored in the `remember_token` cookie. When a request arrives without an active session, `autoSignIn` in [`routers/web/auth/auth.go`](https://github.com/go-gitea/gitea/blob/main/routers/web/auth/auth.go) validates the token against the database via `CheckAuthToken`. Upon successful validation, Gitea generates a fresh token, updates the cookie, and populates the session with the user's identity, allowing persistent authentication across browser sessions without storing passwords client-side.

### Why does Basic Auth fail for users with WebAuthn enabled?

As implemented in [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go), the `Verify` method explicitly checks for WebAuthn enrollments and returns `ErrUserAuthMessage` when hardware-based two-factor authentication is detected. This security measure prevents automated brute-force attacks against high-security accounts using password-only authentication. Users with WebAuthn must use personal access tokens or OAuth2 for API access instead of password-based Basic Auth.

### How does Gitea distinguish between email addresses and usernames during login?

In [`services/auth/signin.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/signin.go), the `UserSignIn` function performs string inspection on the provided identifier. If the input contains the "@" character, Gitea queries the `email_address` table to resolve the corresponding user ID. Otherwise, the identifier is normalized to lowercase and matched against the `user` table's username column. This dual-lookup approach allows users to authenticate using either their registered email or username without requiring explicit selection.

### Can Gitea authenticate API requests using OAuth2 tokens?

Yes. The `Basic.Verify` method in [`services/auth/basic.go`](https://github.com/go-gitea/gitea/blob/main/services/auth/basic.go) first attempts to validate the Authorization header as an OAuth2 access token through `GetOAuthAccessTokenScopeAndUserID`. This occurs before checking for personal access tokens or falling back to password authentication. The OAuth2 validation extracts the user ID and scope from the token, enabling fine-grained permissions for third-party applications accessing the Gitea API.