Search by

vielhuber / simpleauth

vielhuber

Simple php authentication library.

Package info

github.com/vielhuber/simpleauth

pkg:composer/vielhuber/simpleauth

Statistics

Installs: 584

Dependents: 1

Suggesters: 0

Stars: 3

Open Issues: 0

1.9.10 2026-10-16 10:59 UTC

README

build status GitHub Tag Code Style License Last Commit PHP Version Support Packagist Downloads

🔒 simpleauth 🔒

simpleauth is a simple php based authentication library.

it leverages:

  • json web tokens
  • bcrypted passwords
  • full api

installation

install once with composer:

composer require vielhuber/simpleauth

now simply create the following files inside a new folder called auth inside your public directory:

/auth/index.php

<?php
require_once __DIR__ . '/../vendor/autoload.php';
use vielhuber\simpleauth\simpleauth;
$auth = new simpleauth(config: __DIR__ . '/../.env', table: 'users', login: 'email', ttl: 1, uuid: false);
$auth->init();

/auth/.htaccess

RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /auth/index.php [L,QSA]

/.env

create a jwt secret (openssl rand -base64 64 | tr -d '\n' | xclip -selection clipboard)
and populate an .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=simpleauth
DB_USERNAME=root
DB_PASSWORD=root
JWT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

if you want to migrate and seed data, simply run

php auth/index.php migrate
php auth/index.php create "david@vielhuber.de" "secret"

and you should be done (a test user 'david@vielhuber.de' with the password 'secret' is created).
you can now fully authenticate with the routes below.
if you want to authenticate via username instead of email, simply change login to 'username'.
if you need uuids instead of integers as your user ids, change uuid to true. access tokens are valid for one day by default. you can adjust this with ttl. cors is enabled for all origins by default and can be restricted with cors. use cors: true or cors: '*' to allow all origins. Use cors: false to disable cors headers:

$auth = new simpleauth(
    /* ... */
    cors: ['https://example.tld']
);

Access tokens are bound to persistent sessions created automatically in <table>_sessions (for example users_sessions), alongside the other authentication tables. The database account needs permission to create the table and its indexes. Do not run migrate on an existing production database: it deletes all authentication tables. Each login creates an independent session, refresh keeps that session, and logout invalidates all of that session's original and refreshed tokens without affecting other sessions. Password changes, password resets, login identifier changes and user deletion through this library invalidate the affected user's sessions.

All access-token checks require an existing user, the matching login identifier and the current password hash. Direct database password changes invalidate tokens while the stored hash differs. To keep sessions revoked even if a previous password hash is restored, use setPassword / updateUser or delete the user's session rows in the same transaction as the password change. Applications that delete users directly must also delete their <table>_sessions rows (by user_id) in the same transaction, especially if IDs can be reused or restored. Direct login identifier changes invalidate tokens while the identifier differs; use updateUser for permanent revocation.

Upgrading from stateless access tokens requires a one-time login for existing users. Tokens without the new purpose: access and sid claims are rejected and cannot be refreshed. Existing token claims, public method signatures, endpoints and successful response fields remain unchanged. Signed password-reset tokens and application-specific service tokens are not user-session tokens.

login throttling is enabled by default: after 5 failed login attempts per login and IP within 15 minutes, /auth/login responds with status 429. You can disable it with throttle: false or adjust the limits with throttle:

$auth = new simpleauth(
    /* ... */
    throttle: [
        'attempts' => 5,
        'minutes' => 15,
        'table' => 'users_login_attempts'
    ]
);

captcha validation is disabled by default.
the matching captcha token is sent by the frontend on /auth/login.

$auth = new simpleauth(
    /* ... */
    captcha: [
        'provider' => '...', // 'turnstile'|'hcaptcha'
        'sitekey' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
        'secret' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    ]
);

passkeys are supported via WebAuthn and are available after running migrate. Browsers require a secure context for passkeys, except on localhost. You can disable passkeys with passkeys: false or adjust the table names with passkeys:

$auth = new simpleauth(
    /* ... */
    passkeys: [
        'table' => 'users_passkeys',
        'table_challenge' => 'users_passkeys_challenges'
    ]
);

password reset mails are sent as HTML emails through mailhelper. The reset token is a signed, stateless JWT and no additional database table is required. Configure SMTP through .env:

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USERNAME=username
SMTP_PASSWORD=password
SMTP_ENCRYPTION=tls
SMTP_FROM_EMAIL=noreply@example.com
SMTP_FROM_NAME=simpleauth
PASSWORD_RESET_URL=/reset-password

For Microsoft 365 / Exchange SMTP via OAuth, use tenant credentials instead of username/password:

SMTP_HOST=smtp.office365.com
SMTP_PORT=587
SMTP_TENANT_ID=tenant-id
SMTP_CLIENT_ID=client-id
SMTP_CLIENT_SECRET=client-secret
SMTP_ENCRYPTION=tls
SMTP_FROM_EMAIL=noreply@example.com
SMTP_FROM_NAME=simpleauth
PASSWORD_RESET_URL=/reset-password

PASSWORD_RESET_URL can be absolute (https://example.com/reset-password) or relative (/reset-password). Relative URLs use the current request's scheme and host, including the existing reverse-proxy handling; no configuration change is required. If the URL contains {token}, the placeholder is replaced. Otherwise the token is added as the token query parameter.

Relative reset URLs assume a secure deployment. The web server or reverse proxy must accept only authorized hostnames, remove or overwrite client-supplied forwarding headers, and prevent clients from bypassing the trusted proxy. The library does not verify this deployment policy; hostname syntax checks alone do not prevent reset-link poisoning. An absolute configured URL remains supported when the request metadata cannot be trusted.

The password reset flow is:

  1. Add a "forgot password" form in your application that posts the login field, for example email, to /auth/password-reset-request.
  2. simpleauth sends an email with a signed reset token link to PASSWORD_RESET_URL.
  3. Add a reset page in your application at that URL, for example /reset-password.
  4. The reset page reads the token query parameter and shows a form with a new password field.
  5. Submit token and password to /auth/password-reset.
  6. simpleauth verifies the token and sets the new password.

You can customize the password reset email with passwordResetMail. Return subject and content. The callback receives the login and the reset link:

$auth = new simpleauth(
    /* ... */
    passwordResetMail: fn(string $login, string $link): array => [
        'subject' => 'Reset your password',
        'content' =>
            '<p>Hello ' .
            htmlspecialchars($login) .
            ',</p><p><a href="' .
            htmlspecialchars($link) .
            '">Set a new password</a></p>'
    ]
);

routes

the following routes are provided automatically:

route method arguments header response
/auth/login POST email password captcha-response -- ([ 'success' => true, 'message' => 'auth successful', 'public_message' => '...', 'data' => [ 'access_token' => '...', 'expires_in' => 86400, 'user_id' => 42 ] ], 200)
/auth/refresh POST -- Authorization: Bearer token ([ 'success' => true, 'message' => 'auth successful', 'public_message' => '...', 'data' => [ 'access_token' => '...', 'expires_in' => 86400, 'user_id' => 42 ] ], 200)
/auth/logout POST -- Authorization: Bearer token ([ 'success' => true, 'message' => 'logout successful', 'public_message' => '...' ], 200)
/auth/check POST access_token -- ([ 'success' => true, 'message' => 'valid token', 'public_message' => '...', 'data' => [ 'expires_in' => 86400, 'user_id' => 42, 'client_id' => 7000000 ] ], 200)
/auth/password-reset-request POST email -- ([ 'success' => true, 'message' => 'password reset requested', 'public_message' => '...' ], 200)
/auth/password-reset POST token password -- ([ 'success' => true, 'message' => 'password reset successful', 'public_message' => '...' ], 200)
/auth/passkey-register-options POST -- Authorization: Bearer token ([ 'success' => true, 'message' => 'passkey registration options created', 'public_message' => '...', 'data' => [ 'publicKey' => [] ] ], 200)
/auth/passkey-register POST credential Authorization: Bearer token ([ 'success' => true, 'message' => 'passkey registered', 'public_message' => '...' ], 200)
/auth/passkey-login-options POST email optional -- ([ 'success' => true, 'message' => 'passkey login options created', 'public_message' => '...', 'data' => [ 'publicKey' => [] ] ], 200)
/auth/passkey-login POST credential -- ([ 'success' => true, 'message' => 'auth successful', 'public_message' => '...', 'data' => [ 'access_token' => '...', 'expires_in' => 86400, 'user_id' => 42 ] ], 200)
/auth/passkey-delete POST id Authorization: Bearer token ([ 'success' => true, 'message' => 'passkey deleted', 'public_message' => '...' ], 200)

tests

php -S localhost:8007 -t auth
./vendor/bin/phpunit

further usage

you can use the following functions inside your own application:

require __DIR__ . '/vendor/autoload.php';
use vielhuber\simpleauth\simpleauth;
$auth = new simpleauth(config: __DIR__ . '/../.env', table: 'users', login: 'email', ttl: 1, uuid: false);
$auth->isLoggedIn();
$auth->getCurrentUserId();
$auth->migrate();
$auth->getUsers();
$auth->getUser(login: 'david@vielhuber.de');
$auth->createUser(login: 'david@vielhuber.de', password: 'secret2');
$auth->updateUser(login: 'david@vielhuber.de', login_new: 'david@vielhuber.de', password_new: 'secret3');
$auth->setPassword(login: 'david@vielhuber.de', password: 'secret4');
$auth->requestPasswordReset(login: 'david@vielhuber.de');
$token = $auth->createPasswordResetToken(login: 'david@vielhuber.de');
$auth->resetPassword(token: $token, password: 'secret5');
$auth->getPasskeys(login: 'david@vielhuber.de');
$auth->deletePasskey(login: 'david@vielhuber.de', passkey_id: 1);
$auth->deleteUser(login: 'david@vielhuber.de');

frontend

if you need a neat frontend library that works together with
simpleauth seemlessly, try out jwtbutler.