* feat: add Medusa Cloud OAuth provider * add Cloud login button * fetch whether cloud auth is enabled through api * allow unregistered to get session * handle existing users * address PR comments * prevent double execution * a few more fixes * fix callback url * fix spelling * refresh session * 200 instead of 201 * only allow cloud identities to create user * fix condition
226 lines
7.1 KiB
TypeScript
226 lines
7.1 KiB
TypeScript
import {
|
|
AuthIdentityDTO,
|
|
IAuthModuleService,
|
|
UserDTO,
|
|
} from "@medusajs/framework/types"
|
|
import { ContainerRegistrationKeys, Modules } from "@medusajs/framework/utils"
|
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
|
import jwt from "jsonwebtoken"
|
|
import {
|
|
adminHeaders,
|
|
createAdminUser,
|
|
} from "../../../../helpers/create-admin-user"
|
|
|
|
jest.setTimeout(100000)
|
|
|
|
medusaIntegrationTestRunner({
|
|
testSuite: ({ getContainer, api, dbConnection }) => {
|
|
let authModule: IAuthModuleService
|
|
let jwtSecret: string
|
|
let existingUser: UserDTO
|
|
let existingAuthIdentity: AuthIdentityDTO
|
|
|
|
beforeEach(async () => {
|
|
const container = getContainer()
|
|
authModule = container.resolve(Modules.AUTH)
|
|
|
|
const config = container.resolve(ContainerRegistrationKeys.CONFIG_MODULE)
|
|
jwtSecret = config.projectConfig.http.jwtSecret!.toString()
|
|
|
|
const adminUser = await createAdminUser(
|
|
dbConnection,
|
|
adminHeaders,
|
|
getContainer()
|
|
)
|
|
existingUser = adminUser.user
|
|
existingAuthIdentity = adminUser.authIdentity
|
|
})
|
|
|
|
describe("POST /cloud/auth/users", () => {
|
|
it("should create a new user when user doesn't exist", async () => {
|
|
// Create an auth identity (simulating cloud auth callback)
|
|
const authIdentity = await authModule.createAuthIdentities({
|
|
provider_identities: [
|
|
{
|
|
provider: "cloud",
|
|
entity_id: "cloud-user-123",
|
|
user_metadata: {
|
|
email: "john@doe.com",
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
],
|
|
})
|
|
|
|
// Generate a token for this auth identity (without actor_id since user doesn't exist yet)
|
|
const token = jwt.sign(
|
|
{
|
|
actor_id: "",
|
|
actor_type: "user",
|
|
auth_identity_id: authIdentity.id,
|
|
user_metadata: {
|
|
email: "john@doe.com",
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
jwtSecret,
|
|
{ expiresIn: "1d" }
|
|
)
|
|
|
|
// Call the endpoint to create the user
|
|
const createUserResponse = await api.post(
|
|
"/cloud/auth/users",
|
|
{},
|
|
{ headers: { authorization: `Bearer ${token}` } }
|
|
)
|
|
expect(createUserResponse.status).toEqual(200)
|
|
expect(createUserResponse.data.user).toMatchObject({
|
|
id: expect.any(String),
|
|
email: "john@doe.com",
|
|
first_name: "John",
|
|
last_name: "Doe",
|
|
})
|
|
const createdUserId = createUserResponse.data.user.id
|
|
expect(createdUserId).not.toEqual(existingUser.id)
|
|
|
|
// Refresh the token to get updated actor_id which should be the user's id
|
|
const refreshResponse = await api.post(
|
|
"/auth/token/refresh",
|
|
{},
|
|
{ headers: { authorization: `Bearer ${token}` } }
|
|
)
|
|
expect(refreshResponse.status).toEqual(200)
|
|
const refreshedToken = refreshResponse.data.token
|
|
expect(jwt.decode(refreshResponse.data.token)).toMatchObject({
|
|
actor_type: "user",
|
|
auth_identity_id: authIdentity.id,
|
|
actor_id: createdUserId,
|
|
})
|
|
|
|
// Verify the user was created
|
|
const meResponse = await api.get("/admin/users/me", {
|
|
headers: {
|
|
authorization: `Bearer ${refreshedToken}`,
|
|
},
|
|
})
|
|
expect(meResponse.status).toEqual(200)
|
|
expect(meResponse.data.user).toMatchObject({
|
|
id: createdUserId,
|
|
email: "john@doe.com",
|
|
first_name: "John",
|
|
last_name: "Doe",
|
|
})
|
|
})
|
|
|
|
it("should link existing user to auth identity when user with same email already exists", async () => {
|
|
// Create an auth identity (simulating cloud auth callback)
|
|
const authIdentity = await authModule.createAuthIdentities({
|
|
provider_identities: [
|
|
{
|
|
provider: "cloud",
|
|
entity_id: "cloud-user-123",
|
|
user_metadata: {
|
|
email: existingUser.email,
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
],
|
|
})
|
|
|
|
// Generate a token for this auth identity (without actor_id since user doesn't exist yet)
|
|
const token = jwt.sign(
|
|
{
|
|
actor_id: "",
|
|
actor_type: "user",
|
|
auth_identity_id: authIdentity.id,
|
|
user_metadata: {
|
|
email: existingUser.email,
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
jwtSecret,
|
|
{ expiresIn: "1d" }
|
|
)
|
|
|
|
// Call the endpoint to create the user
|
|
const createUserResponse = await api.post(
|
|
"/cloud/auth/users",
|
|
{},
|
|
{ headers: { authorization: `Bearer ${token}` } }
|
|
)
|
|
expect(createUserResponse.status).toEqual(200)
|
|
expect(createUserResponse.data.user).toMatchObject({
|
|
id: existingUser.id,
|
|
email: existingUser.email,
|
|
})
|
|
|
|
// Refresh the token to get updated actor_id, which should be the user id
|
|
const refreshResponse = await api.post(
|
|
"/auth/token/refresh",
|
|
{},
|
|
{ headers: { authorization: `Bearer ${token}` } }
|
|
)
|
|
expect(refreshResponse.status).toEqual(200)
|
|
expect(jwt.decode(refreshResponse.data.token)).toMatchObject({
|
|
actor_type: "user",
|
|
auth_identity_id: authIdentity.id,
|
|
actor_id: existingUser.id,
|
|
})
|
|
|
|
// Verify the previous auth identity is still linked to the user
|
|
const updatedAuthIdentity = await authModule.retrieveAuthIdentity(
|
|
existingAuthIdentity.id
|
|
)
|
|
expect(updatedAuthIdentity.app_metadata?.user_id).toEqual(
|
|
existingUser.id
|
|
)
|
|
})
|
|
|
|
it("should not allow non-cloud identities to create a user", async () => {
|
|
// Create an auth identity
|
|
const authIdentity = await authModule.createAuthIdentities({
|
|
provider_identities: [
|
|
{
|
|
provider: "github",
|
|
entity_id: "github-user-123",
|
|
user_metadata: {
|
|
email: "john@doe.com",
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
],
|
|
})
|
|
|
|
// Generate a token for this auth identity (without actor_id since user doesn't exist yet)
|
|
const token = jwt.sign(
|
|
{
|
|
actor_id: "",
|
|
actor_type: "user",
|
|
auth_identity_id: authIdentity.id,
|
|
user_metadata: {
|
|
email: "john@doe.com",
|
|
given_name: "John",
|
|
family_name: "Doe",
|
|
},
|
|
},
|
|
jwtSecret,
|
|
{ expiresIn: "1d" }
|
|
)
|
|
|
|
// Call the endpoint to create the user
|
|
const createUserResponse = await api.post(
|
|
"/cloud/auth/users",
|
|
{},
|
|
{ headers: { authorization: `Bearer ${token}` } }
|
|
).catch((error) => error.response)
|
|
expect(createUserResponse.status).toEqual(401)
|
|
})
|
|
})
|
|
},
|
|
})
|