Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

All notable changes to Roadiz will be documented in this file.

## Unreleased

### Documentation

- **OpenID Authentication**: Documented the decision to maintain Roadiz's custom OpenID Connect implementation rather than migrating to Symfony's native OIDC support. The two systems serve different use cases:
- Symfony's native OIDC is designed for stateless API authentication with Bearer tokens
- Roadiz OpenID implements the OAuth2 Authorization Code Flow for web application SSO
- See [OpenID vs Native Symfony OIDC Comparison](docs/developer/security/openid-native-comparison.md) for details

## [2.6.26](https://github.com/roadiz/core-bundle-dev-app/compare/v2.6.25...v2.6.26) - 2025-11-21

### Bug Fixes
Expand Down
115 changes: 115 additions & 0 deletions OPENID_DECISION_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# OpenID Authentication Decision Summary

## Issue
The issue requested an investigation into migrating from Roadiz's custom OpenID bundle to Symfony's native OIDC authentication (introduced in Symfony 6.3+).

## Investigation Findings

### Symfony's Native OIDC (6.3+)
Symfony introduced native OpenID Connect support through:
- `OidcTokenHandler`: Validates OIDC JWT tokens
- `OidcUserInfoTokenHandler`: Fetches user info from OIDC UserInfo endpoint
- `access_token` authenticator: Built-in access token authentication

**Design Purpose**: Stateless API authentication with Bearer tokens
**Use Cases**: Resource servers, API authentication, mobile apps, SPAs

### Roadiz OpenID Bundle
The `roadiz/openid` package provides:
- Full OAuth2 Authorization Code Flow implementation
- Session-based web application authentication
- UI integration with Rozier backoffice login
- Discovery service with automatic configuration
- Hybrid user support (local + virtual OIDC users)
- Role mapping from JWT claims

**Design Purpose**: Web application Single Sign-On (SSO)
**Use Cases**: Rozier backoffice login, enterprise SSO, session-based authentication

## Decision: Maintain Roadiz OpenID Bundle

**The Roadiz OpenID bundle remains necessary** because:

1. **Different Authentication Flow**
- Symfony: Token validation (assumes token already obtained)
- Roadiz: Authorization code flow (redirect to provider, obtain token)

2. **Different State Management**
- Symfony: Stateless (no sessions)
- Roadiz: Stateful (session-based)

3. **Different Use Cases**
- Symfony: API authentication
- Roadiz: Web application SSO

4. **UI Integration**
- Symfony: No UI components
- Roadiz: Login button, callback handling, session management

5. **User Management**
- Symfony: Basic token validation
- Roadiz: Hybrid local/virtual users, role strategies

## No Migration Required

**No code changes are necessary.** The existing OpenID configuration continues to work as designed.

## Documentation Added

This PR adds comprehensive documentation:

1. **[openid-native-comparison.md](docs/developer/security/openid-native-comparison.md)** (177 lines)
- Detailed comparison of Symfony native OIDC vs Roadiz OpenID
- Use cases for each approach
- Feature comparison table
- Recommended architecture patterns

2. **[MIGRATION_GUIDE.md](lib/OpenId/MIGRATION_GUIDE.md)** (160 lines)
- Potential future enhancements
- Decision matrix for considering migrations
- What should NOT be migrated
- Short/medium/long term recommendations

3. **Updated [lib/OpenId/README.md](lib/OpenId/README.md)**
- Clarified purpose of the bundle
- Explained differences from Symfony native OIDC
- Listed key features

4. **Updated [docs/developer/security/security.md](docs/developer/security/security.md)**
- Added reference to comparison documentation

5. **Updated [UPGRADE.md](UPGRADE.md)**
- Added section explaining the decision
- Confirmed no migration required

6. **Updated [CHANGELOG.md](CHANGELOG.md)**
- Documented the decision

## Future Considerations

While maintaining the bundle, potential future enhancements could include:
- Using Symfony's JWT validation infrastructure (if beneficial)
- Adding optional API token support using native OIDC for API endpoints
- Leveraging Symfony's OIDC discovery caching mechanisms

However, the core authorization code flow implementation should remain custom.

## Conclusion

**The Roadiz OpenID bundle is still relevant and necessary.** Symfony's native OIDC support solves a different problem (stateless API authentication) than what Roadiz requires (stateful web SSO).

The documentation now clearly explains:
- Why both solutions exist
- When to use each approach
- That no migration is required or recommended
- Potential future enhancements that could leverage Symfony's infrastructure

## Files Changed
- `CHANGELOG.md` - Added entry documenting the decision
- `UPGRADE.md` - Added clarification that no migration is needed
- `docs/developer/security/security.md` - Added reference to comparison doc
- `docs/developer/security/openid-native-comparison.md` - **NEW** - Comprehensive comparison
- `lib/OpenId/MIGRATION_GUIDE.md` - **NEW** - Future enhancement guide
- `lib/OpenId/README.md` - Updated with purpose and comparison

Total: 402 lines of new documentation added
16 changes: 16 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Upgrade to 2.7

## OpenID Authentication

Roadiz continues to use its custom OpenID Connect implementation (`roadiz/openid` package) for web application SSO, rather than Symfony's native OIDC support. This decision is intentional because:

- Symfony's native OIDC (`OidcTokenHandler`) is designed for **stateless API authentication** with Bearer tokens
- Roadiz requires the **OAuth2 Authorization Code Flow** for web-based SSO with session management
- The Roadiz implementation provides UI integration, callback handling, and hybrid user support

For a detailed explanation, see:
- [OpenID vs Native Symfony OIDC Comparison](docs/developer/security/openid-native-comparison.md)
- [OpenID Bundle Migration Guide](lib/OpenId/MIGRATION_GUIDE.md)

**No migration is required.** The existing OpenID configuration remains unchanged.

---

## ⚠ Breaking changes

- NodeSourceWalkerContext requires a new service `NodeTypeClassLocatorInterface` in its constructor.
Expand Down
177 changes: 177 additions & 0 deletions docs/developer/security/openid-native-comparison.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Roadiz OpenID Bundle vs Symfony Native OIDC

## Overview

This document compares Roadiz's custom OpenID Connect implementation with Symfony's native OIDC authentication support introduced in Symfony 6.3 and enhanced in 7.3.

## Symfony Native OIDC Support

### What Symfony Provides (6.3+)

Symfony introduced native OIDC support through the `access_token` authenticator system:

- **OidcTokenHandler**: Validates OIDC JWT tokens (ID tokens or access tokens)
- **OidcUserInfoTokenHandler**: Fetches user information from the OIDC UserInfo endpoint
- Built-in JWT validation with automatic JWKS discovery
- Support for token introspection (OAuth2 RFC 7662)

### Configuration Example

```yaml
# config/packages/security.yaml
security:
firewalls:
api:
stateless: true
access_token:
token_handler:
oidc_user_info: https://your-oidc-server.com/realms/demo/protocol/openid-connect/userinfo
```

Or with JWT validation:

```yaml
security:
firewalls:
api:
stateless: true
access_token:
token_handler:
oidc:
claim: sub
audience: your-client-id
issuers:
- https://your-oidc-server.com/realms/demo
```

### Use Cases

Symfony's native OIDC is designed for:

1. **Stateless API authentication** with Bearer tokens
2. **Resource server** validation of access tokens
3. **Service-to-service** authentication
4. **Mobile and Single Page Application (SPA)** applications that obtain tokens independently

## Roadiz OpenID Bundle

### What Roadiz Provides

The `roadiz/openid` package implements a complete OpenID Connect authentication flow:

- **Authorization Code Flow**: Full OAuth2/OIDC authorization code flow with PKCE support
- **Discovery Service**: Automatic configuration from `.well-known/openid-configuration`
- **Session-based Authentication**: Traditional web application authentication with sessions
- **UI Integration**: Seamless integration with Rozier backoffice login page
- **Hybrid User Support**: Both local database users and virtual OIDC-only users
- **Role Mapping**: Maps OIDC claims to Symfony roles

### Current Implementation

```yaml
# config/packages/roadiz_rozier.yaml
roadiz_rozier:
open_id:
discovery_url: '%env(string:OPEN_ID_DISCOVERY_URL)%'
hosted_domain: '%env(string:OPEN_ID_HOSTED_DOMAIN)%'
oauth_client_id: '%env(string:OPEN_ID_CLIENT_ID)%'
oauth_client_secret: '%env(string:OPEN_ID_CLIENT_SECRET)%'
requires_local_user: false
granted_roles:
- ROLE_USER
- ROLE_BACKEND_USER
```

### Use Cases

Roadiz OpenID is designed for:

1. **Web application SSO** for Rozier backoffice
2. **User-facing login flows** with redirect-based authentication
3. **Enterprise SSO** with providers like Authentik, Keycloak, Azure AD
4. **Session-based** authenticated users
5. **Mixed authentication** (local users + OIDC users)

## Key Differences

| Feature | Symfony Native OIDC | Roadiz OpenID Bundle |
|---------|-------------------|---------------------|
| **Authentication Flow** | Bearer Token (stateless) | Authorization Code Flow (stateful) |
| **Use Case** | API / Resource Server | Web Application SSO |
| **State Management** | Stateless | Session-based |
| **User Flow** | Token already obtained | Full login redirect flow |
| **UI Integration** | None (API-focused) | Rozier login button |
| **Discovery** | Manual or auto | Automatic with caching |
| **Local Users** | Not applicable | Optional requirement |
| **Role Mapping** | Basic | Advanced with strategies |
| **CSRF Protection** | Not needed | Built-in with state parameter |
| **Redirect Handling** | Not applicable | Full callback handling |

## Why Both Are Relevant

### Roadiz OpenID Bundle Should Be Kept Because:

1. **Different Use Case**: Symfony's native OIDC is for stateless API authentication, while Roadiz needs stateful web SSO
2. **Authorization Code Flow**: Symfony doesn't provide the OAuth2 authorization code flow (redirect to provider, callback handling, token exchange)
3. **UI Integration**: The bundle integrates with Rozier's login page and provides redirect links
4. **Callback Handling**: Manages the OAuth2 callback endpoint, state verification, and session establishment
5. **Hybrid Authentication**: Supports mixing OIDC authentication with local database users in the same firewall

### Potential Refactoring Opportunities

While the full bundle should be maintained, some components could leverage Symfony's native features:

1. **JWT Validation**: Use Symfony's JWT validation infrastructure instead of manual lcobucci/jwt implementation
2. **JWKS Caching**: Leverage Symfony's OIDC discovery caching mechanisms
3. **Token Introspection**: For API scenarios, add support for Symfony's token introspection
4. **UserInfo Endpoint**: Consider using `OidcUserInfoTokenHandler` for user info fetching

## Recommended Architecture

### For Rozier Backoffice (Web UI)
**Use Roadiz OpenID Bundle** - Provides the complete authorization code flow needed for web-based SSO.

```yaml
security:
firewalls:
main:
custom_authenticator:
- RZ\Roadiz\RozierBundle\Security\RozierAuthenticator
- roadiz_rozier.open_id.authenticator # Roadiz OpenID
```

### For API Endpoints
**Could Use Symfony Native OIDC** - For stateless API authentication with Bearer tokens.

```yaml
security:
firewalls:
api:
stateless: true
access_token:
token_handler:
oidc_user_info: https://your-provider.com/userinfo
```

## Migration Path (Future)

If Symfony adds authorization code flow support in the future:

1. **Phase 1**: Refactor JWT validation to use Symfony's infrastructure
2. **Phase 2**: Replace discovery mechanism with Symfony's native discovery
3. **Phase 3**: Evaluate if Symfony provides sufficient authorization code flow support
4. **Phase 4**: Only if Symfony provides full flow, migrate or deprecate custom implementation

## Conclusion

**The Roadiz OpenID bundle remains necessary and relevant** because it solves a different problem than Symfony's native OIDC support. Symfony focuses on stateless API token validation, while Roadiz provides a complete web application SSO flow with UI integration.

The bundle could be enhanced by leveraging some of Symfony's native OIDC infrastructure for JWT validation and discovery, but a complete replacement is not appropriate at this time.

## References

- [Symfony Access Token Authentication](https://symfony.com/doc/current/security/access_token.html)
- [Symfony 6.3 OpenID Connect Token Handler](https://symfony.com/blog/new-in-symfony-6-3-openid-connect-token-handler)
- [Symfony 7.3 Security Improvements](https://symfony.com/blog/new-in-symfony-7-3-security-improvements)
- [OAuth 2.0 Authorization Code Flow](https://oauth.net/2/grant-types/authorization-code/)
- [OpenID Connect Core Specification](https://openid.net/specs/openid-connect-core-1_0.html)
4 changes: 4 additions & 0 deletions docs/developer/security/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Roadiz uses Symfony's security component to manage user authentication and autho
entity to manage roles in bulk.
This user entity can be used in classic session firewall, in API authentication with JWT tokens or even with OpenID (openid user must match a local user).

::: tip
Roadiz provides its own OpenID Connect implementation for web application SSO. See the [OpenID vs Native Symfony OIDC comparison](./openid-native-comparison.md) to understand the differences between Roadiz's OpenID bundle and Symfony's native OIDC token authentication.
:::

```yaml
# config/packages/security.yaml
security:
Expand Down
Loading