Merge branch 'chore/added-google-login-from-mobile-app' of https://github.com/Worklenz/worklenz into upstreame-release-v2.1.4
This commit is contained in:
@@ -181,4 +181,94 @@ export default class AuthController extends WorklenzControllerBase {
|
|||||||
res.status(500).send(new ServerResponse(false, null, DEFAULT_ERROR_MESSAGE));
|
res.status(500).send(new ServerResponse(false, null, DEFAULT_ERROR_MESSAGE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HandleExceptions({logWithError: "body"})
|
||||||
|
public static async googleMobileAuth(req: IWorkLenzRequest, res: IWorkLenzResponse) {
|
||||||
|
const {idToken} = req.body;
|
||||||
|
|
||||||
|
if (!idToken) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "ID token is required"));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://oauth2.googleapis.com/tokeninfo?id_token=${idToken}`);
|
||||||
|
const profile = response.data;
|
||||||
|
|
||||||
|
// Validate token audience (client ID) - accept web, Android, and iOS client IDs
|
||||||
|
const allowedClientIds = [
|
||||||
|
process.env.GOOGLE_CLIENT_ID, // Web client ID
|
||||||
|
process.env.GOOGLE_ANDROID_CLIENT_ID, // Android client ID
|
||||||
|
process.env.GOOGLE_IOS_CLIENT_ID, // iOS client ID
|
||||||
|
].filter(Boolean); // Remove undefined values
|
||||||
|
|
||||||
|
console.log("Token audience (aud):", profile.aud);
|
||||||
|
console.log("Allowed client IDs:", allowedClientIds);
|
||||||
|
console.log("Environment variables check:");
|
||||||
|
console.log("- GOOGLE_CLIENT_ID:", process.env.GOOGLE_CLIENT_ID ? "Set" : "Not set");
|
||||||
|
console.log("- GOOGLE_ANDROID_CLIENT_ID:", process.env.GOOGLE_ANDROID_CLIENT_ID ? "Set" : "Not set");
|
||||||
|
console.log("- GOOGLE_IOS_CLIENT_ID:", process.env.GOOGLE_IOS_CLIENT_ID ? "Set" : "Not set");
|
||||||
|
|
||||||
|
if (!allowedClientIds.includes(profile.aud)) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "Invalid token audience"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate token issuer
|
||||||
|
if (!["https://accounts.google.com", "accounts.google.com"].includes(profile.iss)) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "Invalid token issuer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check token expiry
|
||||||
|
if (Date.now() >= profile.exp * 1000) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "Token expired"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!profile.email_verified) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "Email not verified"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing local account
|
||||||
|
const localAccountResult = await db.query("SELECT 1 FROM users WHERE email = $1 AND password IS NOT NULL AND is_deleted IS FALSE;", [profile.email]);
|
||||||
|
if (localAccountResult.rowCount) {
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, `No Google account exists for email ${profile.email}.`));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user exists
|
||||||
|
const userResult = await db.query(
|
||||||
|
"SELECT id, google_id, name, email, active_team FROM users WHERE google_id = $1 OR email = $2;",
|
||||||
|
[profile.sub, profile.email]
|
||||||
|
);
|
||||||
|
|
||||||
|
let user: any;
|
||||||
|
if (userResult.rowCount) {
|
||||||
|
// Existing user - login
|
||||||
|
user = userResult.rows[0];
|
||||||
|
} else {
|
||||||
|
// New user - register
|
||||||
|
const googleUserData = {
|
||||||
|
id: profile.sub,
|
||||||
|
displayName: profile.name,
|
||||||
|
email: profile.email,
|
||||||
|
picture: profile.picture
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerResult = await db.query("SELECT register_google_user($1) AS user;", [JSON.stringify(googleUserData)]);
|
||||||
|
user = registerResult.rows[0].user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create session
|
||||||
|
req.login(user, (err) => {
|
||||||
|
if (err) {
|
||||||
|
log_error(err);
|
||||||
|
return res.status(500).send(new ServerResponse(false, null, "Authentication failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
user.build_v = FileConstants.getRelease();
|
||||||
|
return res.status(200).send(new AuthResponse("Login Successful!", true, user, null, "User successfully logged in"));
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
log_error(error);
|
||||||
|
return res.status(400).send(new ServerResponse(false, null, "Invalid ID token"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ authRouter.get("/google/verify", (req, res) => {
|
|||||||
})(req, res);
|
})(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Mobile Google Sign-In
|
||||||
|
authRouter.post("/google/mobile", safeControllerFunction(AuthController.googleMobileAuth));
|
||||||
|
|
||||||
// Passport logout
|
// Passport logout
|
||||||
authRouter.get("/logout", AuthController.logout);
|
authRouter.get("/logout", AuthController.logout);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user