Содержание
- CORS does not seem to activate #1134
- Comments
- I’m submitting a.
- Current behavior
- Expected behavior
- Minimal reproduction of the problem with instructions
- What is the motivation / use case for changing the behavior?
- Environment
- Footer
- How to solve CORS error in Node.js and React applications
- Table of Contents
- Project Setup
- What is CORS (Cross-Origin Resource Sharing)?
- Why CORS (Cross-Origin Resource Sharing)?
- Nest.js выдает ошибку cors, даже если cors включен
- 2 ответа
- ReactJS CORS Options
- Nest.js выдает ошибку cors, даже если cors включен
- 2 ответа
CORS does not seem to activate #1134
I’m submitting a.
Current behavior
CORS simply does not work regardless of how I enable it.
- const app = await NestFactory.create(AppModule, < cors: true >);
- const app = await NestFactory.create(AppModule, < cors: >);
- app.enableCors() ;
- app.enableCors()
- app.use(cors());
HTTP OPTIONS requests just end up with a 404
This is during development and not production. I’ve checked whether or not compiling to javascript was successful as well.
Expected behavior
CORS should be enabled.
Minimal reproduction of the problem with instructions
Create a new project and just attempt to enable CORS with previously mentioned methods above.
What is the motivation / use case for changing the behavior?
Environment
The text was updated successfully, but these errors were encountered:
You have a reproducable repo? I can’t seem to reproduce this error.
No. I’m probably testing it incorrectly. Let me do more homework and possibly reopen
I can confirm now. That said, it might not be a bug but a config issue with both my Mac and Windows machines.
I’m getting the «Response for preflight does not have HTTP ok status» error
I can share the repo privately if possible
Please share the repo, I can not reproduce the issue either.
Probably a config issue on my end then. I’ll post on StackOverflow. I’ll try to share a repo this week.
I was updating main.hmr.ts instead of main.ts.
im having this same issue after upgrading to the latest
@williamchristophercooley I had this problem due to user error. I made my changes to a file called main.hmr.ts, when the actual file that I needed to change was main.ts.
It’s done in express via app.options(‘*’, cors()) . It seems to me that NestJS does not support this.
@kamilmysliwiec any ideas how could it be implemented?
@kamilmysliwiec it does not enable preflight request handling.
You can pass additional parameters to configure your cors middleware app.enableCors(options) . If something doesn’t work for you, I’d suggest asking on StackOverflow
app.use(cors()) does not enable pre-flight request.
Just edited my answer, thanks. Simply customize cors behavior by passing options object:
https://github.com/expressjs/cors#configuration-options
I totally forgot that cors() supports configuration options. Thank you.
However, most of these options seem to not make any effect. (5.6.2)
these codes are not working again in version nestjs@6.0.4
@blueway Try adding the allowedHeaders config option
If you still struggling with origin has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ‘*’ use this conf
Dunno why < origin: ‘*’ >does not work
Looks like something wrong with big amount requests. Because first 20 requests is OK, but then always appear the same error for me:
I’ve tried everything 🙁
I can use PAW or Postman and make API calls to my nest project, but if I use my mobile app, it fails.
import < NestFactory >from ‘@nestjs/core’;
import < AppModule >from ‘./app.module’;
async function bootstrap() <
const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3000);
>
bootstrap();
Looks like something wrong with big amount requests. Because first 20 requests is OK, but then always appear the same error for me:
Getting the same. some requests are then others are not
I have the same issue as @tskweres. Currently developing a mobile app using nest and ionic. When my Ionic app is deployed on my phone, it can’t communicate with my NestJS server on the same WIFI network. Tried to access my server URL directly on my phone’s browser, it doesn’t work.
I have tested various cors origin configurations such as * , /.+/ , and my phone’s IP address, but nothing seems to work.
Same issue debugging from Ionic, Web and Mobile:
@chaostheory can you reopen this issue please? I’m getting this warning from Android and Chrome:
This is the code I’m using to enable CORS:
Also, I tried a list of allowed origins too:
The endpoint I’m debugging is a multipart/form-data :
Any help is really appreciated! 🙏
Issue still here, tried everything from above.
It seems the following code works. But passing CorsOptions obj or use app.enableCors(options) won’t work. When the browse try to run OPTIONS method, nestjs spits 404 error.
In my case CORS is working for application/json with POST and PUST requests, but I’m having issues in multiple devices uploading files with a multipart/form-data, getting a lot of errors related with CORS.
Any help would be appreciated! 👍
In my case CORS is working for application/json with POST and PUST requests, but I’m having issues in multiple devices uploading files with a multipart/form-data, getting a lot of errors related with CORS.
Any help would be appreciated! 👍
I am also experiencing this. Using multipart/form-data throws an error Access to XMLHttpRequest at ‘http://localhost:5030/upload-file’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
Here is my configuration
Any help will be appreciated.
I thought I had CORS error as well, but it turns out nginx has a low max body size for FormData. Check your nginx error log to see if that’s the case and make sure you have client_max_body_size 10M
CORS is generally not handled by nginx itself. The app.enableCors(options); seems to work as intended.
In my case the issue was because I was running nest from an azure functions instance. If you are using AWS Lambda or another similar implementation you may need to adjust the hosting service’s configuration as well. I updated local.settings.json and it worked like a charm.
Edit: in local.settings.json
«Host»:
Same here, above does works for me, postman is fine but not front end requests
Same here, above does works for me, postman is fine but not front end requests
Ok my issue was that I was posting to ‘localhost:3000’, but if I post to ‘http://localhost:3000’, it’s fine.
© 2023 GitHub, Inc.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Источник
How to solve CORS error in Node.js and React applications
Table of Contents
In the previous article, I have explained how to deploy a Node.js application to Heroku. In this tutorial, we will be making use of the endpoint created there and see if we can use it in our React project.
Project Setup
Let’s create a React project using the following command:
Now update the App.js with the following code:
Here we have a local state called message , which we show to the user. If the message is empty, then we display them with a loading text. When the component is mounted (useEffect), we make a call to the API endpoint and fetch the message.
Now let’s run this and see if it works:
You will see that only «Loading..» text is displayed and the message never loads. If we inspect the page and see the console, we will see the following error:
Access to fetch at ‘https://nodejs-using-github.herokuapp.com/’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.
In the next sections, we will see what is CORS and how to fix this error.
What is CORS (Cross-Origin Resource Sharing)?
CORS stands for Cross-Origin Resource Sharing, which is an HTTP header based mechanism that helps the server to tell the browser, from which all domain requests can be made (except the same domain).
That is, in our case, the Node.js server hosted at https://nodejs-using-github.herokuapp.com/ , does not tell the browser that request can be made from http://localhost:3000 .
When this happens, your browser will throw an error as seen earlier.
Why CORS (Cross-Origin Resource Sharing)?
The next question that would come to your mind is why do we really need this mechanism. Imagine you are logged into your bank account or any social media website, then you visit a malicious website. This malicious website could run some scripts in the background to make API calls to your banking or social media to get your personal details.
To prevent this, your browser checks if the request to the banking or social media server can be made from the malicious website and throws the CORS error.
So CORS exists to share certain resources between trusted third-parties (across different origins/domains), hence the name Cross-Origin Resource Sharing.
Источник
Nest.js выдает ошибку cors, даже если cors включен
Я разрабатываю приложение next.js с nest.js в качестве бэкэнда. Теперь у меня возникает ошибка cors, даже если у меня включена cors в моем файле main.ts файла nest.js.
Вот мой файл main.ts.
Еще пробовал следующее.
Я также попытался это
Теперь из внешнего интерфейса в _app.js я определяю глобальную конфигурацию Axios, как показано ниже.
Затем в моем файле login.tsx я отправляю запрос в приложение nest.js, как показано ниже.
Вот значения — это объект, который имеет имя пользователя и пароль.
Я также пробовал любое другое решение из других вопросов StackOverflow. Но ни один из них не решил мою проблему. Это действительно сработало несколько дней назад. Я не знаю, что случилось.
Что я здесь делаю неправильно? Это сводит меня с ума. Если вам нужно, я могу предоставить больше кода.
2 ответа
Как поясняется в веб-документах MDN о CORS, вы не можете использовать подстановочный знак ( * ), будь то для разрешения источника или заголовков запроса (или методов запроса) в сочетании с запросами с учетными данными. Более авторитетным, но, возможно, более сухим источником является стандарт Fetch :
Для заголовков ответов Access-Control-Expose-Headers , Access-Control-Allow-Methods и Access-Control-Allow-Headers значение * считается подстановочным знаком для запросов без учетных данных. Для таких запросов нет способа сопоставить только имя заголовка или метод, который равен * .
Вы должны полностью отказаться от подстановочных знаков и явно указать разрешенный источник и разрешенные заголовки запроса, например:
Источник
ReactJS CORS Options
In ReactJS, Cross-Origin Resource Sharing (CORS) refers to the method that allows you to make requests to the server deployed at a different domain. As a reference, if the frontend and backend are at two different domains, we need CORS there.
Method to setup CORS requests in react app at front-end level:
- In axios: Axios always use base URL to start the request and the browser confirms that in the beginning HTTP OPTIONS requests by itself. Many times we need to pass tokens for authentication and the token which we are using is identified by Bearer. Now, the main part we need to pass some additional headers for CORS named as Access-Control-Allow-Credentials. This one is required because the browser needs to confirm the server if that is allowed to access resources.
- In fetch: To use CORS in fetch we need to use the mode option and set it to cors.
Let’s create an application in react to demonstrate the above concepts:
Creating React Application
Step 1: Create a React application using the following command:
Step 2: After creating your project folder i.e. example, move to it using the following command:
Step 3: Here we are using the Axios library for fetching API data, we need to install that by using the command from the root directory.
Project structure: It will look like this.
Источник
Nest.js выдает ошибку cors, даже если cors включен
Я разрабатываю приложение next.js с nest.js в качестве бэкэнда. Теперь у меня возникает ошибка cors, даже если у меня включена cors в моем файле main.ts файла nest.js.
Вот мой файл main.ts.
Еще пробовал следующее.
Я также попытался это
Теперь из внешнего интерфейса в _app.js я определяю глобальную конфигурацию Axios, как показано ниже.
Затем в моем файле login.tsx я отправляю запрос в приложение nest.js, как показано ниже.
Вот значения — это объект, который имеет имя пользователя и пароль.
Я также пробовал любое другое решение из других вопросов StackOverflow. Но ни один из них не решил мою проблему. Это действительно сработало несколько дней назад. Я не знаю, что случилось.
Что я здесь делаю неправильно? Это сводит меня с ума. Если вам нужно, я могу предоставить больше кода.
2 ответа
Как поясняется в веб-документах MDN о CORS, вы не можете использовать подстановочный знак ( * ), будь то для разрешения источника или заголовков запроса (или методов запроса) в сочетании с запросами с учетными данными. Более авторитетным, но, возможно, более сухим источником является стандарт Fetch :
Для заголовков ответов Access-Control-Expose-Headers , Access-Control-Allow-Methods и Access-Control-Allow-Headers значение * считается подстановочным знаком для запросов без учетных данных. Для таких запросов нет способа сопоставить только имя заголовка или метод, который равен * .
Вы должны полностью отказаться от подстановочных знаков и явно указать разрешенный источник и разрешенные заголовки запроса, например:
Источник
This is not react related but I see people asking questions on vercel here, so I thought I’d add my doubt as well.I have to deploy a nestJs backend to vercel.
Here is my vercel.json file
{
"version": 2,
"env": {
"DATABASE_URL":"db"
},
"builds": [
{
"src": "src/main.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/main.ts",
"methods": [
"GET",
"POST",
"PUT",
"DELETE"
]
}
]
}
I’m able to query the endpoints and get the response from Postman.
However when I’m trying to connect my Angular app to this end point its throwing me this error
Access to XMLHttpRequest at 'https://backend-url/users/email/a' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
However, I have this code in my backend that should allow all origins
main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Accept');
next();
});
app.enableCors({
allowedHeaders: '*',
origin: '*',
});
console.log(app);
await app.listen(3000);
}
bootstrap();
I tried adding a header key to vercel.json, but I got an error saying routes and headers cannot co-exist
{
"headers": [
{
"source": "/api/(.*)",
"headers": [
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
{ "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
]
}
]
}
Any help would be appreciated thanks! I tried asking the same in StackOverflow and got downvoted with no responses
Clone the application repo and check out the creating-endpoints-data-validation
branch:
git clone git@github.com:auth0-blog/wab-menu-api-nestjs.git
nest-restaurant-api
--branch creating-endpoints-data-validation
Make the project folder your current directory:
cd nest-restaurant-api
Then, install the project dependencies:
npm i
Finally, create a .env
hidden file:
touch .env
Populate .env
with this:
PORT=7000
So far, you’ve built an API that allows anyone to read and write data. However, one of the security requirements of this API is that only a selected number of users can write records to the store. To restrict access to API endpoints, you need to implement authorization.
Which Comes First: The Authorization Chicken or the Authentication Egg?
Your API needs to determine what users can and cannot access with each request. This process is known as authorization. There’s a bit of a catch here. For you to know what a user can do, you first need to know who that user is. This process is known as authentication. For example, a user identified as an admin would have access to endpoints that write data while a user identified as a customer wouldn’t.
Web applications commonly perform authentication by asking users to provide a set of credentials to log in through a client application, like a ReactJS interface. The client application sends the credentials to an authentication server that validates them. Upon success, the authentication server can share information about the identity and access of the user with the web application. To implement these Identity and Access Management (IAM) tasks easily, developers can use OAuth, an authorization framework, and OpenID Connect (OIDC), a simple identity layer on top of OAuth.
OAuth encapsulates access information in something called an access token. In turn, OpenID Connect encapsulates identity information in something called an ID token. The authentication server can send these two tokens to the client application that initiated the authentication process. When the user needs to make a request to a protected API endpoint, the client application must send the access token with the request for the API to then carry out the authorization process.
As such, in this architecture model, authentication starts on the client-side, while authorization starts on the server-side. Since the scope of this tutorial is to build a secure NestJS API, your main focus is to implement authorization. But, you will have access to a demo client application that you can use to authenticate a user, get an access token, and issue API requests to provide you with a realistic flow of identity management.
You won’t have to worry about implementing OAuth and OpenID Connect or building an authentication server. Instead, you’ll delegate that job to Auth0.
Set Up API Authorization
With Auth0, you can manage the authorization requirements of this application-to-server architecture easily. To start, you need to create a free Auth0 account if you don’t have one yet.
Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. Your team and organization can avoid the cost, time, and risk that comes with building your own solution to authenticate and authorize users. We offer tons of guidance and SDKs for you to get started and integrate Auth0 in your stack easily.
After you create your account, you’ll create an Auth Tenant, which is a container that Auth0 uses to store your identity service configuration and your users in isolation — no other Auth0 customer can peek into or access your tenant. It’s similar to you being a tenant in an apartment building. Auth0 looks after the building while the apartment is all yours to live in and customize. However, each apartment is fully isolated (no windows, soundproof walls, etc.) so that neighbors can’t intrude on your privacy.
After creating your tenant, you need to create an Auth0 API, which is an API that you define within your Auth0 tenant and that you can consume from your applications to process authentication and authorization requests.
Head to the APIs section of the Auth0 Dashboard and hit the Create API button.
Then, in the form that comes up:
- Add a Name to your API:
Menu API
- Set the Identifier:
https://menu-api.demo.com
- Leave the signing algorithm as
RS256
. It’s the best option from a security standpoint.
As you may have more than one API that requires authorization services, you can create as many Auth0 APIs as you need. As such, the identifier is a unique string that Auth0 uses to differentiate between your Auth0 APIs. We recommend structuring the value of identifiers as URLs to make it easy to create unique identifiers predictably. Bear in mind that Auth0 never calls these URLs.
Once you’ve added those values, hit the Create button.
A new page loads and presents you with your Auth0 API information. Click on the Quick Start tab. This page offers guidance on how to set up different backend technologies to consume the Authorization API you’ve created. From the code box, choose Node.js.
To configure Passport in the next sections, you’ll need two values from Auth0: an Auth0 Issuer URL and an Auth0 Audience. You’ll store these values in your .env
file. Update .env
like so using the values from the Node.js code snippet under the Quick Start tab:
PORT=7000
AUTH0_ISSUER_URL=https://<AUTH0-TENANT-NAME>.auth0.com/
AUTH0_AUDIENCE=https://menu-api.demo.com
The AUTH0_ISSUER_URL
is the value of the issuer
property and the AUTH0_AUDIENCE
is the value of the audience
property, which is the same as the identifier that you created earlier.
Create an Authorization Module
Auth0 will manage users and credentials for you. To run the authorization process within the boundaries of NestJS, you’ll use Passport. Although Passport is a Node.js authentication library, NestJS offers a Passport utility module that makes it easy to integrate Passport in your application and use some of its available methods to carry out the authorization process.
As an alternative, you can use a NestJS middleware function with
express-jwt
to perform the authorization step.
As explained in the introduction, the Identity and Access Management (IAM) flow for this application architecture is as follows:
-
Users will start by authenticating with a username and password managed by Auth0.
-
Once authenticated, the Auth0 authentication server issues a JWT (JSON Web Token) to the client called an access token.
-
The access token has information on what the client application can do in your API on behalf of an authenticated user: it represents authorization to access data. As such, the client application must include the access token in the authorization header of every request it makes to protected endpoints.
⚠️ While the access token is a byproduct of the authentication process, it does not prove user identity or authentication. It’s crucial to secure the access token properly as anyone holding the token can use it to access the APIs, which is why it’s also known as a bearer token.
- The server validates the access token. If valid, the server determines if the token bearer can access the requested endpoint using information within the token.
To put authorization in effect, you will create guards to protect endpoints that write to the store. These guards will reject any request that doesn’t contain a valid access token. You will bundle all of your authorization logic in a module named AuthzModule
.
As before, use the NestJS CLI to create your new authorization module:
nest generate module authz
Just like that, NestJS creates an authz
directory under the src
directory and places an authz.module.ts
file within it that defines the basic structure for AuthzModule
. If you inspect the AppModule
definition, you’ll see that NestJS has added AuthzModule
to its imports
array, which exposes the functionality of AuthzModule
throughout your application:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ItemsModule } from './items/items.module';
import { AuthzModule } from './auth/authz.module';
@Module({
imports: [ItemsModule, AuthzModule],
controllers: [],
providers: [],
})
export class AppModule {}
You are now ready to set up Passport and get Auth0 to work for you!
Use Passport with NestJS
Passport.js offers different authentication mechanisms, known as Strategies, to cater to the unique authentication requirements each application has. Passport packages Strategies as individual modules, and you can choose which Strategies to employ, without creating unnecessary dependencies. The @nestjs/passport
module wraps these strategies into idiomatic NestJS constructs.
For your application, you’ll create a JSON Web Token (JWT) Passport Strategy that you’ll bundle within AuthzModule
. To start, install the following dependencies:
npm i passport @nestjs/passport passport-jwt jwks-rsa
Here’s a breakdown of what these packages do:
-
passport
: Express-compatible authentication middleware for Node.js. -
@nestjs/passport
: The Passport utility module for Nest. -
passport-jwt
: Passport Strategy for authenticating with a JSON Web Token (JWT). -
jwks-rsa
: A library to retrieve RSA signing keys from a JWKS (JSON Web Key Set) endpoint.
In reality, Passport acts more like an authentication framework than a library. It abstracts the authentication process into a series of standard steps that are customized based on the Strategy that you are implementing. You can use some of these steps to perform authorization tasks such as validating the access token and attaching its information into the Request
object.
These are the key elements of Passport that apply to the authorization process:
-
Configuration options for the Strategy.
-
A verify callback, which has the purpose of finding the user that possesses a set of credentials. When Passport validates a request, it parses the credentials or any other identity-related information contained in the request. It then invokes the verify callback with the authentication data as arguments, such as an access token, that your application can then consume.
Let’s see this in action.
To configure a JwtStrategy
Passport Strategy, you need two values from your .env
file that you’ll load using dotenv.config()
when the Strategy initializes: AUTH0_ISSUER_URL
and AUTH0_AUDIENCE
.
Next, create a jwt.strategy.ts
file under the src/authz
directory:
touch src/authz/jwt.strategy.ts
Update this file as follows:
// src/authz/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { passportJwtSecret } from 'jwks-rsa';
import * as dotenv from 'dotenv';
dotenv.config();
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `${process.env.AUTH0_ISSUER_URL}.well-known/jwks.json`,
}),
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
audience: process.env.AUTH0_AUDIENCE,
issuer: `${process.env.AUTH0_ISSUER_URL}`,
algorithms: ['RS256'],
});
}
validate(payload: unknown): unknown {
return payload;
}
}
You can create a Passport Strategy by extending the abstract class returned by the PassportStrategy
function from the @nestjs/passport
module. This function takes as argument the Strategy
that you want to implement, which in this case is the JWT Strategy imported from passport-jwt
.
The Strategy options passed through the super()
call within the constructor
let you parse JWT-formatted access tokens and configure your API to accept RS256
signed tokens.
With the NestJS wrapper applied, the verify callback becomes the validate()
method. In this case, the implementation of validate()
is straightforward because Auth0 handles all of the user authentication tasks for you in your tenant. By the time your application calls validate()
, Auth0 has already determined the identity of the logged-in user and passes data about that user within the payload
object. Your application then attaches the payload
to the request
object, which you can access throughout the request-response cycle through controllers and middleware.
The next step is to configure Passport with JwtStrategy
and to register it with AuthzModule
. Open src/authz/authz.module.ts
and update it as follows:
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [PassportModule.register({ defaultStrategy: 'jwt' })],
providers: [JwtStrategy],
exports: [PassportModule],
})
export class AuthzModule {}
You import the PassportModule
, which is a NestJS wrapper on Passport, and register jwt
as its default Strategy.
You have the mechanics in place to receive an access token and verify if the request is authorized or not. The next step is to create guards that will block unauthorized requests to protected endpoints using the @UseGuards()
decorator.
Create Endpoint Guards with NestJS
Update src/items/items.controller.ts
as follows:
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
UseGuards,
} from '@nestjs/common';
import { ItemsService } from './items.service';
import { Items } from '../items';
import { Item } from '../item';
import { AuthGuard } from '@nestjs/passport';
@Controller('items')
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
@Get()
async findAll(): Promise<Items> {
return this.itemsService.findAll();
}
@Get(':id')
async find(@Param('id') id: number): Promise<Item> {
return this.itemsService.find(id);
}
@UseGuards(AuthGuard('jwt'))
@Post()
async create(@Body('item') item: Item): Promise<void> {
this.itemsService.create(item);
}
@UseGuards(AuthGuard('jwt'))
@Put()
async update(@Body('item') item: Item): Promise<void> {
this.itemsService.update(item);
}
@UseGuards(AuthGuard('jwt'))
@Delete(':id')
async delete(@Param('id') id: number): Promise<void> {
this.itemsService.delete(id);
}
}
Let’s look at the POST items/
endpoint:
@UseGuards(AuthGuard('jwt'))
@Post()
async create(@Body('item') item: Item): Promise<void> {
this.itemsService.create(item);
}
With @UseGuards(AuthGuard('jwt'))
, you are using an AuthGuard
that @nestjs/passport
automatically provisioned for your AuthzModule
when you configured the passport-jwt
module in your Passport Strategy, JwtStrategy
. The @UseGuards()
decorator references this guard by its default name, jwt
, which matches the value of the defaultStrategy
you registered for the PassportModule
within the AuthzModule
definition.
When you make a request to the POST items/
protected endpoint, the guard automatically invokes your JwtStrategy
configuration logic and blocks the request if the access token is not valid or it’s absent.
Try it out:
curl -X POST -H 'Content-Type: application/json' -d '{
"item": {
"name": "Salad",
"price": 4.99,
"description": "Fresh",
"image": "https://cdn.auth0.com/blog/whatabyte/salad-sm.png"
}
}' http://localhost:7000/items -i
It won’t work. You’ll get a 401 Unauthorized
response indicating that the server has rejected the request due to not having the authorization required by the target resource, the POST items/
endpoint.
However, making a request to the GET items/
endpoint works:
curl http://localhost:7000/items -i
To test the authentication feature of your application, you’ll need a valid access token. A client, such as a Single-Page Application (SPA), would get the access token by performing a login and then passing the access token in an authorization header to your API. You don’t have a client built yet, but you can use this Demo Client: WHATABYTE Dashboard.
Register a Client Application with Auth0
WHATABYTE Dashboard is a client application that can consume the API that you have created so far. To use Auth0 to manage the authentication process of a client application, you need to register that client application with Auth0.
Once registered, Auth0 provides you with a Client ID to identify that client application when it interacts with the Auth0 service, such as when it accesses the Auth0 APIs to perform a login transaction or get user-profile information. Auth0 also provides you with a Domain value to use as the base URL to make requests to those Auth0 APIs.
The process of registering a client application with Auth0 is quite easy:
-
Open the Auth0 Applications section of the Auth0 Dashboard.
-
Click on the Create Application button.
-
Provide a Name value such as
WAB Dashboard
. -
Choose Single Page Web Applications as the application type.
-
Click on the Create button.
All done. A page should load, presenting you with all the configuration options and information about this newly configured client application. Click on the Settings tab to find the Client ID and Domain values.
Now, head to the WAB Dashboard client and enter these two values in the Auth0 Demo Settings form.
Notice that the Auth0 Demo Settings form has three other values. «Your API Base URL» should map to the domain where your NestJS server is running. The other two, «Auth0 Callback URL» and «Auth0 API Audience», are values that Auth0 also requires for your client application to interact with its authentication service securely.
Verify that these other fields are set to these values:
Auth0 Callback URL
https://dashboard-v1.whatabyte.app/home
API Audience
https://menu-api.demo.com
API URL
http://localhost:7000
For now, click on the «Save» button and then click on the «Settings» tab of the WAB Dashboard to reaccess the configuration values as you’ll need them in the next section.
Connect a Client Application With Auth0
Head back to the Settings tab of your Auth0 client application and update the following setting fields:
Allowed Callback URLs
Use the value of Auth0 Callback URL from the Auth0 Demo Settings form:
https://dashboard-v1.whatabyte.app/home
After your users authenticate, Auth0 will only redirect your users to any of the URLs listed here. You can specify multiple valid URLs by comma-separating them (typically to handle different environments like QA or testing). Make sure to specify the protocol, http:// or https://, otherwise the callback may fail in some cases.
Allowed Web Origins
Use the following:
https://dashboard-v1.whatabyte.app
This field holds a comma-separated list of allowed origins for use with web message response mode, which makes it possible to log in using a pop-up, as you’ll soon see in the next section.
Allowed Logout URLs
Use the following:
https://dashboard-v1.whatabyte.app/home
This field holds the URLs that Auth0 should use to redirect your users after they log out of your application. The demo client has been configured to use the provided value for redirecting users.
Once these values are in place, scroll to the bottom and click on the Save Changes button.
You are ready to start consuming your NestJS API with this provided demo client.
Enable CORS in NestJS
To test the connection between the client and the server, click on the Menu tab of the WAB Dashboard. You will see the following error:
Network Error. Unable to retrieve menu information.
If you open the browser developer console, you’ll notice that a CORS error has occurred. However, fixing this is easy with NestJS.
Open src/main.ts
and update it as follows:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
import { ValidationPipe } from '@nestjs/common';
dotenv.config();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
app.useGlobalPipes(
new ValidationPipe({
disableErrorMessages: true,
}),
);
await app.listen(process.env.PORT);
}
bootstrap();
You call the enableCors()
method of your app
instance to allow another domain to request resources.
Head back to the WAB Dashboard Menu View and refresh it. This time around, the three items available in your NestJS store load up, and the CORS error disappears from the developer console.
Sign In
In the WAB Dashboard, click on the Sign In button. Since this may be the first user you are adding to Auth0, go ahead and click on the Sign Up tab in the pop-up that comes up and provide an email and password to register your new user.
Once signed in, the user interface changes:
-
The Sign In button becomes a Sign Out button
-
A user tab shows up below the Sign Out button.
Click on the User tab, and you’ll see a custom page with your email as the title.
The WAB Dashboard caters to two types of users: regular users who can only read data and users with a menu-admin
role who can read and write data. This role allows the user to create, update, and delete menu items using the WAB Dashboard.
In the next section, you are going to create the menu-admin
role, associate permissions with it, and assign it to a new user that you’ll create through the Auth0 Dashboard. This privileged user can unlock the admin features of the WAB Dashboard.
Next Step: I’ve secured my NestJS app with Auth0 authorization
I ran into an issue
Table of Contents
In the previous article, I have explained how to deploy a Node.js application to Heroku.
In this tutorial, we will be making use of the endpoint created there and see if we can use it in our React project.
Project Setup
Let’s create a React project using the following command:
1npx create-react-app react-cors
Now update the App.js
with the following code:
App.js
1import { useEffect, useState } from "react"
2import "./App.css"
3
4function App() {
5 const [message, setMessage] = useState("")
6 useEffect(() => {
7 fetch("https://nodejs-using-github.herokuapp.com/")
8 .then(response => response.json())
9 .then(data => {
10 setMessage(data.message)
11 })
12 .catch(err => console.log(err))
13 }, [])
14 return <div className="App">{message ? message : "Loading.."}</div>
15}
16
17export default App
Here we have a local state called message
, which we show to the user.
If the message is empty, then we display them with a loading text.
When the component is mounted (useEffect), we make a call to the API endpoint and fetch the message.
Now let’s run this and see if it works:
You will see that only «Loading..» text is displayed and the message never loads.
If we inspect the page and see the console, we will see the following error:
Access to fetch at 'https://nodejs-using-github.herokuapp.com/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
In the next sections, we will see what is CORS and how to fix this error.
What is CORS (Cross-Origin Resource Sharing)?
CORS stands for Cross-Origin Resource Sharing,
which is an HTTP header based mechanism that helps the server to tell the browser,
from which all domain requests can be made (except the same domain).
That is, in our case, the Node.js server hosted at https://nodejs-using-github.herokuapp.com/
,
does not tell the browser that request can be made from http://localhost:3000
.
When this happens, your browser will throw an error as seen earlier.
Why CORS (Cross-Origin Resource Sharing)?
The next question that would come to your mind is why do we really need this mechanism.
Imagine you are logged into your bank account or any social media website, then you visit a malicious website.
This malicious website could run some scripts in the background to make API calls to your banking or social media to get your personal details.
To prevent this, your browser checks if the request to the banking or social media server can be made from the malicious website and throws the CORS error.
So CORS exists to share certain resources between trusted third-parties (across different origins/domains), hence the name Cross-Origin Resource Sharing.
How to configure CORS in Node.js
Since we are clear about what and why is CORS required, let’s see how to enable CORS in the Node.js application.
You may clone the Node.js code from this repo.
Once the project is cloned, open it in your code editor and install cors package.
Now open index.js
and update it with the following code:
index.js
1const express = require("express")
2const cors = require("cors")
3const app = express()
4const port = process.env.PORT || 3000
5
6const whitelist = ["http://localhost:3000"]
7const corsOptions = {
8 origin: function (origin, callback) {
9 if (!origin || whitelist.indexOf(origin) !== -1) {
10 callback(null, true)
11 } else {
12 callback(new Error("Not allowed by CORS"))
13 }
14 },
15 credentials: true,
16}
17app.use(cors(corsOptions))
18
19app.get("/", (req, res) => {
20 res.send({ message: "Hello World!" })
21})
22
23app.listen(port, () => {
24 console.log(`Example app listening at Port: ${port}`)
25})
Here we check if the origin (client’s domain) is in the whitelist, then we tell the clients that requests can be made.
If it is not in the list then we throw an error saying the client is not allowed to make CORS requests to this server.
The domain should not have any trailing slashes (/)
We can deploy the changes to Heroku and see if this works.
Now if you reload your page, you should be able to see the message.
You will also see that a response header called Access-Control-Allow-Origin
has been added with the value http://localhost:3000
.
Making CORS domains configurable
If you have multiple client origins to be connected to you, and you want them to be configurable, you can do so by using environment variables:
index.js
1const express = require("express")
2const cors = require("cors")
3const app = express()
4const port = process.env.PORT || 3000
5
6const domainsFromEnv = process.env.CORS_DOMAINS || ""
7
8const whitelist = domainsFromEnv.split(",").map(item => item.trim())
9
10const corsOptions = {
11 origin: function (origin, callback) {
12 if (!origin || whitelist.indexOf(origin) !== -1) {
13 callback(null, true)
14 } else {
15 callback(new Error("Not allowed by CORS"))
16 }
17 },
18 credentials: true,
19}
20app.use(cors(corsOptions))
21
22app.get("/", (req, res) => {
23 res.send({ message: "Hello World!" })
24})
25
26app.listen(port, () => {
27 console.log(`Example app listening at Port: ${port}`)
28})
Testing environment variables locally
To test environment variables locally, you can install the package called dotenv
:
Now create a file called .env
in the root directory of your project with the domains:
1CORS_DOMAINS = http://localhost:3000, http://localhost:3001, https://example.com
Update index.js
to use the dotenv
package:
index.js
1const express = require("express")
2const cors = require("cors")
3const app = express()
4const port = process.env.PORT || 3000
5
6if (process.env.NODE_ENV !== "production") {
7 require("dotenv").config()
8}
9
10const domainsFromEnv = process.env.CORS_DOMAINS || ""
11
12const whitelist = domainsFromEnv.split(",").map(item => item.trim())
13
14const corsOptions = {
15 origin: function (origin, callback) {
16 if (!origin || whitelist.indexOf(origin) !== -1) {
17 callback(null, true)
18 } else {
19 callback(new Error("Not allowed by CORS"))
20 }
21 },
22 credentials: true,
23}
24app.use(cors(corsOptions))
25
26app.get("/", (req, res) => {
27 res.send({ message: "Hello World!" })
28})
29
30app.listen(port, () => {
31 console.log(`Example app listening at Port: ${port}`)
32})
Here we made sure that .env
files are loaded only in non-production environments.
It is recommended to store the configurations in the server host rather than in .env files for production.
Remember to add
.env*
to the.gitignore
file so that you don’t accidentally push them to the repo.
Configuring environment files in heroku
With our latest code, we can configure environment files in the heroku settings:
Go to your project settings and click on «Reveal Config Vars». Now you can provide the key and values here and click on «Add»
Once added, you can push your changes and see if the changes work.
If you have liked article, stay in touch with me by following me on twitter.