@sylph01.s01.ninja Freelance web developer, focused on cryptography and authz/authn Lives in Matsuyama, Ehime I currently work at: codeTakt (did stuff related to this talk) Leaner Technologies Does many things (see background) 3
not using frontier cryptography, and we're not hand-rolling cryptographic libraries this time. So, do try this in production, but still be aware of your risks. 5
(as in The Lord of the Rings) As long as The One Ring exists, malicious actors can abuse The One Ring, and the world is in constant danger Throw The One Ring away into Mount Doom And you don't have to do a 1000+ pages worth adventure for that image: https://www.needpix.com/photo/742654/ring-gold-middle-earth-golden-ring-metallic-map-cgi-jewellery-free-pictures 8
be the Mount Doom you're looking for Self-contained JWT access tokens have to wait until expiration If expiration never happens... If you can't destroy it normally, it needs to vanish before it exists 10
phrase used for authentication Token is a signifier of authorization It is essentially a ticket (see background) Tickets only come with the result of authorization. They don't have identity verification (= authentication) image: https://www.flickr.com/photos/kolix/2539213620 by K.L. (CC BY-NC-SA 2.0). New York Metro's token 11
might be talking about NIST SP 800-63B-4, but: Forcing password resets should be avoided in order to avoid selecting weak passwords Of course both passwords and tokens need to be hard to predict 12
(period of expiration) x (# of servers tokens are sent) Taking any one active token is a successful attack If the expiry never happens, the time you can attempt an attack is infinite More servers to be sent, more vulnerable against server/traffic misconfiguration 14
a bit better than a long-living access token. Access tokens are used mulitple times, against multiple resource servers Refresh tokens are used only against the authorization server, and some implementations make it single-use. The refresh token being single-use is implementation dependent, not forced by the standard 23
by OAuth OIDC Core §11 "When offline access is requested, a prompt parameter value of consent MUST be used unless other conditions for processing the request permitting offline access to the requested resources are in place." It is essentially a different grant The token still lives after logout, outlives the session 24
after logout = Leaving The One Ring on the table offline_access = "Give me a long-living token for the user" You don't just request offline_access because you want a refresh token 25
a token most of the time. It's the same application, use a session handler. If the admin UI and the API is separate: User identity of the admin, AuthCode grant sub claim of the admin scope signifying admin capabilities Token expires with the UI session 26
jobs -> Sidekiq picks it up later We don't have an admin access token when Sidekiq picks it up Let's persist the admin access token! ... in this way we've created a "long-living token"... 28
admin's credentials The credential that Sidekiq uses is Client Credentials We record the audit trail that the admin enqueued the job Splits who is accountable for the action and who is accountable for the enqueue itself 29
secrets for authentication Server-side apps Public Client Clients that are not confidential clients = clients that cannot hold secrets safely Native apps, browser-based apps (SPAs) 32
relevant) DO NOT IMPLANT client_secret IN YOUR NATIVE APPS Especially in the age of LLMs, reverse engineering is free To use Authorization Code Grant with public clients, use Proof Key for Code Exchange (PKCE) 33
(Financial-grade API) requires methods that don't rely on shared secrets. private_key_jwt Sign the payload of the token request with a private key AS verifies the signature with the pre-registered public key mTLS (mutual TLS) RFC 7523: JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants 36
behind load balancers and reverse proxies that terminate TLS connections. In that case, these endpoints communicate mTLS information with HTTP headers, which sometimes causes security issues. 37
ALB use headers such as X-Amzn-Mtls-Clientcert to obtain mTLS information If user-specified headers directly reach the Rails app, mTLS guarantees are nullified When headers aren't split at ingress When Rails apps can be directly accessed without ALB 38
operation passthrough: Backend has to extract the certificate from X-AmznMtls-Clientcert , then manually authenticate the client. This includes the verification of the full certificate chain verify: ALB verifies the certificate against a trust store, parsed metadata is passed in the header Using verify mode is simpler 39
key pairs Operations are only accessible via APIs, keys are not accessible Trusted Platform Module (TPM) Secure Enclave Hardware Security Module (HSM) External hardware 41
to a specific sender Does not just accept the token by its face value (= Bearer tokens) private_key_jwt protects token retrieveal, Sender-Constrained ATs protect token usage 42
{public key, payload, signature} = DPoP proof JWT AS issues an access token bound to the public key Each resource requests send the access token + the DPoP proof JWT for that request RS verifies DPoP proof JWT, checks the binding of token to key Stealing the access token becomes insufficient for an attack! 43
and verifying them SPIRE is a reference implementation of the standard To issue IDs, SPIFFE uses attestation of evidences that the workload has instead of shared secrets Who the node is (based on cloud/Kubernetes attributes), who the process is (workload seen from the SPIFFE agent) This could totally be a full talk by itself 44
inter-service authentication for now. There is an Internet-Draft called draft-ietf-oauth-spiffe-client-auth that tries to bridge SPIFFE with OAuth. As of 2026/06, it is a WG Item, and the draft number is -02. 45
The Seven, and The Nine for lesser operations Separate strong operations into a different scope The Nine might not be a good example of privilege minimization, because the bearers of The Nine became Nazgûls... 47
workers. Refresh tokens are used in user contexts If you really want to automate then create multiple sessions, but having multiple sessions increases the risk of compromise... 54
an event at the same time and performs an operation, but only one worker succeeds in operating, and the others will fail. Also called cache stampede when used in terms of caching. When the cycle of expiration is constant, refreshes happen simultaneously, causing this problem. 56
timing instead of refreshing at the same time Avoids simlutaneous refresh race_condition_ttl option in Rails.cache.write / fetch First request goes through normally For the next requests, this will return the first request's result for the duration of race_condition_ttl instead of actually performing the request again 57
days I recommend Solid Cache with Active Record Encryption Being written to the database means it gets backed up, so encryption is important Add encrypt: true , bin/rails db:encryption:init 58
runs when entry does not exist response = request_token # call token request jitter = rand(0.7..0.9) # random value from 70 to 90 percent options.expires_in = (response[:expires_in] * jitter).seconds response.fetch(:access_token) { raise TokenRequestError } end 59
for security, but: You can't share a Sender-Constrained Access Token Especially with hardware-backed keys Per-worker Sender-Constrained keys will worsen cache hit rates You can have a process that handles the token and proxies requests, but that process will bear the full load 60
functionality in the Authorization Server. (Similar PRs in other repos exist) The race condition prevention code is almost the same as production code. 64