This document describes how to use the API key functionality for application-to-application authentication using non-expiring API tokens.
This environment includes a built-in API key system that allows you to manage API tokens with an unlimited lifetime and assign them the necessary roles. This is primarily used for seamless integration with external applications and third-party services.
The current implementation allows you to:
- Create an unlimited number of API tokens.
- Define custom roles and user groups.
- Assign API keys to specific user groups to strictly control access levels.
The application allows you to store API key tokens in the database in one of two formats: Encrypted or Plain text (Unencrypted).
You can configure this behavior in your .env file using the following parameters:
###> Api-key options ###
API_KEY_TOKEN_OPEN_SSL_ENCRYPT=1
API_KEY_TOKEN_HASH_ALGO=sha256
###< Api-key options ###
###> Open ssl options ###
OPEN_SSL_ALGORITHM=aes-128-gcm
OPEN_SSL_KEY=systemsdk
###< Open ssl options ###
⚠️ Important Warning: Modifying any of these values after API keys have been generated will invalidate all existing tokens in the database. You will need to generate new ones.
Parameter Details:
API_KEY_TOKEN_OPEN_SSL_ENCRYPT: Set to1to enable OpenSSL encryption for tokens in the database, or0to store them as plain text.API_KEY_TOKEN_HASH_ALGO: Specifies the hashing algorithm (e.g., md5, sha1, sha256, sha512). This is used when encryption is enabled.OPEN_SSL_ALGORITHM: The cipher method used for encryption (the default isaes-128-gcm).OPEN_SSL_KEY: The secret passphrase used for encryption. You must set a strong, unique value for this key in your production environment and store it securely.
Consider a scenario where you need to expose two separate API endpoints for two different external applications: Application1 and Application2. To maintain strict security, Application1 must not be able to access the endpoint designated for Application2 and vice versa.
To support this level of granular access control, the system relies on the following database tables:
-
api_keyStores the unique API tokens (
token) and their descriptions. You can generate a new API token using the command:./bin/console api-key:create.Tip: All necessary default roles and user groups will be created automatically on the first run if they are missing.
-
roleStores the unique
rolestring (which acts as the primary key) and a detaileddescriptionof its permissions. You can extend the list of roles within theApp\Role\Domain\Enum\Role.phpclass. To synchronize (create/delete) these roles with the database, use the command:./bin/console user:create-roles. -
user_groupStores user groups, consisting of a mandatory
nameand an associatedrole(allowing for flexible logical grouping without strict permissions). You can create additional groups using the command:./bin/console user:create-group. -
api_key_has_user_groupA pivot table that establishes a many-to-many relationship. It uses a composite primary key linking the UUIDs (
api_key_idanduser_group_id) of API tokens and their assigned user groups. You can assign one or multiple groups (comma-separated) directly when creating a new token via theapi-key:createcommand.
Routing Note: All endpoints utilizing this API key functionality must be configured under the
^/apiURL prefix.
Security Note: Following Symfony security standards, all roles defined in
App\Role\Domain\Enum\Role.phpmust begin with theROLE_prefix. For more details, refer to the Symfony Security Documentation.
Follow these steps to implement the scenario described above:
-
Create the API endpoint for Application1
Configure the route and restrict access using the specific role for Application1.
#[Route( path: '/application1/endpoint1', name: 'api_to_api_application1_endpoint1', requirements: [ 'role' => new EnumRequirement(Role::class), ], methods: [Request::METHOD_GET], )] #[IsGranted(Role::APPLICATION1->value)] -
Create the API endpoint for Application2
Similarly, configure the route and restrict access using the specific role for Application2.
#[Route( path: '/application2/endpoint1', name: 'api_to_api_application2_endpoint1', requirements: [ 'role' => new EnumRequirement(Role::class), ], methods: [Request::METHOD_GET], )] #[IsGranted(Role::APPLICATION2->value)] -
Define the Roles
Extend the role list and add descriptions inside the
App\Role\Domain\Enum\Role.phpclass (e.g., addROLE_APPLICATION1andROLE_APPLICATION2). -
Synchronize Roles
Run the following console command to sync your new roles with the database:
./bin/console user:create-roles
-
Create User Groups
Run this command to create a distinct user group for each of the new roles:
./bin/console user:create-group
-
Generate API Keys
Run the key generation command to create an API token for each application, assigning the appropriate user group to each token:
./bin/console api-key:create
-
Test the Authentication
You can verify the created API key tokens by sending a request with the following header:
Authorization:ApiKey your_token_here.
That's it! You can now use both tokens. The token assigned to Application1 will successfully authenticate against its specific endpoint but will receive an "Access Denied" response if it attempts to access the endpoint designated for Application2.
⚠️ Important Note: The system includes a built-in internal role calledROLE_API. All generated API keys are implicitly assigned thisROLE_APIby default. This means that if an endpoint is protected strictly by#[IsGranted(Role::API->value)], all existing valid API keys will be able to access it, regardless of which specific user groups are assigned to them.