adrien2p / medusa-plugins

A collection of awesome plugins for medusa :rocket:
https://medusa-plugins.vercel.app
MIT License
156 stars 46 forks source link

Google OAuth login not working for admin #87

Closed nhatanhit closed 1 year ago

nhatanhit commented 1 year ago

Hello, I am using plugin medusa-plugin-auth for Google OAuth login on admin site (localhost:7000) . My localhost for backend is : localhost:9000

Below are configurations that I have done on my Google Developer Console image My admin configurations for the plugin is as following image

When I click onto the button Login By Google , it redirects me to this screen image

Can you please let me know which step I am missing ? Thanks

adrien2p commented 1 year ago

Do you have any places in your backend where you would have applied the authenticate middleware to /admin?

nhatanhit commented 1 year ago

Hi @adrien2p Yes, I applied the middleware for some /admin sub paths, but not for entire admin paths . Following is paths that I applied the middleware image image

adrien2p commented 1 year ago

Sounds strange, I feel like the unauthorised comes from the authenticate middleware, cause you get than once you call the end point. Would you have a small repo to reproduce it that i can test? It does not have to include all your project, just the simpliest repo to reproduce this behaviour

odhekar commented 1 year ago

I am facing the same issue and it seems the strategyNames returns blank for key "google". This is what I get in my setup,

{
  "auth0": { "admin": "auth0.admin.medusa-auth-plugin", "store": "auth0.store.medusa-auth-plugin" },
  "facebook": { "admin": "facebook.admin.medusa-auth-plugin", "store": "facebook.store.medusa-auth-plugin" },
  "google": {},
  "linkedin": { "admin": "linkedin.admin.medusa-auth-plugin", "store": "linkedin.store.medusa-auth-plugin" },
  "firebase": { "admin": "firebase.admin.medusa-auth-plugin", "store": "firebase.store.medusa-auth-plugin" },
  "azure_oidc": { "admin": "azure-oidc.admin.medusa-auth-plugin", "store": "azure-oidc.store.medusa-auth-plugin" }
}

https://github.com/adrien2p/medusa-plugins/blob/main/packages/medusa-plugin-auth/src/core/validate-callback.ts#L54C1-L54C1

adrien2p commented 1 year ago

@odhekar thanks for that info, where did you get that? Which version do you have ?

odhekar commented 1 year ago

I added some logging in node_modules/medusa-plugin-auth/core/validate-callback.js when I couldn't repro the problem with my custom callback (that still didn't work until I realized that the callback has to return user.id). This particular check in validate-callback.js fails because the google key is blank,

if ((strict === 'all' || strict === 'admin') &&
      (!user.metadata || user.metadata[AUTH_PROVIDER_KEY] !== strategyNames[strategyErrorIdentifier].admin)) {
      throw new MedusaError(MedusaError.Types.INVALID_DATA, `Admin with email ${email} already exists`);
    }

I have just started evaluating medusa and therefore, this is pretty much fresh backend + admin install with a Google login widget on the login screen.

  "dependencies": {
    "@medusajs/admin": "^7.0.0-beta-20230624084632",
    "@medusajs/cache-inmemory": "^1.8.7",
    "@medusajs/cache-redis": "^1.8.7",
    "@medusajs/event-bus-local": "^1.9.4",
    "@medusajs/event-bus-redis": "^1.8.7",
    "@medusajs/file-local": "^1.0.1",
    "@medusajs/medusa": "^1.12.2-beta-20230624084632",
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "^16.1.4",
    "express": "^4.17.2",
    "medusa-fulfillment-manual": "^1.1.37",
    "medusa-interfaces": "^1.3.7",
    "medusa-payment-manual": "^1.0.23",
    "medusa-payment-stripe": "^6.0.0",
    "medusa-plugin-auth": "^1.5.1",
    "typeorm": "^0.3.16"
  },
odhekar commented 1 year ago

One thing I would add here - I wish the documentation on this plugin is a bit more descriptive. It's not obvious what are the basic steps needed to use only the social login. I want to use just Google and facebook but the plugin documentation skips the need to have authProvider in metadata and how admin users should be provisioned on a fresh install etc.
Just my two cents, this is a great product and I wish the documentation was easier.

odhekar commented 1 year ago

@adrien2p just checked and the validate-callback.js works fine for facebook in admin but fails for google.

adrien2p commented 1 year ago

@odhekar indeed, in order for an admin to use the social login, the authProvider needs to be set in order to prevent anybody from being able to authenticate to your admin and to get an account created for security reason. For the store, the authProvider is set the first time the user create an account with a social login. Also, those authProvider are explained at the end of each provider description in the doc. Maybe, would you like to create a pr to improve the part you think are lacking explanation ? ☺️

adrien2p commented 1 year ago

Just to be sure, the unabridged is still due to the constraints you have shown above or did you solve it by adding the authProvider?

odhekar commented 1 year ago

@adrien2p The "why" is clear but the problem is "how" πŸ˜‰. The provider description in the documentation assumes somebody new (such as me) knows about "authProvider" in metadata and what it is supposed to contain i.e. familiarity with plugin codebase is assumed. I will see if I can do a PR in the doc on this.
It was also not clear to me that the medusa db still remains the main user store (hope this is correct?).

Here is what I did,

  1. OOTB medusa backend + admin
  2. Added auth-plugin with Google config
  3. Created a widget to add a login with Google button on the login page
  4. Created a user using medusa cli with but without password.
    medusa user -e <username>@gmail.com
  5. Updated the metadata column in in the user table to
    {"authProvider":"google.admin.medusa-auth-plugin"}
  6. Got the "Admin with email @gmail.com already exists" error when I tried to login
  7. I then added some logging as described here and realized the problem mentioned here.
  8. I then wrote a custom callback with checks for "strict" removed (didn't look like "strict" is available in the callback) and then finally login worked!
  9. Repeated this for adding facebook login and that works without any custom callback.
  10. In summary - there is some bug that causes the problem in step# 7

In between 6 & 7 I also tried custom callback but the documentation doesn't mention it is supposed to return user.id and I wasn't sure what the callback is supposed to do!

adrien2p commented 1 year ago

Indeed i assumed that people would look at the typings I suppose, maybe as part of the doc we can also add the js types import special comment above the custom callback prop wdyt?

The thing i don't get is how do you get the strategyNames to be empty for google. They are declared here https://github.com/adrien2p/medusa-plugins/blob/main/packages/medusa-plugin-auth/src/types/index.ts line 60 and unless they are re assigned somewhere i don't see how it is possible.

The custom callback is the one that validate the callback info returned by the provider before being authorised or rejected. I assumed that if someone wanted to do a custom callback, then it would be a more advanced case and therefore the user should know about passport and know what is a callback. Can't wait to see your pr 🀞

adrien2p commented 1 year ago

When I try to reproduce the missing strategy name I don't succeed. It seems really weird that this key is missing since it is a const populated in the types and never reassigned

Screenshot 2023-07-06 at 09 03 13
adrien2p commented 1 year ago

let me know what you think about that pr https://github.com/adrien2p/medusa-plugins/pull/89/files

odhekar commented 1 year ago

When I try to reproduce the missing strategy name I don't succeed. It seems really weird that this key is missing since it is a const populated in the types and never reassigned

Screenshot 2023-07-06 at 09 03 13

which build was this? I tried with a fresh install using stable builds (so no admin widgets) and still got this,

image

packages.json

"dependencies": {
    "@medusajs/admin": "^6.0.1",
    "@medusajs/cache-inmemory": "^1.8.7",
    "@medusajs/cache-redis": "^1.8.7",
    "@medusajs/event-bus-local": "^1.9.4",
    "@medusajs/event-bus-redis": "^1.8.7",
    "@medusajs/file-local": "^1.0.1",
    "@medusajs/medusa": "^1.12.0",
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "^16.1.4",
    "express": "^4.17.2",
    "medusa-fulfillment-manual": "^1.1.37",
    "medusa-interfaces": "^1.3.7",
    "medusa-payment-manual": "^1.0.23",
    "medusa-payment-stripe": "^6.0.0",
    "medusa-plugin-auth": "^1.5.1",
    "typeorm": "^0.3.16"
  }

medusa-config.json

  {
    resolve: "medusa-plugin-auth",
    options: {
      google: {
        clientID: "client id",
        clientSecret: "secret",
        admin: {
          callbackUrl: `${BACKEND_URL}/admin/auth/google/cb`,
          failureRedirect: `${ADMIN_URL}/login`,

          // The success redirect can be overriden from the client by adding a query param `?redirectTo=your_url` to the auth url
          // This query param will have the priority over this configuration
          successRedirect: `${ADMIN_URL}/app`,
        },
      },
    },
  },
nhatanhit commented 1 year ago

Hi @adrien2p Please checkout this one : https://github.com/nhatanhit/fresh-medusa/tree/master/src , it's my small fresh medusa repo and it's initialized by running command npx create-medusa-app

nhatanhit commented 1 year ago

Another update I tried debugging, the cors settings already applied for the path /admin/auth/google with domain image But it still displays "Unauthorized" image

Tried putting the breakpoint on line 38 , but it's not reach when visiting the URL http://localhost:9000/admin/auth/google image

adrien2p commented 1 year ago

@nhatanhit this is not a cors issue since you receive unauthorised. Can you put a bp in the validate callback as you might have the same issue as de discussed with @odhekar.

As soon as I can I will test with the deps and config from @odhekar and see if i can figure it out or reproduce

nhatanhit commented 1 year ago

@adrien2p I tried putting a bp in the validate call back as @odhekar put before, but it's strange that it's not reach on my end The constructor of GoogleAdminStrategy works normally However , when try putting a bp in function validate , nothing happen image It seems that the authorization flow is failed at Google side , the validate callback not work Following is my setting on Google Console Cloud image

adrien2p commented 1 year ago

Could you add the 7001 for the admin, i think the default is 7001

odhekar commented 1 year ago

I am using these,

image

nhatanhit commented 1 year ago

Thank @odhekar Just make sure that we are on the same page, did you get problem when accessing this URL ? :http://localhost:9000/admin/auth/google

Hi @adrien2p Mine still not working My admin local is http://localhost:7000 . Following is my medusa config

// CORS when consuming Medusa from admin
const ADMIN_CORS = process.env.ADMIN_CORS || "http://localhost:7000,http://localhost:7001,http://localhost:9000";
const BACKEND_URL = process.env.BACKEND_URL || "localhost:9000"
const ADMIN_URL = process.env.ADMIN_URL || "localhost:7000"
const STORE_URL = process.env.STORE_URL || "localhost:8000"
const plugins = [
   {
    resolve: "medusa-plugin-auth",
    options: {
        // strict: "all", // or "none" or "store" or "admin"
        google: {
            clientID: 'MY_GOOGLE_CLIENT_ID',
            clientSecret: 'MY_GOOGLE_CLIENT_SECRET',

            admin: {
                callbackUrl:`${BACKEND_URL}/admin/auth/google/cb`,
                failureRedirect: `${ADMIN_URL}/login`,
                successRedirect: `${ADMIN_URL}/`,
                authPath: '/admin/auth/google',
            }

        }
    }
  }
]
odhekar commented 1 year ago

@nhatanhit no errors when accessing that URL. That's how I had to initiate login in the absence of widgets.

adrien2p commented 1 year ago

Sorry guys i didnt get the bandwidth yet to investigate the missing google scopes. @odhekar if you put some breakpoint here and there during the bootstrap, is there a moment where the google scopes are present and then disappear?

adrien2p commented 1 year ago

Good news, I ve been able to reproduce, I ve released a new version,can I get you to try please

odhekar commented 1 year ago

I can confirm that 1.6.0 fixed the issue for me. πŸ™‚ thanks @adrien2p

nhatanhit commented 1 year ago

Hi @adrien2p Mine still not working . The good news is I don't receive the "Unauthorized" message.

When entering the url "http://localhost:9000/admin/auth/google" , browser redirects me to Google Authorization Screen image It says that the request : localhost:9000/admin/auth/google/cb is invalid Then I tried pasting above URL into another tab of browser It redirects me to this page image

I updated the version of plugin : medusa-plugin-auth to version : 1.6.0 as well

Please let me know anything that I can provide to help you investigate Thanks

adrien2p commented 1 year ago

@nhatanhit looks like an issue with you.r google configuration, maybe in credentials? I mean in your google account πŸ˜‡

nhatanhit commented 1 year ago

Hi @adrien2p No , I don't think so I tried creating new Express project (not using medusa ) , and it's using Google OAuth Passport Library and same OAuth Credential as well , but my acc is verified successfully image

image It's very strange .....

adrien2p commented 1 year ago

have you added you user in the test users in the google cloud console for non published app? as per google docs While publishing status is set to 'Testing,' only test users are able to access

nhatanhit commented 1 year ago

yes @adrien2p I already added my google account as test user in the google cloud console. image

The thing is : with one pair of Google Client ID / Google Client secret , I able to login into my Express project using my google account but not for my medusa project.

adrien2p commented 1 year ago

That sounds very strange, it works fo me and @odhekar so i don't understand the difference with you that makes that behaviour happening.

But if you look at the google doc the authorization error you are receiving is die to that. @odhekar do you have something different on your side?

And even on my side when i tested the fix i was able to authenticate.

Also, your code and the plugin are doing the same thing under the hood.

nhatanhit commented 1 year ago

@adrien2p Do you mind if you can upload your medusa-config.js with hiding all your credentials ? I will do a check again

It would be greater if you can create small repository for demonstrating Google Authorization (including medusa backend / medusa admin + your plugin: medusa-plugin-auth ) , I will check it out and run on my local machine ?

adrien2p commented 1 year ago

here it is, I ve replaced the creds with '*'

const ADMIN_CORS = `/http:\\/\\/*/`;
const STORE_CORS =
  "/http:\\/\\/*/";
const redisUrl = "redis://127.0.0.1:6379"
const DATABASE_URL="postgres://postgres@localhost:5432/medusa"

const plugins = [
  "medusa-fulfillment-manual",
  "medusa-payment-manual",
  {
    resolve: "@medusajs/admin",
    /** @type {import('@medusajs/admin').PluginOptions} */
    options: {
      autoRebuild: true,
    },
  },
  {
    resolve: "medusa-plugin-auth",
    /** @type {import('medusa-plugin-auth')} */
    options: {
      google: {

        clientID:
          "*",
        clientSecret: "*",
        admin: {
          callbackUrl: "http://localhost:9000/admin/auth/google/cb",

          failureRedirect: "http://localhost:7001/login",
          successRedirect: "http://localhost:7001/",

          authPath: "/admin/auth/google",
          authCallbackPath: "/admin/auth/google/cb",
        },
        store: {
          callbackUrl: "http://localhost:9000/store/auth/google/cb",

          failureRedirect: "http://localhost:8000/login",
          successRedirect: "http://localhost:8000/",

          authPath: "/store/auth/google",
          authCallbackPath: "/store/auth/google/cb",
        },
      },
      facebook: {
        clientID: "*",
        clientSecret: "*",
        admin: {
          callbackUrl: "http://localhost:9000/admin/auth/facebook/cb",

          failureRedirect: "http://localhost:7001/login",
          successRedirect: "http://localhost:7001/",

          authPath: "/admin/auth/facebook",
          authCallbackPath: "/admin/auth/facebook/cb",
        },
      },
    },
  },
];

module.exports = {
  plugins,
  projectConfig: {
    redis_url: redisUrl,
    admin_cors: ADMIN_CORS,
    store_cors: STORE_CORS,
    database_url: DATABASE_URL,
    database_type: "postgres",
    database_extra: {
      idle_in_transaction_session_timeout: 20000,
      idle_session_timeout: 20000,
      max: 20,
    },
    database_logging: false,
    jwt_secret: "test",
    cookie_secret: "test",
  },
  /*featureFlags: {
    tax_inclusive_pricing: true,
  },*/
  modules: {
    eventBus: {
      resolve: "@medusajs/event-bus-redis",
      options: {
        redisUrl
      }
    },
    cacheService: {
      resolve: "@medusajs/cache-redis",
      options: {
        redisUrl
      }
    },
  }
};
adrien2p commented 1 year ago

Can you share your finding @nhatanhit

nhatanhit commented 1 year ago

Hi @adrien2p , sorry for replying late

Unfortunately, It's still not working on my side. I went on doing the debug and think the Google Strategy which was initialized and linked to passport instance in loader function is NOT found when creating Google Auth routes, as you can see in following screens, Passport strategy only has session strategy.

medusa_passport_loader

I tried running a simple example on my local Express server, and see the Google Strategy is fully loaded when creating routes

google_stg_express

It will take time to investigate this issue so I closed the issue. Besides, it seems that the issue only happens with my local computer πŸ˜„

I am following this guideline : https://samippoudel.hashnode.dev/google-oauth-using-typescript-expressjs-passportjs-and-mongodb-ckvatnioy0hsu45s1db0r3qoy to implement the Google Auth for my backend server, it works with the route : /admin/auth/google , I can get my google profile in the callback (line 40 in following screenshot)

image

However , due to some unknown issue in underlying npm modules, my Google profile isn't serialized into the session ( I haven't figured out how to fix it ) . Plus I don't know to call one services in the callback (line 30 in above screenshot ) to save the profileObject in the database.

@adrien2p if you know any pattern to resolve services in the callback, can you please suggest ?

Thanks

adrien2p commented 1 year ago

In general if it is not in the passport instance it means that the passport version in the plugin and the one in medusa does not match, it has to be the same version

nhatanhit commented 1 year ago

@adrien2p Not sure that I understand your meaning, you mean MedusaJS already had an existing passport instance , and it's not compatible with passport version in your plugin ?

adrien2p commented 1 year ago

Can you check in the node modules, if there is multiple passport installed between medusa and the plugins and the root. In theory, the plugin has the same version as medusa and i don't think medusa changed the passport version can you check

nhatanhit commented 1 year ago

@adrien2p I tried with new fresh medusa After debugging, google strategy still not available in passport's strategies medusa_google_1

My package.json as you can see in following pic, there is no passport lib in the list of dependencies medusa_2_screenshot Or you can find full list in below link https://github.com/nhatanhit/fresh-medusa/blob/master/package.json

Is ^1.5.1 correct version for medusa plugin auth ?

adrien2p commented 1 year ago

What i meant is that medusa is i stalling passport, and the plugin expect the same version as medusa, can you check if there is a passport package in node_modules/medusa-plugin-auth? I expect none of course

adrien2p commented 1 year ago

I ve just checked and both medusa and the plugin target passport ^0.6.0 so in theory in your node module you should have only one passport package installed. Meaning that once everything is loaded including the plugin all the strategies should be in that passport instance

adrien2p commented 1 year ago

Unfortunately if it was on my projet on my machine i could debug it very easily but i can't reproduce your issue and other users don't encounter it as well so i don't get why you have this behaviour, unless you are linking the package locally for your dev and debug and in that case you might have some dev deps installed

nhatanhit commented 1 year ago

@adrien2p As your suggestion, I created public small repo : https://github.com/nhatanhit/fresh-medusa image

I got similar issue in this repo as well, can you please check it out and run it when you have time ? If it runs successfully on your local machine, I will do a check on all global node modules in my local machine.

adrien2p commented 1 year ago

I look at it when i come back from vacation πŸ˜… i am away for almost a month

nhatanhit commented 1 year ago

@adrien2p Have a good trip , and please help me when you come back πŸ˜