feat(auth): add authentication endpoints (#6265)
**What** - Add authentication endpoints: - `/auth/[scope]/[provider]` - `/auth/[scope]/[provider]/callback` - update authenticate-middleware handler - Add scope field to user - Add unique constraint on scope and entity_id note: there's still some remaining work related to jwt auth to be handled, this is mainly focussed on session auth with endpoints Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
This commit is contained in:
co-authored by
Sebastian Rindom
parent
061c449179
commit
9fda6a6824
@@ -0,0 +1,46 @@
|
||||
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { scope, authProvider } = req.params
|
||||
|
||||
const service: IAuthModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
const authData = {
|
||||
url: req.url,
|
||||
headers: req.headers,
|
||||
query: req.query,
|
||||
body: req.body,
|
||||
authScope: scope,
|
||||
protocol: req.protocol,
|
||||
} as AuthenticationInput
|
||||
|
||||
const authResult = await service.validateCallback(authProvider, authData)
|
||||
|
||||
const { success, error, authUser, location } = authResult
|
||||
if (location) {
|
||||
res.redirect(location)
|
||||
return
|
||||
}
|
||||
|
||||
if (success) {
|
||||
req.session.auth_user = authUser
|
||||
req.session.scope = authUser.scope
|
||||
|
||||
return res.status(200).json({ authUser })
|
||||
}
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.UNAUTHORIZED,
|
||||
error || "Authentication failed"
|
||||
)
|
||||
}
|
||||
|
||||
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
await GET(req, res)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { scope, authProvider } = req.params
|
||||
|
||||
const service: IAuthModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
const authData = {
|
||||
url: req.url,
|
||||
headers: req.headers,
|
||||
query: req.query,
|
||||
body: req.body,
|
||||
authScope: scope,
|
||||
protocol: req.protocol,
|
||||
} as AuthenticationInput
|
||||
|
||||
const authResult = await service.authenticate(authProvider, authData)
|
||||
|
||||
const { success, error, authUser, location } = authResult
|
||||
if (location) {
|
||||
res.redirect(location)
|
||||
return
|
||||
}
|
||||
|
||||
if (success) {
|
||||
req.session.auth_user = authUser
|
||||
req.session.scope = authUser.scope
|
||||
|
||||
return res.status(200).json({ authUser })
|
||||
}
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.UNAUTHORIZED,
|
||||
error || "Authentication failed"
|
||||
)
|
||||
}
|
||||
|
||||
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
await GET(req, res)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const id = req.auth_user!.app_metadata.customer_id
|
||||
const id = req.auth_user!.app_metadata?.customer_id
|
||||
|
||||
const customerModule = req.scope.resolve(ModuleRegistrationName.CUSTOMER)
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@ import {
|
||||
StorePostCustomersMeAddressesAddressReq,
|
||||
StoreGetCustomersMeAddressesParams,
|
||||
} from "./validators"
|
||||
import authenticate from "../../../utils/authenticate-middleware"
|
||||
import * as QueryConfig from "./query-config"
|
||||
|
||||
import { authenticate } from "../../../utils/authenticate-middleware"
|
||||
|
||||
export const storeCustomerRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: "ALL",
|
||||
|
||||
@@ -17,7 +17,7 @@ export const defaultStoreCustomersFields: (keyof CustomerDTO)[] = [
|
||||
]
|
||||
|
||||
export const retrieveTransformQueryConfig = {
|
||||
defaultFields: defaultStoreCustomersFields,
|
||||
defaultFields: defaultStoreCustomersFields as string[],
|
||||
defaultRelations: defaultStoreCustomersRelations,
|
||||
allowedRelations: allowedStoreCustomersRelations,
|
||||
isList: false,
|
||||
|
||||
@@ -1,8 +1,30 @@
|
||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||
import { createCustomerAccountWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import { CreateCustomerDTO } from "@medusajs/types"
|
||||
import { createCustomerAccountWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
if (req.auth_user?.app_metadata?.customer_id) {
|
||||
const remoteQuery = req.scope.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_QUERY
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "customer",
|
||||
variables: { id: req.auth_user.app_metadata.customer_id },
|
||||
fields: [],
|
||||
})
|
||||
const [customer] = await remoteQuery(query)
|
||||
|
||||
res.status(200).json({ customer })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const createCustomers = createCustomerAccountWorkflow(req.scope)
|
||||
const customersData = req.validatedBody as CreateCustomerDTO
|
||||
|
||||
@@ -10,5 +32,9 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
input: { customersData, authUserId: req.auth_user!.id },
|
||||
})
|
||||
|
||||
// Set customer_id on session user if we are in session
|
||||
if (req.session.auth_user) {
|
||||
req.session.auth_user.app_metadata.customer_id = result.id
|
||||
}
|
||||
res.status(200).json({ customer: result })
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { Customer, User } from "../models"
|
||||
import type { NextFunction, Request, Response } from "express"
|
||||
|
||||
import type { Customer, User } from "../models"
|
||||
import { AuthUserDTO } from "@medusajs/types"
|
||||
import type { MedusaContainer } from "./global"
|
||||
|
||||
export interface MedusaRequest extends Request {
|
||||
user?: (User | Customer) & { customer_id?: string; userId?: string }
|
||||
scope: MedusaContainer
|
||||
session?: any
|
||||
requestId?: string
|
||||
auth_user?: { id: string; app_metadata: Record<string, any>; scope: string }
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { AuthUserDTO, IAuthModuleService } from "@medusajs/types"
|
||||
import { NextFunction, RequestHandler } from "express"
|
||||
import { MedusaRequest, MedusaResponse } from "../types/routing"
|
||||
import { NextFunction, RequestHandler } from "express"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
const SESSION_AUTH = "session"
|
||||
const BEARER_AUTH = "bearer"
|
||||
|
||||
type MedusaSession = {
|
||||
auth: {
|
||||
[authScope: string]: {
|
||||
user_id: string
|
||||
}
|
||||
}
|
||||
auth_user: AuthUserDTO
|
||||
scope: string
|
||||
}
|
||||
|
||||
type AuthType = "session" | "bearer"
|
||||
|
||||
export default (
|
||||
export const authenticate = (
|
||||
authScope: string,
|
||||
authType: AuthType | AuthType[],
|
||||
options: { allowUnauthenticated?: boolean } = {}
|
||||
@@ -36,19 +34,18 @@ export default (
|
||||
|
||||
let authUser: AuthUserDTO | null = null
|
||||
if (authTypes.includes(SESSION_AUTH)) {
|
||||
if (session.auth && session.auth[authScope]) {
|
||||
authUser = await authModule
|
||||
.retrieveAuthUser(session.auth[authScope].user_id)
|
||||
.catch(() => null)
|
||||
if (session.auth_user && session.scope === authScope) {
|
||||
authUser = session.auth_user
|
||||
}
|
||||
}
|
||||
|
||||
if (authTypes.includes(BEARER_AUTH)) {
|
||||
if (!authUser && authTypes.includes(BEARER_AUTH)) {
|
||||
const authHeader = req.headers.authorization
|
||||
if (authHeader) {
|
||||
const re = /(\S+)\s+(\S+)/
|
||||
const matches = authHeader.match(re)
|
||||
|
||||
// TODO: figure out how to obtain token (and store correct data in token)
|
||||
if (matches) {
|
||||
const tokenType = matches[1]
|
||||
const token = matches[2]
|
||||
|
||||
Reference in New Issue
Block a user