Skip to content

Login UX and Identity Provider Gap v1

As of: April 14, 2026

Purpose

Define the target login screen direction and the platform gaps for supporting GitHub, Google, and Hugging Face sign-in.

This is a product and architecture planning document. It does not replace the current auth contracts in doc/api/openapi.draft.yaml.

Current Implementation

The current /auth/login screen is implemented as a single-pane sign-in card:

  1. Primary SSO:
  2. optional work email or tenant slug hint,
  3. Continue with SSO through GET /api/v1/auth/oidc/authorize,
  4. optional Continue with SAML when NEXT_PUBLIC_AUTH_SAML_ENABLED=true.
  5. Social provider shortcuts:
  6. GitHub and Hugging Face are enabled by default unless explicitly disabled,
  7. Google is available when NEXT_PUBLIC_AUTH_SOCIAL_GOOGLE_ENABLED=true,
  8. provider buttons use provider_hint on the OIDC authorize endpoint and Keycloak broker routing remains server-side.
  9. Password login:
  10. hidden behind Use password instead,
  11. controlled by NEXT_PUBLIC_AUTH_PERSONAL_ENABLED,
  12. platform-control/release image builds default this flag to false,
  13. local kind/dev builds keep it enabled for bootstrap and test users.
  14. Rate-limit state:
  15. 429 uses Retry-After.
  16. Error state:
  17. mapped through canonical API error handling and preserves correlation data.

Current feature flags:

  1. NEXT_PUBLIC_AUTH_PERSONAL_ENABLED
  2. controls whether personal username/password controls are shown.
  3. NEXT_PUBLIC_AUTH_SAML_ENABLED
  4. controls whether the SAML button is shown.
  5. NEXT_PUBLIC_AUTH_SOCIAL_GITHUB_ENABLED
  6. controls whether the GitHub shortcut is shown; default is enabled.
  7. NEXT_PUBLIC_AUTH_SOCIAL_HUGGINGFACE_ENABLED
  8. controls whether the Hugging Face shortcut is shown; default is enabled.
  9. NEXT_PUBLIC_AUTH_SOCIAL_GOOGLE_ENABLED
  10. controls whether the Google shortcut is shown; default is disabled.

Target UX Direction

Move login to a single-pane screen.

The screen should make the recommended production path obvious while keeping dev/personal login available only when enabled.

Primary Layout

Sign in to GPUaaS
Access GPU workspaces, notebooks, clusters, and inference apps.

[ Email or workspace __________________________ ]
[ Continue                                     ]

or

[ Continue with SSO ]

[ GitHub ] [ Google ] [ Hugging Face ] only when enabled

Use password instead                    only when personal/dev login enabled

Need access? Contact your workspace admin or platform support.

Visual Direction

Use GPUaaS styling, not a generic consumer-login clone:

  1. one centered card,
  2. subdued infrastructure/cloud background,
  3. strong primary blue action,
  4. compact provider buttons,
  5. no tab strip by default,
  6. support and access guidance in the footer,
  7. clear rate-limit and correlation-id error states.

Interaction Model

  1. User enters email or workspace.
  2. Continue decides which advisory hint to send:
  3. email address -> identity_hint,
  4. workspace/tenant slug -> tenant_hint.
  5. Server remains authoritative for tenant/provider resolution.
  6. If password login is enabled, Use password instead expands the personal username/password form inside the same card.
  7. If signup is enabled, show Don't have access? Request access or Sign up, depending on product policy.

Do not show signup as a default affordance unless self-service account creation is intentionally enabled for the environment.

Signup Product Decision

The existing personal signup API creates a personal tenant and default project. That is useful for dev and possible self-service users, but it is not the same as enterprise workspace access.

Before showing Sign up broadly, decide:

  1. Is public self-service signup allowed?
  2. Does signup create a personal tenant or request access to an existing tenant?
  3. Does signup require email verification?
  4. Does signup require billing setup before provisioning?
  5. Is personal signup disabled in platform-control/prod by default?

Recommended first production behavior:

  1. show Need access? Contact your workspace admin,
  2. keep personal signup hidden unless explicitly enabled,
  3. add an access-request flow later if tenant admins should approve users.

Provider Support Options

There are two viable implementation models.

Option A: Broker Providers Through Keycloak

Keycloak owns GitHub, Google, Hugging Face, and other social/provider integrations. GPUaaS continues to use the existing OIDC authorize/exchange flow.

Benefits:

  1. smallest GPUaaS API change,
  2. Keycloak handles provider-specific OAuth/OIDC details,
  3. centralized callback and client secret configuration,
  4. compatible with current JWT/JWKS validation path.

Required GPUaaS work:

  1. decide whether provider buttons map to Keycloak kc_idp_hint or equivalent,
  2. add a safe API contract for provider hinting instead of browser-building Keycloak URLs directly,
  3. bind callback state to provider hint,
  4. map brokered identities to local users,
  5. define account-linking and duplicate-email behavior,
  6. audit provider selected, resolved issuer, and local user binding.

Option B: GPUaaS Owns Provider Integrations

GPUaaS adds first-class /auth/social/* or provider-aware authorize endpoints.

Benefits:

  1. full control over provider-specific UX and policy,
  2. less dependency on Keycloak identity-broker behavior,
  3. easier to enforce GPUaaS-specific account-linking rules in one place.

Costs:

  1. more API surface,
  2. provider-specific OAuth client management in GPUaaS,
  3. more security review,
  4. more callback and token-validation code,
  5. more operational secret management.

Recommendation:

Start with Option A through Keycloak brokering, but make the GPUaaS API the public contract. The browser should call GPUaaS, not construct provider URLs or Keycloak-specific hints directly.

Provider-Specific Notes

Google

Google can be:

  1. enterprise Google Workspace federation,
  2. consumer/social Google sign-in.

For enterprise Google Workspace, use tenant federation and verified domain binding. For consumer Google sign-in, require explicit account-linking or self-service personal signup policy.

GitHub

GitHub is usually social/developer identity, not tenant federation by default.

Before enabling GitHub, decide:

  1. Is GitHub sign-in only for personal tenants?
  2. Can tenant admins allowlist GitHub organizations or teams?
  3. Is GitHub email verification required?
  4. What happens when GitHub returns a private email or multiple emails?
  5. Can a GitHub identity link to an existing enterprise account?

Do not grant project access from GitHub org/team membership until that is an explicit contract with audit and tenant-admin control.

Hugging Face

Hugging Face is the better default third provider for GPUaaS because users are likely to bring model identities, model repositories, gated-model access, and inference workflows.

Hugging Face sign-in should still be treated as identity only:

  1. signing in with Hugging Face does not grant GPUaaS project access by itself,
  2. model access tokens are separate runtime secrets, not login credentials,
  3. private or gated model access must be explicitly requested through a runtime secret binding flow,
  4. Hugging Face organization membership should not map to GPUaaS tenant access until tenant admins opt into that policy,
  5. verified email and account-linking rules still apply.

Recommended first behavior:

  1. show Continue with Hugging Face only after provider discovery reports it,
  2. request only identity scopes needed for login first,
  3. add model/token scopes later as part of a separate runtime-secret feature,
  4. never store user Hugging Face tokens in app profile config or launch params.

Microsoft

Microsoft should not be a default social button for GPUaaS. Treat Microsoft as enterprise SSO unless a specific product decision is made to support consumer Microsoft accounts.

Enterprise Microsoft Entra ID should be modeled as tenant federation:

  1. tenant admin configures Entra ID OIDC or SAML,
  2. tenant domain binding resolves the tenant,
  3. membership or invite gates authorization,
  4. no implicit access is granted just because the user has a Microsoft identity.

If consumer Microsoft sign-in is added later, it should follow the same account-linking and personal-tenant policy as Google/GitHub.

Contract Gaps

Current OpenAPI already defines:

  1. GET /api/v1/auth/oidc/authorize,
  2. POST /api/v1/auth/oidc/exchange,
  3. GET /api/v1/auth/saml/authorize,
  4. POST /api/v1/auth/saml/callback,
  5. POST /api/v1/auth/personal/login,
  6. POST /api/v1/auth/personal/signup.

Needed before social/provider buttons are production:

  1. Provider hint contract.
  2. Add a server-validated query field such as provider_hint.
  3. Allowed values should be server-configured, for example sso, github, google, huggingface.
  4. Do not let the browser pass arbitrary IdP aliases.
  5. Provider discovery/read model.
  6. UI needs to know which buttons to show per environment or tenant.
  7. Candidate endpoint: GET /api/v1/auth/providers.
  8. Account-linking model.
  9. Store provider issuer/name, subject, verified email, and link state.
  10. Prevent silent takeover when email matches an existing local user.
  11. Signup/request-access policy.
  12. Separate personal login from personal signup.
  13. Add environment and policy controls for whether public signup is visible.
  14. Invite or membership gate.
  15. Enterprise federation and social login authenticate identity only.
  16. Authorization still requires tenant/project membership or approved invite.
  17. Error taxonomy.
  18. Existing codes may be enough for MVP, but product copy needs specific details for no provider, no membership, account already linked, and signup disabled cases.
  19. Audit events.
  20. Log provider selected, provider resolved, link created, link failed, membership denied, and signup created.

Data Model Gaps

The current users model has OIDC issuer/subject fields, but multi-provider identity linking needs a separate table.

Recommended table direction:

user_identity_links (
  id uuid primary key,
  user_id uuid not null references users(id),
  provider_type text not null,
  provider_name text not null,
  issuer text null,
  subject text not null,
  email text null,
  email_verified boolean not null default false,
  link_state text not null,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now(),
  deleted_at timestamptz null
)

Required constraints:

  1. unique active provider identity: (provider_name, subject),
  2. no automatic merge by email alone,
  3. soft-delete for audit continuity.

Security Requirements

  1. Callback state remains opaque, single-use, TTL-limited, and provider-bound.
  2. Provider hints are advisory and server-validated.
  3. Issuer and audience validation are exact-match.
  4. Social sign-in cannot bypass membership or invite checks.
  5. Account linking requires proof of current session or approved invite.
  6. Provider secrets live in Vault or encrypted provider config.
  7. Query-string auth tokens remain prohibited.
  8. Login errors must avoid account enumeration.

Updated Login Screen Acceptance Criteria

  1. Production default shows a single card, not Work/Personal tabs.
  2. The primary field accepts email or workspace slug.
  3. Continue with email sends identity_hint.
  4. Continue with workspace slug sends tenant_hint.
  5. Provider buttons appear only when the API reports them enabled.
  6. Password login is collapsed behind Use password instead and appears only when personal/dev auth is enabled.
  7. Signup is hidden unless signup policy explicitly enables it.
  8. Rate-limit, provider resolution failure, no-membership, and callback failure states render actionable copy.
  9. Correlation ID is shown when the API returns one.
  10. Existing callback/session behavior remains unchanged.

Phase 1: UX-only simplification

  1. Replace tabs with one centered card.
  2. Keep current OIDC/SAML/personal APIs.
  3. Collapse password login behind Use password instead.
  4. Hide signup unless a new frontend flag enables it.
  5. Do not show GitHub/Google/Hugging Face buttons yet.

Phase 2: Provider discovery and button rendering

  1. Add contract for provider discovery.
  2. Add provider hint to authorize contract.
  3. Wire buttons to GPUaaS authorize endpoint with provider hint.
  4. Keep Keycloak as provider broker behind GPUaaS.

Phase 3: Account linking and signup policy

  1. Add identity-link data model.
  2. Add explicit account-linking rules.
  3. Add invite/request-access flow.
  4. Add tenant-admin controls for allowed providers.

Phase 4: Enterprise federation maturity

  1. Finish tenant federation admin UX.
  2. Support Microsoft Entra ID and Google Workspace as tenant IdPs.
  3. Add verified domain binding UX.
  4. Add runbooks and audit surfaces for auth failures.

Open Questions

  1. Should platform-control allow personal signup at all?
  2. Should GitHub/Google/Hugging Face create personal tenants, or only link to invited users?
  3. Should provider availability be global, tenant-specific, or both?
  4. Should Hugging Face login unlock any model-developer experience directly, or should model repository/token access remain a separate app runtime feature?
  5. Do we need password reset UX, or should password login remain dev-only?
  6. Do tenant admins need an access-request queue before self-service signup is exposed?
  7. Should Microsoft ever be shown as a social provider, or only as tenant SSO through labels like Continue with your work account?
  1. doc/product/ux-mocks/user-auth.md
  2. doc/product/UX_Implementation_Spec.md
  3. doc/architecture/Tenant_Federation_SSO_Model.md
  4. doc/architecture/Platform_IAM_Model_v1.md
  5. doc/api/openapi.draft.yaml
  6. Hugging Face Hub OAuth docs: https://huggingface.co/docs/hub/oauth
  7. Hugging Face Hub Spaces OAuth docs: https://huggingface.co/docs/hub/spaces-oauth