Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/.env.dist.local
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,5 @@ CROWD_GITHUB_IS_SNOWFLAKE_ENABLED=false
CROWD_TINYBIRD_BASE_URL=http://localhost:7181/

# Auth0
CROWD_AUTH0_ISSUER_BASE_URL=
CROWD_AUTH0_ISSUER_BASE_URLS=
CROWD_AUTH0_AUDIENCE=
2 changes: 1 addition & 1 deletion backend/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
"auth0": {
"clientId": "CROWD_AUTH0_CLIENT_ID",
"jwks": "CROWD_AUTH0_JWKS",
"issuerBaseURL": "CROWD_AUTH0_ISSUER_BASE_URL",
"issuerBaseURLs": "CROWD_AUTH0_ISSUER_BASE_URLS",
Comment thread
skwowet marked this conversation as resolved.
"audience": "CROWD_AUTH0_AUDIENCE"
},
"sso": {
Expand Down
52 changes: 45 additions & 7 deletions backend/src/api/public/middlewares/oauth2Middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ import { UnauthorizedError } from '@crowd/common'
import type { Auth0Configuration } from '@/conf/configTypes'
import type { Auth0TokenPayload } from '@/types/api'

function resolveIssuer(req: Request): string | undefined {
const token = req.headers.authorization?.split(' ')[1]
if (!token) return undefined
try {
const { iss } = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString())
return typeof iss === 'string' ? iss : undefined
} catch {
return undefined
}
}

function resolveActor(req: Request, _res: Response, next: NextFunction): void {
const payload = (req.auth?.payload ?? {}) as Auth0TokenPayload

Expand All @@ -26,11 +37,38 @@ function resolveActor(req: Request, _res: Response, next: NextFunction): void {
}

export function oauth2Middleware(config: Auth0Configuration): RequestHandler[] {
return [
auth({
issuerBaseURL: config.issuerBaseURL,
audience: config.audience,
}),
resolveActor,
]
const issuers = config.issuerBaseURLs
.split(',')
.map((s) => s.trim())
.filter(Boolean)

Comment thread
skwowet marked this conversation as resolved.
if (issuers.length === 0) {
throw new Error('No auth0 issuers configured')
}

const handlersByIssuer = new Map(
issuers.map((issuerBaseURL) => [
issuerBaseURL.replace(/\/$/, ''),
auth({ issuerBaseURL, audience: config.audience }),
]),
)

const verifyJwt: RequestHandler = (req, res, next) => {
const iss = resolveIssuer(req)
if (!iss) {
next(new UnauthorizedError('Missing or malformed bearer token'))
return
}

const handler = handlersByIssuer.get(iss.replace(/\/$/, ''))

if (!handler) {
next(new UnauthorizedError('Unknown token issuer'))
return
Comment thread
skwowet marked this conversation as resolved.
}

handler(req, res, next)
}

return [verifyJwt, resolveActor]
}
2 changes: 1 addition & 1 deletion backend/src/conf/configTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export interface ApiConfiguration {
export interface Auth0Configuration {
clientId: string
jwks: string
issuerBaseURL: string
issuerBaseURLs: string
audience: string
}

Expand Down
Loading