Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not getting the user data and cookies #10149

Open
RPdvtPT opened this issue Dec 23, 2024 · 3 comments
Open

Not getting the user data and cookies #10149

RPdvtPT opened this issue Dec 23, 2024 · 3 comments
Labels
status: needs-triage Possible bug which hasn't been reproduced yet

Comments

@RPdvtPT
Copy link

RPdvtPT commented Dec 23, 2024

Describe the Bug

I'm using a Custom Strategy to login via OpenID without the login form:

This is my auth file:

import { type Payload, PayloadHandler, PayloadRequest, User, parseCookies, AuthStrategyFunction, AuthStrategyFunctionArgs, AuthStrategyResult } from 'payload'
import * as openid from 'openid-client'
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const server: URL = new URL(process.env.OPEN_ID_URL || ''),
    clientId: string = process.env.OPEN_ID_CLIENT || '',
    clientSecret: string = process.env.OPEN_ID_SECRET || '',
    redirect_uri: string = `${process.env.SERVER_URL}/admin/login`,
    scope: string = process.env.OPEN_ID_SCOPE;

const jwks = jwksClient({
    jwksUri: process.env.OPEN_ID_KEYS || ''
});

const config: openid.Configuration = await openid.discovery(server, clientId, clientSecret);

export const decode = (token: string) => new Promise(next => {
    var decoded = jwt.decode(token, {
        complete: true
    });
    const kid = decoded?.header?.kid;
    jwks.getSigningKey(kid, (error, key: any) => !error ? jwt.verify(token, key.publicKey || key.rsaPublicKey, (err: any, decoded: any) => next(!err ? decoded : null)) : next(null));
})

// endpoint: /api/login
export const login: PayloadHandler = async (req: PayloadRequest): Promise<Response> => {
    let code_verifier: string = openid.randomPKCECodeVerifier(),
        code_challenge: string = await openid.calculatePKCECodeChallenge(code_verifier),
        state!: string,
        nonce!: string,
        parameters: Record<string, string> = {
            redirect_uri,
            scope,
            code_challenge,
            code_challenge_method: 'S256',
            response_type: 'id_token'
        }
    if (!config.serverMetadata().supportsPKCE()) {
        state = openid.randomState();
        parameters.state = state;
        nonce = openid.randomNonce();
        parameters.nonce = nonce;
    }
    let redirectTo: URL = openid.buildAuthorizationUrl(config, parameters);
    return Response.redirect(redirectTo.href);
};

const getUser = async (payload: Payload, email?: string): Promise<User | null> => {
    if (!email) return null;
    const find = await payload.find({
        collection: 'users',
        where: {
            email: {
                equals: email
            }
        }
    });
    return !find?.docs?.[0]?.email ? null : {
        ...find.docs[0],
        email: find.docs[0].email || '',
        collection: 'users'
    };
}

// endpoint: /api/login/:token, called in BeforeLogin.tsx, if returns the user, the provided token is set to a cookie
export const check: PayloadHandler = async (req: PayloadRequest): Promise<Response> => {
    if (!req?.routeParams?.token) return Response.error();
    const user = await decode(typeof req.routeParams.token !== 'string' ? '' : req.routeParams.token) as User | null;
    return !user?.email ? Response.error() : Response.json(user)
};

// collection strategy
export const authenticate: AuthStrategyFunction = async ({ payload, headers }: AuthStrategyFunctionArgs): Promise<AuthStrategyResult> => {
    const cookie = parseCookies(headers),
        token = cookie.get('token');
    if (!token) return { user: null }; // /api/me stops here
    const data: any = await decode(token);
    if (!data?.email) return { user: null }
    let user: User | null = await getUser(payload, data.email);
    if (!user) {
        await payload.create({
            collection: 'users',
            data: {
                name: data.name,
                email: data.email
            }
        });
        user = await getUser(payload, data.email);
    }
    return {
        user
    };
};

But the response from /api/me is this one:

{
   "user":null,
   "message":"Account"
}

With a custom API this also returns null in the user property:

  const payload = await getPayload({ config })
  const requestHeaders = await headers()

  const { user } = await payload.auth({ headers: requestHeaders })

This is the request:

fetch('/api/test', { 
    method: 'POST', 
    credentials: 'include' 
})

Link to the code that reproduces this issue

https://github.com/RPdvtPT/payload-strategy-test

Reproduction Steps

  • create a sample project based on the blank template
  • apply the strategy
  • after login, check /api/me responses

Which area(s) are affected? (Select all that apply)

Not sure

Environment Info

- payload: 3.11.0
- react: 19.0.0
- next: 15.1.2
- jsonwebtoken: 9.0.2
- jwks-rsa: 3.1.0
- openid-client: 6.1.7
@RPdvtPT RPdvtPT added status: needs-triage Possible bug which hasn't been reproduced yet validate-reproduction labels Dec 23, 2024
Copy link
Contributor

Please add a reproduction in order for us to be able to investigate.

Depending on the quality of reproduction steps, this issue may be closed if no reproduction is provided.

Why was this issue marked with the invalid-reproduction label?

To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository created with create-payload-app@beta -t blank or a forked/branched version of this repository with tests added (more info in the reproduction-guide).

To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as minimal as possible. This means that you should remove unnecessary code, files, and dependencies that do not contribute to the issue. Ensure your reproduction does not depend on secrets, 3rd party registries, private dependencies, or any other data that cannot be made public. Avoid a reproduction including a whole monorepo (unless relevant to the issue). The easier it is to reproduce the issue, the quicker we can help.

Please test your reproduction against the latest version of Payload to make sure your issue has not already been fixed.

I added a link, why was it still marked?

Ensure the link is pointing to a codebase that is accessible (e.g. not a private repository). "example.com", "n/a", "will add later", etc. are not acceptable links -- we need to see a public codebase. See the above section for accepted links.

Useful Resources

@RPdvtPT
Copy link
Author

RPdvtPT commented Dec 23, 2024

authenticate: async ({ payload, headers }: AuthStrategyFunctionArgs): Promise<AuthStrategyResult> => {

          const cookie = parseCookies(headers),
            token = cookie.get('token');

          console.log('token', token);

          return { user: null }
};

these cookies are available, except in the /api/users/me.

Working on a sample example project

@RPdvtPT
Copy link
Author

RPdvtPT commented Dec 23, 2024

The repo is this one: https://github.com/RPdvtPT/payload-strategy-test

This is a simple version without OpenID and JWT

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs-triage Possible bug which hasn't been reproduced yet
Projects
None yet
Development

No branches or pull requests

2 participants
@RPdvtPT and others