3.6.7. Protected API Routes

In this chapter, you’ll learn how to create protected API routes.

What is a Protected API Route?#

By default, an API route is publicly accessible, meaning that any user can access it without authentication. This is useful for public API routes that allow users to browse products, view collections, and so on.

A protected API route is an API route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access.

Protected API routes are useful for routes that require user authentication, such as creating a product or managing an order. These routes must only be accessed by authenticated admin users.

Tip: Refer to the API Reference for Admin and Store to learn how to send authenticated requests.

Default Protected Routes#

Any API route, including your custom API routes, are protected if they start with the following prefixes:

Route Prefix

Access

/admin

Only authenticated admin users can access.

/store/customers/me

Only authenticated customers can access.

Refer to the API Reference for Admin and Store to learn how to send authenticated requests.

Opt-Out of Default Authentication Requirement#

If you create a custom API route under a prefix that is protected by default, you can opt-out of the authentication requirement by exporting an AUTHENTICATE variable in the route file with its value set to false.

For example, to disable authentication requirement for a custom API route created at /admin/custom, you can export an AUTHENTICATE variable in the route file:

src/api/admin/custom/route.ts
1import type { 2  AuthenticatedMedusaRequest, 3  MedusaResponse,4} from "@medusajs/framework/http"5
6export const GET = async (7  req: AuthenticatedMedusaRequest, 8  res: MedusaResponse9) => {10  res.json({11    message: "Hello",12  })13}14
15export const AUTHENTICATE = false

Now, any request sent to the /admin/custom API route is allowed, regardless if the admin user is authenticated.


Protect Custom API Routes#

You can protect API routes using the authenticate middleware from the Medusa Framework. When applied to a route, the middleware checks that:

  • The correct actor type (for example, user, customer, or a custom actor type) is authenticated.
  • The correct authentication method is used (for example, session, bearer, or api-key).

For example, you can add the authenticate middleware in the src/api/middlewares.ts file to protect a custom API route:

src/api/middlewares.ts
1import { 2  defineMiddlewares,3  authenticate,4} from "@medusajs/framework/http"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/custom/admin*",10      middlewares: [authenticate("user", ["session", "bearer", "api-key"])],11    },12    {13      matcher: "/custom/customer*",14      middlewares: [authenticate("customer", ["session", "bearer"])],15    },16  ],17})

The authenticate middleware function accepts three parameters:

  1. The type of user authenticating. Use user for authenticating admin users, and customer for authenticating customers. You can also pass * to allow all types of users, or pass an array of actor types.
  2. An array of types of authentication methods allowed. Both user and customer scopes support session and bearer. The admin scope also supports the api-key authentication method.
  3. An optional object of configurations accepting the following properties:
    • allowUnauthenticated: (default: false) A boolean indicating whether authentication is required. For example, you may have an API route where you want to access the logged-in customer if available, but guest customers can still access it too.
    • allowUnregistered (default: false): A boolean indicating if unregistered users should be allowed access. This is useful when you want to allow users who aren’t registered to access certain routes.

Example: Custom Actor Type#

For example, to require authentication of a custom actor type manager to an API route:

src/api/middlewares.ts
1import { 2  defineMiddlewares,3  authenticate,4} from "@medusajs/framework/http"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/manager*",10      middlewares: [authenticate("manager", ["session", "bearer"])],11    },12  ],13})
Tip: Refer to the Custom Actor-Type Guide for detailed explanation on how to create a custom actor type and apply authentication middlewares.

Example: Allow Multiple Actor Types#

To allow multiple actor types to access an API route, pass an array of actor types to the authenticate middleware:

src/api/middlewares.ts
1import { 2  defineMiddlewares,3  authenticate,4} from "@medusajs/framework/http"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/custom*",10      middlewares: [authenticate(["user", "customer"], ["session", "bearer"])],11    },12  ],13})

Override Authentication for Medusa's API Routes#

In some cases, you may want to override the authentication requirement for Medusa's API routes. For example, you may want to allow custom actor types to access existing protected API routes.

It's not possible to change the authentication middleware applied to an existing API route. Instead, you need to replicate the API route and apply the authentication middleware to it.

Learn more in the Override API Routes chapter.


Access Authentication Details in API Routes#

To access the authentication details in an API route, such as the logged-in user's ID, set the type of the first request parameter to AuthenticatedMedusaRequest. It extends MedusaRequest:

Code
1import type {2  AuthenticatedMedusaRequest,3  MedusaResponse,4} from "@medusajs/framework/http"5
6export const GET = async (7  req: AuthenticatedMedusaRequest,8  res: MedusaResponse9) => {10  // ...11}

The auth_context.actor_id property of AuthenticatedMedusaRequest holds the ID of the authenticated user or customer. If there isn't any authenticated user or customer, auth_context is undefined.

For example:

src/api/store/custom/route.ts
1import type {2  AuthenticatedMedusaRequest,3  MedusaResponse,4} from "@medusajs/framework/http"5
6export const GET = async (7  req: AuthenticatedMedusaRequest,8  res: MedusaResponse9) => {10  const id = req.auth_context?.actor_id11
12  // ...13}

In this example, you retrieve the ID of the authenticated user, customer, or custom actor type from the auth_context property of the AuthenticatedMedusaRequest object.

Note: If you opt-out of authentication in a route as mentioned in the Opt-Out section, you can't access the authenticated user or customer anymore. Use the authenticate middleware instead to protect the route.

Retrieve Logged-In Customer's Details#

You can access the logged-in customer’s ID in all API routes starting with /store using the auth_context.actor_id property of the AuthenticatedMedusaRequest object. You can then use Query to retrieve the customer details, or pass the ID to a workflow that performs business logic.

For example:

src/api/store/custom/route.ts
1import type {2  AuthenticatedMedusaRequest,3  MedusaResponse,4} from "@medusajs/framework/http"5
6export const GET = async (7  req: AuthenticatedMedusaRequest,8  res: MedusaResponse9) => {10  const customerId = req.auth_context?.actor_id11  const query = req.scope.resolve("query")12
13  const { data: [customer] } = await query.graph({14    entity: "customer",15    fields: ["*"],16    filters: {17      id: customerId,18    },19  }, {20    throwIfKeyNotFound: true,21  })22
23  // do something with the customer data...24}

In this example, you retrieve the customer's ID and resolve Query from the Medusa container.

Then, you use Query to retrieve the customer details. The throwIfKeyNotFound option throws an error if the customer with the specified ID is not found.

After that, you can use the customer's details in your API route.

Retrieve Logged-In Admin User's Details#

You can access the logged-in admin user’s ID in all API routes starting with /admin using the auth_context.actor_id property of the AuthenticatedMedusaRequest object. You can then use Query to retrieve the user details, or pass the ID to a workflow that performs business logic.

For example:

src/api/admin/custom/route.ts
1import type {2  AuthenticatedMedusaRequest,3  MedusaResponse,4} from "@medusajs/framework/http"5
6export const GET = async (7  req: AuthenticatedMedusaRequest,8  res: MedusaResponse9) => {10  const userId = req.auth_context?.actor_id11  const query = req.scope.resolve("query")12
13  const { data: [user] } = await query.graph({14    entity: "user",15    fields: ["*"],16    filters: {17      id: userId,18    },19  }, {20    throwIfKeyNotFound: true,21  })22
23  // do something with the user data...24}

In this example, you retrieve the admin user's ID and resolve Query from the Medusa container.

Then, you use Query to retrieve the user details. The throwIfKeyNotFound option throws an error if the user with the specified ID is not found.

After that, you can use the user's details in your API route.

Was this chapter helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break