@indiekit/endpoint-auth
Version:
IndieAuth authentication and authorization endpoint for Indiekit. Grants and verifies access tokens and authenticates users.
81 lines (71 loc) • 2.42 kB
JavaScript
import { IndiekitError } from "@indiekit/error";
import { getCanonicalUrl } from "@indiekit/util";
import { verifyCode } from "../pkce.js";
import { validateRedirect } from "../redirect.js";
import { verifyToken } from "../token.js";
import { getRequestParameters } from "../utils.js";
/**
* Validate authorization code before redeeming
* @type {import("express").RequestHandler}
*/
export const codeValidator = (request, response, next) => {
try {
const { client, usePkce } = request.app.locals;
const parameters = getRequestParameters(request);
const { client_id, code, code_verifier, grant_type, redirect_uri } =
parameters;
// Validate presence of required parameters
for (const parameter of [
"client_id",
"code",
"grant_type",
"redirect_uri",
]) {
if (!Object.keys(parameters).includes(parameter)) {
throw IndiekitError.badRequest(
response.locals.__("BadRequestError.missingParameter", parameter),
);
}
}
// `grant_type` must equal `authorization_code`
if (grant_type !== "authorization_code") {
throw IndiekitError.badRequest(
response.locals.__("BadRequestError.invalidValue", "grant_type"),
);
}
// Validate `client_id` against that provided in authorization request
if (getCanonicalUrl(client_id) !== getCanonicalUrl(client.id)) {
throw IndiekitError.unauthorized(
response.locals.__("BadRequestError.invalidValue", "client_id"),
);
}
// Validate `redirect_uri`
const validRedirect = validateRedirect(redirect_uri, client_id);
if (!validRedirect) {
throw IndiekitError.badRequest(
response.locals.__("BadRequestError.invalidValue", "redirect_uri"),
);
}
// Verify token
try {
request.verifiedToken = verifyToken(code);
} catch {
throw IndiekitError.unauthorized(
response.locals.__("UnauthorizedError.invalidToken"),
);
}
// PKCE (Proof Key for Code Exchange)
if (usePkce) {
const { code_challenge } = request.verifiedToken;
const verifiedCode = verifyCode(code_verifier, code_challenge);
if (!verifiedCode) {
throw IndiekitError.unauthorized(
response.locals.__("BadRequestError.invalidValue", "code_verifier"),
);
}
}
return next();
} catch (error) {
return next(error);
}
};