Support Quest Platform SDK auth with oculusId fallback

When the Data Use Checkup hasn't granted numeric ID access, the SDK
returns oculusId (string username) instead. This makes nonce optional,
skips nonce verification for non-numeric userIds, and uses oculusId as
the metaId when numeric ID is unavailable.
This commit is contained in:
2026-02-24 12:41:54 +01:00
parent 0eab05f15b
commit 7351003c6b
4 changed files with 66 additions and 48 deletions

View File

@@ -1,6 +1,6 @@
import { FastifyPluginAsync } from 'fastify';
import { randomUUID } from 'node:crypto';
import { exchangeMetaCode, fetchMetaProfile } from '../../services/meta-auth.service.js';
import { verifyUserProof, fetchOculusProfile } from '../../services/meta-auth.service.js';
import { signAccessToken } from '../../plugins/auth.js';
import { hashToken } from '../../services/crypto.service.js';
import { config } from '../../config.js';
@@ -15,37 +15,52 @@ const metaAuthRoutes: FastifyPluginAsync = async (fastify) => {
schema: {
body: {
type: 'object',
required: ['code'],
required: ['userId'],
properties: {
code: { type: 'string', minLength: 1 },
userId: { type: 'string', minLength: 1 },
nonce: { type: 'string' },
deviceInfo: { type: 'string' },
},
additionalProperties: false,
},
},
}, async (request, reply) => {
const { code, deviceInfo } = request.body;
const { userId, nonce, deviceInfo } = request.body;
request.log.info({ userId, hasNonce: !!nonce, deviceInfo }, 'Meta callback received');
// Exchange code for Meta access token
const { accessToken: metaToken } = await exchangeMetaCode(code);
// Nonce validation requires a numeric user ID from Meta's graph API.
// When DUC hasn't granted numeric ID access, the SDK returns oculusId (a string username).
const isNumericId = /^\d+$/.test(userId);
// Fetch user profile
const profile = await fetchMetaProfile(metaToken);
if (nonce && isNumericId) {
const isValid = await verifyUserProof(userId, nonce);
if (!isValid) {
throw new AppError(401, 'Invalid user proof');
}
request.log.info('Nonce verified successfully');
} else {
request.log.warn({ isNumericId, hasNonce: !!nonce },
'Skipping nonce verification (non-numeric userId or missing nonce)');
}
// Try to fetch profile from Oculus graph; fall back to userId as display name
let metaId = userId;
let displayName = userId;
if (isNumericId) {
try {
const profile = await fetchOculusProfile(userId);
metaId = profile.metaId;
displayName = profile.displayName;
} catch (e) {
request.log.warn(e, 'Failed to fetch Oculus profile');
}
}
// Upsert user
const user = await fastify.prisma.user.upsert({
where: { metaId: profile.metaId },
update: {
displayName: profile.displayName,
email: profile.email,
avatarUrl: profile.avatarUrl,
},
create: {
metaId: profile.metaId,
displayName: profile.displayName,
email: profile.email,
avatarUrl: profile.avatarUrl,
},
where: { metaId },
update: { displayName },
create: { metaId, displayName },
});
// Create session with hashed refresh token