Skip to content
Open
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
18 changes: 18 additions & 0 deletions src/components/Head.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from 'react';
import { Helmet } from 'react-helmet';
import { JsonLdSchema, serializeJsonLd } from '../utilities/json-ld';

export const Head = ({
title,
canonical,
description,
metaTitle,
keywords,
jsonLd,
}: {
title: string;
canonical: string;
description: string;
metaTitle?: string;
keywords?: string;
jsonLd?: JsonLdSchema | JsonLdSchema[];
}) => (
<Helmet>
<title>{metaTitle || title}</title>
Expand All @@ -24,6 +27,21 @@ export const Head = ({
<meta name="twitter:description" content={description} />
{keywords && <meta name="keywords" content={keywords} />}

{/* JSON-LD Structured Data */}
{jsonLd && (
Array.isArray(jsonLd) ? (
jsonLd.map((schema, index) => (
<script key={`jsonld-${index}`} type="application/ld+json">
{serializeJsonLd(schema)}
</script>
))
) : (
<script type="application/ld+json">
{serializeJsonLd(jsonLd)}
</script>
)
)}

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
<link
Expand Down
11 changes: 11 additions & 0 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ export type Frontmatter = {
meta_description: string;
meta_keywords?: string;
redirect_from?: string[];
jsonld_type?: string;
jsonld_date_published?: string;
jsonld_date_modified?: string;
jsonld_author_name?: string;
jsonld_author_type?: string;
jsonld_image?: string;
jsonld_image_description?: string;
jsonld_sdks?: string[];
jsonld_faqs?: Array<{ question: string; answer: string }>;
jsonld_howto_steps?: Array<{ name: string; text: string }>;
[key: string]: unknown;
};

export type PageContextType = {
Expand Down
48 changes: 47 additions & 1 deletion src/components/Layout/MDXWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useSiteMetadata } from 'src/hooks/use-site-metadata';
import { ProductName } from 'src/templates/template-data';
import { getMetaTitle } from '../common/meta-title';
import UserContext from 'src/contexts/user-context';
import { generateCompleteSchema } from 'src/utilities/json-ld';

type MDXWrapperProps = PageProps<unknown, PageContextType>;

Expand Down Expand Up @@ -184,6 +185,51 @@ const MDXWrapper: React.FC<MDXWrapperProps> = ({ children, pageContext, location
const { canonicalUrl } = useSiteMetadata();
const canonical = canonicalUrl(location.pathname);

// Generate JSON-LD schema for the page
const jsonLd = useMemo(() => {
// Extract custom JSON-LD fields from frontmatter
const customFields: Record<string, unknown> = {};

// Handle special structured fields
if (frontmatter?.jsonld_image) {
customFields.image = frontmatter.jsonld_image;
}
if (frontmatter?.jsonld_image_description) {
customFields.imageDescription = frontmatter.jsonld_image_description;
}
if (frontmatter?.jsonld_sdks) {
customFields.sdks = frontmatter.jsonld_sdks;
}
if (frontmatter?.jsonld_faqs) {
customFields.faqs = frontmatter.jsonld_faqs;
}
if (frontmatter?.jsonld_howto_steps) {
customFields.howToSteps = frontmatter.jsonld_howto_steps;
}

// Collect any frontmatter fields that start with 'jsonld_custom_'
Object.entries(frontmatter || {}).forEach(([key, value]) => {
if (key.startsWith('jsonld_custom_')) {
const schemaKey = key.replace('jsonld_custom_', '');
customFields[schemaKey] = value;
}
});

return generateCompleteSchema({
title,
description,
url: canonical,
pathname: location.pathname,
keywords,
schemaType: frontmatter?.jsonld_type,
datePublished: frontmatter?.jsonld_date_published,
dateModified: frontmatter?.jsonld_date_modified,
authorName: frontmatter?.jsonld_author_name,
authorType: frontmatter?.jsonld_author_type,
customFields,
});
}, [title, description, canonical, keywords, frontmatter, location.pathname]);

// Use the copyable headers hook
useCopyableHeaders();

Expand All @@ -206,7 +252,7 @@ const MDXWrapper: React.FC<MDXWrapperProps> = ({ children, pageContext, location

return (
<SDKContext.Provider value={{ sdk, setSdk }}>
<Head title={title} metaTitle={metaTitle} canonical={canonical} description={description} keywords={keywords} />
<Head title={title} metaTitle={metaTitle} canonical={canonical} description={description} keywords={keywords} jsonLd={jsonLd} />
<Article>
<MarkdownProvider
components={{
Expand Down
35 changes: 35 additions & 0 deletions src/pages/docs/auth/basic.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
---
title: Basic auth
meta_description: "Basic authentication allows you to authenticate a secure server using an Ably API key and secret."
meta_keywords: "authentication, api key, basic auth, security"

# Image from the page
jsonld_image: "https://ably.com/docs/images/content/diagrams/Ably-API-Auth1.png"
jsonld_image_description: "Basic authentication process diagram"

# All SDKs shown in code examples
jsonld_sdks:
- javascript
- nodejs
- ruby
- python
- java
- swift
- objc
- csharp
- go
- flutter
- php

# FAQs extracted from "When to use basic auth" section
jsonld_faqs:
- question: "When should I use basic authentication with Ably?"
answer: "Basic authentication is recommended only for server-side use. It should be used when authenticating secure servers with Ably. It's more efficient to use the REST interface rather than realtime when the server is used solely for authentication."
- question: "Why shouldn't I use basic auth on the client side?"
answer: "Basic authentication should not be used client-side because: 1) The secret is passed directly to Ably and can only be used over TLS connections, 2) All configured capabilities of the key are implicitly available which could be abused, 3) Clients can claim any client ID they choose, making client IDs untrustworthy."
- question: "Do I need to use the realtime interface for basic authentication?"
answer: "No, basic authentication is primarily designed for authenticating secure servers, so it's more efficient to use the REST interface of an Ably SDK to avoid the overhead of maintaining a realtime connection."

# HowTo steps for implementing basic auth
jsonld_howto_steps:
- name: "Obtain API key"
text: "Get an API key from your Ably account dashboard"
- name: "Initialize SDK with API key"
text: "Pass the API key when instancing an Ably SDK using the key parameter in ClientOptions"
---

Basic authentication is the simplest way to authenticate with Ably. It requires passing an [API key](/docs/auth#api-key) when instancing an SDK.
Expand Down
Loading