Skip to content
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

Can't download image due to CORS policy, though server replies with Access-Control-Allow-Origin: * #1549

Closed
3 tasks done
jfoclpf opened this issue Jan 23, 2023 · 5 comments
Closed
3 tasks done

Comments

@jfoclpf
Copy link

jfoclpf commented Jan 23, 2023

Bug Report

Problem

What is expected to happen?

Be able to download an image with fetch() considering the server replies with Access-Control-Allow-Origin: *

This image. As you can see here, the Access-Control-Allow-Origin header is *

What does actually happen?

CORS policy blocks the HTTP request

image

Information

Command or Code

In console of the Chrome dev tools, I typed:
fetch('https://servidor.nomeubairro.app/image_server/debug_n1_2023_01_21_22_11_abrantes_bemposta_gfzsowzdap.jpg')

Environment, Platform, Device

Version information

cordova info:

$ cordova info

Cordova Packages:

    cli: 11.1.0
        common: 4.1.0
        create: 4.1.0
        lib: 11.1.0
            common: 4.1.0
            fetch: 3.1.0
            serve: 4.0.0

Project Installed Platforms:

    android: 10.1.2

Project Installed Plugins:

    @globules-io/cordova-plugin-ios-xhr: 1.2.4
    cordova-pdf-generator: 2.1.1
    cordova-plugin-androidx-adapter: 1.1.3
    cordova-plugin-app-version: 0.1.14
    cordova-plugin-camera: 6.0.0
    cordova-plugin-device: 2.1.0
    cordova-plugin-email-composer: 0.9.2
    cordova-plugin-file: 7.0.0
    cordova-plugin-geolocation: 4.1.0
    cordova-plugin-inappbrowser: 5.0.0
    cordova-plugin-is-debug: 1.0.0
    cordova-plugin-network-information: 3.0.0
    cordova-plugin-screen-orientation: 3.0.2
    cordova-plugin-simple-image-resizer: 0.1.1
    cordova-plugin-splashscreen: 6.0.2
    cordova-plugin-statusbar: 3.0.0
    cordova-plugin-whitelist: 1.3.5
    es6-promise-plugin: 4.2.2

Environment:

    OS: Ubuntu 20.04.5 LTS (linux 5.15.0-58-generic) x64
    Node: v16.19.0
    npm: 9.3.0

android Environment:

    android:
[=======================================] 100% Fetch remote repository...       
Available Android targets:
----------
id: 1 or "android-10"
     Name: Android API 10
     Type: Platform
     API level: 10
     Revision: 2
----------
id: 2 or "android-11"
     Name: Android API 11
     Type: Platform
     API level: 11
     Revision: 2
----------
id: 3 or "android-12"
     Name: Android API 12
     Type: Platform
     API level: 12
     Revision: 3
----------
id: 4 or "android-13"
     Name: Android API 13
     Type: Platform
     API level: 13
     Revision: 1
----------
id: 5 or "android-14"
     Name: Android API 14
     Type: Platform
     API level: 14
     Revision: 4
----------
id: 6 or "android-15"
     Name: Android API 15
     Type: Platform
     API level: 15
     Revision: 5
----------
id: 7 or "android-16"
     Name: Android API 16
     Type: Platform
     API level: 16
     Revision: 5
----------
id: 8 or "android-17"
     Name: Android API 17
     Type: Platform
     API level: 17
     Revision: 3
----------
id: 9 or "android-18"
     Name: Android API 18
     Type: Platform
     API level: 18
     Revision: 3
----------
id: 10 or "android-19"
     Name: Android API 19
     Type: Platform
     API level: 19
     Revision: 4
----------
id: 11 or "android-20"
     Name: Android API 20
     Type: Platform
     API level: 20
     Revision: 2
----------
id: 12 or "android-21"
     Name: Android API 21
     Type: Platform
     API level: 21
     Revision: 2
----------
id: 13 or "android-22"
     Name: Android API 22
     Type: Platform
     API level: 22
     Revision: 2
----------
id: 14 or "android-23"
     Name: Android API 23
     Type: Platform
     API level: 23
     Revision: 3
----------
id: 15 or "android-24"
     Name: Android API 24
     Type: Platform
     API level: 24
     Revision: 2
----------
id: 16 or "android-25"
     Name: Android API 25
     Type: Platform
     API level: 25
     Revision: 3
----------
id: 17 or "android-26"
     Name: Android API 26
     Type: Platform
     API level: 26
     Revision: 2
----------
id: 18 or "android-27"
     Name: Android API 27
     Type: Platform
     API level: 27
     Revision: 3
----------
id: 19 or "android-28"
     Name: Android API 28
     Type: Platform
     API level: 28
     Revision: 6
----------
id: 20 or "android-29"
     Name: Android API 29
     Type: Platform
     API level: 29
     Revision: 5
----------
id: 21 or "android-30"
     Name: Android API 30
     Type: Platform
     API level: 30
     Revision: 3
----------
id: 22 or "android-31"
     Name: Android API 31
     Type: Platform
     API level: 31
     Revision: 1
----------
id: 23 or "android-32"
     Name: Android API 32
     Type: Platform
     API level: 32
     Revision: 1
----------
id: 24 or "android-33"
     Name: Android API 33, extension level 3
     Type: Platform
     API level: 33
     Revision: 2
----------
id: 25 or "android-33-ext4"
     Name: Android API 33, extension level 4
     Type: Platform
     API level: 33
     Revision: 1


Project Setting Files:

    config.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<widget id="com.in.my.district" version="1.2.30" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
  <name>No meu Bairro!</name>
  <description>
    Comunique ao seu município anomalias no seu bairro, como buracos na calçada ou lixo por recolher
  </description>
  <author email="joao.pimentel.ferreira@gmail.com" href="https://www.joaopimentel.com/">
    João Pimentel Ferreira
  </author>
  <content src="index.html"/>
  <icon height="512" src="res/icon/universal/512.png" width="512"/>
  <icon density="xhdpi" height="196" src="res/icon/universal/196.png" width="196"/>
  <icon density="xxxhdpi" height="192" src="res/icon/universal/192.png" width="192"/>
  <icon density="xxhdpi" height="144" src="res/icon/universal/144.png" width="144"/>
  <icon density="hdpi" height="72" src="res/icon/universal/72.png" width="72"/>
  <icon density="mdpi" height="48" src="res/icon/universal/48.png" width="48"/>
  <access origin="*"/>
  <allow-navigation href="*"/>
  <allow-intent href="http://*/*"/>
  <allow-intent href="https://*/*"/>
  <preference name="OverrideUserAgent" value="APP/com.in.my.district"/>
  <preference name="windows-target-version" value="10.0"/>
  <preference name="windows-phone-target-version" value="10.0"/>
  <preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle"/>
  <preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets"/>
  <preference name="StatusBarOverlaysWebView" value="false"/>
  <preference name="StatusBarBackgroundColor" value="#FFFFFF"/>
  <preference name="StatusBarStyle" value="blacktranslucent"/>
  <preference name="AndroidXEnabled" value="true"/>
  <hook src="hooks/generateJsFiles.js" type="before_prepare"/>
  <hook src="hooks/convertHbsToHtml.js" type="after_prepare"/>
  <hook src="hooks/minifyFiles.js" type="after_prepare"/>
  <hook src="node_modules/cordova-import-npm/scripts/importNpmPackages.js" type="before_prepare"/>
  <platform name="android">
    <preference name="android-minSdkVersion" value="30"/>
    <preference name="android-targetSdkVersion" value="33"/>
    <preference name="scheme" value="https" />
    <preference name="hostname" value="app.nomeubairro.app"/>
    <preference name="AndroidPersistentFileLocation" value="Compatibility"/>
    <allow-intent href="market:*"/>
    <icon height="512" src="res/icon/android/512.png" width="512"/>
    <icon density="xhdpi" height="192" src="res/icon/android/192.png" width="192"/>
    <icon density="xxxhdpi" height="192" src="res/icon/android/192.png" width="192"/>
    <icon density="xxhdpi" height="144" src="res/icon/android/144.png" width="144"/>
    <icon density="hdpi" height="72" src="res/icon/android/72.png" width="72"/>
    <icon density="mdpi" height="48" src="res/icon/android/48.png" width="48"/>
    <splash density="hdpi" src="res/screen/android/screen-hdpi-portrait.png"/>
    <splash density="port-hdpi" src="res/screen/android/screen-hdpi-portrait.png"/>
    <splash density="ldpi" src="res/screen/android/screen-ldpi-portrait.png"/>
    <splash density="port-ldpi" src="res/screen/android/screen-ldpi-portrait.png"/>
    <splash density="mdpi" src="res/screen/android/screen-mdpi-portrait.png"/>
    <splash density="port-mdpi" src="res/screen/android/screen-mdpi-portrait.png"/>
    <splash density="xhdpi" src="res/screen/android/screen-xhdpi-portrait.png"/>
    <splash density="port-xhdpi" src="res/screen/android/screen-xhdpi-portrait.png"/>
  </platform>
  <platform name="ios">
    <allow-intent href="itms:*"/>
    <allow-intent href="itms-apps:*"/>
    <icon src="res/icon/ios/16.png" width="16" height="16"/>
    <icon src="res/icon/ios/20.png" width="20" height="20"/>
    <icon src="res/icon/ios/29.png" width="29" height="29"/>
    <icon src="res/icon/ios/32.png" width="32" height="32"/>
    <icon src="res/icon/ios/40.png" width="40" height="40"/>
    <icon src="res/icon/ios/48.png" width="48" height="48"/>
    <icon src="res/icon/ios/50.png" width="50" height="50"/>
    <icon src="res/icon/ios/55.png" width="55" height="55"/>
    <icon src="res/icon/ios/57.png" width="57" height="57"/>
    <icon src="res/icon/ios/58.png" width="58" height="58"/>
    <icon src="res/icon/ios/60.png" width="60" height="60"/>
    <icon src="res/icon/ios/64.png" width="64" height="64"/>
    <icon src="res/icon/ios/72.png" width="72" height="72"/>
    <icon src="res/icon/ios/76.png" width="76" height="76"/>
    <icon src="res/icon/ios/80.png" width="80" height="80"/>
    <icon src="res/icon/ios/87.png" width="87" height="87"/>
    <icon src="res/icon/ios/88.png" width="88" height="88"/>
    <icon src="res/icon/ios/100.png" width="100" height="100"/>
    <icon src="res/icon/ios/114.png" width="114" height="114"/>
    <icon src="res/icon/ios/120.png" width="120" height="120"/>
    <icon src="res/icon/ios/128.png" width="128" height="128"/>
    <icon src="res/icon/ios/144.png" width="144" height="144"/>
    <icon src="res/icon/ios/152.png" width="152" height="152"/>
    <icon src="res/icon/ios/167.png" width="167" height="167"/>
    <icon src="res/icon/ios/172.png" width="172" height="172"/>
    <icon src="res/icon/ios/180.png" width="180" height="180"/>
    <icon src="res/icon/ios/196.png" width="196" height="196"/>
    <icon src="res/icon/ios/216.png" width="216" height="216"/>
    <icon src="res/icon/ios/256.png" width="256" height="256"/>
    <icon src="res/icon/ios/512.png" width="512" height="512"/>
    <icon src="res/icon/ios/1024.png" width="1024" height="1024"/>
    <preference name="iosPersistentFileLocation" value="Library"/>
    <preference name="NativeXHRLogging" value="full"/>
    <preference name="AllowUntrustedCerts" value="true"/>
    <preference name="InterceptRemoteRequests" value="all"/>
    <preference name="CustomUserAgent" value="APP/com.in.my.district"/>
    <preference name="allowFileAccessFromFileURLs" value="true"/>
    <preference name="allowUniversalAccessFromFileURLs" value="true"/>
    <edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município</string>
    </edit-config>
    <edit-config target="NSLocationAlwaysAndWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município</string>
    </edit-config>
    <edit-config target="NSLocationAlwaysUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município</string>
    </edit-config>
    <edit-config target="NSLocationAlwaysAndWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município</string>
    </edit-config>
    <edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da acesso à câmera para tirar fotos às anomalias</string>
    </edit-config>
    <edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da acesso à galeria, caso você já tenha tirado anteriormente a foto à ocorrência</string>
    </edit-config>
    <edit-config target="NSPhotoLibraryAddUsageDescription" file="*-Info.plist" mode="merge">
      <string>Necessita da acesso à galeria para guardar algumas fotos, mesmo que temporariamente</string>
    </edit-config>
    <edit-config target="UIFileSharingEnabled" file="*-Info.plist" mode="merge">
      <true/>
    </edit-config>
    <edit-config target="LSSupportsOpeningDocumentsInPlace" file="*-Info.plist" mode="merge">
      <true/>
    </edit-config>
    <edit-config target="UISupportsDocumentBrowser" file="*-Info.plist" mode="merge">
      <true/>
    </edit-config>
  </platform>
</widget>

    package.json:
--- Start of Cordova JSON Snippet ---
{
  "plugins": {
    "cordova-plugin-geolocation": {
      "GPS_REQUIRED": "true"
    },
    "cordova-plugin-email-composer": {
      "ANDROID_SUPPORT_V4_VERSION": "27.+"
    },
    "cordova-plugin-statusbar": {},
    "cordova-plugin-screen-orientation": {},
    "cordova-plugin-device": {},
    "cordova-plugin-whitelist": {},
    "cordova-pdf-generator": {},
    "cordova-plugin-splashscreen": {},
    "cordova-plugin-inappbrowser": {},
    "cordova-plugin-is-debug": {},
    "cordova-plugin-androidx-adapter": {},
    "cordova-plugin-camera": {
      "ANDROID_SUPPORT_V4_VERSION": "27.+",
      "ANDROIDX_CORE_VERSION": "1.6.+"
    },
    "cordova-plugin-network-information": {},
    "cordova-plugin-app-version": {},
    "@globules-io/cordova-plugin-ios-xhr": {},
    "cordova-plugin-simple-image-resizer": {
      "ANDROID_EXIFINTERFACES_VERSION": "27.+"
    },
    "cordova-plugin-file": {
      "ANDROIDX_WEBKIT_VERSION": "1.4.0"
    }
  },
  "platforms": [
    "ios",
    "android"
  ]
}
--- End of Cordova JSON Snippet ---

Checklist

  • I searched for existing GitHub issues
  • I updated all Cordova tooling to most recent version
  • I included all the necessary information above
@jfoclpf
Copy link
Author

jfoclpf commented Jan 23, 2023

I read #1354 but it does not solve the problem

@jfoclpf jfoclpf changed the title Can't download image although server replies with Access-Control-Allow-Origin *; Can't download image although server replies with Access-Control-Allow-Origin: * Jan 23, 2023
@jfoclpf jfoclpf changed the title Can't download image although server replies with Access-Control-Allow-Origin: * Can't download image due to CORS policy, though server replies with Access-Control-Allow-Origin: * Jan 23, 2023
@breautek
Copy link
Contributor

breautek commented Jan 23, 2023

Have a read of my blog, particularly on preflight requests.

In short, having Access-Control-Allow-Origin is only part of the CORS compatibility. In some cases, the browser may perform a "preflight request". This is done behind the scenes but effectively before it does the GET request, it may send an OPTIONS request to the server, this is called a preflight request.

The server should respond to the preflight request by giving no content body e.g. Content-Length should be 0, and the HTTP status should be 204 (OK, No Content). The response should also have the Access-Control headers also defined.

I tested the url in your example and I see it giving Access-Control-Allow-Origin on the GET request but it doesn't have the OPTIONS request implemented. I also see that you're using an NGINX server, so you can easily get OPTIONS for all your API endpoints by doing something like

if ($request_method = 'OPTIONS') {
    add_header 'Access-Control-Allow-Origin' $http_origin always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PUT, OPTIONS, HEAD' always;
    add_header 'Access-Control-Allow-Headers' 'Accept, Content-Type, Access-Control-Allow-Origin' always;
    add_header 'Content-Type' 'text/plain charset=UTF-8' always;
    add_header 'Content-Length' 0 always;
    return 204;
}

In your location block.

EDIT: I forgot to update the Access-Contrl-Allow-Origin but add_header 'Access-Control-Allow-Origin' $http_origin always; is effectively the same as a wildcard *. It works by reading the request's Origin header and setting it as the value for the Access-Control-Allow-Origin response header.

The reason why I do it that way is I've previously had issues using the wildcard on older iOS devices (like iOS 9 or 10... devices that aren't actually supported anymore)

@jfoclpf
Copy link
Author

jfoclpf commented Jan 24, 2023

Thank you very much @breautek , I will check it out this evening

@jfoclpf
Copy link
Author

jfoclpf commented Jan 24, 2023

I could solve the problem using the express.static for static content and cors()

const cors = require('cors')

const app = express()

app.use(cors())
app.use('/image_server', express.static(path.join(__dirname, 'uploadedImages')))

I don't know why but NPM express package cors deals everything without the need of nginx special configurations

Thank you very much @breautek anyway

@jfoclpf jfoclpf closed this as completed Jan 24, 2023
@breautek
Copy link
Contributor

I don't know why but NPM express package cors deals everything without the need of nginx special configurations

Doesn't really matter what HTTP technology you use, just as long as it responds to OPTIONS request of your API endpoint and it sends the CORS headers.

So if you're using Node with express, then the cors express middleware is definitely a valid approach to take.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants