Skip to content

Authentication Model

YAPTIDE supports two authentication methods: native Yaptide auth (username/password) and Keycloak SSO (PLGrid federation). Both issue JWT tokens stored in httpOnly cookies.

MethodWhen UsedUsers
Yaptide NativeDevelopment, standalone deploymentsAny registered user
Keycloak SSOProduction, PLGrid-integrated deploymentsPLGrid-federated users

Simple username/password registration and login. Passwords are hashed with Werkzeug’s security module.

┌──────────┐ ┌─────────┐
│ UI │ │ Backend │
└────┬─────┘ └────┬────┘
│ │
│ PUT /auth/register │
│ { username, password } │
│─────────────────────────────────────>│
│ │ Hash password
│ │ Create YaptideUserModel
│ 201 Created │
│<─────────────────────────────────────│
│ │
│ POST /auth/login │
│ { username, password } │
│─────────────────────────────────────>│
│ │ Verify password hash
│ │ Generate JWT access + refresh tokens
│ 200 OK │
│ Set-Cookie: access_token (httpOnly) │
│ Set-Cookie: refresh_token (httpOnly)│
│ Body: { access_exp } │
│<─────────────────────────────────────│
│ │
│ GET /auth/refresh │
│ Cookie: refresh_token │
│─────────────────────────────────────>│
│ │ Validate refresh token
│ │ Generate new access token
│ 200 OK │
│ Set-Cookie: access_token (httpOnly) │
│<─────────────────────────────────────│
TokenLifetimeStorage
Access token10 minuteshttpOnly cookie
Refresh token120 minuteshttpOnly cookie
Simulation update key7 daysBackend internal

The UI auto-refreshes the access token at 1/3 of its lifetime (approximately every 3 minutes) by hitting GET /auth/refresh.

Used for PLGrid-integrated deployments. The UI manages the Keycloak session, then exchanges the Keycloak token with the backend for a local JWT.

┌──────────┐ ┌───────────┐ ┌──────────┐
│ UI │ │ Keycloak │ │ Backend │
└────┬─────┘ └─────┬─────┘ └────┬─────┘
│ │ │
│ OIDC login (PKCE S256) │
│ Redirect to Keycloak │
│───────────────────>│ │
│ │ │
│ User authenticates│ │
│ (PLGrid credentials) │
│<───────────────────│ │
│ Keycloak tokens │ │
│ (access + refresh)│ │
│ │ │
│ POST /auth/keycloak │
│ Authorization: Bearer <keycloak_token>│
│───────────────────────────────────────>│
│ │
│ Validate token against │
│ Keycloak JWKS endpoint │
│ Check PLG_YAPTIDE_ACCESS │
│ Fetch SSH certificates │
│ Create/update KeycloakUser │
│ Generate local JWT │
│ │
│ 200 OK │
│ Set-Cookie: access_token (httpOnly) │
│ Set-Cookie: refresh_token (httpOnly) │
│<───────────────────────────────────────│

The UI uses keycloak-js SDK with these settings:

SettingValue
FlowStandard (Authorization Code)
PKCE challengeS256
Silent SSO checkEnabled (silentCheckSsoRedirectUri)
Token refreshAuto-refresh when <5 min remaining

Required environment variables:

Terminal window
REACT_APP_KEYCLOAK_BASE_URL=https://keycloak.example.com
REACT_APP_KEYCLOAK_REALM=yaptide
REACT_APP_KEYCLOAK_CLIENT_ID=my-client
REACT_APP_ALT_AUTH=plg

When a Keycloak token arrives, the backend checks the PLG_YAPTIDE_ACCESS claim in the token. This ensures the user has been granted access to the YAPTIDE service in the PLGrid infrastructure.

The backend also:

  1. Fetches SSH certificates from a dedicated cert-auth service (CERT_AUTH_URL)
  2. Stores the certificate and private key in KeycloakUserModel
  3. Uses these credentials for SSH connections to HPC clusters when submitting batch jobs

When REACT_APP_TARGET=demo, authentication is bypassed entirely and only in-browser Geant4 simulations are available. See Frontend Demo — Local for setup instructions.

All protected endpoints use the @requires_auth() decorator, which:

  1. Extracts the JWT access token from the access_token cookie
  2. Decodes and validates the token (signature, expiry)
  3. Loads the UserModel from the database
  4. Injects the user object into the Flask request context
@requires_auth()
def post(self, user: UserModel):
# user is automatically injected
simulation = SimulationModel(user_id=user.id, ...)

The user model uses SQLAlchemy polymorphic inheritance on the auth_provider discriminator:

UserModel (base)
├── YaptideUserModel (auth_provider="yaptide")
│ └── password_hash
└── KeycloakUserModel (auth_provider="keycloak")
├── cert (SSH certificate)
└── private_key (SSH private key)

This allows the backend to transparently handle both auth methods while storing auth-specific fields only where needed.

  • All tokens are stored in httpOnly cookies — not accessible to JavaScript (document.cookie)
  • CORS is configurable via FLASK_USE_CORS (enabled for local dev with localhost:3000)
  • Nginx terminates TLS (self-signed cert for development, real cert for production)
  • Passwords are hashed with Werkzeug’s generate_password_hash (PBKDF2)
  • Keycloak tokens are validated against the JWKS endpoint (asymmetric signature verification)