Fake Clerk OAuth in Coolify
After I fell in love with the way Coolify makes deployments stupidly simple, it was bound to stay. But I wanted to use OAuth to authenticate users.
Officially Coolify supports Azure, BitBucket, GitLab and Google OAuth providers. While not explicitly mentioned in the docs, it supports a few more (Authentik, Clerk, Discord, Infomaniak and Citadel).
But mine (PocketID) unfortunately isn’t on the list and all the providers differ in their implementations in minute details, like data structures or URL schemas.
Fake Clerk Proxy
An hour of back and forth with Claude later, Clerk was made out to be the closest match to PocketID. All that is needed is a small proxy that rewrites a few urls:
:80 {
log {
output file /var/log/caddy/access.log
format json
}
@authorize path /oauth/authorize
handle @authorize {
uri query scope openid+email+profile
redir {$POCKETID_URL}/authorize?{query} 302
}
rewrite /oauth/token /api/oidc/token
rewrite /oauth/userinfo /api/oidc/userinfo
reverse_proxy {$POCKETID_URL}
}
Combined with a small Dockerfile:
FROM caddy:2-alpine
ENV POCKETID_URL=""
COPY Caddyfile /etc/caddy/Caddyfile
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost/ || exit 1
Custom Claims
Coolify expects additional Claims from “Clerk” that PocketID does not provide by default, using a custom User Group that injects these hardcoded values we can fake it.
email_verified = true
user_id = pocketid-user
The actual values don’t matter, Coolify merely expects them to be set and then ignores it. Users are matched by their email address.
Configure Coolify OAuth
Once deployed and running, the “Clerk” OAuth can be configured as usual and the Base URL/Authentication Endpoint pointing to the “Clerk” Proxy.
PocketID being Passkeys-only is considered safe, but the old Username/Password login is vulnerable to brute-force attacks.
To prevent this we add a redirect in the Server Configuration -> Proxy -> Dynamic Configurations we add a file called login-redirect.yaml (Don’t forget to replace {coolify-host} with your domain):
http:
middlewares:
redirect-to-clerk:
redirectRegex:
regex: "^https://{coolify-host}/login$"
replacement: "https://{coolify-host}/auth/clerk/redirect"
permanent: false
routers:
coolify-login-redirect:
rule: "Host(`{coolify-host}`) && Path(`/login`)"
middlewares:
- redirect-to-clerk@file
service: coolify
entryPoints:
- https
tls:
certresolver: letsencrypt
Coolify’s Traefik should automatically pick up dynamic configs and the next login should immediately redirect you to your OAuth Endpoint.
Further considerations
This will break authentication if the “Clerk” Proxy container or PocketID is ever not up and reachable.
Should either one of those go down we can place this shell script on the host as an easy way to gain emergency access:
#!/bin/sh
FILE="/data/coolify/proxy/dynamic/login-redirect.yaml"
if [ -f "$FILE" ]; then
rm "$FILE"
echo "Login redirect disabled — password login restored"
else
cat > "$FILE" << 'EOF'
http:
middlewares:
redirect-to-clerk:
redirectRegex:
regex: "^https://{coolify-host}/login$"
replacement: "https://{coolify-host}/auth/clerk/redirect"
permanent: false
routers:
coolify-login-redirect:
rule: "Host(`{coolify-host}`) && Path(`/login`)"
middlewares:
- redirect-to-clerk@file
service: coolify
entryPoints:
- https
tls:
certresolver: letsencrypt
EOF
echo "Login redirect enabled — passkey login enforced"
fi