import type { Request, Response, Router } from "express";
import type { AuthSessions } from "./auth-session.js";
/**
* The identity provider redirects here without a Termix session: the sign-in
* may finish in another browser, so it is matched by the OAuth state.
*/
export const PUBLIC_PATHS = ["/callback"];
export function escapeHtml(value: string): string {
return value
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
export function resultPage(ok: boolean, message: string): string {
return `
Termix
${ok ? "Signed in" : "Sign-in failed"}
${escapeHtml(message)}
`;
}
export function registerRoutes(router: Router, sessions: AuthSessions): void {
/**
* @openapi
* /plugin-api/step-ca/callback:
* get:
* summary: OIDC callback for a Step CA sign-in
* description: Public. The redirect URI registered with the identity provider behind the CA's OIDC provisioner. Exchanges the code, has the CA sign the key and tells the terminal to reconnect. The 2.8 URI /host/step-ca-callback redirects here.
* tags: [Step CA]
* parameters:
* - name: state
* in: query
* schema:
* type: string
* - name: code
* in: query
* schema:
* type: string
* responses:
* 200:
* description: Signed in; an HTML page the browser can close.
* 400:
* description: The sign-in failed or is no longer active.
*/
router.get("/callback", async (req: Request, res: Response) => {
const query = (name: string): string | undefined => {
const value = req.query[name];
return typeof value === "string" ? value : undefined;
};
const result = await sessions.complete({
state: query("state"),
code: query("code"),
error: query("error"),
error_description: query("error_description"),
});
res
.status(result.ok ? 200 : 400)
.type("html")
.send(resultPage(result.ok, result.message));
});
}