Like what we’re doing? Star us on GitHub!

Problem with custom auth strategy

ScriptDroid
4 months ago
30

I created a google oauth strategy with passport, I added it to payload and in the login everything works well (you can login, generate a token, and the token is set to the payload-token http-only cookie) but in the admin panel I can't access, also, the api isn't working, i'm getting this error in the console for the admin







My users collection:

import GoogleStrategy from "../auth/google";
import finalise from "../auth/jwt";
import passport from "passport";

const Users = {
  slug: "users",
  auth: {
    tokenExpiration: 60 * 60 * 24 * 7, // 1 week
    verify: true,
    maxLoginAttempts: 5,
    lockTime: 600 * 1000,
    useAPIKey: true,
    strategies: [
      {
        name: "google",
        strategy: GoogleStrategy,
      },
    ],
  },
  endpoints: [
    {
      path: "/google",
      method: "get",
      handler: async (req, res) => {
        passport.authenticate("google", {
          scope: ["profile", "email"],
        })(req, res);
      },
    },
    {
      path: "/google/callback",
      method: "get",
      handler: async (req, res) => {
        req.payload.authenticate(req, res, () => {
          if (req?.user) {
            const final = finalise(req, res);
            return final.res.status(200).send({ token: final.token });
          }
          return res.status(401).send({ message: "Unauthorized" });
        });
      },
    },
  ],
};

export default Users;


the google strategy:



import payload from "payload";
import Strategy from "passport-google-oauth20";
import { v4 as uuidv4 } from "uuid";
import { Forbidden, LockedAuth } from "payload/errors";

require("dotenv").config();

const GoogleStrategy = new Strategy(
  {
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: "/api/users/google/callback",
    scope: ["profile", "email"],
  },
  async (accessToken, refreshToken, profile, done) => {
    const { emails } = profile;
    const email = emails[0]?.value;

    if (!email) {
      done(Forbidden, false);
    }

    try {
      let user = await payload.find({
        collection: "users",
        where: {
          email: {
            equals: email,
          },
        },
      });

      if (user?.totalDocs > 0) {
        user = user.docs[0];
      }

      const isLocked = (date) => !!(date && date > Date.now());
      if (user && isLocked(user.lockUntil)) {
        throw new LockedAuth();
      }

      if (user?.totalDocs < 1) {
        user = await payload.create({
          collection: "users",
          data: {
            email,
            password: uuidv4(),
            avatar: "1",
            _verified: true,
          },
          disableVerificationEmail: true,
        });
      }

      if (user) {
        user.collection = "users";
        user._strategy = "google";
        done(null, user);
      } else {
        done(null, false);
      }
    } catch (e) {
      done(e, false);
    }
  }
);

export default GoogleStrategy;


token:



import jwt from "jsonwebtoken";

const finalise = (req, res) => {
  let user = req.user;
  user = JSON.parse(JSON.stringify(user));
  user = sanitizeInternalFields(user);

  const collections = req.payload.collections;
  const userCollection = collections[req.payload.config.admin.user];
  const collectionConfig = userCollection.config;

  const fieldsToSign = getFieldsToSign(collectionConfig, user);

  const token = jwt.sign(fieldsToSign, req.payload.secret, {
    expiresIn: collectionConfig.auth.tokenExpiration,
  });

  const cookieOptions = {
    path: "/",
    httpOnly: true,
    expires: getCookieExpiration(collectionConfig.auth.tokenExpiration),
    secure: collectionConfig.auth.cookies.secure,
    sameSite: collectionConfig.auth.cookies.sameSite,
    domain: undefined,
  };

  if (collectionConfig.auth.cookies.domain) {
    cookieOptions.domain = collectionConfig.auth.cookies.domain;
  }

  res.cookie(`payload-token`, token, cookieOptions);

  req.user = user;

  return { req, res, token };
};

export default finalise;


any ideas?

image.png
  • 7wonders
    4 months ago

    have you set all the relevant origins and callbacks in cloud console?





    make sure to add both localhost and localhost:3100 in your case



    and by the way, I updated the google-one-tap plugin yesterday so it should work out the box now

  • Sora
    4 months ago

    how to use it for auth from client?

  • 7wonders
    4 months ago

    Its not built for auth from client, just as an auth for payloadcms admin. Auth from client depends a lot on what client but you should be able to quite easily copy the code in my repo and make the necessary adjustments. The main difference would be that I save the jwt token using payloads setToken useAuth whereas you would be wanting to do similar client side:


    https://github.com/ScottEAdams/payload-plugin-google-one-tap/blob/main/src/components/button.tsx
  • ScriptDroid
    4 months ago

    oh thank you, I made a little fork for my project



    @7wonders any toughs on this?

  • 7wonders
    4 months ago

    Sorry, been offline a bunch. Have you got the

    app.use(express.json())
  • ScriptDroid
    4 months ago

    yes, but it stills not work



    Now the problem is when an account does not exits resolves with a 401 code





    @7wonders I think i got the problem



    seems like the plugin cannot access to accounts that are not created yet



    so you can't cerate an account in payload using this method



    any thougs in how can i make that?

  • KasparTr
    3 months ago

    As soon as I add this plugin to payload, the application does not compile:



    BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
    This is no longer the case. Verify if you need this module and configure a polyfill for it.


    Any change we can peek inside the

    sanitizeInternalFields, getFieldsToSign and getCookieExpiration

    functions?

  • TheFrontend
    3 weeks ago

    @ScriptDroid was your initial problem solved by entering all origins & callbacks in the cloud console?



    @7wonders do you have any suggestions what else might be the problem if I can't access the admin panel? I made sure the URLs are set in the google console. (and once I open any api route in the browser it redirects me to the google sign-up form)

  • 7wonders
    3 weeks ago

    Really sorry, I havent been very active the last couple of months. I will see if I can check the plugins out this week and get some updating done.



    But remember that its completely open source so you are welcome to submit pull requests with fixes 🙂

Open the post
Continue the discussion in Discord
Can't find what you're looking for?
Get help straight from the Payload team with an Enterprise License.Learn More