A standards-compliant OAuth 2.0 authorization server built with PHP 8.4, using the league/oauth2-server library and the eriklukiman/framework.
- Overview
- Features
- Prerequisites
- Installation
- Configuration
- Database Setup
- Running the Server
- Usage Examples
- Directory Structure
- Testing the Implementation
- Security Notes
This OAuth 2.0 server implementation supports multiple grant types and provides secure token-based authentication and authorization for your applications. It follows the OAuth 2.0 specification (RFC 6749) and implements best practices for secure token management.
- ✅ Client Credentials Grant - Machine-to-machine authentication
- ✅ Authorization Code Grant - Traditional web application flow
- ✅ Refresh Token Support - Long-lived sessions without re-authentication
- ✅ Scope Management - Fine-grained access control
- ✅ PSR-7 Compliant - Modern PHP standards
- ✅ Database-backed - Persistent storage for clients, users, and tokens
Before you begin, ensure you have the following installed:
- PHP 8.4 or higher
- MySQL Database (or compatible MariaDB)
- Composer - PHP dependency manager
- OpenSSL - For key generation
git clone <repository_url>
cd authentication_servercomposer installThis will install:
league/oauth2-server- OAuth 2.0 server implementationnyholm/psr7- PSR-7 HTTP message implementationeriklukiman/framework- Custom routing and MVC framework
The OAuth 2.0 server requires a private/public key pair for signing tokens:
openssl genrsa -out private.key 2048
openssl rsa -in private.key -pubout -out public.key
chmod 600 private.keyNote: The
private.keyfile should be kept secure and never committed to version control.
Edit the database credentials in config/app.php:
define('DB_HOST', '127.0.0.1');
define('DB_USER', 'rx'); // Your MySQL username
define('DB_PASS', 'a'); // Your MySQL password
define('DB_NAME', 'auth_server'); // Your database name
define('DB_PORT', 3306);mysql -u rx -p -e "CREATE DATABASE auth_server;"The schema.sql file contains all necessary table definitions and seed data:
mysql -u rx -p auth_server < schema.sqlThis will create the following tables:
clients- OAuth client applicationsusers- End users who authorize accessscopes- Available permission scopesaccess_tokens- Issued access tokensrefresh_tokens- Issued refresh tokensauth_codes- Authorization codes (for auth code flow)
The schema includes test data:
Test Client:
- Client ID:
testclient - Client Secret:
testsecret - Redirect URI:
http://localhost:8080/callback
Test User:
- Username:
testuser - Password:
password
Test Scope:
- Scope:
basic- Basic Access
Start the PHP built-in development server:
php -S localhost:8080 -t publicThe server will be available at http://localhost:8080
Production Note: For production deployments, use a proper web server like Nginx or Apache instead of the built-in PHP server.
Use this grant type for machine-to-machine authentication where no user is involved.
Request:
curl -X POST http://localhost:8080/access_token \
-d "grant_type=client_credentials" \
-d "client_id=testclient" \
-d "client_secret=testsecret" \
-d "scope=basic"Response:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}This grant type is for traditional web applications where a user needs to authorize the client.
Navigate to this URL in your browser:
http://localhost:8080/authorize?response_type=code&client_id=testclient&redirect_uri=http://localhost:8080/callback&scope=basic&state=xyz
- The user will see an approval form
- Upon approval, they'll be redirected to the callback URL
- The authorization code will be displayed on the callback page
Replace {AUTH_CODE} with the code received from the callback:
curl -X POST http://localhost:8080/access_token \
-d "grant_type=authorization_code" \
-d "client_id=testclient" \
-d "client_secret=testsecret" \
-d "redirect_uri=http://localhost:8080/callback" \
-d "code={AUTH_CODE}"Response:
{
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"refresh_token": "def50200..."
}Use a refresh token to obtain a new access token without requiring user re-authentication:
curl -X POST http://localhost:8080/access_token \
-d "grant_type=refresh_token" \
-d "client_id=testclient" \
-d "client_secret=testsecret" \
-d "refresh_token={REFRESH_TOKEN}"authentication_server/
├── config/
│ ├── app.php # Application and database configuration
│ └── Database.php # Database connection helper
├── Libraries/
│ ├── AuthServerFactory.php # OAuth server factory
│ ├── Entities/ # OAuth2 entity implementations
│ │ ├── AccessTokenEntity.php
│ │ ├── AuthCodeEntity.php
│ │ ├── ClientEntity.php
│ │ ├── RefreshTokenEntity.php
│ │ ├── ScopeEntity.php
│ │ └── UserEntity.php
│ └── Repositories/ # OAuth2 repository implementations
│ ├── AccessTokenRepository.php
│ ├── AuthCodeRepository.php
│ ├── ClientRepository.php
│ ├── RefreshTokenRepository.php
│ ├── ScopeRepository.php
│ └── UserRepository.php
├── Models/ # Database models
│ ├── Base.php # Base model class
│ ├── AccessToken.php
│ ├── AuthCode.php
│ ├── Client.php
│ ├── RefreshToken.php
│ ├── Scope.php
│ └── User.php
├── Modules/ # MVC Controllers
│ ├── AccessToken.php # Handles /access_token endpoint
│ ├── Authorize.php # Handles /authorize endpoint
│ └── Callback.php # Handles OAuth callback
├── public/
│ └── index.php # Application entry point
├── private.key # Private key for JWT signing
├── public.key # Public key for JWT verification
├── schema.sql # Database schema and seed data
├── composer.json # PHP dependencies
└── README.md # This file
-
Modules: MVC controllers that handle HTTP requests
AccessToken.php- Issues access tokens for all grant typesAuthorize.php- Displays authorization form and issues auth codesCallback.php- Receives OAuth callbacks and displays auth codes
-
Models: Database models using Active Record pattern
- Correspond to database tables
- Provide data access layer
-
Libraries/Repositories: Implement OAuth2 repository interfaces
- Interface between OAuth server and database
- Handle token/code persistence and retrieval
-
Libraries/Entities: Implement OAuth2 entity interfaces
- Represent OAuth2 concepts (clients, tokens, scopes)
- Used by the OAuth server during grant processing
- ✅ Server starts without errors
- ✅ Client credentials grant returns access token
- ✅ Authorization endpoint shows approval form
- ✅ Authorization code can be exchanged for access token
- ✅ Refresh token grant works
If you encounter issues:
- Check PHP version:
php -v(must be 8.4+) - Verify database connection: Check credentials in
config/app.php - Check file permissions: Ensure
private.keyhas proper permissions (600) - Review logs: Check PHP error logs for detailed error messages
- Database tables: Verify all tables were created:
SHOW TABLES;
- Never commit
private.key- Add it to.gitignore - Use HTTPS in production - OAuth requires secure connections
- Strong client secrets - Use cryptographically secure random strings
- Validate redirect URIs - Always validate against registered URIs
- Token expiration - Configure appropriate token lifetimes
- Password hashing - User passwords are hashed with bcrypt
- Database security - Use strong database credentials
- CORS configuration - Configure CORS headers appropriately for your use case
Now that your OAuth 2.0 server is running, you can:
- Register new clients - Add entries to the
clientstable - Define custom scopes - Add entries to the
scopestable - Integrate with your API - Validate tokens in your resource server
- Customize authorization UI - Modify the
Authorize.phpmodule - Add additional grant types - Extend with password grant or PKCE
For issues or questions:
- Check the league/oauth2-server documentation
- Review OAuth 2.0 specification: RFC 6749
- Contact: erik.lukiman@gmail.com
This project is part of the Steelytoe framework suite.