# How to Configure Authentication with OAuth2 for Self-Hosted Apps

> Configure OAuth2 authentication for self-hosted apps using Ory Hydra. Secure your APIs with JWT tokens and connect to your identity provider for seamless integration. Learn more!

- Repository: [Michael Royal/Self-Hosting-Guide](https://github.com/mikeroyal/Self-Hosting-Guide)
- Tags: how-to-guide
- Published: 2026-06-17

---

**Configure OAuth2 authentication for self-hosted applications by deploying Ory Hydra as your OAuth2 server, connecting it to your identity provider via a custom login-and-consent flow, and securing your APIs with signed JWT tokens.**

OAuth2 has become the de facto standard for securing self-hosted applications and delegating authentication to external identity providers. The mikeroyal/Self-Hosting-Guide repository recommends Ory Hydra as the core OAuth2 server for self-hosted environments. This guide explains how to configure a complete OAuth2 authentication flow using Hydra's identity-provider-agnostic architecture.

## Architecture Overview

A typical OAuth2 deployment for self-hosted apps follows a three-component architecture. The **Client App** (your web or mobile application) redirects users to **Ory Hydra** (the OAuth2 server), which delegates actual authentication to your chosen **Identity Provider** (such as Ory Kratos, LDAP, or Keycloak).

This separation of concerns allows Hydra to remain stateless while handling token security. According to the [`README.md`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/README.md) in the Self-Hosting-Guide, Hydra provides a fully-featured OpenID Connect implementation that issues signed JWTs without storing session data internally.

## Deploy Ory Hydra with Docker Compose

Begin by deploying Hydra using Docker Compose. The service requires a PostgreSQL database for persistence and specific environment variables to configure your login and consent endpoints.

Create a [`docker-compose.yml`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/docker-compose.yml) file based on the configuration recommended in the guide:

```yaml
version: "3.8"
services:
  hydra:
    image: oryd/hydra:v2.2
    environment:
      - DSN=postgres://hydra:secret@db/hydra?sslmode=disable
      - URLS_SELF_ISSUER=http://localhost:4444/
      - URLS_CONSENT=http://localhost:3000/consent
      - URLS_LOGIN=http://localhost:3000/login
      - SECRETS_SYSTEM=very-secret-system-key
    ports:
      - "4444:4444"   # Public API

      - "4445:4445"   # Admin API

    depends_on:
      - db

  db:
    image: postgres:13
    environment:
      - POSTGRES_USER=hydra
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=hydra
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

```

Start the services with `docker-compose up -d`. Hydra exposes port `4444` for public OAuth2 endpoints and `4445` for administrative operations.

## Register Your Client Application

Before accepting authentication requests, you must register your self-hosted app as an OAuth2 client. Use the Hydra CLI inside the running container to create the client with appropriate grant types and scopes.

Run this command after the container is up:

```bash
docker exec -i $(docker ps -q -f "ancestor=oryd/hydra") \
  hydra clients create \
  --endpoint http://localhost:4445 \
  --id my-selfhosted-app \
  --secret super-secret \
  --grant-types authorization_code,refresh_token \
  --response-types code \
  --scope openid,offline \
  --callbacks http://localhost:8080/callback

```

This registers a client with the ID `my-selfhosted-app` that supports the authorization code flow with refresh tokens. The `--callbacks` parameter specifies where Hydra redirects users after authentication.

## Implement the Login and Consent Flow

Hydra is identity-provider agnostic—it delegates user authentication to a separate login-and-consent application that you build. This app handles credential verification against your identity store and asks users to grant requested scopes.

### Login Endpoint

Your login app receives a `login_challenge` parameter from Hydra at the `/login` endpoint. After verifying credentials against your IdP (such as Ory Kratos or LDAP), accept the login request via Hydra's admin API:

```javascript
const express = require('express')
const fetch = require('node-fetch')
const app = express()

app.get('/login', async (req, res) => {
  const { login_challenge } = req.query
  await fetch('http://localhost:4445/oauth2/auth/requests/login/accept', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ subject: 'user-id-123', remember: true })
  })
  const data = await (await fetch(`http://localhost:4445/oauth2/auth/requests/login?login_challenge=${login_challenge}`)).json()
  res.redirect(data.redirect_to)
})

```

### Consent Endpoint

Similarly, the consent endpoint receives a `consent_challenge` and must explicitly grant scopes:

```javascript
app.get('/consent', async (req, res) => {
  const { consent_challenge } = req.query
  await fetch('http://localhost:4445/oauth2/auth/requests/consent/accept', {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      grant_scope: ['openid', 'offline'],
      remember: true,
      session: { access_token: {}, id_token: {} }
    })
  })
  const data = await (await fetch(`http://localhost:4445/oauth2/auth/requests/consent?consent_challenge=${consent_challenge}`)).json()
  res.redirect(data.redirect_to)
})

app.listen(3000, () => console.log('Login/Consent app listening on :3000'))

```

Run this Node.js application on port `3000` to match the `URLS_LOGIN` and `URLS_CONSENT` values configured in your Hydra environment.

## Secure Your APIs with Token Validation

Once the flow completes, client applications receive access tokens to call your protected APIs. Validate these tokens by checking the JWT signature or using Hydra's introspection endpoint.

Test access to a protected resource using the token:

```bash
curl -H "Authorization: Bearer <access_token>" http://api.myselfhosted.local/secure-data

```

## Key Configuration Files

The Self-Hosting-Guide references several files that support this configuration:

- **[`README.md`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/README.md)** – Contains the Ory Hydra entry listing it among recommended self-hosted services for OAuth2 authentication.
- **`Getting Started with Self-Hosting.dockerfile`** – Demonstrates Docker image building patterns applicable to Hydra deployments and custom login-consent UIs.

## Security and Scalability Considerations

Hydra implements several architectural patterns that benefit self-hosted deployments:

- **Statelessness** – No session data stored in Hydra; all state lives in PostgreSQL or your chosen database backend.
- **Token Security** – Uses signed JWTs for access, refresh, and ID tokens ensuring tamper-proof credentials.
- **High Throughput** – Designed for low-latency deployments and can run behind reverse proxies like Traefik (referenced in the guide's service list).
- **OpenID Connect Compliance** – Certified OIDC implementation ensures compatibility with major identity providers including Google and GitHub for future federation.

## Summary

- Deploy Ory Hydra using Docker Compose with PostgreSQL for stateless OAuth2 token issuance.
- Register client applications using the Hydra CLI with specific grant types and callback URLs.
- Build a custom login-and-consent app that authenticates users against your identity provider and communicates with Hydra's admin API.
- Secure APIs by validating JWT access tokens issued by Hydra.
- Reference the [`README.md`](https://github.com/mikeroyal/Self-Hosting-Guide/blob/main/README.md) in mikeroyal/Self-Hosting-Guide for the complete service catalog and deployment patterns.

## Frequently Asked Questions

### What is the difference between Ory Hydra and a complete identity provider?

Ory Hydra is an OAuth2 and OpenID Connect server that issues tokens but does not handle user authentication itself. It requires a separate login-and-consent application to verify credentials against your identity store (such as Ory Kratos, LDAP, or Keycloak). This separation allows you to customize the authentication experience while relying on Hydra for secure token management.

### Can I run Ory Hydra without Docker?

Yes, though the Self-Hosting-Guide recommends Docker for consistency. You can run Hydra as a binary with the same environment variables (`DSN`, `URLS_SELF_ISSUER`, `SECRETS_SYSTEM`) and command-line flags. Ensure your database (PostgreSQL, MySQL, or SQLite) is accessible and properly configured in the DSN connection string.

### How do I handle user sessions in a self-hosted OAuth2 setup?

Hydra does not store session data internally. Instead, configure your login-and-consent application to maintain sessions using cookies or JWTs. When a user returns, Hydra sends a new login challenge; your app can recognize the session cookie and immediately accept the request without re-authenticating, creating a seamless single sign-on experience.

### Which scopes should I configure for a typical self-hosted application?

For standard applications, configure `openid` for OIDC compliance and `offline` to enable refresh tokens. Additional custom scopes (such as `read:profile` or `admin`) can be defined based on your API's authorization requirements. Always request the minimal set of scopes necessary for the application's functionality.