4spacesdk / ci4authextension
Easy OAuth2 integration with CodeIgniter 4
Requires
- php: >=8.0
- ext-openssl: *
- 4spacesdk/ci4debugtool: 1.0.8
- 4spacesdk/ci4ormextension: ^1.1
- bshaffer/oauth2-server-php: ^1.14.2
- codeigniter4/framework: ^4.7
- kelvinmo/simplejwt: ^1.1.2
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-master
- v1.3.1
- v1.3.0
- v1.2.7
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.1
- v1.0.0
- 1.0.0-beta.15
- 1.0.0-beta.14
- 1.0.0-beta.13
- 1.0.0-beta.12
- 1.0.0-beta.11
- 1.0.0-beta.10
- 1.0.0-beta.9
- 1.0.0-beta.8
- 1.0.0-beta.7
- 1.0.0-beta.6
- 1.0.0-beta.5
- 1.0.0-beta.4
- 1.0.0-beta.3
- 1.0.0-beta.2
- 1.0.0-beta.1
This package is auto-updated.
Last update: 2026-09-22 14:43:41 UTC
README
Installation
Step 1)
composer require 4spacesdk/ci4authextension
Step 2)
Create new file app/Config/AuthExtension.php and add this content
<?php namespace Config; use CodeIgniter\Config\BaseConfig; class AuthExtension extends BaseConfig { /* * Specify the database group. The storage connects with PDO, using the group's host, * credentials and `encrypt` settings (TLS, see CHANGELOG v1.3.1). */ public string $dbGroupName = 'default'; /* * If true, AuthExtension will extend routes with default endpoints * Check CI4AuthExtension/Hooks/PreController.php for details */ public bool $autoRoute = true; /* * OAuth Access token lifetime in seconds */ public int $oauthAccessTokenLifeTime = 15 * MINUTE; /* * OAuth Access token lifetime in seconds */ public int $oauthRefreshTokenLifeTime = 7 * DAY; /* * If true, the authorization endpoint requires a PKCE code challenge and * `oauth_authorization_codes` needs the `code_challenge` and `code_challenge_method` columns. * Clients that do not send a challenge are rejected with 400 missing_code_challenge. */ public bool $enforcePkce = true; /* * If true, access tokens are JWTs and the whole token string is stored, so * `oauth_access_tokens.access_token` must be wide enough to hold it. If false, the server * issues opaque tokens instead. */ public bool $useJwtAccessTokens = true; /* * The `iss` of id tokens and the base of the discovery document's endpoints. Empty uses * `base_url()`. */ public string $issuer = ''; /* * Path to login page */ public string $loginPage = '/login'; }
Step 3)
Add this line to your application/Config/Events.php file
Events::on('pre_system', [\AuthExtension\Hooks\PreController::class, 'execute']); Events::on('pre_command', [\AuthExtension\Hooks\PreController::class, 'execute']);
Step 4)
Add migration file and add this line to up(): \AuthExtension\Migration\Setup::migrateUp(); and this line to down(): \AuthExtension\Migration\Setup::migrateDown();.
Step 5)
Seed new users, ex:
$user = new User(); $user->first_name = 'Firstname'; $user->last_name = 'Lastname'; $user->username = 'some@email.com'; $user->password = password_hash('secret password', PASSWORD_BCRYPT); $user->save();
Step 6)
Add a controller and view for simple username/password login.
You can either use your own check login algorithm or use $loginResponse = AuthExtension::login($username, $password); which will return one of these constants and set user_id in session storage.
class LoginResponse { const Success = 'Success'; const RenewPassword = 'RenewPassword'; const WrongPassword = 'WrongPassword'; const UnknownUser = 'UnknownUser'; }
Upgrading to v1.3.0
v1.3.0 keeps no credentials in the clear in the oauth_* tables. See CHANGELOG.md for the details.
- Make sure the application has an encryption key (
Config\Encryption,encryption.keyin.env). The signing key is encrypted with it; without one it stays unencrypted, with a warning in the log. - Add a migration that calls
\AuthExtension\Migration\Upgrade_1_3_0::migrateUp(). It hashes the stored tokens, authorization codes and client secrets, encrypts the signing key and indexes the token columns. Sessions survive it, it can run before or after the new code is deployed, and running it again changes nothing. It cannot be undone: take a backup first. - Read the tables through the storage from now on. A token is stored as
AuthExtension\OAuth2\Pdo::hashToken($token). A client secret you write yourself must bePdo::hashClientSecret($secret)-AuthExtension\Entities\OAuthClientdoes it for you - and it cannot be shown again afterwards. - The issuer (
issin id tokens, and the base of the discovery document) isbase_url()now, not the request's host. Set$issuerinConfig\AuthExtensionto keep another value. checkLoginWithUsername($username, $scope)no longer takes the unused$password.- Sign-out follows
post_logout_redirect_urionly when it is registered on a client, and revokes no tokens - see "Revoke a user's tokens". - Schedule
AuthExtension::deleteExpiredTokens()- see "Delete expired tokens". - Run
php spark auth:rotate-signing-keyonce. Until v1.3.0 the signing key was stored in the clear, in every dump and backup since; encrypting it does not make a copy already taken useless. See "Rotate the signing key".
Authorize with session
$user = AuthExtension::checkSession();
$user is either FALSE or the authorized User.
Authorize with OAuth2
If you enable autoRoute in Config you can authorize by calling /check with access_token as query parameter or header.
Check AuthExtension\Hooks\PreController for more routes.
Delete expired tokens
Nothing removes expired tokens on its own. Call this from a nightly cron job:
$deleted = AuthExtension::deleteExpiredTokens(); // ['oauth_access_tokens' => 1327, ...]
It deletes expired access tokens, refresh tokens, id tokens and authorization codes. A refresh token that never expires is kept.
Revoke a user's tokens
Sign-out (/endsession) ends the session but revokes no tokens: nothing ties a token to one
browser. Revoke the ones the application holds through /revocation. When a password is changed
or reset, sign the user out everywhere:
AuthExtension::revokeUserTokens($user->id); // every client AuthExtension::revokeUserTokens($user->id, 'webclient'); // one client
post_logout_redirect_uri is followed only when it is a redirect uri registered on a client.
Rotate the signing key
php spark auth:rotate-signing-key
Makes a new signing key of the same kind and size, and retires the one in use. Tokens already
signed stay valid until they expire, and the key set (/openidconfiguration/jwks) keeps
publishing the retired key until then, each under its own kid. deleteExpiredTokens() removes
it afterwards. Needs Upgrade_1_3_0.
Rotate the encryption key
The signing key is encrypted with the application's encryption key. To rotate that key:
- Put the new key in
Config\Encryption::$keyand the old one in$previousKeys- CodeIgniter 4.7 or later; an older version ignorespreviousKeys, and the signing key can no longer be read. - Run
php spark auth:reencrypt-signing-keys. An application with a command of its own that re-encrypts its data should callAuthExtension::reencryptSigningKeys()from that instead, so one command says when the old key can go. Either answers how many keys were written and which could not be read. - Re-encrypt whatever else the application encrypted with the old key.
- Take the old key out of
previousKeys.