0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor(console): update next guide (#6119)

This commit is contained in:
wangsijie 2024-06-27 17:29:25 +08:00 committed by GitHub
parent bd0487ecc3
commit 211c3576d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 107 additions and 243 deletions

View file

@ -26,7 +26,7 @@ To learn more about the rationale and benefits of redirect-based sign-in, see [L
In the following steps, we assume your app is running on <code>http://localhost:3000</code>. In the following steps, we assume your app is running on <code>http://localhost:3000</code>.
</InlineNotification> </InlineNotification>
Now, let's enter your redirect URI. E.g. `http://localhost:3000/callback`. Now, let's enter your redirect URI. E.g. {`${props.callbackUri ?? 'http://localhost:3000/callback'}`}.
<UriInputField name="redirectUris" /> <UriInputField name="redirectUris" />

View file

@ -1,17 +1,18 @@
import UriInputField from '@/mdx-components/UriInputField';
import Tabs from '@mdx/components/Tabs'; import Tabs from '@mdx/components/Tabs';
import TabItem from '@mdx/components/TabItem'; import TabItem from '@mdx/components/TabItem';
import InlineNotification from '@/ds-components/InlineNotification';
import { generateStandardSecret } from '@logto/shared/universal'; import { generateStandardSecret } from '@logto/shared/universal';
import Steps from '@/mdx-components/Steps'; import Steps from '@/mdx-components/Steps';
import Step from '@/mdx-components/Step'; import Step from '@/mdx-components/Step';
import Checkpoint from '../../fragments/_checkpoint.md';
import RedirectUris from '../../fragments/_redirect_uris.mdx';
<Steps> <Steps>
<Step <Step
title="Add Logto SDK as a dependency" title="Installation"
subtitle="Please select your favorite package manager" subtitle="Install Logto SDK"
> >
<Tabs> <Tabs>
<TabItem value="npm" label="npm"> <TabItem value="npm" label="npm">
@ -41,13 +42,9 @@ pnpm add @logto/next
title="Prepare configs" title="Prepare configs"
> >
<InlineNotification> Prepare configuration for the Logto client:
In the following steps, we assume your app is running on <code>http://localhost:3000</code>.
</InlineNotification>
Prepare configuration for the Logto client. Create a new file `app/logto.ts` and add the following code: <Code title="app/logto.ts" className="language-ts">
<Code className="language-ts">
{`export const logtoConfig = { {`export const logtoConfig = {
endpoint: '${props.endpoint}', endpoint: '${props.endpoint}',
appId: '${props.app.id}', appId: '${props.app.id}',
@ -61,21 +58,12 @@ Prepare configuration for the Logto client. Create a new file `app/logto.ts` and
</Step> </Step>
<Step <Step
title="Sign in" title="Implement callback route"
> >
### Configure Redirect URI Add a callback route to your app:
First, lets enter your redirect URI. E.g. `http://localhost:3000/callback`. ```tsx title="/app/callback/route.ts"
<UriInputField name="redirectUris" />
### Implement callback page
Add a callback page to your app:
```tsx
// pages/callback/page.tsx
import { handleSignIn } from '@logto/next/server-actions'; import { handleSignIn } from '@logto/next/server-actions';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { NextRequest } from 'next/server'; import { NextRequest } from 'next/server';
@ -89,12 +77,26 @@ export async function GET(request: NextRequest) {
} }
``` ```
### Implement sign-in button </Step>
The sign-in button will call the method we just created, it is a client component: <Step
title="Configure redirect URIs"
```tsx subtitle="2 URIs"
// app/sign-in.tsx >
<RedirectUris />
</Step>
<Step
title="Implement sign-in and sign-out"
>
### Implement sign-in and sign-out button
In Next.js App Router, events are handled in client components, so we need to create two components first: `SignIn` and `SignOut`.
```tsx title="/app/sign-in.tsx"
'use client'; 'use client';
type Props = { type Props = {
@ -116,53 +118,7 @@ const SignIn = ({ onSignIn }: Props) => {
export default SignIn; export default SignIn;
``` ```
### Add sign in button to home page ```tsx title="/app/sign-out.tsx"
We're almost there! Add this button to home page at `/app/page.tsx` and implement the `onSignIn` function:
```tsx
import { signIn } from '@logto/next/server-actions';
import SignIn from './sign-in';
import { logtoConfig } from './logto';
export default async function Home() {
return (
<main>
<h1>Hello Logto.</h1>
<div>
<SignIn
onSignIn={async () => {
'use server';
await signIn(logtoConfig);
}}
/>
</div>
</main>
);
}
```
Now you will be navigated to Logto sign-in page when you click the button.
</Step>
<Step
title="Sign out"
>
### Configure URI
After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below.
<UriInputField name="postLogoutRedirectUris" />
### Implement a sign-out button
The sign-out button is also a client component, so we will create it in `/app/sign-out.tsx`:
```tsx
// app/sign-out.tsx
'use client'; 'use client';
type Props = { type Props = {
@ -184,63 +140,26 @@ const SignOut = ({ onSignOut }: Props) => {
export default SignOut; export default SignOut;
``` ```
### Add sign out button to home page Remember to add `'use client'` to the top of the file to indicate that these components are client components.
Then add the sign-out button to the home page in `/app/page.tsx`: ### Add buttons to home page
```tsx Now let's add the sign-in and sign-out buttons in your hoem page. We need to call the server actions in SDK when needed. To help with this, use `getLogtoContext` to fetch authentication status.
import { signIn, signOut } from '@logto/next/server-actions';
import SignIn from './sign-in';
import SignOut from './sign-out';
import { logtoConfig } from './logto';
export default async function Home() { ```tsx title="/app/page.tsx"
return (
<main>
<h1>Hello Logto.</h1>
<div>
<SignOut
onSignOut={async () => {
'use server';
await signOut(logtoConfig);
}}
/>
<SignIn
onSignIn={async () => {
'use server';
await signIn(logtoConfig);
}}
/>
</div>
</main>
);
}
```
</Step>
<Step
title="Handle authentication state"
>
We can call the function `getLogtoContext` to get context as the authentication state in pages, let's modify the home page:
```tsx
import { getLogtoContext, signIn, signOut } from '@logto/next/server-actions'; import { getLogtoContext, signIn, signOut } from '@logto/next/server-actions';
import SignIn from './sign-in'; import SignIn from './sign-in';
import SignOut from './sign-out'; import SignOut from './sign-out';
import { logtoConfig } from './logto'; import { logtoConfig } from './logto';
export default async function Home() { const Home = () => {
const { isAuthenticated } = await getLogtoContext(logtoConfig); const { isAuthenticated, claims } = await getLogtoContext(logtoConfig);
return ( return (
<main> <nav>
<h1>Hello Logto.</h1> {isAuthenticated ? (
<div> <p>
{isAuthenticated ? ( Hello, {claims?.sub},
<SignOut <SignOut
onSignOut={async () => { onSignOut={async () => {
'use server'; 'use server';
@ -248,7 +167,9 @@ export default async function Home() {
await signOut(logtoConfig); await signOut(logtoConfig);
}} }}
/> />
) : ( </p>
) : (
<p>
<SignIn <SignIn
onSignIn={async () => { onSignIn={async () => {
'use server'; 'use server';
@ -256,13 +177,23 @@ export default async function Home() {
await signIn(logtoConfig); await signIn(logtoConfig);
}} }}
/> />
)} </p>
</div> )}
</main> </nav>
); );
} };
export default Home;
``` ```
</Step> </Step>
<Step
title="Checkpoint: Test your application"
>
<Checkpoint />
</Step>
</Steps> </Steps>

View file

@ -1,17 +1,18 @@
import UriInputField from '@/mdx-components/UriInputField';
import Tabs from '@mdx/components/Tabs'; import Tabs from '@mdx/components/Tabs';
import TabItem from '@mdx/components/TabItem'; import TabItem from '@mdx/components/TabItem';
import InlineNotification from '@/ds-components/InlineNotification';
import { generateStandardSecret } from '@logto/shared/universal'; import { generateStandardSecret } from '@logto/shared/universal';
import Steps from '@/mdx-components/Steps'; import Steps from '@/mdx-components/Steps';
import Step from '@/mdx-components/Step'; import Step from '@/mdx-components/Step';
import Checkpoint from '../../fragments/_checkpoint.md';
import RedirectUris from '../../fragments/_redirect_uris.mdx';
<Steps> <Steps>
<Step <Step
title="Installation" title="Installation"
subtitle="Install Logto SDK for your project" subtitle="Install Logto SDK"
> >
<Tabs> <Tabs>
<TabItem value="npm" label="npm"> <TabItem value="npm" label="npm">
@ -41,15 +42,10 @@ pnpm add @logto/next
title="Init LogtoClient" title="Init LogtoClient"
> >
<InlineNotification>
In the following steps, we assume your app is running on <code>http://localhost:3000</code>.
</InlineNotification>
Import and initialize LogtoClient: Import and initialize LogtoClient:
<Code className="language-ts"> <Code title="libraries/logto.js" className="language-ts">
{`// libraries/logto.js {`import LogtoClient from '@logto/next';
import LogtoClient from '@logto/next';
export const logtoClient = new LogtoClient({ export const logtoClient = new LogtoClient({
endpoint: '${props.endpoint}', endpoint: '${props.endpoint}',
@ -63,24 +59,13 @@ export const logtoClient = new LogtoClient({
</Step> </Step>
<Step <Step title="Prepare API routes">
title="Implement sign-in"
>
### Configure Redirect URI
First, lets enter your redirect URI. E.g. `http://localhost:3000/api/logto/sign-in-callback`.
<UriInputField name="redirectUris" />
### Prepare API routes
Prepare [API routes](https://nextjs.org/docs/api-routes/introduction) to connect with Logto. Prepare [API routes](https://nextjs.org/docs/api-routes/introduction) to connect with Logto.
Go back to your IDE/editor, use the following code to implement the API routes first: Go back to your IDE/editor, use the following code to implement the API routes first:
```ts ```ts title="pages/api/logto/[action].ts"
// pages/api/logto/[action].ts
import { logtoClient } from '../../../libraries/logto'; import { logtoClient } from '../../../libraries/logto';
export default logtoClient.handleAuthRoutes(); export default logtoClient.handleAuthRoutes();
@ -93,122 +78,70 @@ This will create 4 routes automatically:
3. `/api/logto/sign-out`: Sign out with Logto. 3. `/api/logto/sign-out`: Sign out with Logto.
4. `/api/logto/user`: Check if user is authenticated with Logto, if yes, return user info. 4. `/api/logto/user`: Check if user is authenticated with Logto, if yes, return user info.
### Implement sign-in button </Step>
We're almost there! In the last step, we will create a sign-in button: <Step
title="Configure redirect URIs"
subtitle="2 URIs"
>
```tsx <RedirectUris callbackUri="http://localhost:3000/api/logto/sign-in-callback" />
import { useRouter } from 'next/router';
const { push } = useRouter();
<button onClick={() => push('/api/logto/sign-in')}>Sign In</button>;
```
Now you will be navigated to Logto sign-in page when you click the button.
</Step> </Step>
<Step <Step
title="Implement sign-out" title="Implement sign-in and sign-out"
> >
Calling `/api/logto/sign-out` will clear all the Logto data in memory and cookies if they exist. We have prepared the API routes, now let's implement the sign-in and sign-out buttons in your home page. We need to redirect the user to the sign-in or sign-out route when needed. To help with this, use `useSWR` to fetch authentication status from `/api/logto/user`.
After signing out, it'll be great to redirect user back to your website. Let's add `http://localhost:3000` as the Post Sign-out URI below before calling `/api/logto/sign-out`.
<UriInputField name="postLogoutRedirectUris" />
### Implement a sign-out button
```tsx
<button onClick={() => push('/api/logto/sign-out')}>Sign Out</button>
```
</Step>
<Step
title="Handle authentication status"
>
### Through API request in the frontend
You can fetch user info by calling `/api/logto/user`.
```tsx
import { LogtoUser } from '@logto/next';
import useSWR from 'swr';
const Home = () => {
const { data } = useSWR<LogtoUser>('/api/logto/user');
return <div>User ID: {data?.claims?.sub}</div>;
};
export default Profile;
```
Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`. Check [this guide](https://swr.vercel.app/docs/getting-started) to learn more about `useSWR`.
### Through `getServerSideProps` in the backend ```tsx title="/pages/index.tsx"
import { type LogtoContext } from '@logto/next';
import useSWR from 'swr';
```tsx const Home = () => {
import { LogtoUser } from '@logto/next'; const { data } = useSWR<LogtoContext>('/api/logto/user');
import { logtoClient } from '../libraries/logto';
type Props = { return (
user: LogtoUser; <nav>
{data?.isAuthenticated ? (
<p>
Hello, {data.claims?.sub},
<button
onClick={() => {
window.location.assign('/api/logto/sign-out');
}}
>
Sign Out
</button>
</p>
) : (
<p>
<button
onClick={() => {
window.location.assign('/api/logto/sign-in');
}}
>
Sign In
</button>
</p>
)}
</nav>
);
}; };
const Profile = ({ user }: Props) => { export default Home;
return <div>User ID: {user.claims?.sub}</div>;
};
export default Profile;
export const getServerSideProps = logtoClient.withLogtoSsr(({ request }) => {
const { user } = request;
return {
props: { user },
};
});
``` ```
Check [Next.js documentation](https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props) for more details on `getServerSideProps`.
### Protect API routes
Wrap your handler with `logtoClient.withLogtoApiRoute`.
```ts
// pages/api/protected-resource.ts
import { logtoClient } from '../../libraries/logto';
export default logtoClient.withLogtoApiRoute((request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });
return;
}
response.json({
data: 'this_is_protected_resource',
});
});
```
</Step> </Step>
<Step <Step
title="Checkpoint: Test your application" title="Checkpoint: Test your application"
> >
Now, you can test your application: <Checkpoint />
1. Run your application, you will see the sign-in button.
2. Click the sign-in button, and you will be redirected to the sign in route, and the SDK will then init the sign-in process and redirect to the Logto sign-in page.
3. After you signed in, you will be redirect back to your application and see user id and the sign-out button.
4. Click the sign-out button to sign-out.
</Step> </Step>