Skip to content

Commit

Permalink
fix: add stricts eslint rules
Browse files Browse the repository at this point in the history
  • Loading branch information
sgomez committed Nov 25, 2023
1 parent 53020d2 commit d6702fb
Show file tree
Hide file tree
Showing 15 changed files with 119 additions and 46 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-sort": "^2.11.0",
"eslint-plugin-unicorn": "^49.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"husky": "^8.0.3",
"jest": "^29.7.0",
Expand All @@ -57,6 +58,7 @@
"jest": true
},
"extends": [
"plugin:unicorn/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:jest/recommended"
Expand Down
4 changes: 2 additions & 2 deletions src/auth/access-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface AccessMapEntry {
}

interface AccessMapPattern {
roles: string[] | InternalRole[] | null
roles: string[] | InternalRole[]
}

export default class AccessMap {
Expand All @@ -32,6 +32,6 @@ export default class AccessMap {
}
}

return { roles: null }
return { roles: [] }
}
}
4 changes: 2 additions & 2 deletions src/auth/authorized.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export default function Authorized(config: NextAuthFirewallConfig) {

const security = new Security({ accessControlRules })

return (params: { auth: Session | null; request: NextRequest }) =>
security.authorized(params)
return (parameters: { auth: Session | null; request: NextRequest }) =>
security.authorized(parameters)
}
4 changes: 2 additions & 2 deletions src/auth/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ class Security {
this.accessMap = this.createAccessMap(config)
}

async authorized(params: {
async authorized(parameters: {
auth: Session | null
request: Request | NextRequest
}): Promise<boolean> {
const { auth, request } = params
const { auth, request } = parameters
const { roles } = this.accessMap.pattern(request)

if (!roles || roles.includes('PUBLIC_ACCESS')) {
Expand Down
8 changes: 6 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { NextAuthFirewallConfig } from './types'

export type * from './types'

type AppRouteHandlers = Record<'POST', (req: NextRequest) => Promise<Response>>
type AppRouteHandlers = Record<
'POST',
(request: NextRequest) => Promise<Response>
>

export interface NextAuthFirewallResult extends NextAuthResult {
firewallHandler: AppRouteHandlers
Expand All @@ -18,7 +21,8 @@ export default function NextAuthFirewall(
): NextAuthFirewallResult {
const { accessControl, callbacks = {}, ...rest } = config

const firewallHttpHandler = (req: NextRequest) => AuthFirewall(req, config)
const firewallHttpHandler = (request: NextRequest) =>
AuthFirewall(request, config)

const nextAuth = NextAuth({
callbacks: {
Expand Down
9 changes: 4 additions & 5 deletions src/request/is-internal-request.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { NextRequest } from 'next/server'
import * as process from 'process'

const INTERNAL_ADDRESS = ['127.0.0.1', '::ffff:127.0.0.1', '::1']
const INTERNAL_ADDRESS = new Set(['127.0.0.1', '::ffff:127.0.0.1', '::1'])

function isInternalRequest(request: NextRequest): boolean {
const authorization = request.headers.get('authorization')
const forwardedFor = request.headers.get('x-forwarded-for')

if (!process.env.AUTH_SECRET) {
if (!process.env?.AUTH_SECRET) {
console.warn('Set AUTH_SECRET environment variable')

return false
}

if (
authorization !== `Bearer ${process.env.AUTH_SECRET}` ||
authorization !== `Bearer ${process.env?.AUTH_SECRET}` ||
!forwardedFor ||
!INTERNAL_ADDRESS.includes(forwardedFor) ||
!INTERNAL_ADDRESS.has(forwardedFor) ||
request.method.toUpperCase() !== 'POST'
) {
console.warn(
Expand Down
2 changes: 1 addition & 1 deletion src/request/request-matcher/path-request-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export default class PathRequestMatcher implements RequestMatcherInterface {
matches(request: Request | NextRequest): boolean {
const url = new URL(request.url)

return !!url.pathname.match(this.pathname)
return this.pathname.test(url.pathname)
}
}
2 changes: 1 addition & 1 deletion src/request/roles-for-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Session } from '@auth/core/types'
const USER_PROVIDER_URL = `${process.env.AUTH_URL as string}/firewall/`

export default async function rolesForSession(
auth: Session | null,
auth?: Session | null,
): Promise<string[]> {
const email = auth?.user?.email

Expand Down
2 changes: 1 addition & 1 deletion tests/auth/access-map.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,6 @@ describe('AccessMap', () => {
const pattern = accessMap.pattern(request)

// Assert
expect(pattern).toEqual({ roles: null })
expect(pattern).toEqual({ roles: [] })
})
})
6 changes: 3 additions & 3 deletions tests/auth/auth-firewall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import AuthFirewall from '../../src/auth/auth-firewall'
import RequestExamples from '../features/request.examples'

describe('AuthFirewall', () => {
const env = process.env
const environment = process.env

beforeEach(() => {
process.env = {
...env,
...environment,
AUTH_SECRET: 'AUTH_SECRET',
}
})

afterEach(() => {
process.env = env
process.env = environment
})

it('should return user roles from email', async () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/auth/security.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import RequestExamples from '../features/request.examples'

jest.mock('../../src/request/roles-for-session')

// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-var-requires,unicorn/prefer-module
const rolesForSession = require('../../src/request/roles-for-session').default

describe('Security', () => {
Expand Down
36 changes: 19 additions & 17 deletions tests/features/request.examples.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
import { NextRequest } from 'next/server'

export default class RequestExamples {
static basic(path: string = '/', options: { method?: string } = {}) {
const RequestExamples = {
basic: (path: string = '/', options: { method?: string } = {}) => {
return new NextRequest(new URL(`http://internal${path}`), options)
}
},

static internalAuthorized() {
return new NextRequest(new URL('http://internal'), {
headers: {
authorization: 'Bearer AUTH_SECRET',
'x-forwarded-for': '::1',
},
method: 'POST',
})
}

static externalAuthorized() {
externalAuthorized: () => {
return new NextRequest(new URL('http://internal'), {
headers: {
authorization: 'Bearer AUTH_SECRET',
'x-forwarded-for': '192.168.1.1',
},
method: 'POST',
})
}
},

static firewallRequest() {
firewallRequest: () => {
return {
headers: new Headers({
authorization: 'Bearer AUTH_SECRET',
Expand All @@ -34,5 +24,17 @@ export default class RequestExamples {
json: jest.fn().mockResolvedValue({ email: '[email protected]' }),
method: 'POST',
} as unknown as NextRequest
}
},

internalAuthorized: () => {
return new NextRequest(new URL('http://internal'), {
headers: {
authorization: 'Bearer AUTH_SECRET',
'x-forwarded-for': '::1',
},
method: 'POST',
})
},
}

export default RequestExamples
12 changes: 6 additions & 6 deletions tests/request/is-internal-requests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { isInternalRequest } from '../../src/request/is-internal-request'
import RequestExamples from '../features/request.examples'

describe('is-internal-request', () => {
const env = process.env
const environment = process.env

afterEach(() => {
process.env = env
process.env = environment
})

it('should returns true if request is internal', () => {
// Arrange
process.env = {
...env,
...environment,
AUTH_SECRET: 'AUTH_SECRET',
}
const request = RequestExamples.internalAuthorized()
Expand All @@ -26,7 +26,7 @@ describe('is-internal-request', () => {
it('should returns false if AUTH_SECRET is not configured', () => {
// Arrange
process.env = {
...env,
...environment,
}
const request = RequestExamples.internalAuthorized()

Expand All @@ -40,7 +40,7 @@ describe('is-internal-request', () => {
it('should returns false if AUTH_SECRET is invalid', () => {
// Arrange
process.env = {
...env,
...environment,
AUTH_SECRET: 'OTHER_AUTH_SECRET',
}
const request = RequestExamples.internalAuthorized()
Expand All @@ -55,7 +55,7 @@ describe('is-internal-request', () => {
it('should returns false if request is external', () => {
// Arrange
process.env = {
...env,
...environment,
AUTH_SECRET: 'AUTH_SECRET',
}
const request = RequestExamples.externalAuthorized()
Expand Down
2 changes: 1 addition & 1 deletion tests/request/roles-for-session.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('rolesForSession', () => {

it('should return an empty array for an invalid session', async () => {
// Arrange
const mockSession = null
const mockSession = undefined

// Act
const result = await rolesForSession(mockSession)
Expand Down
Loading

0 comments on commit d6702fb

Please sign in to comment.