0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor: update nuxt guide (#6114)

* refactor: update nuxt guide

* refactor: polish content
This commit is contained in:
Gao Sun 2024-06-27 13:06:44 +08:00 committed by GitHub
parent 42cef43cb1
commit 8fa440579b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 125 additions and 45 deletions

View file

@ -117,6 +117,7 @@
"react-syntax-highlighter": "^15.5.0",
"react-timer-hook": "^3.0.5",
"recharts": "^2.1.13",
"rehype-mdx-code-props": "^3.0.1",
"remark-gfm": "^4.0.0",
"stylelint": "^15.0.0",
"swr": "^2.2.0",

View file

@ -3,6 +3,7 @@
import { compile } from '@mdx-js/mdx';
import { default as ThrowableDiagnostic } from '@parcel/diagnostic';
import { Transformer } from '@parcel/plugin';
import rehypeMdxCodeProps from 'rehype-mdx-code-props';
export default new Transformer({
async transform({ asset }) {
@ -15,6 +16,7 @@ export default new Transformer({
development: true,
jsx: true,
providerImportSource: '@mdx-js/react',
rehypePlugins: [[rehypeMdxCodeProps, { tagName: 'code' }]],
});
} catch (error) {
const { start, end } = error.position;

View file

@ -0,0 +1,20 @@
Before we dive into the details, here's a quick overview of the end-user experience. The sign-in process can be simplified as follows:
```mermaid
graph LR
A(Your app) -->|1. Invoke sign-in| B(Logto)
B -->|2. Finish sign-in| A
```
1. Your app invokes the sign-in method.
2. The user is redirected to the Logto sign-in page. For native apps, the system browser is opened.
3. The user signs in and is redirected back to your app (configured as the redirect URI).
Regarding redirect-based sign-in:
1. This authentication process follows the [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) protocol, and Logto enforces strict security measures to protect user sign-in.
2. If you have multiple apps, you can use the same identity provider (Logto). Once the user signs in to one app, Logto will automatically complete the sign-in process when the user accesses another app.
To learn more about the rationale and benefits of redirect-based sign-in, see [Logto sign-in experience explained](https://docs.logto.io/docs/tutorials/get-started/sign-in-experience).
---

View file

@ -5,20 +5,22 @@ import InlineNotification from '@/ds-components/InlineNotification';
import Steps from '@/mdx-components/Steps';
import Step from '@/mdx-components/Step';
import Checkpoint from '../../fragments/_checkpoint.md';
import SignInExplanation from '../../fragments/_sign_in_explanation.md';
import { generateStandardSecret } from '@logto/shared/universal';
export const cookieEncryptionKey = generateStandardSecret();
<Steps>
<InlineNotification severity="alert">
Logto Nuxt SDK only works with Nuxt 3.
</InlineNotification>
<Step
title="Installation"
subtitle="Install Logto SDK"
>
<InlineNotification severity="alert">
Logto Nuxt SDK only works with Nuxt 3.
</InlineNotification>
<Tabs>
<TabItem value="npm" label="npm">
@ -46,18 +48,9 @@ pnpm add @logto/nuxt
<Step title="Register Logto module">
In your Nuxt config file (`nuxt.config.ts`), add the Logto module:
In your Nuxt config file, add the Logto module and configure it:
```ts
export default defineNuxtConfig({
modules: ['@logto/nuxt'],
// ...other configurations
});
```
The minimal configuration for the module is as follows:
<Code className="language-tsx">
<Code title="nuxt.config.ts" className="language-tsx">
{`export default defineNuxtConfig({
modules: ['@logto/nuxt'],
runtimeConfig: {
@ -74,7 +67,7 @@ The minimal configuration for the module is as follows:
Since these information are sensitive, it's recommended to use environment variables (`.env`):
<Code className="language-bash">
<Code title=".env" className="language-bash">
{`NUXT_LOGTO_ENDPOINT=${props.endpoint}
NUXT_LOGTO_APP_ID=${props.app.id}
NUXT_LOGTO_APP_SECRET=${props.app.secret}
@ -88,15 +81,17 @@ See [runtime config](https://nuxt.com/docs/guide/going-further/runtime-config) f
<Step title="Configure your app">
<SignInExplanation />
<InlineNotification>
In the following steps, we assume your app is running on <code>http://localhost:3000</code>.
</InlineNotification>
First, let's enter your redirect URI. E.g. `http://localhost:3000/callback`. [Redirect URI](https://www.oauth.com/oauth2-servers/redirect-uris/) is an OAuth 2.0 concept which implies the location should redirect after authentication.
Now, let's enter your redirect URI. E.g. `http://localhost:3000/callback`.
<UriInputField name="redirectUris" />
After signing out, it'll be great to redirect user back to your website. For example, add `http://localhost:3000` as the post sign-out redirect URI below.
Just like signing in, users should be redirected to Logto for signing out of the shared session. Once finished, it would be great to redirect the user back to your website. For example, add `http://localhost:3000` as the post sign-out redirect URI below.
<UriInputField name="postLogoutRedirectUris" />
@ -107,7 +102,7 @@ When registering `@logto/nuxt` module, it will do the following:
These routes are configurable via `logto.pathnames` in the module options, for example:
```ts
```ts title="nuxt.config.ts"
export default defineNuxtConfig({
logto: {
pathnames: {
@ -122,32 +117,24 @@ export default defineNuxtConfig({
Check out the [type definition file](https://github.com/logto-io/js/blob/HEAD/packages/nuxt/src/runtime/utils/types.ts) in the `@logto/nuxt` package for more information.
Note: If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly.
<InlineNotification>
If you configure the callback route to a different path, you need to update the redirect URI in Logto accordingly.
</InlineNotification>
</Step>
<Step title="Implement sign-in and sign-out">
Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed.
Since Nuxt pages will be hydrated and become a single-page application (SPA) after the initial load, we need to redirect the user to the sign-in or sign-out route when needed. To help with this, our SDK provides the `useLogtoUser()` composable, which can be used in both server and client side.
```html
<a :href="/sign-in">Sign in</a>
<br />
<a :href="/sign-out">Sign out</a>
```
</Step>
<Step title="Display user information">
To display the user's information, you can use the `useLogtoUser()` composable, which is available on both server and client side:
```html
```html title="index.vue"
<script setup lang="ts">
import { useLogtoUser } from '#imports'; // Add this line if auto-import is disabled
const user = useLogtoUser();
</script>
<template>
<ul v-if="Boolean(user)">
<!-- Display user information when signed in -->
<ul v-if="Boolean(user)">
<li v-for="(value, key) in user"><b>{{ key }}:</b> {{ value }}</li>
</ul>
<!-- Simplified button for sign-in and sign-out -->

View file

@ -8,6 +8,13 @@
position: relative;
overflow-y: auto;
.title {
margin-top: _.unit(-2);
padding-bottom: _.unit(2);
margin-bottom: _.unit(3);
border-bottom: 1px solid var(--color-border);
}
.placeholder {
position: absolute;
inset: _.unit(6);

View file

@ -12,6 +12,7 @@ import { lineNumberContainerStyle, lineNumberStyle, customStyle } from './utils'
type Props = {
readonly className?: string;
readonly title?: string;
readonly language?: string;
readonly isReadonly?: boolean;
readonly value?: string;
@ -23,6 +24,7 @@ type Props = {
function CodeEditor({
className,
title,
language,
isReadonly = false,
value,
@ -85,6 +87,7 @@ function CodeEditor({
return (
<>
<div className={classNames(styles.container, className)}>
{title && <pre className={styles.title}>{title}</pre>}
{isShowingPlaceholder && <div className={styles.placeholder}>{placeholder}</div>}
<CopyToClipboard value={value ?? ''} variant="icon" className={styles.copy} />
<div ref={editorRef} className={classNames(styles.editor, isReadonly && styles.readonly)}>

View file

@ -2,7 +2,7 @@ import CodeEditor from '@/ds-components/CodeEditor';
import Mermaid from '../Mermaid';
export default function Code({ className, children }: JSX.IntrinsicElements['code']) {
export default function Code({ className, children, title }: JSX.IntrinsicElements['code']) {
const [, language] = /language-(\w+)/.exec(String(className ?? '')) ?? [];
if (language === 'mermaid') {
@ -17,6 +17,7 @@ export default function Code({ className, children }: JSX.IntrinsicElements['cod
// To be investigated.
language={language === 'ts' ? 'typescript' : language}
value={String(children).trimEnd()}
title={title}
/>
) : (
<code>{String(children).trimEnd()}</code>

View file

@ -8,7 +8,7 @@ import { onKeyDownHandler } from '@/utils/a11y';
import Sample from '../Sample';
import { type Props as StepProps } from '../Step';
import type Step from '../Step';
import Step from '../Step';
import FurtherReadings from './FurtherReadings';
import * as styles from './index.module.scss';
@ -47,7 +47,10 @@ export default function Steps({ children: reactChildren }: Props) {
[metadata.fullGuide]
);
const children: Array<ReactElement<StepProps, typeof Step>> = useMemo(() => {
const steps = Array.isArray(reactChildren) ? reactChildren : [reactChildren];
const steps = (Array.isArray(reactChildren) ? reactChildren : [reactChildren]).filter(
(child) => child.type === Step
);
return isApiResourceGuide ? steps : steps.concat(furtherReadings);
}, [isApiResourceGuide, furtherReadings, reactChildren]);

View file

@ -3127,6 +3127,9 @@ importers:
recharts:
specifier: ^2.1.13
version: 2.1.13(prop-types@15.8.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
rehype-mdx-code-props:
specifier: ^3.0.1
version: 3.0.1
remark-gfm:
specifier: ^4.0.0
version: 4.0.0
@ -8512,6 +8515,9 @@ packages:
estree-util-to-js@2.0.0:
resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==}
estree-util-value-to-estree@3.1.2:
resolution: {integrity: sha512-S0gW2+XZkmsx00tU2uJ4L9hUT7IFabbml9pHh2WQqFmAbxit++YGZne0sKJbNwkj9Wvg9E4uqWl4nCIFQMmfag==}
estree-util-visit@2.0.0:
resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==}
@ -9040,6 +9046,9 @@ packages:
hast-util-parse-selector@2.2.5:
resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==}
hast-util-properties-to-mdx-jsx-attributes@1.0.0:
resolution: {integrity: sha512-MZEdAYiXC8wDBfntAc7syyWHbcg/X1h03DQ7IQ6MKagMttpYhnKqOZR/nia0657Dt2v2vuXB8YuKNExw0Fljew==}
hast-util-to-estree@3.1.0:
resolution: {integrity: sha512-lfX5g6hqVh9kjS/B9E2gSkvHH4SZNiQFiqWS0x9fENzEl+8W12RqdRxX6d/Cwxi30tPQs3bIO+aolQJNp1bIyw==}
@ -9301,6 +9310,9 @@ packages:
inline-style-parser@0.1.1:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
inline-style-parser@0.2.3:
resolution: {integrity: sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==}
inquirer@9.1.4:
resolution: {integrity: sha512-9hiJxE5gkK/cM2d1mTEnuurGTAoHebbkX0BYl3h7iEg7FYfuNIom+nDfBCSWtvSnoSrWCeBxqqBZu26xdlJlXA==}
engines: {node: '>=12.0.0'}
@ -11834,6 +11846,9 @@ packages:
resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==}
hasBin: true
rehype-mdx-code-props@3.0.1:
resolution: {integrity: sha512-BWWKn0N6r7/qd7lbLgv5J8of7imz1l1PyCNoY7BH0AOR9JdJlQIfA9cKqTZVEb2h2GPKh473qrBajF0i01fq3A==}
remark-gfm@4.0.0:
resolution: {integrity: sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==}
@ -12382,9 +12397,15 @@ packages:
style-search@0.1.0:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
style-to-js@1.1.12:
resolution: {integrity: sha512-tv+/FkgNYHI2fvCoBMsqPHh5xovwiw+C3X0Gfnss/Syau0Nr3IqGOJ9XiOYXoPnToHVbllKFf5qCNFJGwFg5mg==}
style-to-object@0.4.2:
resolution: {integrity: sha512-1JGpfPB3lo42ZX8cuPrheZbfQ6kqPPnPHlKMyeRYtfKD+0jG+QsXgXN57O/dvJlzlB2elI6dGmrPnl5VPQFPaA==}
style-to-object@1.0.6:
resolution: {integrity: sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==}
stylelint-config-xo@0.22.0:
resolution: {integrity: sha512-E4IoDwgJqG+Q3LjeZGXNi3uOXOH5Sx6mCyxp1V4eaAm1DhuA+3X40c2GtobEIHfCv6itN/T3QKRb4V4/snIxUg==}
engines: {node: '>=16'}
@ -18378,10 +18399,6 @@ snapshots:
dayjs@1.11.6: {}
debug@3.2.7:
dependencies:
ms: 2.1.3
debug@3.2.7(supports-color@5.5.0):
dependencies:
ms: 2.1.3
@ -18854,7 +18871,7 @@ snapshots:
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
debug: 3.2.7(supports-color@5.5.0)
is-core-module: 2.13.1
resolve: 1.22.8
transitivePeerDependencies:
@ -18879,7 +18896,7 @@ snapshots:
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.7.0(eslint@8.57.0)(typescript@5.3.3))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7
debug: 3.2.7(supports-color@5.5.0)
optionalDependencies:
'@typescript-eslint/parser': 7.7.0(eslint@8.57.0)(typescript@5.3.3)
eslint: 8.57.0
@ -18912,7 +18929,7 @@ snapshots:
array.prototype.findlastindex: 1.2.5
array.prototype.flat: 1.3.2
array.prototype.flatmap: 1.3.2
debug: 3.2.7
debug: 3.2.7(supports-color@5.5.0)
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
@ -19148,6 +19165,10 @@ snapshots:
astring: 1.8.3
source-map: 0.7.4
estree-util-value-to-estree@3.1.2:
dependencies:
'@types/estree': 1.0.5
estree-util-visit@2.0.0:
dependencies:
'@types/estree-jsx': 1.0.5
@ -19737,6 +19758,19 @@ snapshots:
hast-util-parse-selector@2.2.5: {}
hast-util-properties-to-mdx-jsx-attributes@1.0.0:
dependencies:
'@types/estree': 1.0.5
'@types/hast': 3.0.1
comma-separated-tokens: 2.0.2
estree-util-value-to-estree: 3.1.2
mdast-util-mdx-jsx: 3.1.2
property-information: 6.2.0
space-separated-tokens: 2.0.1
style-to-js: 1.1.12
transitivePeerDependencies:
- supports-color
hast-util-to-estree@3.1.0:
dependencies:
'@types/estree': 1.0.5
@ -20008,6 +20042,8 @@ snapshots:
inline-style-parser@0.1.1: {}
inline-style-parser@0.2.3: {}
inquirer@9.1.4:
dependencies:
ansi-escapes: 6.0.0
@ -23387,6 +23423,18 @@ snapshots:
dependencies:
jsesc: 0.5.0
rehype-mdx-code-props@3.0.1:
dependencies:
'@types/hast': 3.0.1
hast-util-properties-to-mdx-jsx-attributes: 1.0.0
mdast-util-from-markdown: 2.0.0
mdast-util-mdx: 3.0.0
micromark-extension-mdxjs: 3.0.0
unified: 11.0.3
unist-util-visit-parents: 6.0.1
transitivePeerDependencies:
- supports-color
remark-gfm@4.0.0:
dependencies:
'@types/mdast': 4.0.1
@ -24025,10 +24073,18 @@ snapshots:
style-search@0.1.0: {}
style-to-js@1.1.12:
dependencies:
style-to-object: 1.0.6
style-to-object@0.4.2:
dependencies:
inline-style-parser: 0.1.1
style-to-object@1.0.6:
dependencies:
inline-style-parser: 0.2.3
stylelint-config-xo@0.22.0(stylelint@15.11.0(typescript@5.3.3)):
dependencies:
stylelint: 15.11.0(typescript@5.3.3)