feat: returns queried user alongside refreshed token (#2813)

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
Jacob Fletcher
2023-07-17 09:35:34 -04:00
committed by GitHub
parent 7927dd485a
commit 2fc03f196e
16 changed files with 350 additions and 112 deletions

29
test/auth/AuthDebug.tsx Normal file
View File

@@ -0,0 +1,29 @@
import React, { useEffect, useState } from 'react';
import { useAuth } from '../../src/admin/components/utilities/Auth';
import { User } from '../../src/auth';
import { UIField } from '../../src/fields/config/types';
export const AuthDebug: React.FC<UIField> = () => {
const [state, setState] = useState<User | null | undefined>();
const { user } = useAuth();
useEffect(() => {
const fetchUser = async () => {
const userRes = await fetch(`/api/users/${user?.id}`)?.then((res) => res.json());
setState(userRes);
};
fetchUser();
}, [user]);
return (
<div id="auth-debug">
<div id="use-auth-result">
{user?.custom as string}
</div>
<div id="users-api-result">
{state?.custom as string}
</div>
</div>
);
};

View File

@@ -2,6 +2,7 @@ import { v4 as uuid } from 'uuid';
import { mapAsync } from '../../src/utilities/mapAsync';
import { buildConfig } from '../buildConfig';
import { devUser } from '../credentials';
import { AuthDebug } from './AuthDebug';
export const slug = 'users';
@@ -36,6 +37,21 @@ export default buildConfig({
saveToJWT: true,
hasMany: true,
},
{
name: 'custom',
label: 'Custom',
type: 'text',
},
{
name: 'authDebug',
label: 'Auth Debug',
type: 'ui',
admin: {
components: {
Field: AuthDebug,
},
},
},
],
},
{
@@ -65,6 +81,7 @@ export default buildConfig({
data: {
email: devUser.email,
password: devUser.password,
custom: 'Hello, world!',
},
});

View File

@@ -1,5 +1,5 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { test, expect } from '@playwright/test';
import { AdminUrlUtil } from '../helpers/adminUrlUtil';
import { initPayloadE2E } from '../helpers/configHelpers';
import { login, saveDocAndAssert } from '../helpers';
@@ -42,5 +42,19 @@ describe('auth', () => {
await saveDocAndAssert(page);
});
test('should have up-to-date user in `useAuth` hook', async () => {
await page.goto(url.account);
await expect(await page.locator('#users-api-result')).toHaveText('Hello, world!');
await expect(await page.locator('#use-auth-result')).toHaveText('Hello, world!');
const field = await page.locator('#field-custom');
await field.fill('Goodbye, world!');
await saveDocAndAssert(page);
await expect(await page.locator('#users-api-result')).toHaveText('Goodbye, world!');
await expect(await page.locator('#use-auth-result')).toHaveText('Goodbye, world!');
});
});
});

View File

@@ -3,6 +3,7 @@ import payload from '../../src';
import { initPayloadTest } from '../helpers/configHelpers';
import { slug } from './config';
import { devUser } from '../credentials';
import type { User } from '../../src/auth';
require('isomorphic-fetch');
@@ -11,6 +12,7 @@ let apiUrl;
const headers = {
'Content-Type': 'application/json',
};
const { email, password } = devUser;
describe('Auth', () => {
@@ -68,6 +70,8 @@ describe('Auth', () => {
describe('logged in', () => {
let token: string | undefined;
let loggedInUser: User | undefined;
beforeAll(async () => {
const response = await fetch(`${apiUrl}/${slug}/login`, {
body: JSON.stringify({
@@ -80,6 +84,7 @@ describe('Auth', () => {
const data = await response.json();
token = data.token;
loggedInUser = data.user;
});
it('should return a logged in user from /me', async () => {
@@ -99,6 +104,7 @@ describe('Auth', () => {
it('should allow authentication with an API key with useAPIKey', async () => {
const apiKey = '0123456789ABCDEFGH';
const user = await payload.create({
collection: slug,
data: {
@@ -107,10 +113,11 @@ describe('Auth', () => {
apiKey,
},
});
const response = await fetch(`${apiUrl}/${slug}/me`, {
headers: {
...headers,
Authorization: `${slug} API-Key ${user.apiKey}`,
Authorization: `${slug} API-Key ${user?.apiKey}`,
},
});
@@ -135,6 +142,30 @@ describe('Auth', () => {
expect(data.refreshedToken).toBeDefined();
});
it('should refresh a token and receive an up-to-date user', async () => {
expect(loggedInUser?.custom).toBe('Hello, world!');
await payload.update({
collection: slug,
id: loggedInUser?.id || '',
data: {
custom: 'Goodbye, world!',
},
});
const response = await fetch(`${apiUrl}/${slug}/refresh-token`, {
method: 'post',
headers: {
Authorization: `JWT ${token}`,
},
});
const data = await response.json();
expect(response.status).toBe(200);
expect(data.user.custom).toBe('Goodbye, world!');
});
it('should allow a user to be created', async () => {
const response = await fetch(`${apiUrl}/${slug}`, {
body: JSON.stringify({
@@ -201,6 +232,7 @@ describe('Auth', () => {
expect(verificationResponse.status).toBe(200);
const afterVerifyResult = await db.collection('public-users').findOne({ email: emailToVerify });
// @ts-expect-error trust
const { _verified: afterVerified, _verificationToken: afterToken } = afterVerifyResult;
expect(afterVerified).toBe(true);
expect(afterToken).toBeUndefined();

View File

@@ -0,0 +1,28 @@
import React, { useEffect, useState } from 'react';
import { useAuth } from '../../../src/admin/components/utilities/Auth';
import { UIField } from '../../../src/fields/config/types';
import { User } from '../../../src/auth';
export const AuthDebug: React.FC<UIField> = () => {
const [state, setState] = useState<User | null | undefined>();
const { user } = useAuth();
useEffect(() => {
if (user) {
fetch(`/api/users/${user.id}`).then((r) => r.json()).then((newUser) => {
setState(newUser);
});
}
}, [user]);
return (
<div id="auth-debug-ui-field">
<div id="users-api-result">
{state?.custom as string}
</div>
<div id="use-auth-result">
{user?.custom as string}
</div>
</div>
);
};