Skip to content

Commit 3b2a362

Browse files
docs(): add instructions on how to enable auth globally
1 parent f4c2122 commit 3b2a362

File tree

1 file changed

+83
-73
lines changed

1 file changed

+83
-73
lines changed

content/security/authentication.md

Lines changed: 83 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Replace the default contents of these generated files as shown below. For our sa
5858
@@filename(users/users.service)
5959
import { Injectable } from '@nestjs/common';
6060

61+
// This should be a real class/interface representing a user entity
6162
export type User = any;
6263

6364
@Injectable()
@@ -73,11 +74,6 @@ export class UsersService {
7374
},
7475
{
7576
userId: 2,
76-
username: 'chris',
77-
password: 'secret',
78-
},
79-
{
80-
userId: 3,
8177
username: 'maria',
8278
password: 'guess',
8379
},
@@ -102,11 +98,6 @@ export class UsersService {
10298
},
10399
{
104100
userId: 2,
105-
username: 'chris',
106-
password: 'secret',
107-
},
108-
{
109-
userId: 3,
110101
username: 'maria',
111102
password: 'guess',
112103
},
@@ -262,7 +253,7 @@ export class LocalStrategy extends PassportStrategy(Strategy) {
262253

263254
We've followed the recipe described earlier for all Passport strategies. In our use case with passport-local, there are no configuration options, so our constructor simply calls `super()`, without an options object.
264255

265-
> info **Hint** We can pass an options object in the call to `super()` to customize the behavior of the passport strategy. In this example, the passport-local strategy by default expects properties called `username` and `password` in the request body. Pass an options object to specify different property names, for example: `super({{ '{' }} usernameField: 'email' {{ '}' }})`. See the [Passport documentation](http://www.passportjs.org/docs/configure/) for more information.
256+
> info **Hint** We can pass an options object in the call to `super()` to customize the behavior of the passport strategy. In this example, the passport-local strategy by default expects properties called `username` and `password` in the request body. Pass an options object to specify different property names, for example: `super({{ '{' }} usernameField: 'email' {{ '}' }})`. See the [Passport documentation](http://www.passportjs.org/docs/configure/) for more information.
266257
267258
We've also implemented the `validate()` method. For each strategy, Passport will call the verify function (implemented with the `validate()` method in `@nestjs/passport`) using an appropriate strategy-specific set of parameters. For the local-strategy, Passport expects a `validate()` method with the following signature: `validate(username: string, password:string): any`.
268259

@@ -789,35 +780,92 @@ Note that in the `AuthModule`, we configured the JWT to have an expiration of `6
789780

790781
We've now completed our JWT authentication implementation. JavaScript clients (such as Angular/React/Vue), and other JavaScript apps, can now authenticate and communicate securely with our API Server. You can find a complete version of the code in this chapter [here](https://github.com/nestjs/nest/tree/master/sample/19-auth-jwt).
791782

792-
<app-banner-enterprise></app-banner-enterprise>
783+
#### Extending guards
784+
785+
In most cases, using a provided `AuthGuard` class is sufficient. However, there might be use-cases when you would like to simply extend the default error handling or authentication logic. For this, you can extend the built-in class and override methods within a sub-class.
786+
787+
```typescript
788+
import {
789+
ExecutionContext,
790+
Injectable,
791+
UnauthorizedException,
792+
} from '@nestjs/common';
793+
import { AuthGuard } from '@nestjs/passport';
794+
795+
@Injectable()
796+
export class JwtAuthGuard extends AuthGuard('jwt') {
797+
canActivate(context: ExecutionContext) {
798+
// Add your custom authentication logic here
799+
// for example, call super.logIn(request) to establish a session.
800+
return super.canActivate(context);
801+
}
802+
803+
handleRequest(err, user, info) {
804+
// You can throw an exception based on either "info" or "err" arguments
805+
if (err || !user) {
806+
throw err || new UnauthorizedException();
807+
}
808+
return user;
809+
}
810+
}
811+
```
812+
813+
#### Enable authentication globally
793814

794-
#### Default strategy
815+
If the vast of your endpoints should be protected by default, you can register the authentication guard as a [global guard](/guards#binding-guards) and instead of using `@UseGuards()` decorator on top of each controller, you could simply flag which routes should be public.
795816

796-
In our `AppController`, we pass the name of the strategy in the `AuthGuard()` function. We need to do this because we've introduced **two** Passport strategies (passport-local and passport-jwt), both of which supply implementations of various Passport components. Passing the name disambiguates which implementation we're linking to. When multiple strategies are included in an application, we can declare a default strategy so that we no longer have to pass the name in the `AuthGuard` function if using that default strategy. Here's how to register a default strategy when importing the `PassportModule`. This code would go in the `AuthModule`:
817+
First, register the `JwtAuthGuard` as a global guard using the following construction (in any module):
797818

798819
```typescript
799-
import { Module } from '@nestjs/common';
800-
import { AuthService } from './auth.service';
801-
import { LocalStrategy } from './local.strategy';
802-
import { UsersModule } from '../users/users.module';
803-
import { PassportModule } from '@nestjs/passport';
804-
import { JwtModule } from '@nestjs/jwt';
805-
import { jwtConstants } from './constants';
806-
import { JwtStrategy } from './jwt.strategy';
820+
providers: [
821+
{
822+
provide: APP_GUARD,
823+
useClass: JwtAuthGuard,
824+
},
825+
],
826+
```
807827

808-
@Module({
809-
imports: [
810-
PassportModule.register({ defaultStrategy: 'jwt' }),
811-
JwtModule.register({
812-
secret: jwtConstants.secret,
813-
signOptions: { expiresIn: '60s' },
814-
}),
815-
UsersModule,
816-
],
817-
providers: [AuthService, LocalStrategy, JwtStrategy],
818-
exports: [AuthService],
819-
})
820-
export class AuthModule {}
828+
With this in place, Nest will automatically bind `JwtAuthGuard` to all endpoints.
829+
830+
Now we must provide a mechanism for declaring routes as public. For this, we can create a custom decorator using the `SetMetadata` decorator factory function.
831+
832+
```typescript
833+
import { SetMetadata } from '@nestjs/common';
834+
835+
export const IS_PUBLIC_KEY = 'isPublic';
836+
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
837+
```
838+
839+
In the file above, we exported two constants. One being our metadata key named `IS_PUBLIC_KEY`, and the other being our new decorator itself that we’re going to call `Public` (we can alternatively name it `SkipAuth`).
840+
841+
Now that we have a custom `@Public()` decorator, we can use it to decorate any method, as follows:
842+
843+
```typescript
844+
@Public()
845+
@Get()
846+
findAll() {
847+
return [];
848+
}
849+
```
850+
851+
Lastly, we need the `JwtAuthGuard` to return `true` when the `"isPublic"` metadata is found. For this, we'll use the `Reflector` class (read more [here](/guards#putting-it-all-together)).
852+
853+
```typescript
854+
@Injectable()
855+
export class JwtAuthGuard extends AuthGuard('jwt') {
856+
constructor(private reflector: Reflector) {}
857+
858+
canActivate(context: ExecutionContext) {
859+
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
860+
context.getHandler(),
861+
context.getClass(),
862+
]);
863+
if (isPublic) {
864+
return true;
865+
}
866+
return super.canActivate(context);
867+
}
868+
}
821869
```
822870

823871
#### Request-scoped strategies
@@ -859,36 +907,6 @@ async validate(
859907

860908
In the example above, the `resolve()` method will asynchronously return the request-scoped instance of the `AuthService` provider (we assumed that `AuthService` is marked as a request-scoped provider).
861909

862-
#### Extending guards
863-
864-
In most cases, using a provided `AuthGuard` class is sufficient. However, there might be use-cases when you would like to simply extend the default error handling or authentication logic. For this, you can extend the built-in class and override methods within a sub-class.
865-
866-
```typescript
867-
import {
868-
ExecutionContext,
869-
Injectable,
870-
UnauthorizedException,
871-
} from '@nestjs/common';
872-
import { AuthGuard } from '@nestjs/passport';
873-
874-
@Injectable()
875-
export class JwtAuthGuard extends AuthGuard('jwt') {
876-
canActivate(context: ExecutionContext) {
877-
// Add your custom authentication logic here
878-
// for example, call super.logIn(request) to establish a session.
879-
return super.canActivate(context);
880-
}
881-
882-
handleRequest(err, user, info) {
883-
// You can throw an exception based on either "info" or "err" arguments
884-
if (err || !user) {
885-
throw err || new UnauthorizedException();
886-
}
887-
return user;
888-
}
889-
}
890-
```
891-
892910
#### Customize Passport
893911

894912
Any standard Passport customization options can be passed the same way, using the `register()` method. The available options depend on the strategy being implemented. For example:
@@ -935,14 +953,6 @@ export class GqlAuthGuard extends AuthGuard('jwt') {
935953
}
936954
```
937955
938-
To use the above construct, be sure to pass the request (`req`) object as part of the context value in the GraphQL Module settings:
939-
940-
```typescript
941-
GraphQLModule.forRoot({
942-
context: ({ req }) => ({ req }),
943-
});
944-
```
945-
946956
To get the current authenticated user in your graphql resolver, you can define a `@CurrentUser()` decorator:
947957
948958
```typescript

0 commit comments

Comments
 (0)