Building a Secure Riot Games Authentication System with PHP
- What is Riot Games RSO?
- Prerequisites
- Project Structure
- Setting Up the Project
- Understanding the Authentication Flow
- Security Considerations
- Key Implementation Details
- Best Practices Implemented
- Conclusion
- Additional Resources
Building a Secure Riot Games Authentication System with PHP
Authentication is a crucial component of any modern web application, especially when integrating with gaming platforms. In this guide, we'll explore how to implement Riot Games' Sign On (RSO) authentication system using PHP, following security best practices and industry standards.
What is Riot Games RSO?
Riot Sign On (RSO) is Riot Games' OAuth2-based authentication system that allows developers to integrate Riot Games authentication into their applications. This enables users to log in using their Riot Games credentials and access their account information securely.
Prerequisites
Before diving in, make sure you have:
- PHP 8.0 or higher
- SSL certificate (HTTPS is mandatory)
- Composer for dependency management
- A registered Riot Games Developer account
- RSO credentials from the Riot Developer Portal
Project Structure
The project follows a clean and organized structure:
├── config.php # Configuration file
├── index.php # Entry point
├── callback.php # OAuth callback handler
├── helpers.php # Utility functions
├── README.md # Project documentation
└── composer.json # Dependencies and autoloading
Setting Up the Project
- First, clone the repository and install dependencies:
git clone https://github.com/REFUZIION/riot-php-rso-example.git
cd riot-php-rso-example
composer install
- Update the configuration with your credentials:
const BASE_URI = 'https://your-domain.com/'; // Must use HTTPS
const RIOT_CLIENT_ID = 'YOUR_CLIENT_ID_HERE';
const RIOT_CLIENT_SECRET = 'YOUR_CLIENT_SECRET_HERE';
Understanding the Authentication Flow
The authentication process consists of three main components:
-
Initial Authorization Request (
index.php
):- Creates the authorization URL with required parameters
- Redirects users to Riot's authentication page
<?php
require_once('config.php');
require_once('helpers.php');
// Validate configuration
validateConfig();
// Ensure proper URL formatting
$appCallbackUrl = ensureTrailingSlash(BASE_URI) . "callback.php";
$provider = "https://auth.riotgames.com";
$authorizeUrl = ensureTrailingSlash($provider) . "authorize";
$clientID = RIOT_CLIENT_ID;
$redirectUri = urlencode($appCallbackUrl);
$authorizationUrl = $authorizeUrl . "?redirect_uri=" . $redirectUri
. "&client_id=" . $clientID
. "&response_type=code"
. "&scope=openid";
?>
<!DOCTYPE html>
<html>
<head>
<title>Riot Games Login</title>
</head>
<body>
<a href="<?= htmlspecialchars($authorizationUrl) ?>">
Click to login with Riot
</a>
</body>
</html>
-
Callback Handler (
callback.php
):- Processes the authorization code from Riot
- Exchanges it for an access token
- Retrieves user account information
<?php
/**
* Riot Games RSO Callback Handler
*
* This script handles the OAuth2 callback from Riot Games authentication.
* It processes the authorization code, exchanges it for an access token,
* and retrieves the user's account information.
*/
declare(strict_types=1);
require_once('config.php');
require_once('helpers.php');
require_once $_SERVER["DOCUMENT_ROOT"] . '/vendor/autoload.php';
// Validate configuration before proceeding
validateConfig();
// Verify the authorization code is present
if (!isset($_GET['code'])) {
die('Authorization code is missing. Please try logging in again.');
}
// Initialize OAuth2 provider
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => RIOT_CLIENT_ID,
'urlAuthorize' => 'https://auth.riotgames.com/authorize',
'urlAccessToken' => 'https://auth.riotgames.com/token',
'urlResourceOwnerDetails' => 'https://auth.riotgames.com/userinfo'
]);
// Configure HTTP client with reasonable timeouts
$client = new \GuzzleHttp\Client([
'timeout' => 30,
'connect_timeout' => 5,
'http_errors' => true,
'verify' => true
]);
try {
// Step 1: Exchange authorization code for access token
$tokenResponse = $client->post('https://auth.riotgames.com/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => ensureTrailingSlash(BASE_URI) . 'callback.php',
'client_id' => RIOT_CLIENT_ID,
'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
'client_assertion' => RIOT_CLIENT_SECRET
]
]);
$tokenData = json_decode($tokenResponse->getBody()->getContents(), true);
if (!isset($tokenData['access_token'])) {
throw new RuntimeException('Access token not found in response');
}
// Step 2: Fetch user account information
$accountResponse = $client->request(
'GET',
'https://europe.api.riotgames.com/riot/account/v1/accounts/me',
[
'headers' => [
'Authorization' => sprintf('Bearer %s', $tokenData['access_token']),
'Accept' => 'application/json',
]
]
);
$accountData = json_decode($accountResponse->getBody()->getContents(), true);
// Validate required user data fields
$requiredFields = ['gameName', 'tagLine', 'puuid'];
foreach ($requiredFields as $field) {
if (!isset($accountData[$field])) {
throw new RuntimeException(sprintf('Missing required field: %s', $field));
}
}
// Prepare sanitized user data
$userData = [
'riot_id' => htmlspecialchars($accountData['gameName']),
'tagline' => htmlspecialchars($accountData['tagLine']),
'puuid' => htmlspecialchars($accountData['puuid'])
];
} catch (Exception $e) {
$errorMessage = handleError($e);
die($errorMessage);
}
// Render the response
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Riot Account Information</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
pre {
background: #f5f5f5;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
}
</style>
</head>
<body>
<h1>Token Response</h1>
<pre><?php print_r($tokenData); ?></pre>
<h1>User Information</h1>
<pre><?php print_r($userData); ?></pre>
</body>
</html>
-
Helper Functions (
helpers.php
):- Provides utility functions for validation and error handling
- Ensures secure configuration
<?php
function ensureTrailingSlash($url)
{
return rtrim($url, '/') . '/';
}
function validateConfig()
{
if (!defined('BASE_URI') || !defined('RIOT_CLIENT_ID') || !defined('RIOT_CLIENT_SECRET')) {
die('Missing required configuration constants. Please check your config.php file.');
}
if (!str_starts_with(strtolower(BASE_URI), 'https://')) {
die('BASE_URI must use HTTPS. Riot Games RSO requires a secure connection.');
}
if (empty(RIOT_CLIENT_ID) || RIOT_CLIENT_ID === 'YOUR_CLIENT_ID_HERE') {
die('Please set a valid RIOT_CLIENT_ID in config.php');
}
if (empty(RIOT_CLIENT_SECRET) || RIOT_CLIENT_SECRET === 'YOUR_CLIENT_SECRET_HERE') {
die('Please set a valid RIOT_CLIENT_SECRET in config.php');
}
}
function handleError($exception)
{
error_log($exception->getMessage());
if (strpos($exception->getMessage(), '401') !== false) {
return 'Authentication failed. Please check your client credentials.';
}
if (strpos($exception->getMessage(), '404') !== false) {
return 'Resource not found. Please check the API endpoints.';
}
return 'An unexpected error occurred. Please try again later.';
}
Security Considerations
The implementation includes several security measures:
-
HTTPS Enforcement: All communications must use HTTPS, enforced through configuration validation.
-
Input Validation: All user inputs and API responses are validated before processing.
-
Output Sanitization: User data is properly sanitized before display using
htmlspecialchars()
. -
Error Handling: Comprehensive error handling for various scenarios:
- Missing/invalid configuration
- Network failures
- Authentication errors
- Invalid responses
Key Implementation Details
Authorization Request
The initial authorization request is handled in index.php
:
$redirectUri = urlencode($appCallbackUrl);
$authorizationUrl = $authorizeUrl . "?redirect_uri=" . $redirectUri
. "&client_id=" . $clientID
. "&response_type=code"
. "&scope=openid";
Token Exchange and User Data Retrieval
The callback handler processes the authentication response:
try {
// Step 1: Exchange authorization code for access token
$tokenResponse = $client->post('https://auth.riotgames.com/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => ensureTrailingSlash(BASE_URI) . 'callback.php',
'client_id' => RIOT_CLIENT_ID,
'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
'client_assertion' => RIOT_CLIENT_SECRET
]
]);
$tokenData = json_decode($tokenResponse->getBody()->getContents(), true);
if (!isset($tokenData['access_token'])) {
throw new RuntimeException('Access token not found in response');
}
// Step 2: Fetch user account information
$accountResponse = $client->request(
'GET',
'https://europe.api.riotgames.com/riot/account/v1/accounts/me',
[
'headers' => [
'Authorization' => sprintf('Bearer %s', $tokenData['access_token']),
'Accept' => 'application/json',
]
]
);
$accountData = json_decode($accountResponse->getBody()->getContents(), true);
Error Handling
The project includes some error handling in helpers.php
:
function handleError($exception)
{
error_log($exception->getMessage());
if (strpos($exception->getMessage(), '401') !== false) {
return 'Authentication failed. Please check your client credentials.';
}
if (strpos($exception->getMessage(), '404') !== false) {
return 'Resource not found. Please check the API endpoints.';
}
return 'An unexpected error occurred. Please try again later.';
}
Best Practices Implemented
-
Dependency Management: Using Composer for reliable package management and autoloading.
-
Configuration Separation: Sensitive credentials are stored in a separate configuration file.
-
Input Validation: All user inputs and API responses are validated.
-
Error Handling: Comprehensive error handling with appropriate user feedback.
-
Security Headers: Proper security headers and HTTPS enforcement.
Conclusion
This implementation provides a solid foundation for integrating Riot Games authentication into your PHP applications. It follows security best practices while maintaining code readability and maintainability.
The complete source code is available on GitHub, and you can find additional documentation in the repository's README.md file.
Remember to always keep your dependencies updated and regularly review Riot Games' documentation for any changes to their authentication system.