diff --git a/src/features/introduction/components/introduction-article/introduction-article.component.tsx b/src/features/introduction/components/introduction-article/introduction-article.component.tsx
index 1a59409c..e121b5e0 100644
--- a/src/features/introduction/components/introduction-article/introduction-article.component.tsx
+++ b/src/features/introduction/components/introduction-article/introduction-article.component.tsx
@@ -3,6 +3,8 @@
import React from "react";
import { getIntroductionContent } from "@/features/localization/services/ui-language-content.service";
import { ArticleComponent } from "@/features/common/components/article/article.component";
+import styles from "./introduction-article.module.scss";
+import { SidebarNavComponent } from "../sidebar-nav/sidebar-nav.component";
interface IntroductionArticleComponentProps {
languageCode: string;
@@ -11,11 +13,12 @@ interface IntroductionArticleComponentProps {
export const IntroductionArticleComponent: React.FC<
IntroductionArticleComponentProps
> = ({ languageCode }) => {
- const { Component: Introduction } = getIntroductionContent({ languageCode });
+ const Introduction = getIntroductionContent({ languageCode });
return (
-
-
-
+
);
};
diff --git a/src/features/introduction/components/introduction-article/introduction-article.module.scss b/src/features/introduction/components/introduction-article/introduction-article.module.scss
new file mode 100644
index 00000000..3c8d256f
--- /dev/null
+++ b/src/features/introduction/components/introduction-article/introduction-article.module.scss
@@ -0,0 +1,11 @@
+@use "@/libs/theme/styles/variables" as *;
+
+.container {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+
+ @media #{$breakpoint-dimension-sm} {
+ flex-direction: row;
+ }
+}
diff --git a/src/features/introduction/components/sidebar-nav/sidebar-nav.component.tsx b/src/features/introduction/components/sidebar-nav/sidebar-nav.component.tsx
new file mode 100644
index 00000000..51a00efc
--- /dev/null
+++ b/src/features/introduction/components/sidebar-nav/sidebar-nav.component.tsx
@@ -0,0 +1,75 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { getIntroductionDictionary } from "@/features/localization/services/language-dictionary.service";
+import styles from "./sidebar-nav.module.scss";
+import clsx from "clsx";
+
+interface SidebarNavComponentProps {
+ languageCode: string;
+}
+
+const scrollToElementWithOffset = (id: string, offset = 0) => {
+ const element = document.getElementById(id);
+ if (element) {
+ const y = element.getBoundingClientRect().top + window.pageYOffset + offset;
+ window.scrollTo({ top: y, behavior: "smooth" });
+ }
+};
+
+export const SidebarNavComponent: React.FC = ({
+ languageCode,
+}) => {
+ const introductionDictionary = getIntroductionDictionary(languageCode);
+ const headings = introductionDictionary.content.headings;
+ const [activeId, setActiveId] = useState(null);
+
+ useEffect(() => {
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ const id = entry.target.id;
+ setActiveId(id);
+ history.replaceState(null, "", `#${id}`);
+ }
+ });
+ },
+ {
+ rootMargin: "0px 0px -40% 0px",
+ threshold: 1,
+ }
+ );
+ const elements = headings
+ .map((heading) => document.getElementById(heading.id))
+ .filter(Boolean) as HTMLElement[];
+ elements.forEach((el) => observer.observe(el));
+ return () => {
+ elements.forEach((el) => observer.unobserve(el));
+ };
+ }, [headings]);
+
+ const handleClick = (id: string) => {
+ scrollToElementWithOffset(id, -120);
+ history.replaceState(null, "", `#${id}`);
+ };
+
+ return (
+
+
+ {introductionDictionary.content.headings.map((heading, index) => (
+ - handleClick(heading.id)}
+ >
+ {heading.title}
+
+ ))}
+
+
+ );
+};
diff --git a/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss b/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss
new file mode 100644
index 00000000..9902c757
--- /dev/null
+++ b/src/features/introduction/components/sidebar-nav/sidebar-nav.module.scss
@@ -0,0 +1,33 @@
+@use "@/libs/theme/styles/variables" as *;
+
+.container {
+ display:none;
+ @media #{$breakpoint-dimension-sm} {
+ display: inline-block;
+ height: 100vh;
+ position: sticky;
+ color: var(--color_fg_link);
+ flex-shrink: 0;
+ top: 0;
+ padding: 160px 30px 0px;
+ margin-bottom: 100px;
+ border-right: 1px solid var(--color_border_bold);
+ max-width: 250px;
+ overflow-y: auto;
+ }
+}
+
+.title {
+ margin-bottom: 36px;
+ padding-right: 8px;
+ cursor: pointer;
+}
+
+.title__active {
+ color: var(--color_fg_selected);
+ border-bottom: 1px solid var(--color_border_selected);
+}
+
+.list {
+ list-style-type: none;
+}
diff --git a/src/features/introduction/docs/en.introduction.mdx b/src/features/introduction/docs/en.introduction.mdx
index a072df92..b111c486 100644
--- a/src/features/introduction/docs/en.introduction.mdx
+++ b/src/features/introduction/docs/en.introduction.mdx
@@ -1,19 +1,19 @@
import { MdxAnchorComponent } from "@/features/common/components/markdown/mdx-anchor.component";
import { MarkdownImage } from "@/features/common/components/markdown/markdown-image";
-## What is JSON Web Token?
+{props.headings[0].title}
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the **HMAC** algorithm) or a public/private key pair using **RSA** or **ECDSA**.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on *signed* tokens. Signed tokens can verify the *integrity* of the claims contained within it, while encrypted tokens *hide* those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.
-## When should you use JSON Web Tokens?
+{props.headings[1].title}
Here are some scenarios where JSON Web Tokens are useful:
- **Authorization**: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays because of its small overhead and its ability to be easily used across different domains.
- **Information Exchange**: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.
-## What is the JSON Web Token structure?
+{props.headings[2].title}
In its compact form, JSON Web Tokens consist of three parts separated by dots (`.`), which are:
- Header
@@ -93,7 +93,7 @@ If you want to play with JWT and put these concepts into practice, you can use <
-## How do JSON Web Tokens work?
+{props.headings[3].title}
In authentication, when the user successfully logs in using their credentials, a JSON Web Token will be returned. Since tokens are credentials, great care must be taken to prevent security issues. In general, you should not keep tokens longer than required.
You also should not store sensitive session data in browser storage due to lack of security.
@@ -120,7 +120,7 @@ The following diagram shows how a JWT is obtained and used to access APIs or res
Do note that with signed tokens, all the information contained within the token is exposed to users or other parties, even though they are unable to change it. This means you should not put secret information within the token.
-## Why should we use JSON Web Tokens?
+{props.headings[4].title}
Let's talk about the benefits of **JSON Web Tokens (JWT)** when compared to **Simple Web Tokens (SWT)** and **Security Assertion Markup Language Tokens (SAML)**.
@@ -138,7 +138,7 @@ _Comparison of the length of an encoded JWT and an encoded SAML_
If you want to read more about JSON Web Tokens and even start using them to perform authentication in your own applications, browse to the JSON Web Token landing page at Auth0.
-## Difference Between Validating and Verifying a JWT
+{props.headings[5].title}
JSON Web Token (JWT) validation and verification are crucial for security, but they address slightly different aspects of JWT security: validation ensures the token is well-formed and contains enforceable claims; verification ensures the token is genuine and unmodified.
@@ -165,7 +165,7 @@ You verify a JWT to make sure the token hasn't been altered maliciously and come
In many systems, these steps are often combined into what might be colloquially called "JWT verification" which encompasses both validation and verification for comprehensive security checks. Nonetheless, their distinction remains.
-## Difference Between Decoding and Encoding a JWT
+{props.headings[6].title}
Encoding a JWT involves transforming the header and payload into a compact, URL-safe format. The header, which states the signing algorithm and token type, and the payload, which includes claims like subject, expiration, and issue time, are both converted to JSON then Base64URL encoded. These encoded parts are then concatenated with a dot, after which a signature is generated using the algorithm specified in the header with a secret or private key. This signature is also Base64URL encoded, resulting in the final JWT string that represents the token in a format suitable for transmission or storage.
diff --git a/src/features/introduction/docs/ja.introduction.mdx b/src/features/introduction/docs/ja.introduction.mdx
index 02e51cd0..d0a3bae0 100644
--- a/src/features/introduction/docs/ja.introduction.mdx
+++ b/src/features/introduction/docs/ja.introduction.mdx
@@ -1,19 +1,19 @@
import { MdxAnchorComponent } from "@/features/common/components/markdown/mdx-anchor.component";
import { MarkdownImage } from "@/features/common/components/markdown/markdown-image";
-## JSON Web Tokenとは?
+{props.headings[0].title}
JSON Web Token (JWT) は標準規格(RFC 7519)で定義されています。この仕様は認証、認可の情報をJSON形式でシステム間で安全にやりとりする際に使用できる、コンパクトで自己完結型のフォーマットを定義しています。デジタル署名を付与することもできるため、検証可能で信頼がおけます。JWTへの署名には共通鍵を使用するHMACアルゴリズムあるいは、公開鍵/秘密鍵ペアを使用するRSAまたはECDSAアルゴリズムを使用できます。
JWTは機密性を保つために暗号化することもできますが、ここでは 署名されたJWT に焦点を当てます。署名されたJWTは完全性を検証でき、暗号化されたトークンは内容を他者から隠すことができます。JWTが秘密鍵を用いて署名された場合、その署名は公開鍵を使うことで、秘密鍵を持つ当事者だけがそのJWTに署名した者であることが確認できます。
-## JSON Web Tokenはいつ使用すべきか?
+{props.headings[1].title}
JSON Web Tokenが役立つシナリオをいくつかご紹介します。
- **認証および認可**: JWTを使用する最も一般的な利用用途です。ユーザーがログインすると、その後の各リクエストにはJWTが含まれ、ユーザーはそのJWTで許可されたエンドポイント、リソースにアクセスできます。オーバーヘッドが小さく、異なるドメインのシステム間で簡単に使用できるため、シングルサインオンやSPAやモバイルアプリケーション、APIの呼び出しなどでJWTが使用されています。
- **情報交換**: JSON Web Tokenは、システム間で認証や認可の情報を安全にやりとりする際に優れた形式です。JWTには署名できるため、例えば公開鍵/秘密鍵のペアを使用して、送信者が本人であることを確認できます。さらに、署名はヘッダーとペイロードを使って計算して作成されるため、コンテンツが改ざんされていないことも確認できます。
-## JSON Web Tokenの構成は?
+{props.headings[2].title}
JSON Web Tokenはコンパクトな形態で、ドット(.)で区切られた以下の3つの部分で構成されます。
- ヘッダー
@@ -93,7 +93,7 @@ JWTを操作してこれらの概念を実際に試したい場合は、
-## JSON Web Tokenの仕組みとは?
+{props.headings[3].title}
認証では、ユーザーが資格情報を使用して正常にログインすると、JSON Web Token (JWT)が返されます。トークンは資格情報であるため、セキュリティの問題が起きないように細心の注意を払う必要があります。一般的に、トークンは必要以上に長く保持するべきではありません。
保護されたエンドポイントにユーザーがアクセスしたい場合、ブラウザなどのクライアントはJWTを送信する必要があります。通常は、Bearerスキーマを使用してAuthorizationヘッダーで送信します。したがって、ヘッダーの内容は次のようになります。
@@ -116,7 +116,7 @@ JWTトークンをHTTPヘッダーで送信する場合は、トークンのサ
署名付きトークンの場合、トークンに含まれるすべての情報は、ユーザーやアクセスを許可をしているサードパーティーに公開されます。その情報が変更されることはありませんが、トークンの中にユーザやサードパーティに知られたくないような機密情報を入れないようにご注意して下さい。
-## JSON Web Tokenを使用すべき理由とは?
+{props.headings[4].title}
**JSON Web Token (JWT)** の利点について、**Simple Web Token (SWT)** および XML を使用する **Security Assertion Markup Language Token (SAML)** と比較してみましょう。
@@ -134,7 +134,7 @@ _エンコードされたJWTとエンコードされたSAMLの長さの比較_
JSON Web Tokenの詳細や、JSON Web Tokenを使用したアプリケーションの認証については、Auth0のJSON Web Tokenランディングページをご覧ください。
-## JWTのバリデーション(妥当性確認)とベリフィケーション(検証)の違い
+{props.headings[5].title}
JSON Web Token(JWT)のバリデーション(妥当性確認)とベリフィケーション(検証)はセキュリティ上非常に重要ですが、JWTセキュリティの異なる側面に対応しています。バリデーションはトークンが適切に構成され、強制可能なクレームを含んでいることを確認します。ベリフィケーションはトークンが真正で、変更されていないことを確認します。
@@ -161,7 +161,7 @@ JSON Web Token(JWT)のバリデーション(妥当性確認)とベリフ
多くのシステムでは、これらの手順は、包括的なセキュリティチェックにおける妥当性の確認と検証の両方を包含する「JWT検証」と呼ばれるものにまとめられることが多々ありますが、両者の区別は存在します。
-## JWTのデコーディングとエンコーディングの違い
+{props.headings[6].title}
JWTのエンコーディングでは、ヘッダーとペイロードをコンパクトでURLに適した形式に変換します。署名アルゴリズムとトークンタイプを記載するヘッダーと、サブジェクト、有効期限、発行時間などのクレームを含むペイロードの両方をJSONに変換しBase64URLエンコードします。これらのエンコードされた部分は、ドット(.)で連結され、その後、ヘッダーで指定されたアルゴリズムを使用して、シークレットキーまたはプライベートキーで署名が生成されます。この署名もBase64URLエンコードされ、送信や保存に適した形式でトークンを表す最終的なJWT文字列が生成されます。
diff --git a/src/features/localization/dictionaries/introduction/en.ts b/src/features/localization/dictionaries/introduction/en.ts
index 41aab194..23695736 100644
--- a/src/features/localization/dictionaries/introduction/en.ts
+++ b/src/features/localization/dictionaries/introduction/en.ts
@@ -44,4 +44,36 @@ export const enIntroductionDictionary: IntroductionDictionaryModel = {
description:
"Learn about JSON Web Tokens, what are they, how they work, when and why you should use them.",
},
+ content: {
+ headings: [
+ {
+ title: "What is JSON Web Token?",
+ id: "what-is-json-web-token",
+ },
+ {
+ title: "When should you use JSON Web Tokens?",
+ id: "when-to-use-json-web-tokens",
+ },
+ {
+ title: "What is the JSON Web Token structure?",
+ id: "what-is-json-web-token-structure",
+ },
+ {
+ title: "How do JSON Web Tokens work?",
+ id: "how-json-web-tokens-work",
+ },
+ {
+ title: "Why should we use JSON Web Tokens?",
+ id: "why-use-json-web-tokens",
+ },
+ {
+ title: "Difference Between Validating and Verifying a JWT",
+ id: "difference-validating-verifying-jwt",
+ },
+ {
+ title: "Difference Between Decoding and Encoding a JWT",
+ id: "difference-decoding-encoding-jwt",
+ },
+ ],
+ },
};
diff --git a/src/features/localization/dictionaries/introduction/ja.ts b/src/features/localization/dictionaries/introduction/ja.ts
index 816173f3..129338d2 100644
--- a/src/features/localization/dictionaries/introduction/ja.ts
+++ b/src/features/localization/dictionaries/introduction/ja.ts
@@ -44,4 +44,37 @@ export const jaIntroductionDictionary: IntroductionDictionaryModel = {
description:
"JSON Web Tokenとは何か、どのように機能するのか、いつ、なぜ使用する必要があるのかについて学びましょう。",
},
+ content: {
+ headings: [
+ {
+ title: "JSON Web Tokenとは?",
+ id: "what-is-json-web-token",
+ },
+ {
+ title: "JSON Web Tokenはいつ使用すべきか?",
+ id: "when-to-use-json-web-tokens",
+ },
+ {
+ title: "JSON Web Tokenの構成は?",
+ id: "what-is-json-web-token-structure",
+ },
+ {
+ title: "JSON Web Tokenの仕組みとは?",
+ id: "how-json-web-tokens-work",
+ },
+ {
+ title: "JSON Web Tokenを使用すべき理由とは?",
+ id: "why-use-json-web-tokens",
+ },
+ {
+ title:
+ "JWTのバリデーション(妥当性確認)とベリフィケーション(検証)の違い",
+ id: "difference-validating-verifying-jwt",
+ },
+ {
+ title: "JWTのデコーディングとエンコーディングの違い",
+ id: "difference-decoding-encoding-jwt",
+ },
+ ],
+ },
};
diff --git a/src/features/localization/models/introduction-dictionary.model.ts b/src/features/localization/models/introduction-dictionary.model.ts
index 0d258092..3a13304f 100644
--- a/src/features/localization/models/introduction-dictionary.model.ts
+++ b/src/features/localization/models/introduction-dictionary.model.ts
@@ -4,4 +4,10 @@ import { HeroMetadataModel } from "@/features/common/models/hero-metadata.model"
export interface IntroductionDictionaryModel {
metadata: PageMetadataModel;
hero: HeroMetadataModel;
+ content: {
+ headings: {
+ title: string,
+ id: string
+ }[]
+ }
}
diff --git a/src/features/localization/services/ui-language-content.service.tsx b/src/features/localization/services/ui-language-content.service.tsx
index a9c806b8..9d4cd430 100644
--- a/src/features/localization/services/ui-language-content.service.tsx
+++ b/src/features/localization/services/ui-language-content.service.tsx
@@ -4,27 +4,17 @@ import React from "react";
import EnIntroduction from "@/features/introduction/docs/en.introduction.mdx";
import JaIntroduction from "@/features/introduction/docs/ja.introduction.mdx";
+import { getIntroductionDictionary } from "./language-dictionary.service";
interface GetIntroductionContentParams {
languageCode: string;
}
-interface GetIntroductionContentResult {
- Component: React.FC;
-}
-
export const getIntroductionContent = (
- params: GetIntroductionContentParams,
-): GetIntroductionContentResult => {
+ params: GetIntroductionContentParams
+) => {
const { languageCode } = params;
-
- if (languageCode === "ja") {
- return {
- Component: JaIntroduction,
- };
- }
-
- return {
- Component: EnIntroduction,
- };
+ const introductionDictionary = getIntroductionDictionary(languageCode)
+ const headings = introductionDictionary.content.headings
+ return languageCode === "ja" ? : ;
};