A worked example
Mei is a talent who uses JodApp Web on her laptop. The ids below are made up.
Mei on the web, from login to an expired session
- Monday 09:00. Mei opens the login page, types her email and password, and clicks "Sign in".
- The page's
clientActionsendsPOST /identities/user_sessionswith a JSON body. - Rails counts one attempt from her IP address, finds her account, and checks the password.
- Rails creates
Identities::UserSession4021: owner Mei, a newuuid,csrf_token: "k7…", her IP address and browser,created_atandupdated_atboth 09:00. - The response sets
jodapp_session_idto the signed4021andCSRF_TOKENtok7…, both expiring in 30 days, and returns the current-session JSON.
- The page's
- The router loads the page she was heading to.
- The JodApp Web server sends
GET /identities/user_sessions/currentwith her cookie forwarded. - Rails checks the signature, finds
Identities::UserSession4021, sees it is not expired, and returns the JSON. The page shows her name. updated_atis seconds old, so Rails does not touch it.
- The JodApp Web server sends
- 09:20. Mei saves a change to her profile.
- The browser sends
PATCH /identities/users/currentwith both cookies andX-CSRF-Token: k7…, added by the API client. - Rails finds
Identities::UserSession4021, compares the header withcsrf_token, and they match. updated_atis 20 minutes old, so Rails sets it to 09:20. This is the only write the session causes all morning.
- The browser sends
- 09:25. Mei opens another page.
Identities::UserSession4021is found again.updated_atis 5 minutes old, so nothing is written.
- Tuesday. Mei opens Jod once.
Identities::UserSession4021is found.updated_atmoves to Tuesday. It is 1 day old, well inside both limits.
- Mei goes on holiday and does not open Jod for 10 days.
- On day 8 after Tuesday,
updated_atpasses the 7-day idle limit. - The next hourly run of
Identities::DeleteExpiredUserSessionsJobdeletesIdentities::UserSession4021. - Had the job not run, the lookup would refuse it anyway, because
not_expiredexcludes it.
- On day 8 after Tuesday,
- Mei comes back and opens Jod.
- Her browser still has the cookie, because its
Expiresis 30 days after login. - The JodApp Web server forwards it. Rails finds no
Identities::UserSession4021and answers401. - JodApp Web sends Mei to the login page. She signs in, gets
Identities::UserSession4788and two fresh cookies, and carries on.
- Her browser still has the cookie, because its
If Mei had used Jod every day instead, the absolute limit would have ended Identities::UserSession 4021 on day 30, and she would have signed in once then.
Mei on the mobile app, when it exists
- Mei signs in on the app.
- The app sends
POST /identities/user_sessionswith"credential": "token"in the JSON body. - Rails creates
Identities::UserSession4790withtoken_digestset to the SHA-256 of a new random token, and nocsrf_token. - The response carries the raw token once. The app stores it in the device's secure store. Rails never shows it again.
- The app sends
- Every later request from the app carries
Authorization: Bearer <token>.- Rails hashes the token, finds
Identities::UserSession4790bytoken_digest, and applies the same limits and the same touch as on the web. - No CSRF check runs, because no cookie was sent.
- Rails hashes the token, finds
- Mei taps "sign out of all devices" on the app.
- The app sends
DELETE /identities/user_sessionswith the bearer header. - Rails destroys every
Identities::UserSessionof Mei's:4788from the laptop and4790from the phone. It answers204. The app deletes its token. - The laptop's next request finds no
Identities::UserSession, gets401, and lands on the login page.
- The app sends