System Information
- 4.10.5:
- Windows:
- sqlite:
- Node version 16.20:
I needed to use Firebase authorization through a phone number. I developed code based on this topic: Is custom JWT Validation available in V4? and put it in the index.js file. This is a custom strategy that has 2 functions - authenticate and verify. They work correctly as evidenced by the console.log output in the verify function.
eyJhbGciOiJIUzI1N...cHyVcggxV0yGhQf6tNbN8
Token is valid { id: 1, iat: 1686254318, exp: 1688846318 }
But I don’t understand at all why the error appears in the console:
ValidationError: 2 errors occurred
I’m expecting a ctx to be returned to me on the frontend application. Please help me understand the problem and fix it. I have been struggling with this for a week now.
index.js:
"use strict";
const { ForbiddenError } = require('@strapi/utils').errors;
module.exports = {
/**
* An asynchronous register function that runs before
* your application is initialized.
*
* This gives you an opportunity to extend code.
*/
register({ strapi }) {
strapi.container.get('auth').register('content-api', {
name: 'firebase-jwt-verifier',
async authenticate(ctx) {
const { authorization } = ctx.request.header;
// Check for JWT token in the header
if (authorization) {
const parts = authorization.split(/\s+/);
if (parts[0].toLowerCase() === 'bearer' && parts.length === 2) {
const token = parts[1];
try {
// Verify and decode Firebase JWT token
const decodedToken = await strapi.firebase.auth().verifyIdToken(token);
// Check if the phone number in the JWT token matches the incoming request
if (decodedToken.phone_number === ctx.request.body.phoneNumber) {
// Get user or create a new user based on the phone number
let user = await strapi.db.query('plugin::users-permissions.user').findOne({
where: { phoneNumber: ctx.request.body.phoneNumber }
});
if (!user) {
// If the user doesn't exist, create a new user
user = await strapi.db.query('plugin::users-permissions.user').create({ phoneNumber: ctx.request.body.phoneNumber });
}
// Set the user in the context state
ctx.state.user = user;
// Generate Strapi JWT token
const jwtToken = await strapi.service('plugin::users-permissions.jwt').issue({ id: user.id });
ctx.state.user.jwt = jwtToken
// Return successful authentication and user information
return { authenticated: true, credentials: user };
}
} catch (error) {
// Handle error during token verification or decoding
console.error('Error verifying Firebase JWT token:', error);
}
}
}
// If authentication fails, return authentication error
return { authenticated: false };
},
async verify(ctx) {
console.log(ctx)
try {
// Check for jwt token in ctx.state.auth
if (ctx.credentials && ctx.credentials.jwt) {
const token = ctx.credentials.jwt
console.log(token)
const tokenPayload = await strapi.service('plugin::users-permissions.jwt').verify(token);
console.log('Token is valid', tokenPayload)
return
}
} catch (error) {
console.error('Error verifying JWT token:', error);
}
}
});
},
/**
* An asynchronous bootstrap function that runs before
* your application gets started.
*
* This gives you an opportunity to set up your data model,
* run jobs, or perform some special logic.
*/
bootstrap({ strapi }) {
const admin = require("firebase-admin");
const serviceAccount = require("../private/firebase/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
strapi.firebase = admin;
},
};