Skip to main content

Overview

Gitea can act as an OAuth2 provider, allowing you to use your Gitea account to authenticate with third-party applications and services. This enables Single Sign-On (SSO) capabilities and allows developers to build applications that integrate with Gitea.

OAuth2 Concepts

Client Types

Gitea supports two types of OAuth2 clients as defined in RFC 6749:
  • Confidential Clients - Applications that can securely store client secrets (server-side apps)
  • Public Clients - Applications that cannot securely store secrets (mobile apps, SPAs)
Reference: models/auth/oauth2.go:43-48

Grant Types

Gitea supports the following OAuth2 grant flows:
  • Authorization Code - Most common flow for web applications
  • Refresh Token - Obtain new access tokens without re-authentication
  • Client Credentials - Machine-to-machine authentication

Token Types

Gitea issues two types of tokens:
  • Access Token - Short-lived token (default expiry varies) for API access
  • Refresh Token - Long-lived token to obtain new access tokens
Reference: services/oauth2_provider/token.go:18-26

Creating an OAuth2 Application

2
  • Click on your profile avatar in the top-right corner
  • Select Settings
  • Click Applications in the sidebar
  • 3
    Register a new application
    4
  • Click Create a new OAuth2 Application
  • Fill in the application details:
    • Application Name: Descriptive name for your application
    • Redirect URIs: One or more authorized callback URLs (one per line)
    • Confidential Client: Check if the application can securely store secrets
    • Skip Authorization: Skip secondary authorization prompt for trusted apps
  • 5
    Save credentials
    6
    After creating the application, you’ll receive:
    7
  • Client ID: Public identifier for your application
  • Client Secret: Secret key for confidential clients (store securely!)
  • 8
    The client secret is only shown once. Store it securely - if lost, you’ll need to regenerate it.

    Authorization Code Flow

    The authorization code flow is the recommended approach for web applications.

    Step 1: Authorization Request

    Redirect users to Gitea’s authorization endpoint:
    GET https://gitea.example.com/login/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&state=RANDOM_STATE
    
    Parameters:
    • client_id (required) - Your application’s client ID
    • redirect_uri (required) - Must match a registered redirect URI
    • response_type (required) - Must be code
    • state (recommended) - Random string to prevent CSRF attacks
    • scope (optional) - Space-separated list of requested scopes

    Step 2: User Authorization

    The user will be prompted to authorize your application. After approval, they’re redirected to:
    https://your-app.com/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE
    

    Step 3: Exchange Code for Token

    Exchange the authorization code for an access token:
    curl -X POST "https://gitea.example.com/login/oauth/access_token" \
      -H "Content-Type: application/json" \
      -d '{
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET",
        "code": "AUTHORIZATION_CODE",
        "grant_type": "authorization_code",
        "redirect_uri": "https://your-app.com/callback"
      }'
    
    Response:
    {
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    Step 4: Use Access Token

    Include the access token in API requests:
    curl "https://gitea.example.com/api/v1/user" \
      -H "Authorization: Bearer ACCESS_TOKEN"
    

    Token Structure

    Gitea uses JWT (JSON Web Tokens) for OAuth2 tokens. The token structure includes:
    type Token struct {
        GrantID int64     // Grant identifier
        Kind    TokenKind // Access or refresh token
        Counter int64     // Token counter for refresh tokens
        jwt.RegisteredClaims
    }
    
    Reference: services/oauth2_provider/token.go:28-34

    Token Validation

    Tokens are validated using the configured JWT signing method:
    // Parse and validate token
    token, err := ParseToken(jwtToken, signingKey)
    if err != nil {
        return nil, err
    }
    
    Reference: services/oauth2_provider/token.go:36-56

    Refresh Tokens

    When an access token expires, use the refresh token to obtain a new one:
    curl -X POST "https://gitea.example.com/login/oauth/access_token" \
      -H "Content-Type: application/json" \
      -d '{
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET",
        "grant_type": "refresh_token",
        "refresh_token": "REFRESH_TOKEN"
      }'
    
    Response:
    {
      "access_token": "NEW_ACCESS_TOKEN",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "NEW_REFRESH_TOKEN"
    }
    
    Each refresh invalidates the previous refresh token and issues a new one.

    OpenID Connect (OIDC)

    Gitea supports OpenID Connect, an identity layer on top of OAuth2.

    ID Token Structure

    type OIDCToken struct {
        jwt.RegisteredClaims
        Nonce string // Nonce for request validation
        
        // Profile scope
        Name              string
        PreferredUsername string
        Profile           string
        Picture           string
        Website           string
        Locale            string
        UpdatedAt         timeutil.TimeStamp
        
        // Email scope
        Email         string
        EmailVerified bool
        
        // Groups scope
        Groups []string // Generated from organizations and teams
    }
    
    Reference: services/oauth2_provider/token.go:66-86

    OIDC Discovery

    Gitea exposes an OIDC discovery endpoint at:
    GET https://gitea.example.com/.well-known/openid-configuration
    
    This endpoint provides metadata about the OIDC provider, including:
    • Authorization endpoint
    • Token endpoint
    • Supported scopes
    • Supported response types
    • JWKs endpoint

    Requesting ID Tokens

    Add openid to the scope parameter:
    GET https://gitea.example.com/login/oauth/authorize
      ?client_id=CLIENT_ID
      &redirect_uri=REDIRECT_URI
      &response_type=code
      &scope=openid%20profile%20email
      &state=RANDOM_STATE
    
    The token response will include an id_token field:
    {
      "access_token": "...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "...",
      "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
    

    OAuth2 Scopes

    Gitea supports the following OAuth2 scopes:
    ScopeDescription
    repoFull access to repositories
    repo:statusAccess to commit status
    public_repoAccess to public repositories
    admin:orgFull control of organizations
    write:orgRead and write access to organizations
    read:orgRead-only access to organizations
    admin:public_keyFull control of public keys
    write:public_keyCreate, update, delete public keys
    read:public_keyRead public keys
    admin:repo_hookFull control of repository hooks
    write:repo_hookCreate, update, delete repository hooks
    read:repo_hookRead repository hooks
    admin:org_hookFull control of organization hooks
    userRead and write access to user profile
    read:userRead user profile data
    user:emailRead user email addresses
    user:followFollow and unfollow users
    delete_repoDelete repositories
    packageAccess to packages
    admin:gpg_keyFull control of GPG keys
    write:gpg_keyCreate, update, delete GPG keys
    read:gpg_keyRead GPG keys
    admin:applicationFull control of applications
    write:applicationCreate, update, delete applications
    read:applicationRead applications

    OpenID Connect Scopes

    ScopeDescriptionClaims
    openidRequired for OIDCsub
    profileUser profile informationname, preferred_username, profile, picture, website, locale, updated_at
    emailUser email addressemail, email_verified
    groupsOrganization and team membershipgroups

    Built-in Applications

    Gitea includes several built-in OAuth2 applications for common Git tools:

    Git Credential Manager

    • Client ID: e90ee53c-94e2-48ac-9358-a874fb9e0662
    • Redirect URIs: http://127.0.0.1, https://127.0.0.1
    • Purpose: Microsoft’s Git Credential Manager
    Reference: models/auth/oauth2.go:73-77

    git-credential-oauth

    • Client ID: a4792ccc-144e-407e-86c9-5e7d8d9c3269
    • Redirect URIs: http://127.0.0.1, https://127.0.0.1
    • Purpose: git-credential-oauth helper
    Reference: models/auth/oauth2.go:68-72

    Tea CLI

    • Client ID: d57cb8c4-630c-4168-8324-ec79935e18d4
    • Redirect URIs: http://127.0.0.1, https://127.0.0.1
    • Purpose: Tea command-line client
    Reference: models/auth/oauth2.go:78-82 These applications can be enabled in your app.ini:
    [oauth2]
    DEFAULT_APPLICATIONS = git-credential-oauth, git-credential-manager, tea
    

    Security Best Practices

    State Parameter

    Always use the state parameter to prevent CSRF attacks:
    // Generate random state
    const state = crypto.randomBytes(32).toString('hex');
    
    // Store in session
    session.oauthState = state;
    
    // Include in authorization URL
    const authUrl = `https://gitea.example.com/login/oauth/authorize
      ?client_id=${clientId}
      &redirect_uri=${redirectUri}
      &response_type=code
      &state=${state}`;
    
    // Verify on callback
    if (req.query.state !== session.oauthState) {
      throw new Error('Invalid state parameter');
    }
    

    Client Secret Protection

    • Never expose client secrets in client-side code
    • Store secrets in environment variables or secure vaults
    • Use confidential client type for server-side applications
    • Rotate secrets regularly
    • Use public client type for applications that can’t store secrets

    Redirect URI Validation

    Gitea performs exact matching on redirect URIs:
    func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
        // OAuth2 requires exact match, no dynamic parts allowed
        return slices.Contains(app.RedirectURIs, redirectURI)
    }
    
    Reference: models/auth/oauth2.go:147-150
    • Always use HTTPS redirect URIs in production
    • Register all valid redirect URIs
    • Never use wildcards in redirect URIs
    • Avoid open redirects

    Token Storage

    • Store access tokens securely (encrypted storage, secure cookies)
    • Never log or transmit tokens in plain text
    • Implement token rotation
    • Clear tokens on logout

    Authorization Code Expiry

    Authorization codes are short-lived and expire after 10 minutes per RFC 6749:
    const oauth2AuthorizationCodeValidity = 10 * time.Minute
    
    Reference: models/auth/oauth2.go:31-32 Codes can only be used once. Attempting to reuse a code will result in an error.

    Revoking Access

    Users can revoke application access at any time:
    2
  • Go to Settings > Applications
  • Click on the Authorized OAuth Apps tab
  • 3
    Revoke access
    4
  • Find the application you want to revoke
  • Click Revoke next to the application
  • Confirm the revocation
  • Revoking access invalidates all tokens issued to that application for your account.

    Testing OAuth2 Flow

    You can test the OAuth2 flow using curl:
    # 1. Get authorization code (opens browser)
    open "https://gitea.example.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:8080/callback&response_type=code&state=random123"
    
    # 2. Exchange code for token (after getting code from callback)
    curl -X POST "https://gitea.example.com/login/oauth/access_token" \
      -H "Content-Type: application/json" \
      -d '{
        "client_id": "YOUR_CLIENT_ID",
        "client_secret": "YOUR_CLIENT_SECRET",
        "code": "AUTHORIZATION_CODE",
        "grant_type": "authorization_code",
        "redirect_uri": "http://localhost:8080/callback"
      }'
    
    # 3. Use access token
    curl "https://gitea.example.com/api/v1/user" \
      -H "Authorization: Bearer ACCESS_TOKEN"
    

    Common Use Cases

    Single Sign-On (SSO)

    Use Gitea as an identity provider for your organization’s applications:
    1. Register your application as an OAuth2 client
    2. Implement the authorization code flow
    3. Use OIDC for user profile information
    4. Map Gitea organizations/teams to application roles

    CI/CD Integration

    Authenticate CI/CD pipelines using OAuth2:
    1. Create a confidential client for your CI/CD system
    2. Use client credentials grant for machine-to-machine auth
    3. Scope tokens to minimum required permissions
    4. Rotate tokens regularly

    Mobile Applications

    Authenticate mobile apps using OAuth2:
    1. Create a public client (no client secret)
    2. Use authorization code flow with PKCE
    3. Store tokens securely in device keychain
    4. Implement token refresh on app launch

    Third-Party Integrations

    Integrate with external services:
    1. Create an OAuth2 application
    2. Configure the service to use Gitea as OAuth2 provider
    3. Map Gitea scopes to service permissions
    4. Test the integration flow

    Troubleshooting

    Invalid Redirect URI

    Error: redirect_uri_mismatch Solution: Ensure the redirect URI in the authorization request exactly matches one registered in the application settings.

    Invalid Client

    Error: invalid_client Solution: Verify that the client ID and client secret are correct.

    Expired Authorization Code

    Error: invalid_grant Solution: Authorization codes expire after 10 minutes. Request a new authorization code.

    Invalid Token

    Error: invalid_token Solution: The access token may have expired. Use the refresh token to obtain a new access token.

    Further Reading

    Build docs developers (and LLMs) love