PHPackages                             moonexpr/kimai-loopback-auth-plugin - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. moonexpr/kimai-loopback-auth-plugin

ActiveKimai-plugin[Authentication &amp; Authorization](/categories/authentication)

moonexpr/kimai-loopback-auth-plugin
===================================

Passwordless auto-login for same-machine (loopback) requests via the REMOTE\_USER FastCGI param. No core files modified.

1.1.0(1mo ago)02↓90%MITPHPPHP ^8.1

Since Jun 4Pushed 1mo agoCompare

[ Source](https://github.com/moonexpr/kimai-loopback-auth-plugin)[ Packagist](https://packagist.org/packages/moonexpr/kimai-loopback-auth-plugin)[ Docs](https://github.com/moonexpr/kimai-loopback-auth-plugin)[ RSS](/packages/moonexpr-kimai-loopback-auth-plugin/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (3)Used By (0)

LoopbackAuth — passwordless same-machine login for Kimai
========================================================

[](#loopbackauth--passwordless-same-machine-login-for-kimai)

A [Kimai](https://www.kimai.org/) plugin that auto-logs-in a user when the request comes from the same machine (loopback). It modifies no Kimai core file, so it survives upgrades.

The plugin holds **no password logic**. It reads one signal — the `REMOTE_USER`server variable, which your web server sets *only* for trusted (by default, loopback) clients — and, as a second independent check, confirms the real peer address is in its own trusted allowlist before logging that user in. Auto-login therefore requires **two gates to agree**: the web-server config and the plugin's `REMOTE_ADDR` allowlist (see [Hardening](#hardening)). A mistake in either one alone does not grant access.

> ### ⚠️ Security model — read before installing
>
> [](#️-security-model--read-before-installing)
>
> This plugin grants a session, **without a password**, to whichever username appears in `REMOTE_USER` — *provided* the request also originates from an allowlisted peer address. There are two independent gates:
>
> 1. **Web server** — must be configured to set `REMOTE_USER` *exclusively* for trusted (default: loopback `127.0.0.1` / `::1`) clients, and strip any client-supplied value on every other path.
> 2. **Plugin** — independently checks the real `REMOTE_ADDR` against its trusted allowlist (default loopback; `LOOPBACK_AUTH_TRUSTED_IPS` to widen) and refuses any peer outside it, even if `REMOTE_USER` is set.
>
> The second gate is defense in depth: a misconfigured `fastcgi_param`, a reverse proxy, or an untrusted upstream that populates `REMOTE_USER` for a non-loopback request is **not** by itself enough to authenticate. It is still a deliberate password bypass, though — treat both gates as part of the plugin, keep their allowlists in agreement, and run [`scripts/security-audit.sh`](scripts/security-audit.sh)to check your deployment.
>
> Intended use is a single-operator, local-only (or authenticated-tunnel) Kimai instance, where typing a password on every visit to your own machine is pure friction.

Requirements
------------

[](#requirements)

- Kimai **2.0** or newer (`extra.kimai.require: 20000`)
- PHP **8.1+**
- A web server that can set the `REMOTE_USER` FastCGI parameter conditionally (the examples below use nginx + PHP-FPM)

How it works
------------

[](#how-it-works)

Kimai is a Symfony application. Symfony's security layer forbids a plugin from declaring firewall configuration in a second file, so this plugin reaches the firewall at the dependency-injection container level instead:

1. **`Security/LoopbackAuthenticator`** is a self-validating [`AbstractAuthenticator`](https://symfony.com/doc/current/security/custom_authenticator.html). Its `supports()` returns `true` only when **all** of these hold: `REMOTE_USER`is a non-empty string; the real peer address (`REMOTE_ADDR`) is inside the trusted allowlist (default loopback — see [Hardening](#hardening)); and the current session is not already authenticated as that same user. It stands aside (returns `false`) otherwise, letting Kimai's normal `form_login` flow apply. The `REMOTE_ADDR` check is defense in depth — see [Hardening](#hardening) for why it does not rely on the web server alone.
2. When it does fire, `authenticate()` returns a `SelfValidatingPassport` built from a `UserBadge` for the `REMOTE_USER` identifier, resolved against Kimai's internal user provider (`security.user.provider.concrete.kimai_internal`). There is no credential to check — the trust boundary is the web server — so the passport self-validates.
3. **`DependencyInjection/Compiler/RegisterLoopbackAuthenticatorPass`** appends the authenticator to the `secured_area` firewall's authenticator manager at compile time. This is the one Symfony-internal coupling in the plugin: it reads `security.authenticator.manager.secured_area` and adds a reference to the authenticator. **It fails safe** — if that service definition is not found (e.g. a future Symfony release renames it), the pass no-ops and the firewall simply lacks loopback auth; normal password login keeps working.

`onAuthenticationSuccess` and `onAuthenticationFailure` both return `null`: success lets the request continue to its intended controller without a redirect, and failure falls through to the other authenticators and the normal login entry point.

Installation
------------

[](#installation)

Kimai plugins are loaded from the `var/plugins/` directory; they are not installed into `vendor/` like ordinary Composer libraries. Use the Git method (the one Kimai documents); the Composer method is offered as a convenience for installs that carry the `kimai/kimai2-composer` installer.

### 1. Install the plugin

[](#1-install-the-plugin)

**Git (recommended):** clone the bundle so its path is exactly `var/plugins/LoopbackAuthBundle/` — the directory name must match the bundle class for Kimai's autoloader to find it:

```
cd /path/to/kimai
git clone https://github.com/moonexpr/kimai-loopback-auth-plugin.git var/plugins/LoopbackAuthBundle
```

**Composer (alternative):** on a Kimai install whose root `composer.json`includes the `kimai/kimai2-composer` installer (Kimai's `kimai-plugin` package type routes the package to `var/plugins/` rather than `vendor/`):

```
composer require moonexpr/kimai-loopback-auth-plugin
```

### 2. Configure the web server

[](#2-configure-the-web-server)

The plugin does nothing until your web server sets `REMOTE_USER` for loopback clients. This is the security-critical half — see [Web server configuration](#web-server-configuration-the-required-other-half)below and the ready-made files in [`examples/nginx/`](examples/nginx/).

### 3. Rebuild the Kimai cache

[](#3-rebuild-the-kimai-cache)

So the compiler pass runs and the authenticator is wired into the firewall:

```
bin/console kimai:reload --env=prod
# or, equivalently:
bin/console cache:clear --env=prod
```

### 4. Confirm Kimai sees the plugin

[](#4-confirm-kimai-sees-the-plugin)

```
bin/console kimai:bundles
```

`LoopbackAuth` should appear in the list.

The user named in `REMOTE_USER` must already exist as a Kimai user — the plugin authenticates an existing account, it does not create one.

Web server configuration (the required other half)
--------------------------------------------------

[](#web-server-configuration-the-required-other-half)

The plugin is inert until your web server sets `REMOTE_USER`, and it is only safe if your web server sets `REMOTE_USER` **only for loopback clients**. Two ready-made files in [`examples/nginx/`](examples/nginx/) do this for nginx + PHP-FPM; adapt them to your deployment rather than copying blindly.

### Step 1 — define the loopback→user map

[](#step-1--define-the-loopbackuser-map)

[`examples/nginx/loopback-auth-map.conf`](examples/nginx/loopback-auth-map.conf)maps the **real peer address** to a username, defaulting to the empty string for everyone else. Drop it into nginx's http context:

```
cp examples/nginx/loopback-auth-map.conf /etc/nginx/conf.d/kimai-loopback-auth.conf
# then edit it: set the username you want auto-logged-in for 127.0.0.1 / ::1
```

```
map $remote_addr $kimai_loopback_user {
    default    "";
    127.0.0.1  "admin";
    ::1        "admin";
}
```

### Step 2 — forward it to PHP as `REMOTE_USER`

[](#step-2--forward-it-to-php-as-remote_user)

The Kimai vhost routes PHP through `location ~ ^/index\.php(/|$)`. One line inside that block forwards the mapped value. [`examples/nginx/enable-remote-user.patch`](examples/nginx/enable-remote-user.patch)adds it for you, against Kimai's documented vhost:

```
patch -p1 --fuzz=3 /etc/nginx/sites-available/kimai.conf
