Using npm:
npm install passport passport-jwt @nestjs/passport
npm install --save-dev @types/passport-jwt
Using yarn:
yarn add passport passport-jwt @nestjs/passport
yarn add -D @types/passport-jwt
Using npm:
npm install nestjs-supabase-auth-v2
Using yarn:
yarn add nestjs-supabase-auth-v2
In this example, I'm passing supabase related options through dotenv and env-cmd package.
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt } from 'passport-jwt';
import { SupabaseAuthStrategy } from 'nestjs-supabase-auth';
@Injectable()
export class SupabaseStrategy extends PassportStrategy(
SupabaseAuthStrategy,
'supabase',
) {
public constructor() {
super({
supabaseUrl: process.env.SUPABASE_URL,
supabaseKey: process.env.SUPABASE_KEY,
supabaseOptions: {},
supabaseJwtSecret: process.env.SUPABASE_JWT_SECRET,
extractor: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
}
async validate(payload: any): Promise<any> {
super.validate(payload);
}
authenticate(req) {
super.authenticate(req);
}
}
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthResolver } from './auth.resolver';
import { SupabaseStrategy } from './supabase.strategy';
import { PassportModule } from '@nestjs/passport';
import supabase from '../../supabase';
@Module({
imports: [PassportModule],
providers: [
AuthService,
AuthResolver,
SupabaseStrategy,
],
exports: [AuthService, SupabaseStrategy],
})
export class AuthModule {}
gql-auth-guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
@Injectable()
export class GqlAuthGuard extends AuthGuard('supabase') {
getRequest(context: ExecutionContext) {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}
auth.resolver - You can use the guard in any resolver.
import { UseGuards } from '@nestjs/common';
import { Args, Context, Query, Mutation, Resolver } from '@nestjs/graphql';
import { GqlAuthGuard } from 'src/common/guards/auth.guard';
import { CurrentUser } from '../../common/decorators/current-user';
import { IUser } from '../user/models/user.interface';
import { AuthService } from './auth.service';
import { SignupInput } from './dto/signup.input';
import { AuthResult } from './models/auth-result';
import { AuthUser as SupabaseAuthUser } from '@supabase/supabase-js';
import { LoginInput } from './dto/login.input';
@Resolver()
export class AuthResolver {
constructor(private readonly authService: AuthService) {}
@Query(() => IUser, { name: 'viewer' })
@UseGuards(GqlAuthGuard)
async me(@CurrentUser() user: SupabaseAuthUser) {
return user;
}
...
}
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
import { GqlExecutionContext } from "@nestjs/graphql";
export const CurrentUser = createParamDecorator(
(_data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.user;
},
);