4

I'm creating a full-stack application with backend being developed in Ktor (Kotlin) and frontend in React (TypeScript). The backend is being hosted on Heroku, while the frontend is still under development, therefore I'm running it locally.

The API is operational and works as intended when tested with Postman. This is an excrept of the Ktor's configuration:

@Suppress("unused")
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {

    install(CORS) {
        anyHost()
    }

    install(StatusPages) {
        // Status pages configuration
    }

    install(ContentNegotiation) {
        jackson {
            enable(SerializationFeature.INDENT_OUTPUT)
            disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
        }
    }

    routing {
        // Declaration of routes
    }
}

I've enabled CORS, allowing every host to access the resources.

In React application, I'm using axios as my HTTP library. This is a sample of how a request to the server looks like:

interface LoginModel {
    email: string;
    password: string;
}

interface TokenAuthModel {
    successful: boolean;
    access_token: string;
}

import * as axios from 'axios';

const requestConfig = {
    headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
    },
        timeout: 15000
};

async login(login: LoginModel): Promise<TokenAuthModel | null> {
    const req = await axios.default.post('some-url', login, requestConfig);
    if (request.status === 200) {
        return request.data as TokenAuthModel;
    }
    return null;
}

which returns the following error message:

Access to XMLHttpRequest at 'https://app-name.herokuapp.com/some/endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

As stated above, the error is only present when making a request from the React frontend, while Postman calls work as intended.

Any sort of help would be extremely appreciated.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197

4 Answers4

7

Following works for my setup (Ktor/axiom too):

fun Application.cors() {
    install(CORS) {
        method(HttpMethod.Options)
        method(HttpMethod.Put)
        method(HttpMethod.Delete)
        method(HttpMethod.Patch)
        header(HttpHeaders.Authorization)
        header(HttpHeaders.AccessControlAllowOrigin)
        allowNonSimpleContentTypes = true
        allowCredentials = true
        allowSameOrigin = true
        host(frontendHost, listOf("http", "https")) // frontendHost might be "*"
        logger.info { "CORS enabled for $hosts" }
    }
}

Might be a bit excessive, but I've just had the same issue with No 'Access-Control-Allow-Origin' ... and that's how I solved it. No need for CORS-proxy.

Debug through ktor sources shown that lack of header(HttpHeaders.AccessControlAllowOrigin) was the main problem.

Jakub
  • 165
  • 2
  • 9
  • 1
    It's bizarre that this would be necessary, since that header is kind of fundamental to CORS operation, but adding `header(HttpHeaders.AccessControlAllowOrigin)` did indeed fix the issue for me. – Clyde Jul 26 '21 at 07:28
5

I use this for cors:

install(CORS) {
        method(HttpMethod.Options)
        method(HttpMethod.Put)
        method(HttpMethod.Delete)
        method(HttpMethod.Patch)
        header(HttpHeaders.Authorization)
        header(HttpHeaders.ContentType)
        // header("any header") if you want to add any header
        allowCredentials = true
        allowNonSimpleContentTypes = true
        anyHost()
    }
Isoq Hakimov
  • 118
  • 1
  • 1
  • 7
1

Above answers are right, but for never versions the methods are a bit different. Now the correct installation would look like this:

install(CORS) {
    // The methods for your server routes
    allowMethod(HttpMethod.Put)
    allowMethod(HttpMethod.Post)
    allowMethod(HttpMethod.Delete)
    allowHeader(HttpHeaders.Authorization)
    allowHeader(HttpHeaders.ContentType)
    allowCredentials = true
    allowNonSimpleContentTypes = true
    anyHost() // or allowHost(yourHost)
}
MrArtyD
  • 113
  • 1
  • 9
0

You have to whitelist your origin(localhost:3000) from your Kotlin backend.

critrange
  • 5,652
  • 2
  • 16
  • 47
  • 2
    Even after whitelisting the `localhost` with `host("localhost:3000")`, the same error persists client-side. –  Jul 18 '20 at 08:27