Skip to content

Commit

Permalink
feat(next): add getAccessToken and getOrganizationToken for pages rou…
Browse files Browse the repository at this point in the history
…ter (#782)
  • Loading branch information
wangsijie authored Aug 12, 2024
1 parent 750b9c3 commit 5610505
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 76 deletions.
7 changes: 7 additions & 0 deletions .changeset/angry-feet-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@logto/next': minor
---

add getAccessToken and getOrganizationToken methods for pages router

You can now use `getAccessToken(request, response, 'resource-indicator')` to get access token directly in your pages router. And `getOrganizationToken` is also available to get organization token.
20 changes: 20 additions & 0 deletions packages/next-sample/pages/api/organization-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { logtoClient } from '../../libraries/logto';

export default logtoClient.withLogtoApiRoute(async (request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });

return;
}

const organizationToken = await logtoClient.getOrganizationToken(
request,
response,
'organization_id'
);

// Do something with the organization token
console.log(organizationToken);

response.end();
});
28 changes: 0 additions & 28 deletions packages/next-sample/pages/api/organizations.ts

This file was deleted.

30 changes: 13 additions & 17 deletions packages/next-sample/pages/api/protected-resource.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { logtoClient } from '../../libraries/logto';

export default logtoClient.withLogtoApiRoute(
(request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });
export default logtoClient.withLogtoApiRoute(async (request, response) => {
if (!request.user.isAuthenticated) {
response.status(401).json({ message: 'Unauthorized' });

return;
}
return;
}

// Get an access token with the target resource
// console.log(request.user.accessToken);
// Get an access token with the target resource
const accessToken = await logtoClient.getAccessToken(request, response, 'resource-indicator');
// Do something with the access token
console.log(accessToken);

response.json({
data: 'this_is_protected_resource',
});
},
{
// (Optional) getAccessToken: true,
// (Optional) resource: 'https://the-resource.domain/'
}
);
response.json({
data: 'this_is_protected_resource',
});
});
31 changes: 0 additions & 31 deletions packages/next-sample/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { type LogtoContext } from '@logto/next';
import Link from 'next/link';
import { useMemo } from 'react';
import useSWR from 'swr';

Expand All @@ -8,11 +7,6 @@ const Home = () => {
const { data } = useSWR<LogtoContext>('/api/logto/user');
// Remote full user info
const { data: dataWithUserInfo } = useSWR<LogtoContext>('/api/logto/user-info');
const { data: protectedResource } = useSWR<{ data: string }>('/api/protected-resource');
const { data: rbacResponse, error: rbacResponseError } = useSWR<{ data: string }, Error>(
'/api/rbac-scope'
);
const { data: organizationsInfo } = useSWR<{ organizations: string[] }>('/api/organizations');

const claims = useMemo(() => {
if (!data?.isAuthenticated || !data.claims) {
Expand Down Expand Up @@ -96,31 +90,6 @@ const Home = () => {
</nav>
{claims}
{userInfo}
{protectedResource && (
<div>
<h2>Protected resource:</h2>
<div>{protectedResource.data}</div>
<h3>
<Link href="/protected">Example1: Require sign in and auto redirect</Link>
</h3>
<h3>
<Link href="/profile-ssr">Example2: Server-render page with getServerSideProps</Link>
</h3>
</div>
)}
<div>
<h2>Protected by RBAC scope:</h2>
<div>{rbacResponse?.data}</div>
<div>{rbacResponseError && 'Access denied.'}</div>
</div>
{organizationsInfo && (
<div>
<h2>Organizations</h2>
{organizationsInfo.organizations.map((organizationId) => (
<div key={organizationId}>{organizationId}</div>
))}
</div>
)}
</div>
);
};
Expand Down
38 changes: 38 additions & 0 deletions packages/next/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const getIdTokenClaims = vi.fn(() => ({
}));
const signOut = vi.fn();
const getContext = vi.fn(async () => true);
const getAccessToken = vi.fn();
const getOrganizationToken = vi.fn();

const mockResponse = (_: unknown, response: NextApiResponse) => {
response.status(200).end();
Expand Down Expand Up @@ -59,6 +61,8 @@ vi.mock('@logto/node', async (importOriginal) => ({
},
handleSignInCallback,
getContext,
getAccessToken,
getOrganizationToken,
getIdTokenClaims,
signOut: () => {
navigate(configs.baseUrl);
Expand Down Expand Up @@ -123,6 +127,40 @@ describe('Next', () => {
});
});

describe('getAccessToken', () => {
it('should call client.getAccessToken', async () => {
const client = new LogtoClient(configs);
await testApiHandler({
pagesHandler: async (request, response) => {
await client.getAccessToken(request, response, 'resource');
response.end();
},
url: '/api/logto/get-access-token',
test: async ({ fetch }) => {
await fetch({ method: 'GET' });
expect(getAccessToken).toHaveBeenCalledWith('resource');
},
});
});
});

describe('getOrganizationToken', () => {
it('should call client.getOrganizationToken', async () => {
const client = new LogtoClient(configs);
await testApiHandler({
pagesHandler: async (request, response) => {
await client.getOrganizationToken(request, response, 'organization_id');
response.end();
},
url: '/api/logto/get-access-token',
test: async ({ fetch }) => {
await fetch({ method: 'GET' });
expect(getOrganizationToken).toHaveBeenCalledWith('organization_id');
},
});
});
});

describe('withLogtoApiRoute', () => {
it('should assign `user` to `request`', async () => {
const client = new LogtoClient(configs);
Expand Down
20 changes: 20 additions & 0 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
type GetServerSidePropsResult,
type GetServerSidePropsContext,
type NextApiHandler,
type NextApiRequest,
type NextApiResponse,
} from 'next';
import { type NextApiRequestCookies } from 'next/dist/server/api-utils/index.js';

Expand Down Expand Up @@ -128,6 +130,24 @@ export default class LogtoClient extends LogtoNextBaseClient {
response.status(404).end();
};

getAccessToken = async (
request: NextApiRequest,
response: NextApiResponse,
resource: string
): Promise<string> => {
const nodeClient = await this.createNodeClientFromNextApi(request, response);
return nodeClient.getAccessToken(resource);
};

getOrganizationToken = async (
request: NextApiRequest,
response: NextApiResponse,
organizationId: string
): Promise<string> => {
const nodeClient = await this.createNodeClientFromNextApi(request, response);
return nodeClient.getOrganizationToken(organizationId);
};

withLogtoApiRoute = (
handler: NextApiHandler,
config: GetContextParameters = {},
Expand Down

0 comments on commit 5610505

Please sign in to comment.