vapor/vapor

UserAuthnetication middleware is bypassed when auth object is not used

mantaslaurinavicius opened this issue · 2 comments

Describe the bug

I have created AsyncBearerAuthenticator and grouped some of my endpoints.
When the endpoint is not using an authenticated object, authentication can be bypassed by sending empty bearer token.
Please let me know if I am doing something wrong as I have waster few days already and can't find why.

Attached sample of my code

To Reproduce

Authenticator code:

struct UserAuthenticator: AsyncBearerAuthenticator {
    func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
        guard !bearer.token.isEmpty else {
            throw Abort(.unauthorized)
        }
        
        guard let token = try await AccessTokenDBModel.query(on: request.db)
            .filter(\.$token == bearer.token)
            .with(\.$user)
            .first() else {
            throw Abort(.unauthorized)
        }
        
        guard let date = token.expiresAt, date > Date() else {
            throw Abort(.unauthorized)
        }
        
        request.auth.login(token.user)
    }
}

My endpoints:

struct AuctionController: RouteCollection {
    func boot(routes: RoutesBuilder) throws {
        let protected = routes.grouped("auctions").grouped(UserAuthenticator())
        
        protected.get("withNoUser", use: getAll)
        protected.get("withUser", use: withUser)
    }
    
    func getAll(req: Request) async throws -> [AuctionDBModel] {
        let all = try await AuctionDBModel.query(on: req.db)
            .with(\.$images)
            .with(\.$links)
            .all()
        return all
    }
    
    func withUser(req: Request) async throws -> [AuctionDBModel] {
        let user = try req.auth.require(UserAuthDBModel.self)
        
        let all = try await AuctionDBModel.query(on: req.db)
            .with(\.$images)
            .with(\.$links)
            .all()
        return all
    }
}

Steps to reproduce the behavior:

When I call "auctions/withNoUser" with Bearer token empty, it returns all data ("Bearer ")
When I can "auctions/withUser" with Bearer token empty, it throws 401 Unauthorized (Correct)

Passing any random value as Bearer fails on both: 401 Unauthorized (Correct) ("Bearer blabla")

Expected behavior

I would expect that even if endpoint is simple and does not use auth user it should still be protected.

Environment

  • Vapor Framework version: 4.91.1
  • Vapor Toolbox version: 18.7.4
  • OS version: macOS 14.1.2
  • swift-tools-version: 5.9
  • Xcode version: 15.2

Additional context

Add any other context about the problem here.

This is deliberate to allow composition of middleware. You should use the guardMiddleware for instances like this. See the docs for more information

mkll commented

@0xTim This is only true in cases where a (properly designed) middleware allows unauthorized requests to pass further down the chain. As can be seen from the code presented by the author, in this case the middleware should have thrown an exception; it has no option to skip further. Therefore, the presence or absence of a guardMiddleware should not affect the authorization flow.

And since the middleware didn't actually throw an exception, we can assume that it actually didn't execute.