feat: Add support for refreshing JWT tokens (#9013)

* feat: Add support for refreshing JWT tokens

* feat: Add refresh method to the auth SDK
This commit is contained in:
Stevche Radevski
2024-09-06 12:58:57 +02:00
committed by GitHub
parent 3ba0ddcd43
commit 62e0c593c8
10 changed files with 136 additions and 50 deletions
@@ -1,15 +1,22 @@
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
import {
AuthenticationInput,
ConfigModule,
IAuthModuleService,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
MedusaError,
ModuleRegistrationName,
generateJwtToken,
} from "@medusajs/utils"
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
import { generateJwtTokenForAuthIdentity } from "../../../utils/generate-jwt-token"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const { actor_type, auth_provider } = req.params
const config: ConfigModule = req.scope.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
const service: IAuthModuleService = req.scope.resolve(
ModuleRegistrationName.AUTH
)
@@ -27,30 +34,14 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
authData
)
const entityIdKey = `${actor_type}_id`
const entityId = authIdentity?.app_metadata?.[entityIdKey] as
| string
| undefined
if (success) {
const { http } = req.scope.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig
if (success && authIdentity) {
const { http } = config.projectConfig
const { jwtSecret, jwtExpiresIn } = http
const token = generateJwtToken(
const token = generateJwtTokenForAuthIdentity(
{ authIdentity, actorType: actor_type },
{
actor_id: entityId ?? "",
actor_type,
auth_identity_id: authIdentity?.id ?? "",
app_metadata: {
[entityIdKey]: entityId,
},
},
{
// @ts-expect-error
secret: jwtSecret,
// @ts-expect-error
expiresIn: jwtExpiresIn,
secret: http.jwtSecret,
expiresIn: http.jwtExpiresIn,
}
)
@@ -34,7 +34,7 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
authData
)
if (success) {
if (success && authIdentity) {
const { http } = config.projectConfig
const token = generateJwtTokenForAuthIdentity(
@@ -38,7 +38,7 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
return res.status(200).json({ location })
}
if (success) {
if (success && authIdentity) {
const { http } = config.projectConfig
const token = generateJwtTokenForAuthIdentity(
@@ -14,6 +14,11 @@ export const authRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/auth/session",
middlewares: [authenticate("*", ["session"])],
},
{
method: ["POST"],
matcher: "/auth/token/refresh",
middlewares: [authenticate("*", "bearer", { allowUnregistered: true })],
},
{
method: ["POST"],
matcher: "/auth/:actor_type/:auth_provider/callback",
@@ -0,0 +1,40 @@
import { IAuthModuleService } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import {
ContainerRegistrationKeys,
ModuleRegistrationName,
} from "@medusajs/utils"
import { generateJwtTokenForAuthIdentity } from "../../utils/generate-jwt-token"
// Retrieve a newly generated JWT token. All checks that the existing token is valid already happen in the auth middleware.
// The token will include the actor ID, even if the token used to refresh didn't have one.
// Note: We probably want to disallow refreshes if the password changes, and require reauth.
export const POST = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const service: IAuthModuleService = req.scope.resolve(
ModuleRegistrationName.AUTH
)
const authIdentity = await service.retrieveAuthIdentity(
req.auth_context.auth_identity_id
)
const { http } = req.scope.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig
const token = generateJwtTokenForAuthIdentity(
{ authIdentity, actorType: req.auth_context.actor_type },
{
secret: http.jwtSecret,
expiresIn: http.jwtExpiresIn,
}
)
return res.json({ token })
}
@@ -1,8 +1,15 @@
import { AuthIdentityDTO } from "@medusajs/types"
import { generateJwtToken } from "@medusajs/utils"
export function generateJwtTokenForAuthIdentity(
{ authIdentity, actorType },
{ secret, expiresIn }
{
authIdentity,
actorType,
}: { authIdentity: AuthIdentityDTO; actorType: string },
{
secret,
expiresIn,
}: { secret: string | undefined; expiresIn: string | undefined }
) {
const entityIdKey = `${actorType}_id`
const entityId = authIdentity?.app_metadata?.[entityIdKey] as