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

Local SLURM setup emulates the PLGrid auth infrastructure locally. It creates a Keycloak instance and a mock certificate authority server. Keycloak config can be viewed here. The mock certificate authority uses the private key /slurm/ca_key/ca_key to sign the certs. Entrypoint script puts the public key /slurm/ca_key/ca_key.pub into the Slurm cluster and configures it to trust any certificates signed by that authority.

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)