Node.js mit TSOA und Authentifizierung - (Teil 4) Header-Validierung in der AuthMiddleware
Wir widmen uns der Authentifizierungsmiddleware. Dabei überprüfen wir den Request-Header und behandeln fehlende oder falsche Informationen. Für einen solchen Problemfall erstellen wir die passende Fehlerbehandlung. Los geht’s!
In diesem Artikel widme ich mich der Überprüfung des eingehenden Requests. Im Fehlerfall – das bedeutet, dass weder ein Authorization-Header noch ein syntaktisch korrekter Header geliefert wurde – soll die Antwort der Applikation „StatusCode 401 Unauthenticated“ lauten. Bevor wir anfangen, empfiehlt es sich, die Vorgänger der Artikelserie zu lesen:
Vorbereitung der Fehlerklasse
Die dazu notwendigen Schritte habe ich bereits in einem vergangenen Artikel beschrieben: Node.js und meine Base-Error-Klasse. Von dieser Base-Error-Klasse leiten wir unseren UnauthenticatedError ab. Wenn du die Schritte aus dem Artikel vollzogen hast, solltest du folgende Ergebnisse vorweisen können:
- Eine neue BaseError-Klasse.
- Eine Datei StringUtils mit einer toString-Funktion und der Error-Prototype-Definition für toJSON.
- Das NPM-Paket
flattedinstalliert haben.
Als Nächstes erstellen wir den Unauthenticated-Error mit dem StatusCode 401. Dieser Fehler besagt, dass wir nicht feststellen konnten, ob der Aufruf der ist, der vorzugeben scheint. Dies steht im Gegensatz zum UnauthorizedError: Hierbei ist der Zugriff für die verifizierte Person nicht gewährt.
import BaseError, { BaseErrorOptions } from '@/errors/BaseError'
class UnauthenticatedError extends BaseError {
constructor(options: BaseErrorOptions) {
const { detail = 'Unauthenticated', cause } = options
super({ detail, cause, statusCode: 401 })
}
public toJSON() {
return super.toJSON()
}
public toString() {
return JSON.stringify(this.toJSON())
}
}
export default UnauthenticatedError
Überprüfung des Authorization-Headers
Jetzt widmen wir uns der AuthMiddleware. Wir erstellen eine validateHeader-Funktion, die den Header prüft und den Token daraus extrahiert. Hierbei ist es wichtig zu betonen, dass wir keinen Error werfen dürfen, sondern einen Promise.reject() aufrufen. Ansonsten brechen wir aus der Express-Middleware aus.
import { Request } from 'express'
import UnauthenticatedError from '@/errors/UnauthenticatedError'
type ValidateHeaderSucceed = {
isValid: true
bearerToken: string
}
type ValidateHeaderFailed = {
isValid: false
error: UnauthenticatedError
}
type ValidateHeaderResult = ValidateHeaderSucceed | ValidateHeaderFailed
const validateHeader = (request: Request): ValidateHeaderResult => {
// look up header for authorization
const authHeader = request.headers.authorization
// validate authorization header
if (!authHeader) {
return {
isValid: false,
error: new UnauthenticatedError({ detail: 'No authorization header provided' }),
}
}
if (!authHeader.startsWith('Bearer ')) {
return {
isValid: false,
error: new UnauthenticatedError({ detail: 'No bearer token provided' }),
}
}
const bearerToken = authHeader.split(' ')[1]
if (!bearerToken) {
return {
isValid: false,
error: new UnauthenticatedError({ detail: 'No bearer token provided' }),
}
}
return {
isValid: true,
bearerToken,
}
}
Die Einbindung erfolgt in der AuthMiddleware, in der wir im Fehlerfall Promise.reject aufrufen.
const expressAuthentication = (
request: Request,
securityName: string,
requiredScopes?: string[],
): Promise<AuthResultType> => {
const validateHeaderResult = validateHeader(request)
if (!validateHeaderResult.isValid) {
console.log(`[AuthMiddleware::${securityName}] Failed due to: ${validateHeaderResult.error.message}`)
return Promise.reject(validateHeaderResult.error)
}
// ... here we will check the token
}
Fazit
An dieser Stelle haben wir es geschafft. Wir prüfen, ob ein eingehender Request den Authorization-Header gesetzt hat. Damit ist der erste Schritt zur Authentifizierung geschafft. Als Nächstes gilt es, das Token zu prüfen. Vorher will ich mich jedoch um das Promise.reject kümmern. Dazu benötigen wir eine Error-Middleware, die solche Fehler in eine Standardantwort übersetzt.