vibetunnel/VibeTunnel/Core/Services/BasicAuthMiddleware.swift
Peter Steinberger 573a3e0eb4 Support health
2025-06-16 20:56:23 +02:00

68 lines
No EOL
2.2 KiB
Swift

import Foundation
import Hummingbird
import HummingbirdCore
import HTTPTypes
import NIOCore
/// Middleware that implements HTTP Basic Authentication
struct BasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
let password: String
let realm: String
init(password: String, realm: String = "VibeTunnel Dashboard") {
self.password = password
self.realm = realm
}
func handle(_ request: Request, context: Context, next: (Request, Context) async throws -> Response) async throws -> Response {
// Skip auth for health check endpoint
if request.uri.path == "/api/health" {
return try await next(request, context)
}
// Extract authorization header
guard let authHeader = request.headers[.authorization],
authHeader.hasPrefix("Basic ") else {
return unauthorizedResponse()
}
// Decode base64 credentials
let base64Credentials = String(authHeader.dropFirst(6))
guard let credentialsData = Data(base64Encoded: base64Credentials),
let credentials = String(data: credentialsData, encoding: .utf8) else {
return unauthorizedResponse()
}
// Split username:password
let parts = credentials.split(separator: ":", maxSplits: 1)
guard parts.count == 2 else {
return unauthorizedResponse()
}
// We ignore the username and only check password
let providedPassword = String(parts[1])
// Verify password
guard providedPassword == password else {
return unauthorizedResponse()
}
// Password correct, continue with request
return try await next(request, context)
}
private func unauthorizedResponse() -> Response {
var headers = HTTPFields()
headers[.wwwAuthenticate] = "Basic realm=\"\(realm)\""
let message = "Authentication required"
var buffer = ByteBuffer()
buffer.writeString(message)
return Response(
status: .unauthorized,
headers: headers,
body: ResponseBody(byteBuffer: buffer)
)
}
}