fix: auth strategy exp (#6945)

## Description

Ensures that exp and auth strategy are available from the `me` and
`refresh` operations as well as passed through the `Auth` provider. Same
as #6943

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
This commit is contained in:
James Mikrut
2024-06-26 14:42:20 -04:00
committed by GitHub
parent ed73dedd14
commit 5ffc5a1248
7 changed files with 95 additions and 71 deletions

View File

@@ -1,16 +1,20 @@
import type { Collection } from 'payload' import type { Collection } from 'payload'
import { isolateObjectProperty, meOperation } from 'payload' import { extractJWT, isolateObjectProperty, meOperation } from 'payload'
import type { Context } from '../types.js' import type { Context } from '../types.js'
function meResolver(collection: Collection): any { function meResolver(collection: Collection): any {
async function resolver(_, args, context: Context) { async function resolver(_, args, context: Context) {
const currentToken = extractJWT(context.req)
const options = { const options = {
collection, collection,
currentToken,
depth: 0, depth: 0,
req: isolateObjectProperty(context.req, 'transactionID'), req: isolateObjectProperty(context.req, 'transactionID'),
} }
const result = await meOperation(options) const result = await meOperation(options)
if (collection.config.auth.removeTokenFromResponses) { if (collection.config.auth.removeTokenFromResponses) {

View File

@@ -379,6 +379,9 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
exp: { exp: {
type: GraphQLInt, type: GraphQLInt,
}, },
strategy: {
type: GraphQLString,
},
token: { token: {
type: GraphQLString, type: GraphQLString,
}, },
@@ -405,6 +408,9 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
refreshedToken: { refreshedToken: {
type: GraphQLString, type: GraphQLString,
}, },
strategy: {
type: GraphQLString,
},
user: { user: {
type: collection.graphQL.JWT, type: collection.graphQL.JWT,
}, },

View File

@@ -7,6 +7,7 @@ import type { ClientUser, User } from '../types.js'
export type MeOperationResult = { export type MeOperationResult = {
collection?: string collection?: string
exp?: number exp?: number
strategy?: string
token?: string token?: string
user?: ClientUser user?: ClientUser
} }
@@ -49,6 +50,7 @@ export const meOperation = async ({
result = { result = {
collection: req.user.collection, collection: req.user.collection,
strategy: req.user._strategy,
user, user,
} }

View File

@@ -14,6 +14,7 @@ import { getFieldsToSign } from '../getFieldsToSign.js'
export type Result = { export type Result = {
exp: number exp: number
refreshedToken: string refreshedToken: string
strategy?: string
user: Document user: Document
} }
@@ -88,6 +89,7 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
let result: Result = { let result: Result = {
exp, exp,
refreshedToken, refreshedToken,
strategy: args.req.user._strategy,
user, user,
} }

View File

@@ -23,7 +23,9 @@ export type AuthContext<T = ClientUser> = {
refreshPermissions: () => Promise<void> refreshPermissions: () => Promise<void>
setPermissions: (permissions: Permissions) => void setPermissions: (permissions: Permissions) => void
setUser: (user: T) => void setUser: (user: T) => void
strategy?: string
token?: string token?: string
tokenExpiration?: number
user?: T | null user?: T | null
} }
@@ -36,6 +38,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
const [user, setUser] = useState<ClientUser | null>() const [user, setUser] = useState<ClientUser | null>()
const [tokenInMemory, setTokenInMemory] = useState<string>() const [tokenInMemory, setTokenInMemory] = useState<string>()
const [tokenExpiration, setTokenExpiration] = useState<number>() const [tokenExpiration, setTokenExpiration] = useState<number>()
const [strategy, setStrategy] = useState<string>()
const pathname = usePathname() const pathname = usePathname()
const router = useRouter() const router = useRouter()
// const { code } = useLocale() // const { code } = useLocale()
@@ -76,6 +79,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
const revokeTokenAndExpire = useCallback(() => { const revokeTokenAndExpire = useCallback(() => {
setTokenInMemory(undefined) setTokenInMemory(undefined)
setTokenExpiration(undefined) setTokenExpiration(undefined)
setStrategy(undefined)
}, []) }, [])
const setTokenAndExpiration = useCallback( const setTokenAndExpiration = useCallback(
@@ -84,6 +88,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
if (token && json?.exp) { if (token && json?.exp) {
setTokenInMemory(token) setTokenInMemory(token)
setTokenExpiration(json.exp) setTokenExpiration(json.exp)
if (json.strategy) setStrategy(json.strategy)
} else { } else {
revokeTokenAndExpire() revokeTokenAndExpire()
} }
@@ -258,6 +263,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
searchParams, searchParams,
admin, admin,
revokeTokenAndExpire, revokeTokenAndExpire,
strategy,
tokenExpiration,
loginRoute, loginRoute,
]) ])

View File

@@ -8,123 +8,124 @@
export interface Config { export interface Config {
collections: { collections: {
posts: Post posts: Post;
users: User users: User;
'payload-preferences': PayloadPreference 'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration 'payload-migrations': PayloadMigration;
} };
globals: { globals: {
menu: Menu menu: Menu;
} };
locale: null locale: null;
user: User & { user: User & {
collection: 'users' collection: 'users';
} };
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts". * via the `definition` "posts".
*/ */
export interface Post { export interface Post {
id: string id: string;
text?: string | null text?: string | null;
richText?: { richText?: {
root: { root: {
type: string type: string;
children: { children: {
type: string type: string;
version: number version: number;
[k: string]: unknown [k: string]: unknown;
}[] }[];
direction: ('ltr' | 'rtl') | null direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '' format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number indent: number;
version: number version: number;
} };
[k: string]: unknown [k: string]: unknown;
} | null } | null;
richText2?: { richText2?: {
root: { root: {
type: string type: string;
children: { children: {
type: string type: string;
version: number version: number;
[k: string]: unknown [k: string]: unknown;
}[] }[];
direction: ('ltr' | 'rtl') | null direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '' format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number indent: number;
version: number version: number;
} };
[k: string]: unknown [k: string]: unknown;
} | null } | null;
updatedAt: string updatedAt: string;
createdAt: string createdAt: string;
_status?: ('draft' | 'published') | null _status?: ('draft' | 'published') | null;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users". * via the `definition` "users".
*/ */
export interface User { export interface User {
id: string id: string;
updatedAt: string updatedAt: string;
createdAt: string createdAt: string;
email: string email: string;
resetPasswordToken?: string | null resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null resetPasswordExpiration?: string | null;
salt?: string | null salt?: string | null;
hash?: string | null hash?: string | null;
loginAttempts?: number | null loginAttempts?: number | null;
lockUntil?: string | null lockUntil?: string | null;
password?: string | null password?: string | null;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences". * via the `definition` "payload-preferences".
*/ */
export interface PayloadPreference { export interface PayloadPreference {
id: string id: string;
user: { user: {
relationTo: 'users' relationTo: 'users';
value: string | User value: string | User;
} };
key?: string | null key?: string | null;
value?: value?:
| { | {
[k: string]: unknown [k: string]: unknown;
} }
| unknown[] | unknown[]
| string | string
| number | number
| boolean | boolean
| null | null;
updatedAt: string updatedAt: string;
createdAt: string createdAt: string;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations". * via the `definition` "payload-migrations".
*/ */
export interface PayloadMigration { export interface PayloadMigration {
id: string id: string;
name?: string | null name?: string | null;
batch?: number | null batch?: number | null;
updatedAt: string updatedAt: string;
createdAt: string createdAt: string;
} }
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "menu". * via the `definition` "menu".
*/ */
export interface Menu { export interface Menu {
id: string id: string;
globalText?: string | null globalText?: string | null;
updatedAt?: string | null updatedAt?: string | null;
createdAt?: string | null createdAt?: string | null;
} }
declare module 'payload' { declare module 'payload' {
// @ts-ignore // @ts-ignore
export interface GeneratedTypes extends Config {} export interface GeneratedTypes extends Config {}
} }

View File

@@ -134,6 +134,8 @@ describe('Auth', () => {
const data = await response.json() const data = await response.json()
expect(data.strategy).toBeDefined()
expect(typeof data.exp).toBe('number')
expect(response.status).toBe(200) expect(response.status).toBe(200)
expect(data.user.email).toBeDefined() expect(data.user.email).toBeDefined()
}) })