Skip to content
This repository has been archived by the owner on Jul 28, 2024. It is now read-only.

Commit

Permalink
readme
Browse files Browse the repository at this point in the history
  • Loading branch information
asafshen committed Feb 7, 2024
1 parent 5340d2f commit c5604cb
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 27 deletions.
253 changes: 251 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,254 @@
# Descope SDK for NextJS

Notes:
The Descope SDK for NextJS provides convenient access to the Descope for an application written on top of NextJS. You can read more on the [Descope Website](https://descope.com).

- document sessionTokenViaCookie is default to true
This SDK uses under the hood the Descope React SDK and Descope Node SDK
Refer to the [Descope React SDK](https://github.com/descope/react-sdk) and [Descope Node SDK](https://github.com/descope/node-sdk) for more details.

## Requirements

- The SDK supports NextJS version 13 and above.
- A Descope `Project ID` is required for using the SDK. Find it on the [project page in the Descope Console](https://app.descope.com/settings/project).

## Installing the SDK

Install the package with:

```bash
npm i --save @descope/nextjs-sdk
```

## Usage

This section contains guides for App router and Pages router.
For Pages router, see the [Pages Router](#pages-router) section.

### App Router

#### Wrap your app layout with Auth Provider

```js
// src/app/layout.tsx

import { AuthProvider } from '@descope/nextjs-sdk'

export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<AuthProvider
projectId="your-descope-project-id"
>
<html lang="en">
<body>{children}</body>
</html>
</AuthProvider>
)
}
```

Note: `AuthProvider` uses `sessionTokenViaCookie` by default, in order that the [AuthMiddleware](<#Require-authentication-for-application-(Middleware)>) will work out of the box.

#### Use Descope to render Flow

You can use **default flows** or **provide flow id** directly to the Descope component

```js
// Login page, e.g. src/app/sign-in.tsx
import { Descope } from '@descope/react-sdk';
// you can choose flow to run from the following without `flowId` instead
// import { SignInFlow, SignUpFlow, SignUpOrInFlow } from '@descope/react-sdk'

const Page = () => {
return (
<Descope
flowId="sign-up-or-in"
onSuccess={(e) => console.log('Logged in!')}
onError={(e) => console.log('Could not logged in!')}
redirectAfterSuccess="/"
// redirectAfterError="/error-page"
/>
);
};
```

Refer to the [Descope React SDK Section](https://github.com/descope/react-sdk?tab=readme-ov-file#2-provide-flow-id) for a list of available props.

**Note:** Descope is a client component. if the component that renders it is a server component, you cannot pass `onSuccess`/`onError`/`errorTransformer`/`logger` props because they are not serializable. To redirect the user after the flow is completed, use the `redirectAfterSuccess` and `redirectAfterError` props.

#### Client Side Usage

Use the `useDescope`, `useSession` and `useUser` hooks in your components in order to get authentication state, user details and utilities

This can be helpful to implement application-specific logic. Examples:

- Render different components if current session is authenticated
- Render user's content
- Logout button

Note: these hooks should be used in a client component only (For example, component with `use client` notation).

```js
'use client';
import { useDescope, useSession, useUser } from '@descope/nextjs-sdk/client';
import { useCallback } from 'react';

const App = () => {
// NOTE - `useDescope`, `useSession`, `useUser` should be used inside `AuthProvider` context,
// and will throw an exception if this requirement is not met
// useSession retrieves authentication state, session loading status, and session token
const { isAuthenticated, isSessionLoading, sessionToken } = useSession();
// useUser retrieves the logged in user information
const { user } = useUser();
// useDescope retrieves Descope SDK for further operations related to authentication
// such as logout
const sdk = useDescope();

if (isSessionLoading || isUserLoading) {
return <p>Loading...</p>;
}

const handleLogout = useCallback(() => {
sdk.logout();
}, [sdk]);

if (isAuthenticated) {
return (
<>
<p>Hello {user.name}</p>
<button onClick={handleLogout}>Logout</button>
</>
);
}

return <p>You are not logged in</p>;
};
```

#### Server Side Usage

##### Require authentication for application (Middleware)

You can use NextJS Middleware to require authentication for a page/route or a group of pages/routes.

Descope SDK provides a middleware function that can be used to require authentication for a page/route or a group of pages/routes.

```js
// src/middleware.ts
import { authMiddleware } from '@descope/nextjs-sdk/server'

export default authMiddleware({
// The Descope project ID to use for authentication
// Defaults to process.env.DESCOPE_PROJECT_ID
projectId: 'your-descope-project-id'

// The URL to redirect to if the user is not authenticated
// Defaults to process.env.SIGN_IN_ROUTE or '/sign-in' if not provided
redirectUrl?: string

// An array of public routes that do not require authentication
// In addition to the default public routes:
// - process.env.SIGN_IN_ROUTE or /sign-in if not provided
// - process.env.SIGN_UP_ROUTE or /sign-up if not provided
publicRoutes?: string[]
})

export const config = {
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)']
}
```

##### Read session information in server side

use the `session()` helper to read session information in Server Components and Route handlers.

Note: `session()` requires the `authMiddleware` to be used for the Server Component or Route handler that uses it.

Server Component:

```js
// src/app/page.tsx

import { session } from '@descope/nextjs-sdk/server';

async function Page() {
const sessionRes = session();
if (!sessionRes) {
// ...
}
// Use the session jwt or parsed token
const { jwt, token } = sessionRes;
}
```

Route handler:

```js
// src/pages/api/routes.ts
export async function GET() {
const currSession = session();
if (!currSession.isAuthenticated) {
// ...
}

// Use the session jwt or parsed token
const { jwt, token } = currSession;
}
```

#### Access Descope SDK in server side

Use `createSdk` function to create Descope SDK in server side.

Refer to the [Descope Node SDK](https://github.com/descope/node-sdk/?tab=readme-ov-file#authentication-functions) for a list of available functions.

Usage example in Route handler:

```js
// src/pages/api/routes.ts
import { createSdk } from '@descope/nextjs-sdk/server';

const sdk = createSdk({
// The Descope project ID to use for authentication
// Defaults to process.env.DESCOPE_PROJECT_ID
projectId: 'your-descope-project-id',

// The Descope management key to use for management operations
// Defaults to process.env.DESCOPE_MANAGEMENT_KEY
managementKey: 'your-descope-management-key'
});

export async function GET(req) {
const { searchParams } = new URL(req.url);
const loginId = searchParams.get('loginId');

const { ok, data: user } = await sdk.management.user.load(loginId);
if (!ok) {
// ...
}
// Use the user data ...
}
```

### Pages Router

This section is Working in progress :-)
In the meantime, you can see the example in the [Pages Router](/examples/pages-router/) folder.

## Code Example

You can find an example react app in the [examples folder](./examples). - [App Router](/examples/app-router/) - [Pages Router](/examples/pages-router/)

## Learn More

To learn more please see the [Descope Documentation and API reference page](https://docs.descope.com/).

## Contact Us

If you need help you can email [Descope Support](mailto:support@descope.com)

## License

The Descope SDK for React is licensed for use under the terms and conditions of the [MIT license Agreement](./LICENSE).
46 changes: 46 additions & 0 deletions examples/app-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# App Router Example

This example demonstrates how to use NextJS Descope SDK in an App Router.

## Setup

1. Build the sdk package:

```bash
(cd ../../ && npm run build)
```

2. Install dependencies:

```bash
npm install
```

3. Set environment variables using the `.env` file:

```bash
NEXT_PUBLIC_DESCOPE_PROJECT_ID=<Your Descope Project-ID>
NEXT_PUBLIC_DESCOPE_FLOW_ID=<Your Descope Flow-ID>
DESCOPE_MANAGEMENT_KEY=<Your Descope Management Key> # Default is sign-up-or-in
# This is an example of a custom route for the sign-in page
# the /login route is the one that this example uses
SIGN_IN_ROUTE="/login"
```

## Run the example

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

## Usage

This app has the following parts

- Layout `src/app/layout.tsx` - a layout that wraps the app layout with the Auth Provider
- Home page `src/app/page.tsx` - a Server Component that renders a Client Component (`UserDetails`)
- Login page `src/app/login.tsx` - a Server Component that renders Descope Flow Component
- Authentication middleware `src/middleware.ts` - a middleware that checks if the user is authenticated and redirects to the login page if not
- Route handler `src/app/api/route.ts` - a route handler that returns the user's details using the Descope Management SDK. use `curl -H "Authorization: Bearer <Descope-Session-Token>" http://localhost:3000/api` to test it
5 changes: 4 additions & 1 deletion examples/app-router/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export default function Login() {
<h1>App Router Login</h1>
{/* Note that if the component is rendered on the server
you cannot pass onSuccess/onError callbacks because they are not serializable. */}
<Descope flowId="sign-up-or-in" redirectAfterSuccess="/" />
<Descope
flowId={process.env.NEXT_PUBLIC_DESCOPE_FLOW_ID || 'sign-up-or-in'}
redirectAfterSuccess="/"
/>
</div>
);
}
46 changes: 46 additions & 0 deletions examples/pages-router/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Pages Router Example

This example demonstrates how to use NextJS Descope SDK in an Pages Router.

## Setup

1. Build the sdk package:

```bash
(cd ../../ && npm run build)
```

2. Install dependencies:

```bash
npm install
```

3. Set environment variables using the `.env` file:

```bash
NEXT_PUBLIC_DESCOPE_PROJECT_ID=<Your Descope Project-ID>
NEXT_PUBLIC_DESCOPE_FLOW_ID=<Your Descope Flow-ID>
DESCOPE_MANAGEMENT_KEY=<Your Descope Management Key> # Default is sign-up-or-in
# This is an example of a custom route for the sign-in page
# the /login route is the one that this example uses
SIGN_IN_ROUTE="/login"
```

## Run the example

```bash
npm run dev
```

Open [http://localhost:3001](http://localhost:3001) with your browser to see the result.

## Usage

This app has the following parts

- Layout `pages/_app.tsx` - a layout that wraps the app layout with the Auth Provider
- Home page `pages/index.tsx` - a component that renders another Component (`UserDetails`) that uses Descope client hooks
- Login page `pages/login.tsx` - a Component that renders Descope Flow Component
- Authentication middleware `src/middleware.ts` - a middleware that checks if the user is authenticated and redirects to the login page if not
- Route handler `src/pages/api/index.ts` - a route handler that returns the user's details using the Descope Management SDK. use `curl -H "Authorization: Bearer <Descope-Session-Token>" http://localhost:3001/api` to test it
7 changes: 2 additions & 5 deletions examples/pages-router/pages/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { createSdk, session } from '@descope/nextjs-sdk/server';
import { createSdk, getSession } from '@descope/nextjs-sdk/server';

const sdk = createSdk({
projectId: process.env.NEXT_PUBLIC_DESCOPE_PROJECT_ID,
Expand All @@ -10,10 +10,7 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Temporary workaround for the headers issue in pages router
const currentSession = session(
new Headers(req.headers as { [key: string]: string })
);
const currentSession = getSession(req);
if (!currentSession) {
return res.status(401).json({ message: 'Unauthorized' });
}
Expand Down
Loading

0 comments on commit c5604cb

Please sign in to comment.