Add rate limiter for auth endpoints

This commit is contained in:
advplyr 2025-07-07 16:23:15 -05:00
parent 9c8900560c
commit ac381854e5
5 changed files with 90 additions and 6 deletions

View file

@ -1,4 +1,5 @@
const { Request, Response, NextFunction } = require('express')
const { rateLimit } = require('express-rate-limit')
const passport = require('passport')
const JwtStrategy = require('passport-jwt').Strategy
const ExtractJwt = require('passport-jwt').ExtractJwt
@ -9,6 +10,7 @@ const TokenManager = require('./auth/TokenManager')
const LocalAuthStrategy = require('./auth/LocalAuthStrategy')
const OidcAuthStrategy = require('./auth/OidcAuthStrategy')
const RateLimiterFactory = require('./utils/rateLimiterFactory')
const { escapeRegExp } = require('./utils')
/**
@ -19,6 +21,9 @@ class Auth {
const escapedRouterBasePath = escapeRegExp(global.RouterBasePath)
this.ignorePatterns = [new RegExp(`^(${escapedRouterBasePath}/api)?/items/[^/]+/cover$`), new RegExp(`^(${escapedRouterBasePath}/api)?/authors/[^/]+/image$`)]
/** @type {import('express-rate-limit').RateLimitRequestHandler} */
this.authRateLimiter = RateLimiterFactory.getAuthRateLimiter()
this.tokenManager = new TokenManager()
this.localAuthStrategy = new LocalAuthStrategy()
this.oidcAuthStrategy = new OidcAuthStrategy()
@ -305,7 +310,7 @@ class Auth {
*/
async initAuthRoutes(router) {
// Local strategy login route (takes username and password)
router.post('/login', passport.authenticate('local'), async (req, res) => {
router.post('/login', this.authRateLimiter, passport.authenticate('local'), async (req, res) => {
// Check if mobile app wants refresh token in response
const returnTokens = req.query.return_tokens === 'true' || req.headers['x-return-tokens'] === 'true'
@ -314,7 +319,7 @@ class Auth {
})
// Refresh token route
router.post('/auth/refresh', async (req, res) => {
router.post('/auth/refresh', this.authRateLimiter, async (req, res) => {
let refreshToken = req.cookies.refresh_token
// If x-refresh-token header is present, use it instead of the cookie
@ -345,7 +350,7 @@ class Auth {
})
// openid strategy login route (this redirects to the configured openid login provider)
router.get('/auth/openid', (req, res) => {
router.get('/auth/openid', this.authRateLimiter, (req, res) => {
const authorizationUrlResponse = this.oidcAuthStrategy.getAuthorizationUrl(req)
if (authorizationUrlResponse.error) {
@ -359,11 +364,12 @@ class Auth {
// This will be the oauth2 callback route for mobile clients
// It will redirect to an app-link like audiobookshelf://oauth
router.get('/auth/openid/mobile-redirect', (req, res) => this.oidcAuthStrategy.handleMobileRedirect(req, res))
router.get('/auth/openid/mobile-redirect', this.authRateLimiter, (req, res) => this.oidcAuthStrategy.handleMobileRedirect(req, res))
// openid strategy callback route (this receives the token from the configured openid login provider)
router.get(
'/auth/openid/callback',
this.authRateLimiter,
(req, res, next) => {
const sessionKey = this.oidcAuthStrategy.getStrategy()._key
@ -436,7 +442,7 @@ class Auth {
*
* @example /auth/openid/config?issuer=http://192.168.1.66:9000/application/o/audiobookshelf/
*/
router.get('/auth/openid/config', this.isAuthenticated, async (req, res) => {
router.get('/auth/openid/config', this.authRateLimiter, this.isAuthenticated, async (req, res) => {
if (!req.user.isAdminOrUp) {
Logger.error(`[Auth] Non-admin user "${req.user.username}" attempted to get issuer config`)
return res.sendStatus(403)