Solavel Solavel Docs

Access Troubleshooting

docs/admin-support/access-troubleshooting.md

Audience: support engineers, admins Difficulty: admin only

What this covers

The most common login and workspace-access dead-ends, plus the SSO handoff between the parent app and Solabooks. Each row gives a concrete check or fix.


The login chain (parent app)

/login → POST /login → Authenticated session → SetClientContext →
  EnsureWorkspaceOnboardingCompleted → /portal/orgs (or /admin)

A user can break out of this chain at five points. Diagnose by where the redirect lands.

Lands on Meaning Fix
/login (loops) Session not persisting (cookie blocked) Different browser; check SESSION_DOMAIN if subdomain involved
/email/verify Email not verified Resend verification from /admin/admins or have user click the link
/onboarding/verify Workspace onboarding not done User completes onboarding (or admin marks it done)
/portal/orgs empty list Authenticated but no user_organizations row Add membership in /admin/clients > [client] > Members
/admin 403 User does not have admin role Confirm Spatie role on user; assign admin if needed

Spatie roles vs. user_organizations.role

Two parallel role systems live on the parent app — understand both.

Spatie role. Stored in model_has_roles. Examples: super-admin, admin, client_owner, client_manager, client_member. Checked by Blade @hasrole, route role: middleware, AdminPolicy::before().

user_organizations.role column. A free-form string column. Used by SuperAdminProvisioningSeeder (writes 'client_owner') and by some controllers when scoping per-org behaviour. Not the same as the Spatie role.

For full access a user needs both:

  • The Spatie role that matches their level (client_owner for owners).
  • A user_organizations row with the correct role string.

Common mismatch: the user is granted Spatie client_owner but their user_organizations.role is still client_member. They see the correct UI affordances but per-org operations fail.

Fix. From /admin/clients > [client] > Members, click the user and re-pick the role. The UI writes both.


Solabooks app role chain

The finance app uses three levels at once:

  1. SSO entry. The handoff token (issued by parent sso.finance.redirect) authenticates the user into the finance session. No role is checked here.
  2. Org membership. EnsureOrgMembership confirms the user is a member of the active organization (or they are switched to a valid one). This reads the finance organizations, users, organization_user tables — separate from the parent's user_organizations.
  3. Permission gate. Each route applies perm:<key> or role:<role> middleware. The perm: middleware reads FinanceRolePermissionSet keyed off the user's finance_role_key column. The role: middleware reads Spatie roles.

Common mismatch: the user is in user_organizations on the parent but the finance tenant DB has no organization_user row. They reach finance and get "Organization not found".

Fix. Run the finance superadmin:setup --client-id=<id>, or re-trigger the parent's tenancy projects sync (/admin/clients > Sync Projects).


SSO handoff failures

The flow:

  1. Parent: user clicks Launch SolabooksGET /sso/finance/redirect.
  2. Parent issues a short-lived signed token via SsoTokenService.
  3. Browser redirects to https://solavel.com/finance/sso/callback?token=….
  4. Solabooks: SsoCallbackController calls back to parent POST /api/sso/validate (open route, throttle 60/min) to verify the token.
  5. On success, finance opens a session for the user and redirects to /finance/.

Failure modes:

Symptom Cause Fix
"SSO token expired" Token TTL elapsed (network slow, redirect cached) Ask user to retry. If repeats, increase TTL in config/sso.php
Solabooks redirects back to parent login auth:sanctum rejected the validate call Check app.key matches between apps; check parent API base URL in finance's config/parent.php
Solabooks shows "Tenant not found" after callback Tenant DB not yet provisioned Run superadmin:setup --client-id=<id>
Validate returns 419 CSRF token / session mismatch Clear cookies for both domains
Solabooks signs in as the wrong user Token reused or replayed Engineering — security incident

Where to look in logs:

  • Parent: storage/logs/laravel.log for Api/SsoValidationController.
  • Solabooks: storage/logs/laravel.log for SsoCallbackController.
  • Apache: /var/log/apache2/access.log for the redirect chain (the /sso/finance/redirect/finance/sso/callback?token=… pair should both return 302 then 200).

"I can't see my organization in the picker."

The finance app shows organizations the user is a member of in the top-bar org switcher. If an org doesn't appear:

  1. Confirm the user has a row in finance's organization_user table for that org.
  2. Confirm the org is is_active = true.
  3. Confirm the user's users.organization_id (default org) hasn't been set to a deleted org — that wedges the resolver.
  4. Confirm the parent's user_organizations row is in sync.

Fix. From parent /admin/clients > [client] > Sync Users. Then have the user sign out and back in.


Customer portal access dead-ends

Distinct system. See customer-portal/secure-document-links and customer-portal/overview.

Symptom Cause Fix
Customer hits 404 on link Token format wrong; tenant DB hint corrupt Re-issue from document
"This portal link is no longer active" Past 24-hour expiry, or revoked Send again from document
"Document/customer mismatch" Document was reassigned Re-issue link
Can't sign in to customer portal Account disabled or password wrong Forgot password; or re-enable account
Customer setup link expired Past 60 minutes New link from /customer-portal/forgot-password

API access dead-ends

Symptom Cause Fix
401 on api/v1/* Missing or wrong API key Re-issue from /finance/admin/api-access
403 on api/v1/* Key valid but missing scope Edit key, add scope
401 on api/workspace-control/* Sanctum token expired Re-issue token from parent admin
419 on api/sso/validate Throttle hit (60/min) Wait or escalate

Related

Source: docs/admin-support/access-troubleshooting.md ← All documentation