Docs/Security/Authentication

Authentication

SecureCodingHub supports multiple authentication methods. Browser sessions use JWT bearer tokens; the public API uses long-lived API keys with scope-based authorization and per-key rate limits.

Authentication Methods

Choose the authentication method that best fits your organization:

Magic Code (Email OTP)

Passwordless login via 6-digit code sent to email. Default method for all users. Codes expire after 10 minutes. No password to manage or remember.

OIDC (OpenID Connect)

Enterprise SSO via Azure AD, Okta, or compatible providers. Authorization code flow with PKCE. Recommended for organizations.

SAML 2.0

Federation-based SSO for enterprise environments. SP-initiated login flow.

Magic Code Flow

The default passwordless authentication flow works as follows:

1

User enters their email address

2

Server sends a 6-digit OTP to the email

3

User enters the code

4

Server verifies the code and issues a JWT

5

JWT is stored in the browser for session management

SSO Flow

When SSO is configured for your organization, the authentication flow changes:

1

User opens the SSO entry point for their organization (the link includes the org slug; there is no automatic email → domain detection today)

2

Browser redirects to the configured identity provider

3

User authenticates with corporate credentials

4

IdP returns an authorization code (OIDC) or SAML assertion to the SecureCodingHub callback

5

Server validates the response, JIT-provisions the user on first sign-in, and issues a JWT

6

User is logged in

JWT Tokens

All browser-facing authenticated sessions are managed using JSON Web Tokens issued by the backend after the magic-code or SSO step succeeds.

PropertyValue
AlgorithmHS256
Lifetime30 days from issue. There is no in-app refresh flow; users re-authenticate via magic code or SSO once the token expires.
ClaimsUser id, organization id, role, name, email, expiry.
TransmissionAuthorization: Bearer … header on every internal API call.
StorageStored client-side in localStorage under the sch-token key. There is no HttpOnly cookie option today.

API Key Bearer (Public API)

The public REST and webhook surface under /api/public/v1/* uses long-lived API keys instead of JWTs. Keys are created from the admin console under Organization → API Keys and sent on every request as a standard bearer header:

Authorization: Bearer scs_live_…
PropertyValue
Formatscs_live_ + 32 random base62 characters. 41 characters total.
StorageOnly the SHA-256 hash, prefix, and last four characters are kept on the server. The full token is shown once at creation and cannot be retrieved again.
Scopes16 fine-grained scopes (e.g. users:read, assignments:write, sarif:ingest). A request without the required scope returns 403 insufficient_scope.
LifetimeIndefinite by default. An optional expiry date can be set at creation time. Keys can be revoked at any moment; revocation takes effect immediately.
Last-used trackingEach successful call updates the key's lastUsedAt field for audit and rotation hygiene.

Full reference at API → Authentication & Scopes.

Rate Limiting

Every public API key is bounded by two sliding windows that protect the platform from runaway scripts and keep one tenant from starving another:

WindowLimitResponse headers
Per minute60 requestsRetry-After, X-RateLimit-Window: per_minute, X-RateLimit-Limit: 60
Per hour1,000 requestsRetry-After, X-RateLimit-Window: per_hour, X-RateLimit-Limit: 1000

Requests that exceed either window receive 429 rate_limited with a Retry-After header in seconds. A separate IP-based limiter (5 requests / 15 minutes) covers the anonymous web-contact form. Internal browser-session traffic against the JWT-protected admin API is not rate-limited today.

Detailed retry guidance and a sample exponential-backoff client at API → Rate Limits.

Audit Logging

Every mutating action — whether it originates from an admin user in the browser or from a public API key — is written to a per-organization audit log table. Read-only actions (list, get, dashboard queries) are not audited; the focus is on changes that have downstream impact.

FieldMeaning
actionDotted identifier of what happened (e.g. user.updated, assignment.created, apikey.revoked, sarif.ingested).
actorEmail / actorRoleEmail and role of the actor. Admin UI mutations carry the human's email and org_admin. API-key mutations carry apikey:<api-key-uuid> and the role api_key.
targetType, targetId, targetLabelThe resource that was changed. The label is a short human-readable description used in the audit-log UI.
metadataJSON-stringified context describing exactly what changed (e.g. the new scopes on an API key, the deadline before vs after on an assignment).
ipAddressThe source IP of the request that caused the change.
createdAtUTC timestamp.

The full audit stream can be queried from the admin UI (Organization → Audit Log) or programmatically via GET /api/public/v1/audit-log. Both surfaces support filtering by action, actor, and date range, and the admin UI exposes a CSV export for evidence packs.

Session Security

  • Tokens are validated on every API request
  • Invalid or expired tokens are rejected
  • SSO sessions respect IdP session policies
  • Magic codes are single-use and time-limited

Authentication design choices SecureCodingHub made and why

The default authentication path on SecureCodingHub is passwordless. We made that decision deliberately. The vast majority of credential compromises we see in customer training data still trace back to password reuse, weak password choices, and phishing of static secrets. Removing the password from the primary login flow eliminates a class of failure modes that no policy can fully mitigate. For organizations that require a traditional password fallback, we enforce minimums consistent with current NIST guidance: a length floor rather than a complexity ruleset, screening against known breached password corpora, and no forced rotation in the absence of evidence of compromise.

Email-based recovery is intentionally retained even for SSO-backed accounts. When an identity provider becomes unreachable, or when a contractor's corporate directory entry is removed mid-engagement, we still need a deterministic, auditable path back into the account for the verified owner. The recovery channel is rate-limited, single-use, and logged separately from the standard login event stream so that security teams can detect anomalous recovery activity without it being lost in normal login noise.

Shared accounts are discouraged at the product level rather than blocked outright. We surface a warning in the admin console when usage patterns suggest a single seat is being shared across multiple humans, because shared seats break individual accountability, distort progress reporting, and complicate any future incident investigation. Organizations that need cross-team access to dashboards should use role assignment and the SCIM-provisioned learner roles described in Data Security rather than reusing credentials.

Mapping authentication to compliance

SecureCodingHub is preparing for SOC 2 Type II and has not yet completed an audit, so we do not claim certification. We do, however, design the authentication subsystem against the same control families that a security team would expect when evaluating a vendor for that audit. SOC 2 CC6.1 expectations around logical access — unique identification of each user, multi-factor or equivalent step-up for sensitive actions, and revocation on termination — are addressed through per-user accounts, the magic code or IdP-issued credential as the second factor, and SCIM-driven deprovisioning.

ISO 27001 Annex A.9 controls covering access control policy, user registration, and privileged access are addressed through documented role definitions, organization-scoped registration restricted to verified domains where requested, and the explicit org_admin / learner role split inside each tenant. For organizations subject to PCI DSS section 8, the relevant control is that we never store cardholder data and never authenticate cardholders against the platform; SecureCodingHub is a training environment, not a payments environment, so the surface that PCI applies to is intentionally narrow.

None of the above should be read as a substitute for your own assessment. Evidence packages including subprocessor lists, data flow diagrams, and current audit posture are available on request from security@securecodinghub.com.

What changes when SSO is enabled

Enabling SSO is not just a swap of one login screen for another — it changes several runtime behaviors that security teams should understand before rollout. Once your organization is connected to an identity provider, the local magic code path is no longer offered to users whose email matches your verified domain. They are redirected to your IdP instead. This is the correct default, but it means recovery during an IdP outage shifts entirely to your IT team rather than SecureCodingHub support. We recommend documenting that escalation path internally before turning SSO on for a large user base.

Lockout behavior also shifts: rate-limited failed attempts on the local magic-code path no longer apply to the same user once SSO is in effect, because all credential validation happens at the IdP. Lockout policy is then governed by your IdP configuration, not by SecureCodingHub defaults. Audit log entries written by mutations the user performs after sign-in continue to carry the user's email and the org_admin / learner role; the platform does not currently store the IdP-side authentication method (for example, whether MFA was satisfied at the IdP) on the audit row, so for that level of detail you would correlate against your central identity provider's own log stream.