fix(sync): rollback to jwt last exp approach
All checks were successful
/ build (map[dockerfile:./services/files/Dockerfile name:files]) (push) Successful in 2m9s
/ build (map[dockerfile:./services/watchers/Dockerfile name:watchers]) (push) Successful in 2m5s
/ build (map[dockerfile:./services/web/Dockerfile name:web]) (push) Successful in 1m59s
/ build (map[dockerfile:./services/app/Dockerfile name:app]) (push) Successful in 1m14s
/ build (map[dockerfile:./services/hasura/Dockerfile name:hasura]) (push) Successful in 1m48s
/ build (map[dockerfile:./services/api/Dockerfile name:api]) (push) Successful in 2m10s
/ build (map[dockerfile:./services/tasks/Dockerfile name:tasks]) (push) Successful in 2m11s
/ deploy (push) Successful in 14s
All checks were successful
/ build (map[dockerfile:./services/files/Dockerfile name:files]) (push) Successful in 2m9s
/ build (map[dockerfile:./services/watchers/Dockerfile name:watchers]) (push) Successful in 2m5s
/ build (map[dockerfile:./services/web/Dockerfile name:web]) (push) Successful in 1m59s
/ build (map[dockerfile:./services/app/Dockerfile name:app]) (push) Successful in 1m14s
/ build (map[dockerfile:./services/hasura/Dockerfile name:hasura]) (push) Successful in 1m48s
/ build (map[dockerfile:./services/api/Dockerfile name:api]) (push) Successful in 2m10s
/ build (map[dockerfile:./services/tasks/Dockerfile name:tasks]) (push) Successful in 2m11s
/ deploy (push) Successful in 14s
This commit is contained in:
parent
02cb943a93
commit
e284f59476
3 changed files with 47 additions and 92 deletions
|
@ -1,9 +1,10 @@
|
|||
const { jwtVerify } = require("jose")
|
||||
const jwtDecode = require("jwt-decode")
|
||||
const getHasuraClaimsFromJWT = require("@modjo/hasura/utils/jwt/get-hasura-claims-from-jwt")
|
||||
const { ctx } = require("@modjo/core")
|
||||
const { reqCtx } = require("@modjo/express/ctx")
|
||||
|
||||
module.exports = function (services) {
|
||||
module.exports = function () {
|
||||
const castIntVars = ["deviceId", "userId"]
|
||||
function sessionVarsFromClaims(claims) {
|
||||
const session = { ...claims }
|
||||
|
@ -26,7 +27,7 @@ module.exports = function (services) {
|
|||
}
|
||||
|
||||
return async function auth(jwt, scopes) {
|
||||
const hasMetaAuthToken = scopes.includes("meta.auth-token")
|
||||
const hasMetaExpUser = scopes.includes("meta.exp-user")
|
||||
let jwtVerified = false
|
||||
|
||||
try {
|
||||
|
@ -41,32 +42,35 @@ module.exports = function (services) {
|
|||
} catch (err) {
|
||||
const logger = ctx.require("logger")
|
||||
|
||||
// Allow expired JWT only if meta.auth-token scope is present
|
||||
if (hasMetaAuthToken && err.code === "ERR_JWT_EXPIRED") {
|
||||
// Allow expired JWT only if meta.exp-user scope is present
|
||||
if (hasMetaExpUser && err.code === "ERR_JWT_EXPIRED") {
|
||||
logger.debug(
|
||||
{ error: err },
|
||||
"Allowing expired JWT for meta.auth-token scope"
|
||||
"Allowing expired JWT for meta.exp-user scope"
|
||||
)
|
||||
const req = reqCtx.get("req")
|
||||
const authTokenJWT = req?.headers?.["x-auth-token"]
|
||||
if (!authTokenJWT) {
|
||||
return false
|
||||
}
|
||||
const authToken =
|
||||
services.authTokenHandler.decodeAuthToken(authTokenJWT)
|
||||
// Create a session that indicates auth token processing is needed
|
||||
const session = { isAuthTokenRequest: true, authToken }
|
||||
reqCtx.set("session", session)
|
||||
return true
|
||||
}
|
||||
// Continue processing with expired JWT
|
||||
} else {
|
||||
logger.error({ error: err }, "jwVerify failed")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Regular user JWT processing
|
||||
const claims = getHasuraClaimsFromJWT(jwt, claimsNamespace)
|
||||
const session = sessionVarsFromClaims(claims)
|
||||
|
||||
// Add exp claim to session if meta.exp-user scope is present
|
||||
if (hasMetaExpUser) {
|
||||
try {
|
||||
const payload = jwtDecode(jwt)
|
||||
if (payload && payload.exp) {
|
||||
session.exp = payload.exp
|
||||
}
|
||||
} catch (err) {
|
||||
const logger = ctx.require("logger")
|
||||
logger.error({ error: err }, "Failed to decode JWT for exp claim")
|
||||
}
|
||||
}
|
||||
|
||||
if (!isScopeAllowed(session, scopes)) {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ const { reqCtx } = require("@modjo/express/ctx")
|
|||
|
||||
const tasks = require("~/tasks")
|
||||
|
||||
module.exports = function ({ services: { authTokenHandler } }) {
|
||||
module.exports = function () {
|
||||
const { addTask } = ctx.require("amqp")
|
||||
const redis = ctx.require("redisHotGeodata")
|
||||
|
||||
|
@ -23,72 +23,41 @@ module.exports = function ({ services: { authTokenHandler } }) {
|
|||
longitude,
|
||||
},
|
||||
} = location
|
||||
// console.log("addOneGeolocSync", req.body)
|
||||
|
||||
const session = reqCtx.get("session")
|
||||
let userId
|
||||
let deviceId
|
||||
let userBearerJwt = null
|
||||
|
||||
// Check if this is an auth token request (set by auth.js)
|
||||
if (session && session.isAuthTokenRequest) {
|
||||
// This is an auth token request, process it
|
||||
try {
|
||||
logger.debug("Processing auth token for geoloc sync")
|
||||
const { deviceId } = session
|
||||
|
||||
const { authToken } = session
|
||||
const {
|
||||
userId: newUserId,
|
||||
deviceId: newDeviceId,
|
||||
roles,
|
||||
} = await authTokenHandler.getOrCreateUserSession(
|
||||
authToken,
|
||||
req.body.phoneModel,
|
||||
req.body.deviceUuid
|
||||
)
|
||||
// Check JWT expiration sequence to prevent replay attacks
|
||||
if (session.exp) {
|
||||
const deviceExpKey = `device:${deviceId}:last_exp`
|
||||
const storedLastExp = await redis.get(deviceExpKey)
|
||||
|
||||
userId = newUserId
|
||||
deviceId = newDeviceId
|
||||
|
||||
// Generate new user JWT for token refresh
|
||||
userBearerJwt = await authTokenHandler.generateUserJwt(
|
||||
userId,
|
||||
deviceId,
|
||||
roles
|
||||
)
|
||||
|
||||
logger.debug({
|
||||
action: "geoloc-sync-auth-token",
|
||||
userId,
|
||||
deviceId,
|
||||
tokenRefreshed: true,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error({ error: error.message }, "Failed to process auth token")
|
||||
if (httpError.isHttpError(error)) {
|
||||
throw error
|
||||
}
|
||||
throw httpError(401, "Invalid auth token")
|
||||
}
|
||||
} else if (session && session.userId && session.deviceId) {
|
||||
// Regular user JWT session
|
||||
userId = session.userId
|
||||
deviceId = session.deviceId
|
||||
logger.debug({ action: "geoloc-sync-user-jwt", userId, deviceId })
|
||||
} else {
|
||||
// Invalid session
|
||||
logger.error({ session }, "Invalid session")
|
||||
throw httpError(401, "Invalid session")
|
||||
if (storedLastExp && session.exp < parseInt(storedLastExp, 10)) {
|
||||
throw httpError(401, "not the latest jwt")
|
||||
}
|
||||
|
||||
if (!userId || !deviceId) {
|
||||
throw httpError(401, "Missing user or device information")
|
||||
// Store the new expiration date
|
||||
if (session.exp !== storedLastExp) {
|
||||
await redis.set(deviceExpKey, session.exp)
|
||||
}
|
||||
}
|
||||
|
||||
const { userId } = session
|
||||
|
||||
logger.debug({ action: "geoloc-sync", userId, deviceId })
|
||||
|
||||
const coordinates = [longitude, latitude]
|
||||
|
||||
await async.parallel([
|
||||
async () => {
|
||||
// const transaction = redis.multi()
|
||||
// transaction.geoadd("device", longitude, latitude, deviceId)
|
||||
// transaction.publish("deviceSet", deviceId)
|
||||
// await transaction.exec()
|
||||
await redis.geoadd("device", longitude, latitude, deviceId)
|
||||
|
||||
await addTask(tasks.GEOCODE_MOVE, { deviceId, userId, coordinates })
|
||||
},
|
||||
async () =>
|
||||
|
@ -103,14 +72,7 @@ module.exports = function ({ services: { authTokenHandler } }) {
|
|||
}),
|
||||
])
|
||||
|
||||
const response = { ok: true }
|
||||
|
||||
// Include userBearerJwt in response if token refresh occurred
|
||||
if (userBearerJwt) {
|
||||
response.userBearerJwt = userBearerJwt
|
||||
}
|
||||
|
||||
return response
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
return [addOneGeolocSync]
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
# description:
|
||||
x-security:
|
||||
- auth: ["user", "meta.auth-token"]
|
||||
parameters:
|
||||
- name: X-Auth-Token
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Auth token for token refresh when user JWT is expired
|
||||
- auth: ["user", "meta.exp-user"]
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
|
@ -108,7 +101,3 @@ responses:
|
|||
properties:
|
||||
ok:
|
||||
type: boolean
|
||||
userBearerJwt:
|
||||
type: string
|
||||
description: New user JWT token when auth token refresh occurred
|
||||
nullable: true
|
||||
|
|
Loading…
Add table
Reference in a new issue