Skip to main content

Protect your first app

You will run a login portal at https://localhost:8443/auth/ and protect / with a role check. Caddy serves a short message as the example application, so you can learn the access flow without setting up another server.

Complete Install and verify first. This example is tested with the AuthCrunch v1.5.0 distribution, caddy-security v1.5.0 and go-authcrunch v1.4.1.

Local learning example

This configuration includes two public demo passwords and a published signing secret. It uses HTTPS with a local certificate and binds to 127.0.0.1. It is intended for a disposable local directory. Read Next steps before adapting it for a deployment.

1. Create the configuration​

In your authcrunch-demo directory, create a file named Caddyfile with the following contents. The same file is available in the examples directory.

It adapts the distribution's end-to-end login Caddyfile with literal values, port 8443 and two local users. No environment variables or separate application files are required.

Caddyfile
# Adapted from authcrunch/authcrunch/assets/e2e-login/Caddyfile.
# Local learning only: public demo credentials and signing key; no environment variables.
{
admin off
persist_config off
auto_https disable_redirects
skip_install_trust
on_demand_tls {
ask http://localhost:5555/
}
storage file_system ./data/caddy

security {
local identity store localdb {
realm local
path :memory:
user alice {
name Alice Example
email alice@example.test
password "LocalDemoPassword123!"
roles authp/user app/member
}
user bob {
name Bob Example
email bob@example.test
password "LocalDemoPassword123!"
roles authp/user
}
}

authentication portal myportal {
enable identity store localdb
crypto default token lifetime 300
crypto key sign-verify "LocalDemoSigningKey-ChangeBeforeDeployment-0123456789"
cookie guess domain enabled
cookie prefix AUTHTEST
trust login redirect uri domain exact localhost:8443 path prefix /
ui {
links {
"Example app" / icon "las la-external-link-alt"
}
}
}

authorization policy apppolicy {
set auth url https://localhost:8443/auth/login
crypto key verify "LocalDemoSigningKey-ChangeBeforeDeployment-0123456789"
allow roles authp/admin authp/user
set session_id cookie name AUTHTEST_SESSION_ID
set access_token cookie name AUTHTEST_ACCESS_TOKEN
}
}
}

http://localhost:5555 {
bind 127.0.0.1
@allowed_localhost query domain=localhost
respond @allowed_localhost 200
respond 400
}

https://localhost:8443 {
bind 127.0.0.1
tls internal {
on_demand
}

route /healthz {
respond "AuthCrunch demo is running." 200
}

route /auth* {
redir /auth /auth/ 308
authenticate with myportal
}

route /* {
authorize with apppolicy
respond "You reached the protected app." 200
}
}

The two users share a demo password and can both access the app:

UserPasswordRolesExpected app access
aliceLocalDemoPassword123!authp/user, app/memberAllowed
bobLocalDemoPassword123!authp/userAllowed

The policy accepts authp/admin or authp/user. Alice's additional app/member role is used in the narrower-policy exercise on the next page.

The local store hashes the configured passwords when creating its user records. The clear-text examples remain in your Caddyfile, so use these credentials only for this demo.

2. Prepare local storage​

mkdir -p data

The Caddyfile contains the same literal demo key in the portal's signing and policy's verification settings. Access tokens last at most 300 seconds. Use this published demo secret only for the local exercise.

Users live in memory (path :memory:) and reset when the process stops. Caddy keeps its local CA and certificates under data/caddy, relative to the directory from which you start the process. No user database is written there.

3. Check and run​

./bin/authcrunch adapt --adapter caddyfile --config Caddyfile >/dev/null
./bin/authcrunch run --config Caddyfile

Adaptation checks whether Caddy can parse the configuration. The second command starts the server in the foreground and provisions the local user store. Leave it running while you use the browser.

Open https://localhost:8443/. Caddy asks the loopback approval endpoint on port 5555 to approve localhost, then issues its local certificate. The endpoint returns 400 for other or missing domains.

The browser will warn that the local CA is untrusted because skip_install_trust prevents automatic changes to your trust store. For this local demo, use the browser's advanced option to continue to https://localhost:8443. You should then reach the login page. Keep localhost consistently in the address; cookies and certificate trust do not transfer to 127.0.0.1.

4. Sign in as Alice​

  1. Enter alice on the login page and proceed.
  2. Enter LocalDemoPassword123! and submit the password form.
  3. The trusted return destination takes you back to / automatically. If you started directly at the portal, select Example app on its Applications page.

The app should display:

You reached the protected app.

Alice's token contains authp/user, which the policy permits. Bob has that role too. The next page checks both logins and shows how a narrower rule denies Bob.

What you should see​

These screenshots come from a Playwright run of the exact Caddyfile above with the v1.5.0 distribution on October 26, 2026. Both desktop and mobile Chromium completed the username/password flow, reached the app, and required login again after signing out. Select an image to see it at full size.

AuthCrunch Sign In form asking for a username or email address, with a Proceed button.
1. Identify the user. Opening / without a login cookie returns HTTP 302 and takes you to this form. Enter alice, then select Proceed.
AuthCrunch Authentication form with a masked password and an Authenticate button.
2. Complete the challenge. Enter the demo password and select Authenticate. A successful password check issues the named access and session cookies.

The browser run also checked access after a reload and in another tab, nested app paths, a token with an altered signature, and the AUTHTEST cookie names with Secure, HttpOnly, SameSite=Lax, and path /. Browser automation accepted the demo's local CA warning; curl separately verified its certificate chain. Mobile coverage is Chromium emulation. Browser logout does not establish revocation of an independently copied token.

How the configuration fits together​

The global security block defines named building blocks. The site block connects those names to HTTP routes.

ConfigurationPurpose
local identity store localdb with path :memory:Creates the two demo users in memory
enable identity store localdbGives myportal a login source
route /auth* with authenticate with myportalServes the portal separately from the protected app
set auth url https://localhost:8443/auth/loginTells the policy where to send a browser that needs to log in
crypto key sign-verify / crypto key verifyConnects the token issuer and verifier using the same secret
trust login redirect uri …Permits returning to / and its descendants on localhost:8443
cookie guess domain enabled / cookie prefix AUTHTESTInfers the cookie domain and gives portal cookies the AUTHTEST prefix
set session_id cookie name … / set access_token cookie name …Makes the policy read the portal's AUTHTEST_SESSION_ID and AUTHTEST_ACCESS_TOKEN cookies
tls internal { on_demand }Issues a local certificate after the loopback endpoint approves the hostname
allow roles authp/admin authp/userAllows either portal role, including both demo users
authorize with apppolicyChecks access before the protected response

The /healthz, /auth*, and /* route blocks keep the public health check, portal, and protected application separate. Caddy selects the more specific health and portal routes before the catch-all app route. Inside the app route, authorize runs before respond; the app's response is reached only after the policy allows the request. It covers / and all remaining application paths.

The portal's cookie prefix and the policy's cookie names must agree. The portal uses the default cookie path.

HTTPS allows the portal to use Secure cookies. The approval site at http://localhost:5555 only approves certificate names; it does not serve login or the app. auto_https disable_redirects avoids an extra HTTP redirect listener. admin off disables Caddy's administration endpoint, and persist_config off disables its saved configuration. Use Ctrl+C to stop this foreground demo; do not use caddy reload with it.

Continue to Verify access before changing the example.