porchlight/examples/rp-reference/templates.py
Johan Lundberg 8e8c33a407
reference RP
2026-06-29 09:23:22 +02:00

80 lines
2.4 KiB
Python

"""Minimal inline HTML. No template engine — just f-strings — to keep the
reference single-purpose and dependency-light. Output is intentionally plain.
"""
from __future__ import annotations
import html
import json
from typing import Any
_PAGE = """<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Porchlight RP Reference</title>
<style>
body {{ font-family: system-ui, sans-serif; max-width: 52rem; margin: 2rem auto;
padding: 0 1rem; line-height: 1.5; }}
h1 {{ font-size: 1.4rem; }}
pre {{ background: #f4f4f5; padding: 1rem; border-radius: .5rem; overflow:auto; }}
.btn {{ display: inline-block; padding: .5rem 1rem; border-radius: .4rem;
background: #1f2937; color: #fff; text-decoration: none; border: 0;
font: inherit; cursor: pointer; }}
.btn.secondary {{ background: #e5e7eb; color: #111; }}
.row {{ display: flex; gap: .5rem; flex-wrap: wrap; margin: 1rem 0; }}
.err {{ background: #fee2e2; padding: 1rem; border-radius: .5rem; }}
section {{ margin: 1.5rem 0; }}
</style>
</head>
<body>
{body}
</body>
</html>
"""
def _block(title: str, obj: Any) -> str:
pretty = html.escape(json.dumps(obj, indent=2, sort_keys=True, default=str))
return f"<section><h2>{html.escape(title)}</h2><pre>{pretty}</pre></section>"
def home_page() -> str:
body = """
<h1>Porchlight OIDC RP — reference</h1>
<p>A hand-rolled Relying Party. Click below to start the Authorization Code
flow (with PKCE) against your porchlight provider.</p>
<div class="row">
<a class="btn" href="/login">Login with Porchlight</a>
</div>
"""
return _PAGE.format(body=body)
def result_page(
*,
id_claims: dict[str, Any],
userinfo: dict[str, Any],
tokens: dict[str, Any],
) -> str:
body = f"""
<h1>Logged in</h1>
<div class="row">
<form method="post" action="/refresh"><button class="btn">Refresh tokens</button></form>
<form method="post" action="/logout"><button class="btn secondary">Log out</button></form>
</div>
{_block("ID token claims (verified)", id_claims)}
{_block("UserInfo response", userinfo)}
{_block("Raw token response", tokens)}
"""
return _PAGE.format(body=body)
def error_page(message: str) -> str:
body = f"""
<h1>Something went wrong</h1>
<div class="err"><pre>{html.escape(message)}</pre></div>
<div class="row"><a class="btn secondary" href="/">Back home</a></div>
"""
return _PAGE.format(body=body)