CORS_ORIGINS doesn't seem to work as shown in another topic/documentation

Hey :slight_smile:

As another user, I wanted to setup my CORS (server-side) and… I saw that I had nothing as β€œOrigin” when making request from Forest Admin (any of my env) to my backend (Go).

Now that I have my CORS set as described in this topic.

Expected behavior

The origins should be https://app.forestadmin.com

Actual behavior

The Origin is empty in the headers (from server-side perspective)

Failure Logs

I have no failure log or anything :slight_smile:

Context

Please provide any relevant information about your setup.

Everything (except middleware/, server.js, app.js) has been translated to TypeScript.

{
  "name": "xxxxxxxxxxxxx",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node ./dist/server.js",
    "legacy-start": "node server.js",
    "build": "tsc",
    "start-dev": "tsc-watch --project ./tsconfig.json --noClear --onSuccess \"nodemon --watch ./dist ./dist/server.js\""
  },
  "dependencies": {
    "@types/express": "^4.17.11",
    "@types/forest-express-mongoose": "^6.3.3",
    "@types/mongoose": "^5.10.3",
    "axios": "^0.21.1",
    "body-parser": "1.19.0",
    "chalk": "~1.1.3",
    "cookie-parser": "1.4.4",
    "cors": "2.8.5",
    "debug": "~4.0.1",
    "dotenv": "~6.1.0",
    "express": "~4.17.1",
    "express-jwt": "5.3.1",
    "forest-express-mongoose": "^6.0.0",
    "mongoose": "~5.8.2",
    "morgan": "1.9.1",
    "nodemon": "^2.0.7",
    "require-all": "^3.0.0",
    "tsc-watch": "^4.2.9",
    "typescript": "^4.1.3"
  }
}

Thanks for any help :slight_smile:

Max,

Hi @Emixam23 :wave: I don’t really understand what do you want to do.
If I understand correctly you have 2 server, one is lumber generated server and the other one is your own backend in GO right?

Hey!

Sorry I have been a lot busy and didn’t take the time to get back over here :slight_smile:

Yes… We have a backoffice that would do almost everything (forest admin) and an API (Go) for our client side. Even if ForestAdmin does almost everything without comunicating to the API (backend Go), we do have a case where we need to ping our Go backend, and so we want to integrate a CORS approach :slight_smile:

I hope I am clear, thanks for your reply!

Max

Hello @Emixam23,

Could you display a schema of your architecture for helping us addressing your issue, please?
Does it look like?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     β”‚             β”‚                      β”‚
β”‚  Forest Admin       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚    Lumber Agent      β”‚
β”‚                     β”‚             β”‚                      β”‚
β”‚                     β”‚             β”‚                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                                              β”‚
                                              β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                    β”‚                       β”‚
                                    β”‚    Go Api             β”‚
                                    β”‚                       β”‚
                                    β”‚                       β”‚
                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Is what you are trying to achieve the communication with CORS between your Lumber Agent and the Go Api?

Let me know :slight_smile:

Hello :slight_smile:

So I made a schema based on your answer :slight_smile:

forestadmin

  • Blue Arrow β†’ The logical exchange when user(s) use our webapp
  • Red Arrow β†’ The logical exchange when an admin use our backoffice

It’s quite simple, as I am a GO developer and as we have a lot of medias related work to get done, I need each time to develope 2 times each features at some points (only for the Medias part and Google Cloud Storage) in Go and in Typescript (forest admin transalated from JS to TS).

We so choose to stop and manage all our medias just with our backend in Go. Now, when we do things such as β€œUploading an image”, instead of uploading it from our BackOffice to GCS, we currently send the image to our Backend (in Go) from our backoffice forest admin and the backend itself do some work on the image and upload it to GCS.

That way, our backoffice request our backend by sending medias among parameters. As we want some security for that part (which is of course a secondary router of our golang backend), we want to implemente a CORS policy.

As said above, I followed the explaination from the link but our cors origin value is always epty coming from forest admin

I hope I could answer your question :slight_smile:

Thanks!

Max

Hello @Emixam23,

Thank you for the detailed explanation. It’s now very clear to me what you want to achieve.

Could you please share with me the way you implemented the request in the forest backend? You can of course share it as a personal message if you feel like it’s confidential.

Thank you

Hello :slight_smile:

It’s quite simple tbh, I just created a smart action and this is the implementation :slight_smile:

// Upload image to gcs
router.post('/actions/items/upload-image', permissionMiddlewareCreator.smartAction(), (req, res) => {
    const attrs = req.body.data.attributes;
    const attrsValues = attrs.values;
    const image = attrsValues['Image'];

    const recordGetter = new RecordGetter(items);
    recordGetter.get(attrs.ids[0])
        .then((item: IItem) => {
            // removing some data that bother me :)
            // let ext = getExtensionFromHeaders(image)
            // if (ext === Extension.Undefined) {
            //     throwException("No extension could be determined for the given image.")
            // }
            // let fileContent = removeHeaderFromFile(image)
            backofficeAPI.uploadImage(fileContent, item._id, ext, [new ImageSize(1920, 1080), /* ... */)
                .then((result: UploadAndResizeImageResponseBody) => {
                    items
                        .updateMany({'_id': item["_id"]}, {
                            '$set': {
                                'image_url': result.PublicURL,
                                'updated_at': getCurrentTimestampInSeconds()
                            }
                        })
                        .then(() => {
                            res.send({success: 'Image uploaded.'});
                        }).catch(error => {
                        throwException("an error happened when updating an item: ", error)
                    });
                })
                .catch(error => {
                    throwException("an error happened when using the backoffice: ", error)
                });
        })
});

// Backoffice API client
class BackofficeAPI {

    /* ... */

    constructor(/* ... */) {
        /* ... */
    }

     /* ... */

    async uploadImage(inputFile: string, id: string, extension: Extension, endSizes: ImageSize[]): Promise<UploadAndResizeImageResponseBody> {
        let body = new UploadAndResizeImageRequestBody();
        body.ImageSourceB64 = inputFile;
        body.ID = id;
        body.Extension = extensionToStr(extension);
        body.EndSizes = endSizes;

        return this.makeAPICall("post", endpointToStr(BackOfficeEndpoints.UploadAndResizeImage), body)
            .then(response => {
                let responseBody = null
                if (response.status != 200) {
                    responseBody = response.data as EndpointError;
                    throwException("Error [" + response.status + "]: " + responseBody.Error);
                } else {
                    responseBody = response.data as UploadAndResizeImageResponseBody;
                    return responseBody;
                }
            }, reasonOnReject => {
                throwException("app call rejected: ", reasonOnReject);
            })
            .catch(reason => {
                throwException("ann error happened when making an http api call: " + reason);
            });
    }

    /////////////////
    //////UTILS//////
    /////////////////

    private async makeAPICall(method: Method, endpoint: string, body: any): Promise<any> {
        let timeout = 1000 * this.DefaultTimeout; // Default timeout being in seconds

        logInfo("method: ", method)
        logInfo("endpoint: ", endpoint)
        logInfo("body: ", body)

        return new Promise((resolve, reject) => {
            axios({
                method: method,
                url: this.BackOfficeAPIHost + "/" + endpoint,
                timeout: timeout, // Wait for 5 seconds
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json',
                    'x-api-key': this.BackOfficeAPIKey
                },
                data: body
            }).then(
                response => resolve(response),
                err => reject(err))
        })
    }
}

export const backofficeAPI = new BackofficeAPI(/* ... */);

throwException(…) is just a simple function which logs and throw new Error on demand :wink:

As you can see, I am not setting any CORS at all from the code, but only from the env file:

CORS_ORIGINS=plop_example

It doesn’t change anything if i put a real value or a wrong one:

CORS_ORIGINS=http://my-website.com
CORS_ORIGINS=https://my-website.com
CORS_ORIGINS=http://localhost:3310
CORS_ORIGINS=localhost:3310
...

I hope we can find a solution :slight_smile:

Thanks

Max

Hi @Emixam23,
I hope you’re doing well.

CORS_ORIGINS environment variable is here to configure CORS and protect your (Forest) admin backend API from non-allowed domain.
So it won’t help for what you want to configure on your own backend.

First question is what CORS configuration do you have in you GO backend? Does it allow calls from your admin backend domains?
If so, you might need to check if you sent the expected headers using axios and especially the Origin header.

Let us know if it helps.

1 Like

Hey!

Thanks for your answer :slight_smile:
As of today, my backend accept:

Now… maybe it comes from axios then (the issue why I have the cors empty while authorizing from Go)… Because on the other end, from my website, I use β€œfetch” and cors works as expected (it gets sent to my Go backend)… The only issue is that I use axios on forest admin because I can set a timeout, which helps me to handle large files uploads…

I don’t know how to do… Maybe I should get on Axios (nodeJs) if there are similar cases on the web where this one doesn’t send its origin, I will try tog et back to you ASAP :slight_smile:

Thanks!

Max

Hey @Emixam23 :wave: ,

In your case:

  • No modification of CORS_ORIGIN on your forest-express-mongoose is needed in your case, since as previously stated it is only beeing used to protect your admin backend from external unallowed calls.
  • If you want to achieve a call from your forest-express-mongoose backend to your Go backend using axios, your Go backend must allow the forest-express-mongoose backend URL in your CORS configuration.

Where your backend allow [ https://www.mywebsite.com ], you must add your forest-express-mongoose backend URL to make this kind of call works.

Let us know if it fixes your issue :pray: