nelmio / NelmioCorsBundle

Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application
https://symfony.com/bundles/NelmioCorsBundle/
MIT License
1.89k stars 108 forks source link

Duplicate control-header / control-origin #61

Closed Quovandius closed 1 year ago

Quovandius commented 8 years ago

Hello,

I try to use this bundle for configure my api. But i have a problem since 2 weeks, and i didn't found any solution. Sorry for the inconvenience. My last configuration is :

Project on server : Debian 8 / nginx / php-fpm

Server side : Symfony 3.0 with FosUserBundle/ FOSRestBundle/ NelmioApiDocBundle / NelmioCorsBundle/ LexikJWTAuthenticationBundle / JMSSerializer

My config.yml :

# FosUserBundle Configuration
fos_user:
    db_driver: orm
    firewall_name: main
    user_class: UserBundle\Entity\User
    group:
        group_class: UserBundle\Entity\Group
        form:
            type: UserBundle\Form\Type\GroupFormType
    profile:
        form:
            type: UserBundle\Form\Type\ProfileFormType

# FOSRestBundle Configuration
fos_rest:
    param_fetcher_listener: true
    body_listener: true
    format_listener: true
    view:
        view_response_listener: 'force'
        formats:
            xml: true
            json : true
        templating_formats:
            html: true
        force_redirects:
            html: true
        failed_validation: HTTP_BAD_REQUEST
        default_engine: twig
    routing_loader:
        default_format: json

# LexikJWTAuthenticationBundle Configuration
lexik_jwt_authentication:
    private_key_path: %jwt_private_key_path%
    public_key_path:  %jwt_public_key_path%
    pass_phrase:      %jwt_key_pass_phrase%
    token_ttl:        %jwt_token_ttl%

# JMSSerializer Configuration
jms_serializer:
    metadata:
        auto_detection: true

# NelmioCorsBundle Configuration
nelmio_cors:
    defaults:
        allow_credentials: true
        allow_origin: []
        allow_headers: []
        allow_methods: []
        expose_headers: []
        max_age: 0
        hosts: []
        origin_regex: false
    paths:
        '^/api/':
            allow_origin: ['*']
            allow_headers: ['Origin', 'Content-Type', 'X-Requested-With', 'Accept']
            allow_methods: ['POST', 'PUT', 'GET', 'DELETE', 'OPTIONS']
            max_age: 3600

My security.yml :

api_doc:
    pattern: ^/api/doc
    anonymous: true

api_login:
    pattern:  ^/api/login
    provider: fos_userbundle
    stateless: true
    anonymous: true
    form_login:
        check_path:     /api/login_check
        require_previous_session: false
        username_parameter: username
        password_parameter: password
        success_handler:          lexik_jwt_authentication.handler.authentication_success
        failure_handler:          lexik_jwt_authentication.handler.authentication_failure

api:
    pattern:   ^/api
    stateless: true
    provider: fos_userbundle
    lexik_jwt:
        authorization_header:
            enabled: true
            prefix:  Bearer
        query_parameter:
            enabled: true
            name:    Bearer
        throw_exceptions:        true
        create_entry_point:      true

## Main firewall
main:
    pattern: ^/
    form_login:
        provider: fos_userbundle
        csrf_token_generator: security.csrf.token_manager
        login_path: fos_user_security_login
        check_path: fos_user_security_check
        remember_me: true
        default_target_path: lgb_onepage_home
    logout:
        path:   fos_user_security_logout
        target: lgb_onepage_home
    anonymous: true
    remember_me:
        secret:      %secret%
        lifetime: 604800 # 1 week in seconds
        path:     /
        domain:   ~ # Defaults to the current domain from $_SERVER
        #                secure:   true
        httponly: true

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/register, role: ROLE_ADMIN }
    - { path: ^/resetting, role: ROLE_ADMIN }
    - { path: ^/intranet/, role: ROLE_USER }
    - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/api,       roles: [IS_AUTHENTICATED_FULLY, ROLE_ADMIN] }

And for my client, i use Ionic with angular JS on base project (with skeleton tabs for have a valid login form) :

My config :

.config(function ($stateProvider, $urlRouterProvider, localStorageServiceProvider, $httpProvider) {
    $stateProvider...;

    // if none of the above states are matched, use this as the fallback
    $urlRouterProvider.otherwise('/app/playlists');

    localStorageServiceProvider
        .setPrefix('lgb-preorder');

    $httpProvider.defaults.headers.common = {};
    $httpProvider.defaults.headers.post = {};
    $httpProvider.defaults.headers.put = {};
    $httpProvider.defaults.headers.patch = {};
});

and my function (doesn't work):

$scope.doLogin = function () {

            var loginData = {
                username: this.login.username,
                password: this.login.password
            };

            $http({
                url: 'http://sub.domain.com/app_dev.php/api/login_check',
                method: 'POST',
                data: loginData,
                headers: {'Content-Type': 'application/json'}
            })
                .success(function (data) {
                    console.log("Success -- login ok with ", data);
                })
                .error(function (error) {
                    console.log("ERROR -- login fail with ", error);
                });
        };

Error send is :

XMLHttpRequest cannot load http://sub.domain.com/app_dev.php/api/login_check. Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8100', but only one is allowed. Origin 'http://localhost:8100' is therefore not allowed access.

it seems because my http request have 2 definitions of Access-Control-Allow-Headers and Access-Control-Allow-Origin :

Request URL:http://sub.domain.com/app_dev.php/api/login_check
Request Method:OPTIONS
Status Code:200 OK
Remote Address:x.x.x.x:80
Response Headers
view source
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Headers:origin, content-type, x-requested-with, accept
Access-Control-Allow-Methods:POST, PUT, GET, DELETE, OPTIONS
Access-Control-Allow-Origin:http://localhost:8100
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600

but why ???____??? It seems i have 2 different handler, nelmio and one other, and each put an Access-Control-Allow-Headers / Access-Control-Allow-Origin. The others access-control are OK.

If anyone have an idea, its really appreciated. Thanx for your help.

Quovandius commented 8 years ago

Anyone have an idea ?? :s

Quovandius commented 8 years ago

up, if someone have an idea ??

Quovandius commented 8 years ago

up, i continue to search a solution .. If anyone have an idea

Quovandius commented 8 years ago

last up, i didn't find any solution currently. I search about nginx configuration, may be its a conflict between nginx vhost and this bundle

tim-massey commented 6 years ago

I can confirm that if Nginx is also configured to send the CORS headers, there are duplicates created. I removed the Nginx CORS config and the bundle works as expected. An idea is to have the bundle check for the headers and create a warning if duplicates are present.

romantaranenko commented 2 years ago

I can confirm that if Nginx is also configured to send the CORS headers, there are duplicates created. I removed the Nginx CORS config and the bundle works as expected. An idea is to have the bundle check for the headers and create a warning if duplicates are present.

Can confirm, had exactly same problem.

devjerry0 commented 2 years ago

+1 symfony: 5.4 nelmiocors: 2.2.0

Navds commented 2 years ago

+1 Symfony: 5.3.15 Nelmiocors: 2.2.0

Seldaek commented 1 year ago

The bundle is asf ar as I know never setting duplicate headers, so if you're +1ing here, please give more details or look at what else you have running in your application. Try to look in the symfony profiler for what event listeners you have running after the CorsListener. Do you have symfony http cache in place or something? Just adding +1 will not help anyone that's for sure.

Seldaek commented 1 year ago

An idea is to have the bundle check for the headers and create a warning if duplicates are present.

The bundle cannot do this check, if you configure your web server to also send CORS headers back in the response what happens is this:

As you can see, the PHP code has no chance to ever see what the web server is doing, so cannot act on that. Only the server admins/application developers can do this. Please learn how your stack works.