Turns models from Skatetrax Core into routes in flask.
Auth business logic (user CRUD, roles, invite tokens, password reset) lives in skatetrax_core at skatetrax/auth/service.py. The Flask layer has two thin adapters: app/user_datastore.py (a UserDatastore subclass required by Flask-Security-Too) and app/blueprints/auth_routes.py (route handlers that parse requests and call the core service). All auth endpoints live under /api/v4/auth.
Frontend preflight to check if an invite token is valid before showing the registration form.
Response: {"valid": true} or 403 with a message ("Registration is currently invite-only." / "This invite has expired or has already been used.").
Create a new user account. Registration is invite-only.
Payload:
{
"aLogin": "username",
"aEmail": "user@example.com",
"aPasswordHash": "plaintext_password",
"phone_number": "555-0100",
"invite_token": "your-invite-token",
"roles": ["adult", "coach"]
}rolesis optional (defaults to["adult"]). Multi-select from:adult,coach,guardian.minoris rejected -- minors cannot self-register.invite_tokenis required. Tokens are validated and consumed on successful registration.
Authenticate and start a session.
Payload:
{
"aLogin": "username",
"aPasswordHash": "plaintext_password"
}Response: {"message": "Login successful"} with a session cookie. Also accepts aEmail instead of aLogin.
Check the current session state.
Response (authenticated):
{
"logged_in": true,
"user_id": 1,
"uSkaterUUID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"onboarding_complete": false
}onboarding_complete is true when a uSkaterConfig row exists for this user's uSkaterUUID.
Response (unauthenticated): 401 with {"logged_in": false}.
Complete the onboarding walkthrough (requires login). Creates the uSkaterConfig profile row.
Payload:
{
"first_name": "Jane",
"last_name": "Doe",
"city": "Denver",
"state": "CO",
"country": "US",
"zip": "80202",
"timezone": "America/Denver",
"coach_id": null,
"rink_id": null,
"skate_config": null
}first_nameandlast_nameare required. All other fields are optional.- Returns
409if onboarding is already complete for this user.
Change the current user's password (requires login).
Payload:
{
"current_password": "old_password",
"new_password": "new_password"
}- Verifies the current password before updating.
- Returns
401if current password is incorrect.
Request a password reset email (public, no login required).
Payload:
{
"email": "user@example.com"
}- Always returns
200with"If that email is registered, a reset link has been sent."to prevent email enumeration. - Returns
503if the email service is not configured (missingMAIL_SERVERenv var). - Reset tokens expire after 1 hour.
Reset a password using a token from the reset email (public, no login required).
Payload:
{
"token": "reset-token-from-email",
"new_password": "new_password"
}- Returns
400if the token is invalid, expired, or already used.
End the current session.
Response: {"message": "Logged out"}.
Password reset emails require Flask-Mail. Set these env vars to enable:
MAIL_SERVER-- SMTP host (e.g.smtp.example.com). If not set, Flask-Mail is not initialized and the app runs without email.MAIL_PORT-- defaults to587MAIL_USE_TLS-- defaults totrueMAIL_USERNAME/MAIL_PASSWORD-- SMTP credentialsMAIL_DEFAULT_SENDER-- defaults tonoreply@skatetrax.comRESET_URL_BASE-- frontend reset page URL (e.g.https://skatetrax.app/reset). The token is appended as?token=....
| Variable | Required | Default | Description |
|---|---|---|---|
| Database | |||
PGDB_HOST |
Yes | (none) | PostgreSQL host (e.g. 127.0.0.1 or a cloud hostname). |
PGDB_NAME |
Yes | (none) | Database name (e.g. skatetrax). |
PGDB_USER |
Yes | (none) | Database user. |
PGDB_PASSWORD |
Yes | (none) | Database password. |
PGDB_SSLMODE |
No | (unset) | PostgreSQL sslmode parameter (e.g. require). Omit for local dev. |
| Flask / Session | |||
FLASK_SECRET_KEY |
Yes (production) | ephemeral random key | Signs session cookies. Must be a stable secret shared across replicas. In dev, an ephemeral key is auto-generated with a warning. |
FLASK_ENV |
No | (unset) | Set to production to enforce strict checks (e.g. missing FLASK_SECRET_KEY raises a RuntimeError). |
CORS_ORIGIN |
No | localhost regex | Comma-separated list of allowed origins (e.g. https://app.skatetrax.com,http://localhost:3000). When unset, defaults to a regex matching localhost, 127.0.0.1, and 192.168.* on port 3000. |
SESSION_COOKIE_SECURE |
No | false |
Set to true when running behind TLS so cookies are only sent over HTTPS. |
SESSION_COOKIE_DOMAIN |
No | (unset -- uses request host) | Explicit cookie domain. Leave unset unless you need cross-subdomain cookies. |
SECURITY_PASSWORD_SALT |
No | skatetrax-salt |
Salt used by Flask-Security-Too for token generation. |
MAIL_SERVER |
No | (unset) | SMTP host. If not set, Flask-Mail is not initialized and password-reset emails are unavailable. |
MAIL_PORT |
No | 587 |
SMTP port. |
MAIL_USE_TLS |
No | false |
Enable STARTTLS (port 587). Mutually exclusive with MAIL_USE_SSL. |
MAIL_USE_SSL |
No | false |
Enable implicit SSL (port 465). Mutually exclusive with MAIL_USE_TLS. |
MAIL_USERNAME / MAIL_PASSWORD |
No | (unset) | SMTP credentials. |
MAIL_DEFAULT_SENDER |
No | noreply@skatetrax.com |
From address for outgoing email. |
RESET_URL_BASE |
No | (unset) | Frontend password-reset page URL. Token is appended as ?token=.... |
The Skater Card is a shareable, resume-style summary of a skater's history. It includes identity info, lifetime stats, recent (3-month rolling) training data, current setup, and shared playlists. The public version omits direct contact info -- only the preferred contact method label is shown.
Returns the full card data for the authenticated user. Used for preview and the authenticated card page.
Generates a UUID share token (if one doesn't exist) and returns it. The frontend constructs the public URL from this token.
Response: {"share_token": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
Revokes the share token, making the public card URL return a 404.
Response: {"status": "ok"}
Public endpoint (no auth required). Returns the same card structure minus the share token. Returns 404 if the token is invalid or has been revoked.
Set or clear the skater's preferred contact method.
Payload:
{"contact_preference": "email"}Accepted values: email, text, phone, social, or null to clear.
- Registration creates the
uAuthTableauth record and assigns roles. The user can authenticate but has no skater profile yet. - Onboarding creates the
uSkaterConfigprofile (name, location, coach, rink, equipment). Until onboarding completes,GET /sessionreturns"onboarding_complete": false.
All data routes (dashboard, ice_time, equipment, maintenance, etc.) use @login_required. User identity comes from current_user (Flask-Login); uSkaterUUID drives data aggregation.
Anything in the main branch should work as expected with as little effort as possible. The dev branch is where new features, defined by milestones, can be added prior to a release. Currently, there is no schedule for releases, but once all items in a milestone are completed, they should be merged into main, with the exception of bug/break fixes.
If you're interested in suggesting changes, please fork the repo, check out the dev branch, and create a feature branch from that. Once a PR is opened against our dev branch, it can be reviewed and pulled into main. There are no naming requirements for PR's or branches, though the project default relies on a year/week_numberPushIdentifier, 2024_47A for example indicates the 2024 year, week 47, first push/merge (A) for that week. If a second feature is worked on that week, the branch for that would be 2024_47B.
Input/comments/feedback for any open PR is always welcome.