Authentication
Softly uses JWT-based authentication with a dual-token strategy: short-lived access tokens for API calls and long-lived refresh tokens for session persistence.
Token Strategy
| Token | Lifetime | Purpose |
|---|---|---|
| Access token | 1 hour | Sent with every API request |
| Refresh token | 30 days | Used to obtain new access tokens |
Both tokens are issued on signup and login. The mobile app stores them in expo-secure-store (encrypted device storage).
Signup
Users register with email, name, and password (minimum 8 characters). The response includes both tokens and the full user object.
POST /api/v1/auth/signupOn success, the app stores both tokens and navigates to the onboarding flow.
Login
Standard email/password login. Tokens are returned in the same format as signup.
POST /api/v1/auth/loginToken Refresh
When an access token expires, the mobile app intercepts the 401 response and calls the refresh endpoint. If the refresh token is also expired, the user is logged out.
POST /api/v1/auth/refreshThe refresh flow is handled transparently by an Axios interceptor in services/api.ts. The user never sees a login screen unless their refresh token expires (30 days of inactivity).
Session Validation
The /me endpoint validates the current access token and returns the full user object including subscription tier, level data, and household info.
GET /api/v1/auth/meThe app calls this on launch to hydrate AuthContext and determine the correct navigation path (auth screens vs main tabs).
Password Reset
Password reset uses a deep link flow:
- User enters email on the forgot password screen
- Backend generates a time-limited token (Rails 8
generates_token_for) and sends it via email - Email contains a deep link:
softly://reset-password?token=... - The app opens
reset-password.tsx, which collects the new password and calls the API - On success, the user gets new access + refresh tokens and is logged in
TIP
The forgot password endpoint always returns a success message, even if the email doesn't exist. This prevents email enumeration attacks.
Settings Update
Authenticated users can update their name, email, and family visibility toggle via:
PATCH /api/v1/auth/settingsPassword changes require the current password to be sent alongside the new password.
Mobile Storage
Tokens are stored using expo-secure-store (wrapper in services/storage.ts), which uses the iOS Keychain and Android EncryptedSharedPreferences. Tokens are never stored in AsyncStorage or any unencrypted location.