Security and Authentication in Clojure

Design authentication, authorization, secret handling, and audit boundaries deliberately so Clojure integrations stay secure without turning middleware into policy chaos.

Security in a Clojure system is not one middleware decision. It is a set of boundaries: identity verification, authorization, secret handling, transport protection, and auditability. The implementation can involve libraries such as Buddy, Ring middleware, OAuth/OIDC providers, and JVM crypto tools, but the deeper design question is always the same: where does trust enter the system, and how is it constrained after that point?

Separate Authentication from Authorization

Teams often mix these two ideas together.

  • Authentication answers: who is this caller?
  • Authorization answers: what may this caller do?

If those boundaries blur, handlers become full of ad hoc if checks and security rules leak into unrelated business code.

    flowchart LR
	    REQ["Incoming request"] --> AUTHN["Authentication"]
	    AUTHN --> ID["Identity on request"]
	    ID --> AUTHZ["Authorization policy"]
	    AUTHZ --> HANDLER["Business handler"]
	    HANDLER --> AUDIT["Audit / security events"]

The main design goal is to make identity explicit on the request map and to keep policy checks close to the route or use case that needs them.

Password Handling: Never Store Reversible Secrets

For username/password flows, store password hashes, not encrypted passwords.

1(ns myapp.auth.passwords
2  (:require [buddy.hashers :as hashers]))
3
4(defn hash-password [raw-password]
5  (hashers/derive raw-password))
6
7(defn valid-password? [raw-password stored-hash]
8  (hashers/check raw-password stored-hash))

That gives you slow password hashing designed for credential storage. Application encryption and password hashing solve different problems and should not be treated as interchangeable.

Token or Session, but Validate Deliberately

Clojure applications often sit behind Ring-style middleware, which makes it natural to attach authenticated identity to the request. Whether you use session cookies, signed tokens, or delegated identity from an external provider, the same rule applies: validate before trusting.

 1(ns myapp.auth.tokens
 2  (:require [buddy.sign.jwt :as jwt]))
 3
 4(def signing-secret "replace-with-secret-from-env")
 5
 6(defn issue-token [identity]
 7  (jwt/sign {:sub (:user-id identity)
 8             :roles (:roles identity)}
 9            signing-secret))
10
11(defn authenticate-token [token]
12  (try
13    (jwt/unsign token signing-secret)
14    (catch Exception _
15      nil)))

Important checks usually include:

  • signature or session integrity
  • expiration
  • issuer and audience where relevant
  • revocation or re-authentication rules for sensitive actions

Authorization Should Be Explicit and Boring

Authorization is safer when the rule is readable and easy to test. For example, route-level access control can be expressed as small policy predicates.

 1(ns myapp.authz)
 2
 3(defn has-role? [request role]
 4  (contains? (set (get-in request [:identity :roles])) role))
 5
 6(defn require-role [handler role]
 7  (fn [request]
 8    (if (has-role? request role)
 9      (handler request)
10      {:status 403
11       :body "Forbidden"})))

This is intentionally simple. The point is not to build a clever authorization DSL before the team even understands its policy surface.

Secret Management and Transport Security

Most real breaches do not come from a missing if. They come from leaked secrets, weak operational boundaries, or insecure defaults.

Minimum expectations:

  • keep secrets out of source control
  • load credentials from environment variables or a secret manager
  • require TLS in transit
  • rotate signing keys and service credentials deliberately
  • avoid logging tokens, passwords, or raw personal data

Clojure runs on the JVM, so the crypto and TLS story is usually operationally mature. The danger is not missing primitives. The danger is careless integration.

Auditability Matters in Enterprise Integrations

Authentication without audit trails is hard to investigate. Authorization without audit trails is hard to defend.

Useful audit events include:

  • login success and failure
  • privilege changes
  • access to sensitive records
  • token issuance and revocation
  • administrative actions

The logs should capture what happened without leaking credential material or secrets.

Common Mistakes

  • treating authentication and authorization as the same problem
  • storing passwords in reversible form
  • trusting token payloads without validating signature and expiry
  • putting authorization checks deep inside business logic where they are easy to miss
  • logging secrets or full credentials during debugging
  • assuming one security library will define the whole security architecture for you

Security is a system property. Libraries help, but they do not choose the trust boundaries for you.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026