Token generation:
- Replace Str::ulid() with bin2hex(random_bytes(32)) for 256-bit entropy
- Store SHA-256 hash in database, never plaintext tokens
- Hash input before lookup on all token endpoints
Invitation tokens:
- InvitationService: generate crypto random, store hash, pass plain
token transiently for email URL via UserInvitation::$plainToken
- InvitationController show/accept: hash input before DB lookup
- AcceptInvitationRequest: hash token before invitation lookup
- Migration: widen user_invitations.token and artists.portal_token
from char(26) to char(64) for SHA-256 hex digests
Portal token auth:
- PortalTokenController: remove Schema::hasTable() runtime checks,
hash token before lookup, return shaped response via PortalEventResource
instead of raw model data
- Create PortalEventResource (name, dates, status only — no internals)
- Handle missing production_requests table gracefully via try/catch
Portal token middleware:
- Implement full token validation: extract from Bearer header or ?token=
query param, hash, look up in artists/production_requests, verify
event exists and is not draft/closed, set portal context on request
- Return generic 401 on any failure (no information leakage)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>