Skip to content

eswan18/identity

Repository files navigation

Identity Provider

OAuth 2.0 / OpenID Connect identity provider built with Go and PostgreSQL.

Features

  • OAuth 2.0 Authorization Code flow with PKCE
  • OpenID Connect UserInfo endpoint
  • User registration and authentication
  • Two-factor authentication (TOTP) — optional MFA via authenticator apps
  • Client management via CLI
  • Session management with secure cookies
  • Token refresh and revocation

Documentation

  • Client Integration Guide — How to integrate your application with this IdP, including user storage, roles, and permissions

Prerequisites

This project requires a few CLI tools:

# swaggo/swag for generating openapi docs
go install github.com/swaggo/swag/cmd/swag@latest
# sqlc-dev/sqlc for generating type-safe database code
go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest
# golang-migrate/migrate for migrations
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest

Tailwind CSS

The project uses Tailwind CSS v4 for styling. This requires Node.js (v18+):

npm install  # install Tailwind dependencies

The CSS is built automatically when you run make build or make run. For development with live reload:

make css-watch  # in a separate terminal

Environment Variables

Create a .env file (or .env.dev for development) in the project root:

# Database connection (required)
DATABASE_URL=postgresql://user:password@localhost:5432/identity?sslmode=disable

# HTTP server address (optional, defaults shown)
HTTP_ADDRESS=http://localhost:8080  # dev
# HTTP_ADDRESS=https://identity.example.com  # production

# Session configuration (optional)
SESSION_SECRET=your-random-secret-key-at-least-32-chars

Setup

  1. Set DATABASE_URL to a non-prod database in .env.dev(I usually copy the connection string for the dev branch of the main db in Neon, but you could also spin up local postgres).

  2. Run database migrations:

    DATABASE_URL="..." make migrate-up
  3. Build the server and CLI:

    make build
  4. Start the server:

    ENV=dev ./identity

    The server will be available at http://localhost:8080.

User Management

Creating Users

Users can register via the web UI:

http://localhost:8080/oauth/register

Or create users programmatically using the database directly.

User Login

Login page (standalone or via OAuth flow):

http://localhost:8080/login

Two-Factor Authentication (MFA)

Users can enable TOTP-based two-factor authentication from their account settings:

http://localhost:8080/oauth/account-settings

When MFA is enabled:

  1. User enters username/password as normal
  2. User is redirected to MFA verification page
  3. User enters 6-digit code from their authenticator app (Google Authenticator, Authy, etc.)
  4. On success, the OAuth flow continues as normal

MFA is off by default. Users can enable/disable it themselves via account settings.

Commands

Build binary:

make build

Build docs:

make docs

Create a new migration:

# Use underscores in migration names
make migrate-new name=create_table_users

Run migrations:

DATABASE_URL="postgresql://..." make migrate-up
DATABASE_URL="postgresql://..." make migrate-down

Regenerate sqlc queries and types.

make sqlc

OAuth Client Management

Adding a New Client

  1. Build the CLI:

    make build
  2. Create a public client (for SPAs like React apps):

    ENV=dev ./identity-cli client create \
      --name "My App (Dev)" \
      --redirect-uris "http://localhost:5173/oauth/callback" \
      --scopes "openid,profile,email"

    Or create a confidential client (for backend services):

    ENV=dev ./identity-cli client create \
      --name "My Service" \
      --redirect-uris "https://myservice.com/callback" \
      --scopes "openid,profile,email" \
      --confidential

    Save the returned client_id (and client_secret if confidential).

Other Client Commands

  • ./identity-cli client list - List all clients
  • ./identity-cli client get <client-id> - Get client details
  • ./identity-cli client update <client-id> --name "New Name" - Update client
  • ./identity-cli client delete <client-id> - Delete client

OAuth Endpoints

Authorization & Token Endpoints

  • GET /oauth/authorize - Start OAuth authorization flow
    • Query params: client_id, redirect_uri, state, scope, code_challenge, code_challenge_method
  • POST /oauth/token - Exchange authorization code for tokens
    • Form params: grant_type, code, client_id, redirect_uri, code_verifier
  • POST /oauth/token - Refresh access token
    • Form params: grant_type=refresh_token, refresh_token, client_id

User Info & Session

  • GET /oauth/userinfo - Get user info from access token
    • Header: Authorization: Bearer <access_token>
    • Returns: sub, username, email, email_verified
  • GET /login - Login page (standalone or via OAuth)
  • POST /login - Process login credentials
  • GET /oauth/register - User registration page
  • POST /oauth/register - Create new user account
  • GET /oauth/success - Post-login success page

MFA Endpoints

  • GET /oauth/mfa - MFA code entry page (during login, if MFA enabled)
  • POST /oauth/mfa - Validate MFA code and complete login
  • GET /oauth/mfa-setup - Setup MFA with QR code (from account settings)
  • POST /oauth/mfa-setup - Verify setup code and enable MFA
  • POST /oauth/mfa-disable - Disable MFA (requires password + current MFA code)

Health & Documentation

  • GET /health - Health check endpoint
  • GET /docs - API documentation (Swagger/OpenAPI)

OAuth Flow Example

For a Single Page Application (SPA) using PKCE:

  1. Generate PKCE values in your client:

    const codeVerifier = generateRandomString(32);
    const codeChallenge = await sha256(codeVerifier);
  2. Redirect to authorization endpoint:

    http://localhost:8080/oauth/authorize?
      client_id=YOUR_CLIENT_ID&
      redirect_uri=http://localhost:5173/oauth/callback&
      response_type=code&
      state=RANDOM_STATE&
      scope=openid profile email&
      code_challenge=CODE_CHALLENGE&
      code_challenge_method=S256
    
  3. User logs in and is redirected back with authorization code:

    http://localhost:5173/oauth/callback?
      code=AUTHORIZATION_CODE&
      state=RANDOM_STATE
    
  4. Exchange code for tokens:

    POST /oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&
    code=AUTHORIZATION_CODE&
    client_id=YOUR_CLIENT_ID&
    redirect_uri=http://localhost:5173/oauth/callback&
    code_verifier=CODE_VERIFIER
  5. Receive tokens:

    {
      "access_token": "...",
      "refresh_token": "...",
      "expires_in": 3600,
      "token_type": "Bearer",
      "scope": ["openid", "profile", "email"]
    }
  6. Use access token to call protected APIs:

    GET /oauth/userinfo
    Authorization: Bearer ACCESS_TOKEN

Development

Start the server in development mode:

ENV=dev make run

Watch logs for debugging:

ENV=dev LOG_LEVEL=debug ./identity

About

a toy (?) identity service

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages