-
Notifications
You must be signed in to change notification settings - Fork 5
v0.0.5 ~ polished authentication flow, create pod implemented, and re… #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+17,346
−11,949
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…source syncing started
This fixes the 401 Unauthorized error when creating pods. The previous version (0.9.10) did not support passing custom headers to requestTokens(), which meant the DPoP header was ignored during token exchange. This resulted in non-DPoP-bound access tokens being issued, which are rejected by Solid servers. Version 1.0.2 includes PR #297 which added support for custom headers, enabling proper DPoP token binding.
- verifyJWTsignature -> verifyJWTSignature with string type hint and bool return - getSessionKey, setSessionKey, unsetSessionKey with string type hints - decodeJWTPublic with string and int type hints These changes ensure compatibility with the parent class method signatures in jumbojett/openid-connect-php v1.0.2.
- Add null checks before calling setClientID() and setClientSecret() - Auto-restore client credentials when identity is provided without clientID - Gracefully handle missing credentials during initial registration This fixes the 'Argument #1 ($clientID) must be of type string, null given' error when checking authentication status.
The parent authenticate() method calls requestTokens() directly without passing headers. By overriding requestTokens(), we ensure the DPoP header is always included in the token endpoint request, which is required for DPoP-bound access tokens. This should fix the 401 Unauthorized error when creating pods.
The requestTokens() override now handles DPoP header injection automatically, so we don't need to create it separately in exchangeCodeForTokens(). The duplicate creation was causing 'invalid DPoP key binding' errors.
Previously, DPoP keys were stored globally while access tokens were stored per-identity. This caused mismatches when multiple users authenticated or when credentials were cleared and regenerated. Now each identity has its own DPoP key pair stored separately, ensuring the access token is always used with the same DPoP key it was bound to. This fixes the 401 Unauthorized errors when creating pods.
Changed createDPoP from static to instance method to work with the per-identity DPoP key management. Updated all callers to use the instance method via $this->oidc->createDPoP(). Fixes 'Non-static method cannot be called statically' error.
- Add migration for CSS credentials storage (email, client_id, client_secret) - Create CssAccountService for account management API integration - Add CssAccountController with endpoints for credential setup - Update PodService to use CSS Account Management API when credentials available - Add fallback to legacy pod creation methods - Implement automated client credentials token generation - Add routes for CSS credentials management This enables seamless pod creation through the Community Solid Server account management system.
- Create setup-css-credentials modal component - Update home controller to check CSS credentials after OIDC auth - Auto-show setup modal if credentials missing - Add serverUrl computed property for CSS account registration link - Integrate with modalsManager for seamless UX Users are now automatically prompted to set up CSS credentials after OIDC authentication, enabling pod creation.
- Use mut helper for proper two-way binding to prevent nested objects - Add validation error display for each field - Show detailed error messages in the modal - Add red border to invalid fields - Display all validation errors in error panel - Clear errors before new submission attempt Users now see clear validation errors without checking network logs.
- Set keepOpen: true to prevent modal from closing on errors - Initialize validationErrors to null in modal options - Users can now see error messages and retry without modal closing
- Use getWebIdFromIdToken() method instead of accessing non-existent array key - Extract issuer from WebID URL instead of token response - Match the same pattern used in PodService for consistency - Add better error messages for missing ID token This fixes the 'WebID not found in token response' error.
… pod creation - Get WebID from ID token using getWebIdFromIdToken() - Parse WebID URL to extract issuer (scheme + host + port) - Prevents fallback to localhost:3000 which fails in Docker - Matches the pattern used in CssAccountController This fixes the 'Failed to connect to localhost port 3000' error during pod creation.
- Change from createDPoP($url, $method) to createDPoP($method, $url) - Fixes 'DPoP proof htm mismatch' error - Method signature is createDPoP(string $method, string $url, ...) This fixes the CSS access token request failure.
- Add await to requestPodCreation.perform() call - Fix modal parameter to access modal.done() - Use pod.name instead of undefined this.podName - Show specific error message from response - Refresh pod list after successful creation - Fixes false 'Failed to create pod' error on success
Frontend:
- Add import-resources modal with checkboxes for resource types
- Update explorer controller with importResources action
- Replace 'Create Something' button with 'Import Resources'
- Show import progress and success/error messages
- Auto-refresh explorer after successful import
Backend:
- Create ResourceSyncService for converting Fleetops data to RDF
- Support Vehicles, Drivers, Contacts, and Orders
- Convert resources to Turtle/RDF format with proper namespaces
- Create containers for each resource type in pods
- Add PodController@importResources endpoint
- Add POST /pods/{podId}/import route
Features:
- Select multiple resource types to import
- Batch import up to 100 resources per type
- Store as .ttl files in pod containers
- Proper RDF formatting with Fleetbase namespace
- Error handling and logging
… better logging for resource import
…pod control URL not found
- Add css_password field to database schema - Store encrypted CSS password during credentials setup - Use email/password login instead of client credentials for pod management - Update pod creation to use CSS-Account-Token from password login - Update pod listing to use password-based authentication - This enables access to controls.account.pod endpoint for pod creation
- Simplify to use authenticated user's pod only - Add getPodUrlFromWebId helper method - Update import resources to not require podId - Add ContainerController for container management - Add container create/delete endpoints - Remove multi-pod complexity - Fix resource sync to work with authenticated pod
- Renamed 'Pods' to 'Data' throughout UI - Renamed 'Container' to 'Folder' for better UX - Simplified routes from multi-pod to single-pod architecture - Removed obsolete PodController and ContainerController - Added DataController for single-pod operations - Added folder management (create/delete) to PodService - Cleaned up 14 obsolete files (12 frontend + 2 backend) - Created 7 new files for data management - Fixed syntax error in PodService.php Breaking Changes: - Routes changed: pods/* -> data/* - API endpoints: /pods -> /data, /containers -> /data/folder - Frontend navigation updated See REFACTORING_SUMMARY.md for complete details.
- DataController was trying to access $identity->webid which doesn't exist - Changed to use $this->podService->getProfileData($identity) to get webid - Applied fix to all methods: index, showFolder, createFolder, deleteItem, importResources - This matches the pattern used in PodService::createPod()
THE REAL ROOT CAUSE - ACL Permissions After extensive debugging, the actual issue was NOT authentication or token problems. CSS default root ACL only grants acl:Read to authenticated users. Without acl:Write or acl:Append permissions, ALL write operations fail with 401 Unauthorized. Evidence: - WAC-Allow header consistently showed: user="read",public="read" - No 'write' or 'append' in the permissions - CSS applies root ACL to all descendants - Must update ACL before any write operations Solution Implemented: 1. AclService - New service for ACL management - hasWritePermissions() - Check WAC-Allow header - grantWritePermissions() - PUT ACL document to <podRoot>/.acl - ensureWritePermissions() - Check and update if needed 2. ACL Document Format (Turtle) - Owner gets: acl:Read, acl:Write, acl:Control - Fleetbase gets: acl:Append, acl:Read - Uses acl:default for inheritance to descendants 3. Integration - DataController checks ACL before folder creation - Automatically updates ACL if lacking write permissions - Returns 403 if ACL update fails How It Works: 1. User attempts to create folder 2. System checks pod root WAC-Allow header 3. If no write/append, PUT new ACL to <podRoot>/.acl 4. ACL grants acl:Append + acl:Read to user's WebID 5. Subsequent operations succeed with proper permissions Why This Is Correct: - CSS has no built-in UI for ACL management - Each pod needs explicit ACL configuration - Root ACL inherits to all children - This is expected CSS behavior, not a bug Files Added: - server/src/Services/AclService.php - ACL_SOLUTION.md (documentation) Files Modified: - server/src/Http/Controllers/DataController.php References: - solid_acl_explanation.pdf - Solid Web Access Control spec - CSS documentation This should finally resolve the 401 errors!
THE SCOPE ISSUE
The access token has empty scope ("") even though we're requesting
'openid webid offline_access' during registration and authentication.
Root Cause:
- CSS caches client registrations
- Client was registered BEFORE we added scope parameter
- Even though we now send scope, CSS uses old registration
- Old registration didn't specify allowed scopes
- CSS rejects scope requests from clients without pre-approved scopes
Evidence from logs:
- [ACCESS TOKEN PAYLOAD] shows "scope": ""
- WWW-Authenticate header requires: scope="openid webid"
- ACL UPDATE FAILED with 401 due to missing webid scope
Solution:
Changed CLIENT_NAME from 'Fleetbase' to 'Fleetbase-v2' to force
CSS to create a NEW client registration with proper scopes.
This will:
1. Register new client with scope: 'openid webid offline_access'
2. CSS will allow this client to request these scopes
3. Access token will include webid scope
4. ACL operations will succeed
User must logout and login again to trigger new registration.
CSS SCOPE LIMITATION After extensive investigation, CSS is NOT granting the webid scope even when properly requested during client registration and authentication. Evidence: - Client re-registered with new name (Fleetbase-v2) - Scope 'openid webid offline_access' requested in registration - Scope ['openid', 'webid', 'offline_access'] added during auth - Access token still has "scope": "" (empty) - New client_id confirms registration succeeded - CSS simply not granting the requested scopes Root Cause: This appears to be a CSS server configuration issue. The CSS instance is configured to NOT grant scopes, regardless of client requests. Impact: - Cannot programmatically update ACLs (requires webid scope) - Cannot use AclService to grant write permissions - Must manually set up root ACL for each pod Solution: Created MANUAL_ACL_SETUP.md with three methods: 1. Using curl to PUT ACL file 2. Direct file system access 3. CSS admin API (if available) The manual ACL setup grants: - acl:Read, acl:Write, acl:Control to pod owner - acl:default for inheritance to all descendants Once root ACL is set up: - Folder creation will work - Resource import will work - All write operations will succeed This is the standard approach for CSS pod initialization when programmatic ACL management is not available. Files Added: - MANUAL_ACL_SETUP.md Next Steps: User must manually create root ACL using one of the provided methods.
- Override requestTokens() to add scope parameter to token request body - Jumbojett library does not include scope in authorization_code flow - CSS requires scope in token request to issue tokens with proper scopes - This fixes 401 Unauthorized errors for write operations - Also fixed composer.json repository path to solid-oidc-client Root cause: Access tokens were issued with empty scope because token exchange request did not include scope parameter. Now sends 'scope=openid webid offline_access' in token request.
…okens - Replace $this->clientID with $this->getClientID() - Replace $this->clientSecret with $this->getClientSecret() - Parent class properties are protected, not public - Fixes 'Undefined property' error during token exchange
- Scopes added in authenticate() are not persisted to callback handler - New OIDC client instance is created for token exchange - Must add scopes again before calling requestTokens() - This ensures scope parameter is included in token request body - Should fix empty scope in access tokens
- Scope should only be sent in authorization request, not token request - This follows the Inrupt Solid Client implementation - CSS should persist the granted scope from authorization - Added logging to verify authorization URL includes scope - Removed non-standard scope parameter from token exchange Per OIDC/OAuth2 spec and Inrupt's implementation, the scope is only sent in the initial authorization request. The authorization server should remember the granted scope and include it in issued tokens.
- Jumbojett library doesn't expose getAuthorizationURL() - authenticate() builds URL internally and redirects - Simplified logging to just show scopes being set
- Log what CSS actually returns in token response - Check if scope is included in the response - This will help debug why tokens have empty scope
- Allow using HTTPS for OIDC discovery (through nginx) - While using HTTP for direct API calls (bypassing SSL verification) - Fixes 'Undefined property: issuer' error when CSS has HTTPS baseUrl
This ensures the Jumbojett OpenIDConnectClient is properly initialized before we set custom properties. Without this, setProviderURL() may not work correctly.
This allows the application container to make HTTPS requests to the nginx-ssl proxy with self-signed certificates. Required for OIDC discovery to work when CSS is behind an HTTPS reverse proxy.
The root cause was that getOpenIdConfiguration() was using SolidClient's HTTP client which constructed URLs from host/port/secure config, resulting in http://solid:3000 instead of https://solid. Now it uses the SOLID_OIDC_ISSUER config to make direct cURL requests to the HTTPS endpoint through nginx, with SSL verification disabled for development.
Added verify=false to HTTP client options when secure=true to allow HTTPS requests with self-signed certificates in development.
Added app()->environment() checks to ensure SSL verification is only disabled when APP_ENV is 'local' or 'development'. Production will properly verify SSL certificates.
Reverted getOpenIdConfiguration() to simple version using SolidClient. Removed fetchURL override and SOLID_OIDC_ISSUER config. Now that we have: - Network alias (solid -> nginx-ssl) - SSL verification disabled in SolidClient for dev - Proper HTTPS configuration The simple approach works correctly.
The parent Jumbojett class fetchURL() method is used for token exchange and other OIDC requests. It needs SSL verification disabled in development to work with self-signed certificates. Only disabled when APP_ENV is 'local' or 'development'.
authenticatedRequest() was using Http::withOptions() directly without the SSL verification disable, causing SSL errors when fetching profile cards and making authenticated requests. Only disabled when secure=true AND APP_ENV is 'local' or 'development'.
Removed all CSS credential-related code to follow Solid protocol properly: - Removed css_email, css_password, css_client_id, css_client_secret fields - Removed CSS fallback logic from SolidIdentity::getAccessToken() - Removed CssAccountController and CssAccountService - Removed CSS credential routes - Removed ACL check from DataController Everything now uses OIDC tokens as intended by the Solid protocol.
Removed frontend components that prompt for CSS credentials: - Removed checkCssCredentials, showCssSetupModal, setupCssCredentials methods - Removed cssCredentialsStatus and hasCssCredentials properties - Removed setup-css-credentials modal component Frontend now only uses OIDC authentication.
Include 'openid webid offline_access' scope during dynamic client registration so CSS knows what scopes this client is allowed to request.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
…source syncing started