Skip to content

wunderkind2k1/auth-server-task

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OAuth2 Server

This is a simple OAuth2 server implementation in Go.

README Introduction

This repository was created in May 2025 as part of my application for a Golang Software Engineer position. The assigned task is documented in task.md, with the implementation status detailed in taskstatus.md.

AI Usage Disclaimer This project was developed with significant assistance from AI tools, both integrated into development environments and accessed via browsers. As a greenfield project with no restrictions on AI usage, I leveraged these tools extensively. However, the development followed rigorous software engineering practices, with iterative development, thorough testing, and quality assurance. Each iteration was carefully reviewed, and classic pull request workflows with quality gates were implemented to ensure robust code quality.

Proposed Next Steps To enhance the project, I recommend the following improvements:

  • CI/CD Optimization: Address caching issues related to Go workspace usage for faster, more reliable builds.
  • Infrastructure as Code (IaC): Transition the Kubernetes configuration to an IaC solution, such as Pulumi (preferred) or Terraform, for better scalability and maintainability.
  • Pipeline as Code (PaC): Adopt a tool like Dagger.io to enable type-safe, high-quality CI/CD pipelines and Docker image builds.
  • Telemetry: Implement observability using OpenTelemetry or a hosted solution like Sentry to monitor application performance and errors.

Features

  • OAuth2 Client Credentials Grant flow (RFC 6749)
  • JWT Access Token issuance (RFC 7519) with RS256 signing
  • Basic Authentication for client credentials
  • Token introspection endpoint (RFC 7662)
  • JWK endpoint for signing keys (RFC 7517)
  • Local deployment using k3d (Kubernetes in Docker)

Prerequisites

Core Requirements

  • Go 1.24 or later (required for building and running the server)

Development Tools

  • golangci-lint (for code linting)
  • Make (optional, for using Makefile commands in keytool)

Local Kubernetes Deployment

  • Docker (required for building and running containers)
  • kubectl (required for Kubernetes cluster interaction)
  • k3d (required for local Kubernetes cluster)

CI/CD

Our CI/CD pipeline is configured using GitHub Actions and includes:

  • Automated linting using golangci-lint
  • Build and test verification
  • Merge gate protection for the main branch
  • Flexible development workflow for feature branches

See .github/doc.md for detailed information about our workflows and their behavior.

Getting Started

Quick Start

  1. Clone the repository:
git clone https://github.com/wunderkind2k1/auth-server-task.git
cd auth-server-task
  1. Generate an RSA key pair for JWT signing:
cd keytool
make run-generate
  1. Start the server with the generated key:
# Export the private key content (replace <keyID> with your actual key ID)
export JWT_SIGNATURE_KEY="$(cat keytool/keys/<keyID>.private.pem)"
go run server/main.go

Alternatively, you can use the convenience script:

cd server
./startServer.sh

The server will start on port 8080.

Configuration

Environment Variables

Variable Description Required
JWT_SIGNATURE_KEY Content of the RSA private key in PEM format for JWT signing Yes

Key Management

The project includes a separate key management tool in the keytool directory. This tool provides commands for:

  • Generating RSA key pairs
  • Listing available keys
  • Deleting key pairs

For development, you can use the pre-generated test keys in keytool/keys/. See keytool/README.md for more details.

User Pool Configuration

The server uses a simple in-memory user pool for authentication. By default, it includes a test user with the following credentials:

  • Client ID: sho
  • Client Secret: test123

To modify the user pool, you can edit the server/internal/userpool/default.go file. The user pool is implemented as a simple map structure where you can add, remove, or modify users. Each user requires a client ID and client secret.

Example of adding a new user:

func Default() map[string]string {
    return map[string]string{
        "sho": "test123",
        "new-user": "new-secret",
    }
}

Note: In a production environment, you should implement a more secure and persistent storage solution for user credentials.

Local Deployment with k3d

For a more production-like environment, you can deploy the server using k3d:

  1. Create a local Kubernetes cluster:
cd deployment/local
./manage-cluster.sh create
  1. Set up the JWT secret:
./setup-secret.sh
  1. Build and deploy the application:
./rebuildServerImage.sh
./deploy.sh
  1. Verify the deployment:
./check-deployment.sh

The server will be accessible at http://localhost:8080. See deployment/local/README.md for detailed deployment instructions.

API Endpoints

Token Endpoint

Issues JWT access tokens using the Client Credentials Grant flow. Tokens are signed using RS256.

curl -X POST http://localhost:8080/token \
  -H "Authorization: Basic $(echo -n 'client_id:client_secret' | base64)" \
  -H "Content-Type: application/json"

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

JWKS Endpoint

Provides the JSON Web Key Set (JWKS) for token verification. The endpoint follows RFC 7517 and only accepts GET requests.

curl -X GET http://localhost:8080/.well-known/jwks.json

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "1",
      "alg": "RS256",
      "n": "...",
      "e": "..."
    }
  ]
}

Token Introspection Endpoint

Validates and provides information about an access token. The endpoint follows RFC 7662 and requires Basic Authentication.

curl -X POST http://localhost:8080/introspect \
  -H "Authorization: Basic $(echo -n 'client_id:client_secret' | base64)" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

Response for valid token:

{
  "active": true,
  "scope": "",
  "client_id": "sho",
  "username": "sho",
  "token_type": "Bearer",
  "exp": 1735689600,
  "iat": 1735686000,
  "nbf": 1735686000,
  "sub": "sho",
  "aud": [],
  "iss": "https://oauth2-server",
  "jti": "unique-token-id"
}

Response for invalid token:

{
  "active": false
}

Testing

Test scripts are provided to verify the functionality of both endpoints:

# Set the JWT_SIGNATURE_KEY environment variable first
export JWT_SIGNATURE_KEY="$(cat keytool/keys/<keyID>.private.pem)"

# Test token endpoint
./test-utils/test-token-endpoint.sh

# Test JWKS endpoint
./test-utils/test-jwks-endpoint.sh

# Test introspection endpoint
./test-utils/test-introspection-endpoint.sh

Development

The project follows Semantic Versioning and maintains a CHANGELOG.md following the Keep a Changelog format.

Enforcing Note on Code Duplication

As Rob Pike famously said in his talk "Go Proverbs", "A little copying is better than a little dependency." This principle is applied in our codebase, particularly in the key management implementation, where we intentionally maintain some code duplication between the main application and the key management tool to avoid tight coupling.

The key parsing logic in the main application (internal/token/keyloader.go) intentionally duplicates some code from this package. This is by design to maintain separation between the core application and its supporting tools. The main application should be self-contained and not depend on this package.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

This is a repo that implements a solution for a task (in https://github.com/wunderkind2k1/auth-server-task/blob/main/task.md) to build a poc for an oauth server in go.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages