-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
2,265 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"name": "@y_nk/nestjs-clerk", | ||
"version": "1.0.0", | ||
"description": "auth and permission guards using clerk for nestjs", | ||
"homepage": "https://github.com/y-nk/nonorepo/tree/main/astro-components", | ||
"keywords": [ | ||
"nestjs", | ||
"clerk", | ||
"auth" | ||
], | ||
"author": "Julien Barbay <[email protected]>", | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
"prebuild": "rimraf dist", | ||
"build": "tsc", | ||
"deploy": "npm publish --access public" | ||
}, | ||
"devDependencies": { | ||
"@clerk/express": "^1.3.34", | ||
"@nestjs/common": "^10.4.15", | ||
"@nestjs/core": "^10.4.15", | ||
"rimraf": "^6.0.1", | ||
"typescript": "^5.7.3" | ||
}, | ||
"peerDependencies": { | ||
"@clerk/express": "^1.3.34", | ||
"@nestjs/common": "^10.4.15", | ||
"@nestjs/core": "^10.4.15" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { getAuth } from "@clerk/express"; | ||
import { | ||
ForbiddenException, | ||
Injectable, | ||
type CanActivate, | ||
type ExecutionContext, | ||
} from "@nestjs/common"; | ||
import { Reflector } from "@nestjs/core"; | ||
|
||
@Injectable() | ||
export class ClerkAclGuard implements CanActivate { | ||
constructor(private reflector: Reflector) {} | ||
canActivate(context: ExecutionContext) { | ||
const permission = this.reflector.get<string | undefined>( | ||
"Acl", | ||
context.getHandler(), | ||
); | ||
|
||
const req = context.switchToHttp().getRequest(); | ||
const auth = getAuth(req); | ||
|
||
if (!permission || !auth.userId) return true; | ||
|
||
if (!auth.has({ permission })) throw new ForbiddenException(); | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { clerkMiddleware, getAuth } from "@clerk/express"; | ||
import { | ||
Inject, | ||
Injectable, | ||
UnauthorizedException, | ||
type CanActivate, | ||
type ExecutionContext, | ||
} from "@nestjs/common"; | ||
import { Reflector } from "@nestjs/core"; | ||
|
||
import { CLERK_CONFIG } from "./tokens"; | ||
import type { ClerkConfig } from "./types"; | ||
|
||
@Injectable() | ||
export class ClerkAuthGuard implements CanActivate { | ||
constructor( | ||
private reflector: Reflector, | ||
@Inject(CLERK_CONFIG) private clerkConfig: ClerkConfig, | ||
) {} | ||
async canActivate(context: ExecutionContext) { | ||
const noAuth = [context.getHandler(), context.getClass()].some((target) => | ||
this.reflector.get<boolean>("NoAuth", target), | ||
); | ||
|
||
if (context.getType() !== "http" || noAuth) return true; | ||
|
||
const req = context.switchToHttp().getRequest(); | ||
const res = context.switchToHttp().getResponse(); | ||
const middleware = clerkMiddleware(this.clerkConfig); | ||
|
||
const error = await new Promise<Error | undefined>((resolve) => { | ||
middleware(req, res, resolve); | ||
}); | ||
|
||
const auth = getAuth(req); | ||
|
||
if (error || !auth.userId) throw new UnauthorizedException(); | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { getAuth } from "@clerk/express"; | ||
import { | ||
createParamDecorator, | ||
SetMetadata, | ||
type CustomDecorator, | ||
type ExecutionContext, | ||
} from "@nestjs/common"; | ||
|
||
export function NoAuth(): CustomDecorator { | ||
return SetMetadata("NoAuth", true); | ||
} | ||
|
||
export function Acl(permission: string): CustomDecorator { | ||
return SetMetadata("Acl", permission); | ||
} | ||
|
||
export const Clerk = createParamDecorator( | ||
(_: unknown, ctx: ExecutionContext) => { | ||
const req = ctx.switchToHttp().getRequest(); | ||
return getAuth(req); | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export * from "./acl.guard"; | ||
export * from "./auth.guard"; | ||
export * from "./decorators"; | ||
export * from "./module"; | ||
export * from "./tokens"; | ||
export * from "./types"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { clerkClient } from "@clerk/express"; | ||
import { Module, type Provider } from "@nestjs/common"; | ||
import { APP_GUARD } from "@nestjs/core"; | ||
|
||
import { ClerkAclGuard } from "./acl.guard"; | ||
import { ClerkAuthGuard } from "./auth.guard"; | ||
import { CLERK_CLIENT, CLERK_CONFIG } from "./tokens"; | ||
import type { ClerkConfig } from "./types"; | ||
|
||
const providers: Provider[] = [ | ||
ClerkAuthGuard, | ||
ClerkAclGuard, | ||
{ | ||
provide: APP_GUARD, | ||
useExisting: ClerkAuthGuard, | ||
}, | ||
{ | ||
provide: APP_GUARD, | ||
useExisting: ClerkAclGuard, | ||
}, | ||
{ | ||
provide: CLERK_CLIENT, | ||
useValue: clerkClient, | ||
}, | ||
]; | ||
|
||
@Module({}) | ||
export class ClerkModule { | ||
static register(clerkConfig?: ClerkConfig) { | ||
return { | ||
module: ClerkModule, | ||
providers: [ | ||
{ | ||
provide: CLERK_CONFIG, | ||
useValue: clerkConfig ?? {}, | ||
}, | ||
...providers, | ||
], | ||
exports: [ClerkAuthGuard, ClerkAclGuard], | ||
}; | ||
} | ||
|
||
static async registerAsync(provider: Provider<ClerkConfig>) { | ||
return { | ||
module: ClerkModule, | ||
providers: [ | ||
{ | ||
provide: CLERK_CONFIG, | ||
...provider, | ||
}, | ||
...providers, | ||
], | ||
exports: [ClerkAuthGuard, ClerkAclGuard], | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export const CLERK_CONFIG = "CLERK_CONFIG"; | ||
export const CLERK_CLIENT = "CLERK_CLIENT"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { AuthObject, clerkMiddleware } from "@clerk/express"; | ||
|
||
export type ClerkConfig = Parameters<typeof clerkMiddleware>[0]; | ||
|
||
// & is to enforce the discriminating union since the type is not available at @clerk/express | ||
export type ClerkAuth = AuthObject & { sessionId: string }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
{ | ||
"compilerOptions": { | ||
"rootDir": "src", | ||
"outDir": "dist", | ||
|
||
"module": "nodenext", | ||
"moduleResolution": "nodenext", | ||
"lib": ["esnext"], | ||
"target": "esnext", | ||
|
||
"allowUnreachableCode": false, | ||
"alwaysStrict": true, | ||
"declaration": true, | ||
"emitDecoratorMetadata": true, | ||
"esModuleInterop": true, | ||
"experimentalDecorators": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": false, | ||
"noImplicitOverride": true, | ||
"noImplicitReturns": true, | ||
"noImplicitThis": false, | ||
"noUncheckedIndexedAccess": true, | ||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"removeComments": true, | ||
"resolveJsonModule": true, | ||
"skipLibCheck": true, | ||
"strict": true, | ||
"strictPropertyInitialization": false | ||
}, | ||
"exclude": ["node_modules", "dist"] | ||
} |