Skip to content

Commit d6f8fce

Browse files
committed
WIP
1 parent d6e68a5 commit d6f8fce

File tree

3 files changed

+161
-1
lines changed

3 files changed

+161
-1
lines changed

.phpunit.cache/test-results

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,37 @@ BEXIO_REDIRECT_URL=/dashboard
115115
4. **For multi-tenant applications:**
116116
- Implement custom `BexioOAuthConfigResolver` interface
117117
- Implement custom `BexioOAuthAuthenticationStoreResolver` interface
118+
- Optionally implement custom `BexioOAuthAuthenticationValidateResolver` interface for validation logic
118119
- Bind your implementations in a service provider
119120

120121
### ✨ New Features
121122

122123
- **OAuth 2.0 Support**: Full OAuth 2.0 implementation with PKCE support
123124
- **Multi-tenant OAuth**: Support for multiple Bexio accounts via custom resolvers
125+
- **OAuth Authentication Validation**: Custom validation logic before storing OAuth tokens with API access and custom redirects
124126
- **Automatic Token Refresh**: OAuth tokens are automatically refreshed when expired
125127
- **Encrypted Token Storage**: OAuth tokens are encrypted when cached
126128
- **Built-in OAuth Routes**: Automatic OAuth flow handling
127129
- **Configurable Cache Stores**: Support for custom cache stores for token storage
128130
- **Comprehensive Scopes**: Support for all Bexio API and OpenID Connect scopes
129131

132+
#### OAuth Authentication Validation
133+
134+
The new `BexioOAuthAuthenticationValidateResolver` allows you to implement custom validation logic that runs after OAuth authentication but before the token is stored. This powerful feature provides:
135+
136+
- **API Access**: Full `BexioConnector` instance with authenticated access to Bexio API
137+
- **Custom Validation**: Validate user permissions, company restrictions, or any business logic
138+
- **Custom Redirects**: Return custom redirect responses with your own error handling
139+
- **Exception Handling**: Gracefully handle API errors during validation
140+
141+
**Example Use Cases:**
142+
- Validate user email against an allowlist
143+
- Check company permissions via Bexio API calls
144+
- Verify required OAuth scopes are present
145+
- Implement custom business rules for authorization
146+
147+
**Default Behavior**: By default, all OAuth authentications are accepted (validation returns success)
148+
130149
### 🔧 Configuration
131150

132151
- **New OAuth Configuration**: Complete OAuth configuration structure
@@ -137,6 +156,7 @@ BEXIO_REDIRECT_URL=/dashboard
137156
### 📚 Documentation
138157

139158
- **Updated README**: Comprehensive OAuth and multi-tenant documentation
159+
- **OAuth Validation Documentation**: Complete guide for custom OAuth authentication validation with examples
140160
- **Migration Examples**: Detailed migration examples for all scenarios
141161
- **Scope Documentation**: Complete OAuth scope enumeration and documentation
142162

README.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,146 @@ $configuration = App::make(\CodebarAg\Bexio\Contracts\BexioOAuthConfigResolver::
349349
$connector = new BexioConnector($configuration);
350350
```
351351

352+
##### Step 5: Custom OAuth Authentication Validation (Optional)
353+
354+
You can implement custom validation logic that runs before the OAuth authenticator is stored. This is useful for:
355+
- Validating user permissions with API calls
356+
- Checking company/organization restrictions
357+
- Implementing custom business logic
358+
- Adding custom error handling with redirects
359+
360+
**Create Custom Validation Resolver:**
361+
362+
```php
363+
<?php
364+
365+
namespace App\Support\Bexio;
366+
367+
use CodebarAg\Bexio\BexioConnector;
368+
use CodebarAg\Bexio\Contracts\BexioOAuthAuthenticationValidateResolver as BexioOAuthAuthenticationValidateResolverContract;
369+
use CodebarAg\Bexio\Dto\OAuthConfiguration\BexioOAuthAuthenticationValidationResult;
370+
use CodebarAg\Bexio\Requests\OpenID\FetchUserInfoRequest;
371+
use CodebarAg\Bexio\Requests\CompanyProfiles\FetchACompanyProfileRequest;
372+
use Illuminate\Support\Facades\Redirect;
373+
374+
class BexioOAuthAuthenticationValidateResolver implements BexioOAuthAuthenticationValidateResolverContract
375+
{
376+
public function resolve(BexioConnector $connector): BexioOAuthAuthenticationValidationResult
377+
{
378+
try {
379+
// Example 1: Validate user info
380+
$userInfo = $connector->send(new FetchUserInfoRequest());
381+
$userData = $userInfo->json();
382+
383+
if (!$this->isValidUser($userData)) {
384+
return BexioOAuthAuthenticationValidationResult::failed(
385+
Redirect::to('/unauthorized')
386+
->with('error', 'User not authorized for this application')
387+
->with('user_email', $userData['email'])
388+
);
389+
}
390+
391+
// Example 2: Validate company permissions
392+
$companyProfile = $connector->send(new FetchACompanyProfileRequest());
393+
$companyData = $companyProfile->json();
394+
395+
if (!$this->isAllowedCompany($companyData['id'])) {
396+
return BexioOAuthAuthenticationValidationResult::failed(
397+
Redirect::to('/company-not-allowed')
398+
->with('error', 'Your company is not authorized to use this application')
399+
->with('company_name', $companyData['name'])
400+
);
401+
}
402+
403+
// All validations passed
404+
return BexioOAuthAuthenticationValidationResult::success();
405+
406+
} catch (\Exception $e) {
407+
// Handle API errors during validation
408+
return BexioOAuthAuthenticationValidationResult::failed(
409+
Redirect::to('/validation-error')
410+
->with('error', 'Unable to validate OAuth permissions: ' . $e->getMessage())
411+
);
412+
}
413+
}
414+
415+
private function isValidUser(array $userData): bool
416+
{
417+
// Your custom user validation logic
418+
return in_array($userData['email'], [
419+
'allowed@example.com',
420+
'admin@mycompany.com'
421+
]);
422+
}
423+
424+
private function isAllowedCompany(int $companyId): bool
425+
{
426+
// Your custom company validation logic
427+
$allowedCompanies = [12345, 67890];
428+
return in_array($companyId, $allowedCompanies);
429+
}
430+
}
431+
```
432+
433+
**Register the Custom Validator:**
434+
435+
Add to your service provider:
436+
437+
```php
438+
<?php
439+
440+
namespace App\Providers;
441+
442+
use Illuminate\Support\ServiceProvider;
443+
444+
class BexioServiceProvider extends ServiceProvider
445+
{
446+
public function register(): void
447+
{
448+
// ... existing bindings ...
449+
450+
$this->app->bind(
451+
\CodebarAg\Bexio\Contracts\BexioOAuthAuthenticationValidateResolver::class,
452+
\App\Support\Bexio\BexioOAuthAuthenticationValidateResolver::class
453+
);
454+
}
455+
}
456+
```
457+
458+
**Validation Result Types:**
459+
460+
```php
461+
use CodebarAg\Bexio\Dto\OAuthConfiguration\BexioOAuthAuthenticationValidationResult;
462+
use Illuminate\Support\Facades\Redirect;
463+
464+
// Success - proceed with storing the authenticator
465+
BexioOAuthAuthenticationValidationResult::success();
466+
467+
// Failed - use default error redirect (configured redirect_url with error message)
468+
BexioOAuthAuthenticationValidationResult::failed();
469+
470+
// Failed - use custom redirect with your own error handling
471+
BexioOAuthAuthenticationValidationResult::failed(
472+
Redirect::to('/custom-error-page')
473+
->with('error', 'Custom validation message')
474+
->with('additional_data', 'any extra data you need')
475+
);
476+
```
477+
478+
**Error Handling:**
479+
480+
When validation fails, users will be redirected with these session variables:
481+
482+
```php
483+
// For default failed validation
484+
session()->get('bexio_oauth_success'); // false
485+
session()->get('bexio_oauth_message'); // 'Authentication validation failed.'
486+
487+
// For custom redirects, you control the session data
488+
session()->get('error'); // Your custom error message
489+
session()->get('user_email'); // Any additional data you passed
490+
```
491+
352492
### Available OAuth Scopes
353493

354494
The package automatically applies default OAuth scopes for OpenID Connect authentication. These default scopes are:

0 commit comments

Comments
 (0)