Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion web/app/[[...mdxPath]]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,35 @@ export const generateStaticParams = generateStaticParamsFor('mdxPath');
export async function generateMetadata(props) {
const params = await props.params;
const { metadata } = await importPage(params.mdxPath);
return metadata;

// Per-page canonical. The root layout declares alternates.canonical: '/',
// which every page would otherwise inherit — pointing all sub-pages'
// canonical at the homepage and telling search engines to treat them as
// duplicates. Resolve it to this page's own path instead.
const pathname = Array.isArray(params.mdxPath) && params.mdxPath.length ? `/${params.mdxPath.join('/')}` : '/';

// Point the page's OG/Twitter card at the dynamic /og endpoint, seeded with
// this page's own title and description so every share card is specific.
const title = typeof metadata?.title === 'string' ? metadata.title : '@exortek/auth';
const heading = title.replace(/\s+[—-]\s+Overview$/i, '').trim();
const desc = typeof metadata?.description === 'string' ? metadata.description : '';
const ogUrl = `/og?title=${encodeURIComponent(heading)}${desc ? `&desc=${encodeURIComponent(desc)}` : ''}`;

return {
...metadata,
alternates: { ...metadata?.alternates, canonical: pathname },
// Re-declare the stable OG fields: setting openGraph here replaces the
// layout's object wholesale, so siteName/type/locale would be lost.
openGraph: {
type: 'website',
siteName: '@exortek/auth',
locale: 'en_US',
...metadata?.openGraph,
url: pathname,
images: [{ url: ogUrl, width: 1200, height: 630 }],
},
twitter: { card: 'summary_large_image', ...metadata?.twitter, images: [ogUrl] },
};
}

const Wrapper = getMDXComponents().wrapper;
Expand Down
29 changes: 29 additions & 0 deletions web/app/apple-icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ImageResponse } from 'next/og';

// Apple touch icon. Generated (rather than a static file) so it stays in sync
// with the brand mark in icon.svg; the apple-icon convention wants a raster
// type, so we render one at the standard 180×180.
export const size = { width: 180, height: 180 };
export const contentType = 'image/png';

export default function appleIcon() {
return new ImageResponse(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: '#17181c',
color: '#e4dcc7',
fontSize: 116,
fontWeight: 700,
fontFamily: 'monospace',
}}
>
@
</div>,
size,
);
}
74 changes: 74 additions & 0 deletions web/app/og/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ImageResponse } from 'next/og';

// Dynamic Open Graph image endpoint. Pages point their openGraph/twitter image
// at /og?title=…&desc=… (see [[...mdxPath]]/page.jsx generateMetadata) so each
// share card is specific to the page. A route handler is used rather than a
// file-based opengraph-image because the optional catch-all segment cannot
// carry a static metadata child (the catch-all must be the last segment).
export const contentType = 'image/png';

const SIZE = { width: 1200, height: 630 };
const BG = '#17181c';
const CREAM = '#e4dcc7';
const GREEN = '#12b76a';
const MUTED = '#8a8f99';

export function GET(request) {
const { searchParams } = new URL(request.url);
const heading = (searchParams.get('title') || '@exortek/auth').slice(0, 80);
const rawSub = searchParams.get('desc') || 'Authentication toolkit for Node.js';
const sub = rawSub.length > 150 ? `${rawSub.slice(0, 147)}…` : rawSub;

return new ImageResponse(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
background: BG,
padding: '72px 80px',
fontFamily: 'sans-serif',
}}
>
<div style={{ display: 'flex', alignItems: 'center', fontSize: 34, fontWeight: 700 }}>
<span style={{ color: CREAM }}>@exortek/</span>
<span style={{ color: GREEN }}>auth</span>
</div>

<div style={{ display: 'flex', flexDirection: 'column' }}>
<div
style={{
display: 'flex',
fontSize: 68,
fontWeight: 800,
color: CREAM,
lineHeight: 1.1,
letterSpacing: '-0.02em',
}}
>
{heading}
</div>
<div
style={{
display: 'flex',
marginTop: 28,
fontSize: 30,
color: MUTED,
lineHeight: 1.35,
maxWidth: 960,
}}
>
{sub}
</div>
</div>

<div style={{ display: 'flex', alignItems: 'center', fontSize: 26, color: MUTED }}>
<span style={{ color: GREEN, marginRight: 14 }}>●</span>
built on node:crypto · Node.js 22+ · zero-dependency
</div>
</div>,
SIZE,
);
}
87 changes: 32 additions & 55 deletions web/app/sitemap.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,40 @@
import { getPageMap } from 'nextra/page-map';

const SITE_URL = 'https://auth.memet.dev';

// Top-level routes plus the multi-page package sections. Kept explicit so the
// sitemap stays correct regardless of how Nextra resolves the page map.
const ROUTES = [
'',
'guides',
'guides/password-login',
'guides/jwt-access-refresh',
'guides/jwks-verify',
'guides/two-factor',
'guides/passkey',
'guides/multi-step',
'guides/magic-link',
'guides/api-keys',
'guides/opaque-tokens',
'guides/nested-jwt',
'guides/security-hardening',
'comparison',
'compliance',
'crypto',
'password',
'otp',
'challenge',
'jwk',
'jws',
'jws/sign',
'jws/verify',
'jws/decode',
'jws/json',
'jws/errors',
'jwt',
'jwt/sign',
'jwt/verify',
'jwt/token-pair',
'jwt/stores',
'jwt/errors',
'jwe',
'jwe/encrypt',
'jwe/decrypt',
'jwe/decode',
'jwe/json',
'jwe/algorithms',
'jwe/errors',
'jwks',
'session',
'security',
'ua',
'apikey',
'magic-link',
'passkey',
'opaque',
];
// Walk the Nextra page map and collect every routable page. Deriving the
// sitemap from the page map (rather than a hand-kept list) keeps it correct as
// packages and pages are added — the previous static ROUTES array had already
// drifted several sections behind the content tree.
const collectRoutes = (nodes, out = new Set()) => {
for (const node of nodes ?? []) {
if (typeof node.route === 'string' && !node.route.includes('[')) {
out.add(node.route);
}
if (Array.isArray(node.children)) {
collectRoutes(node.children, out);
}
}
return out;
};

// Deeper pages get a lower priority; the home page ranks highest, package roots
// above their sub-pages.
const priorityFor = route => {
if (route === '/') return 1;
const depth = route.split('/').filter(Boolean).length;
return depth <= 1 ? 0.8 : 0.6;
};

export default function sitemap() {
export default async function sitemap() {
const pageMap = await getPageMap();
const routes = [...collectRoutes(pageMap)].sort();
const lastModified = new Date();
return ROUTES.map(route => ({
url: route ? `${SITE_URL}/${route}` : SITE_URL,

return routes.map(route => ({
url: route === '/' ? SITE_URL : `${SITE_URL}${route}`,
lastModified,
changeFrequency: 'weekly',
priority: route === '' ? 1 : 0.7,
priority: priorityFor(route),
}));
}
1 change: 1 addition & 0 deletions web/content/apikey/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/apikey — Overview'
description: 'Prefixed API keys (sk_live_id_secret) with HMAC-SHA256 hash-at-rest storage, scopes and middleware. Built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/auth/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/auth — Overview'
description: 'Umbrella package re-exporting all 20 @exortek authentication packages — install one dependency, get the whole toolkit.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/challenge/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/challenge — Overview'
description: 'HMAC-signed multi-step flow tokens binding userId, method and step, with opt-in single-use — for MFA and step-up auth flows.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/compliance.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: 'Compliance mapping — NIST / OWASP / PCI-DSS / FIPS'
description: 'How @exortek/auth packages map onto NIST SP 800-63B, OWASP ASVS 4.0.3, PCI-DSS 4.0 and FIPS — which package satisfies which control.'
sidebarTitle: Compliance
---

Expand Down
1 change: 1 addition & 0 deletions web/content/crypto/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/crypto — Overview'
description: 'Zero-dependency crypto primitives on node:crypto — hashing, HMAC, KDFs, AEAD ciphers, signatures, sealed tokens, encoding and CSPRNG.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/jwe/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/jwe — Overview'
description: 'Encrypt and decrypt JWE with RSA-OAEP, ECDH-ES, A256KW and dir key management per RFC 7516. Zero-dependency, built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/jwk/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/jwk — Overview'
description: 'Generate, import, export and thumbprint JSON Web Keys per RFC 7517, 7638, 8037 and 9278. Zero-dependency, built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/jwks/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/jwks — Overview'
description: 'Fetch, cache and rotate JWKS by kid from a remote URI per RFC 7517 §5 and OpenID Connect Discovery. Verify third-party JWTs.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/jws/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/jws — Overview'
description: 'Sign and verify JWS with detached payloads, b64:false and JSON serialization per RFC 7515 and 7797. Algorithm allowlists mandatory.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/jwt/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/jwt — Overview'
description: 'Sign and verify JWTs with mandatory algorithm allowlists and claims validation per RFC 7519. alg:none refused by construction.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/magic-link/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/magic-link — Overview'
description: 'Passwordless email-link auth with signed, short-lived, single-use tokens. Zero-dependency, built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/oauth2/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/oauth2 — Overview'
description: 'OAuth 2.1 relying-party flow, provider presets and a full authorization server — DPoP, PAR, JAR/JARM, device flow and token exchange.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/oidc/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/oidc — Overview'
description: 'OpenID Connect Core 1.0 on @exortek/oauth2 — discovery-first RP plus OP add-ons for UserInfo, JWKS, logout and session management.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/opaque/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/opaque — Overview'
description: 'Opaque reference tokens with RFC 7662 introspection and RFC 7009 revocation. Store-backed, built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/otp/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/otp — Overview'
description: 'RFC 4226 HOTP and RFC 6238 TOTP with backup codes and otpauth:// provisioning URIs for authenticator apps. Built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/paseto/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/paseto — Overview'
description: 'PASETO v4 local (XChaCha20 + BLAKE2b) and public (Ed25519) tokens with no alg header. Zero-dependency, built on node:crypto.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/passkey/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/passkey — Overview'
description: 'WebAuthn Level 3 / FIDO2 server verification with all seven attestation formats, MDS3 metadata and AAGUID lookup.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/password/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/password — Overview'
description: 'Argon2id, scrypt, bcrypt and PBKDF2 password hashing with strength checks, pepper, history and HIBP breach lookup. NIST SP 800-63B.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/security/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/security — Overview'
description: 'CSRF, rate limiting, helmet-style headers, CORS and safe redirects — defensive HTTP helpers per OWASP ASVS V13/V14.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/session/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/session — Overview'
description: 'Sealed-cookie sessions with rotation, revocation, sudo mode and Redis pub/sub. OWASP ASVS V3, RFC 6265.'
sidebarTitle: Overview
---

Expand Down
1 change: 1 addition & 0 deletions web/content/ua/index.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: '@exortek/ua — Overview'
description: 'User-Agent parsing, device, browser and bot detection, Client Hints and fingerprinting for Node.js. Zero-dependency.'
sidebarTitle: Overview
---

Expand Down