temichelle13 / StudyPlanner

Study Planner Project
MIT License
1 stars 0 forks source link

Task Tile #18

Open temichelle13 opened 1 month ago

temichelle13 commented 1 month ago

What - description of what you me to do Example: Hey @autopilot implement a Google sign-in on my website. Make changes to the front end and the back end of the application

Why - explain why this is important Example: I want to allow users to signup and login using their Google account

codeautopilot[bot] commented 1 month ago

Potential solution

To implement Google sign-in on your website, we need to make changes to both the front end and the back end of the application. The front end will include a Google sign-in button, and the back end will handle the OAuth flow using Passport.js. This will allow users to sign up and log in using their Google accounts.

How to implement

1. Update package.json

Add the necessary dependencies for Google OAuth.

{
  "name": "studyplanner",
  "version": "1.0.0",
  "description": "AI assisted study planner",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js",
    "test": "jest --verbose --coverage",
    "lint": "eslint .",
    "prettier": "prettier --write ."
  },
  "author": "Tea Larson-Hetrick",
  "license": "MIT",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.19.2",
    "jsonwebtoken": "^9.0.2",
    "mongodb": "^6.3.0",
    "mongoose": "^8.0.1",
    "morgan": "^1.10.0",
    "passport": "^0.6.0",
    "passport-google-oauth20": "^2.0.0"
  },
  "devDependencies": {
    "eslint": "^8.54.0",
    "jest": "^29.7.0",
    "nodemon": "^3.0.1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/temichelle13/StudyPlanner.git"
  },
  "bugs": {
    "url": "https://github.com/temichelle13/StudyPlanner/issues"
  },
  "homepage": "https://github.com/temichelle13/StudyPlanner#readme",
  "keywords": []
}

Run npm install to install the new dependencies.

2. Update index.html

Add a Google sign-in button to the HTML.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Study Planner</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Study Planner</h1>
    </header>
    <main>
        <section id="schedule">
            <h2>Your Schedule</h2>
        </section>
        <section id="tasks">
            <h2>Your Tasks</h2>
            <div id="taskForm">
                <form>
                </form>
            </div>
            <div id="taskList">
            </div>
        </section>
        <section id="auth">
            <h2>Login</h2>
            <button id="google-sign-in">Sign in with Google</button>
        </section>
    </main>
    <footer>
        <p>&copy; 2023 Study Planner</p>
    </footer>
    <script src="main.js"></script>
</body>
</html>

3. Update auth/providers.json

Add Google OAuth provider configuration.

{
    "anon-user": {
        "name": "anon-user",
        "type": "anon-user",
        "disabled": false
    },
    "api-key": {
        "name": "api-key",
        "type": "api-key",
        "disabled": true
    },
    "local-userpass": {
        "name": "local-userpass",
        "type": "local-userpass",
        "config": {
            "autoConfirm": true,
            "resetFunctionName": "resetFunc",
            "runConfirmationFunction": false,
            "runResetFunction": true
        },
        "disabled": false
    },
    "google-oauth": {
        "name": "google-oauth",
        "type": "oauth2",
        "config": {
            "clientID": "YOUR_GOOGLE_CLIENT_ID",
            "clientSecret": "YOUR_GOOGLE_CLIENT_SECRET",
            "authorizationURL": "https://accounts.google.com/o/oauth2/auth",
            "tokenURL": "https://oauth2.googleapis.com/token",
            "callbackURL": "http://yourapp.com/auth/google/callback",
            "scope": ["profile", "email"]
        },
        "disabled": false
    }
}

Replace YOUR_GOOGLE_CLIENT_ID and YOUR_GOOGLE_CLIENT_SECRET with your actual Google OAuth client ID and client secret.

4. Update routes/userRoutes.js

Add routes for Google OAuth login and callback handling.

const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const User = require('../models/user');
const router = express.Router();

require('../config/passport');

router.post('/register', async (req, res) => {
    try {
        const user = new User(req.body);
        await user.save();
        const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
        res.status(201).send({ user, token });
    } catch (error) {
        res.status(400).send(error);
    }
});

router.post('/login', async (req, res) => {
    try {
        const user = await User.findByCredentials(req.body.email, req.body.password);
        const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
        res.send({ user, token });
    } catch (error) {
        res.status(400).send(error);
    }
});

router.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));

router.get('/auth/google/callback', 
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    res.redirect('/');
  }
);

module.exports = router;

5. Create config/passport.js

Configure Passport with Google OAuth strategy.

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User = require('../models/user');

passport.use(new GoogleStrategy({
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: "/auth/google/callback"
  },
  async (token, tokenSecret, profile, done) => {
    try {
      let user = await User.findOne({ googleId: profile.id });
      if (!user) {
        user = new User({ googleId: profile.id, name: profile.displayName, email: profile.emails[0].value });
        await user.save();
      }
      return done(null, user);
    } catch (error) {
      return done(error, null);
    }
  }
));

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser(async (id, done) => {
  try {
    const user = await User.findById(id);
    done(null, user);
  } catch (error) {
    done(error, null);
  }
});

module.exports = passport;

6. Update main.js

Add an event listener for the Google sign-in button.

document.addEventListener('DOMContentLoaded', function() {
    const taskForm = document.getElementById('taskForm');
    const taskList = document.getElementById('taskList');
    const googleSignInButton = document.getElementById('google-sign-in');

    taskForm.addEventListener('submit', function(e) {
        e.preventDefault();
        const title = document.getElementById('taskTitle').value;
        const description = document.getElementById('taskDescription').value;
        const dueDate = document.getElementById('taskDueDate').value;

        fetch('/api/tasks', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ title, description, dueDate })
        })
        .then(response => response.json())
        .then(addTaskToList)
        .catch(error => console.error('Error:', error));

        taskForm.reset();
    });

    function addTaskToList(task) {
        const div = document.createElement('div');
        div.innerHTML = `<h3>${task.title}</h3><p>${task.description}</p><p>Due: ${new Date(task.dueDate).toDateString()}</p>`;
        taskList.appendChild(div);
    }

    fetch('/api/tasks')
        .then(response => response.json())
        .then(tasks => tasks.forEach(addTaskToList))
        .catch(error => console.error('Error:', error));

    googleSignInButton.addEventListener('click', function() {
        const clientId = 'YOUR_GOOGLE_CLIENT_ID';
        const redirectUri = 'YOUR_REDIRECT_URI';
        const scope = 'profile email';
        const responseType = 'token';
        const oauthUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=${responseType}`;

        window.location.href = oauthUrl;
    });
});

Replace 'YOUR_GOOGLE_CLIENT_ID' and 'YOUR_REDIRECT_URI' with your actual Google client ID and redirect URI.

7. Configure Express Session

Ensure your main server file (e.g., app.js or server.js) is configured to use sessions and initialize Passport.

const express = require('express');
const session = require('express-session');
const passport = require('passport');
const userRoutes = require('./routes/userRoutes');

const app = express();

app.use(session({
  secret: 'your_secret_key',
  resave: false,
  saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

app.use('/api', userRoutes);

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

By following these steps, you will have implemented Google sign-in on your website, allowing users to sign up and log in using their Google accounts.

Click here to create a Pull Request with the proposed solution

Files used for this task:

Changes on package.json To implement Google OAuth in your application, you need to add the necessary dependencies to your `package.json` file. Specifically, you will need the `passport-google-oauth20` package, which provides the strategy for authenticating with Google using OAuth 2.0. Here is the updated `package.json` file with the required dependency added: ```json { "name": "studyplanner", "version": "1.0.0", "description": "AI assisted study planner", "main": "app.js", "scripts": { "start": "node app.js", "dev": "nodemon app.js", "test": "jest --verbose --coverage", "lint": "eslint .", "prettier": "prettier --write ." }, "author": "Tea Larson-Hetrick", "license": "MIT", "dependencies": { "bcryptjs": "^2.4.3", "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "mongodb": "^6.3.0", "mongoose": "^8.0.1", "morgan": "^1.10.0", "passport": "^0.6.0", "passport-google-oauth20": "^2.0.0" }, "devDependencies": { "eslint": "^8.54.0", "jest": "^29.7.0", "nodemon": "^3.0.1" }, "repository": { "type": "git", "url": "git+https://github.com/temichelle13/StudyPlanner.git" }, "bugs": { "url": "https://github.com/temichelle13/StudyPlanner/issues" }, "homepage": "https://github.com/temichelle13/StudyPlanner#readme", "keywords": [] } ``` ### Steps to Update `package.json`: 1. Open the `package.json` file in your project. 2. Add `"passport": "^0.6.0"` and `"passport-google-oauth20": "^2.0.0"` to the `dependencies` section. 3. Save the file. ### Next Steps: 1. Run `npm install` to install the new dependencies. 2. Proceed with the changes in the other files as per the plan: - Configure the Google OAuth provider in `auth/providers.json`. - Add routes for Google OAuth login and callback handling in `routes/userRoutes.js`. - Add a Google sign-in button in `index.html`. - Implement the click event handler for the Google sign-in button in `main.js`. This will set up the necessary dependencies for Google OAuth in your project.
Changes on index.html To add a Google sign-in button to the HTML, which will redirect users to the Google OAuth login page, you can follow these steps: 1. **Add the Google Sign-In Button**: Insert a button in the HTML that users can click to initiate the Google OAuth flow. 2. **Link the Button to the OAuth Flow**: Ensure that clicking the button redirects the user to the appropriate route for Google OAuth login. Here's the updated `index.html` file with the necessary changes: ```html Study Planner

Study Planner

Your Schedule

Your Tasks

Login

© 2023 Study Planner

``` ### Explanation: 1. **Header and Main Sections**: These sections remain unchanged. 2. **New Section for Authentication**: Added a new section with an `id` of `auth` that contains a button for Google sign-in. 3. **Google Sign-In Button**: The button with `id="google-sign-in"` will be used to initiate the Google OAuth flow. Next, you will need to implement the click event handler for this button in `main.js` to redirect users to the Google OAuth login page. This will be handled in the `main.js` file as per the task plan. If you have any further questions or need additional changes, feel free to ask!
Changes on auth/providers.json To solve the task of adding Google OAuth provider configuration to the `auth/providers.json` file, follow these steps: 1. **Add Google OAuth Provider Configuration**: - You need to add a new entry for the Google OAuth provider in the `providers.json` file. This entry should include the necessary settings such as `clientID`, `clientSecret`, and any other required configurations. Here is an example of how you can modify the `auth/providers.json` file to include the Google OAuth provider: ```json { "anon-user": { "name": "anon-user", "type": "anon-user", "disabled": false }, "api-key": { "name": "api-key", "type": "api-key", "disabled": true }, "local-userpass": { "name": "local-userpass", "type": "local-userpass", "config": { "autoConfirm": true, "resetFunctionName": "resetFunc", "runConfirmationFunction": false, "runResetFunction": true }, "disabled": false }, "google-oauth": { "name": "google-oauth", "type": "oauth2", "config": { "clientID": "YOUR_GOOGLE_CLIENT_ID", "clientSecret": "YOUR_GOOGLE_CLIENT_SECRET", "authorizationURL": "https://accounts.google.com/o/oauth2/auth", "tokenURL": "https://oauth2.googleapis.com/token", "callbackURL": "http://yourapp.com/auth/google/callback", "scope": ["profile", "email"] }, "disabled": false } } ``` ### Explanation: - **name**: The name of the provider, in this case, "google-oauth". - **type**: The type of the provider, which is "oauth2" for Google OAuth. - **config**: Contains the configuration settings for the Google OAuth provider. - **clientID**: Your Google OAuth client ID. - **clientSecret**: Your Google OAuth client secret. - **authorizationURL**: The URL to redirect users to for Google OAuth authorization. - **tokenURL**: The URL to exchange the authorization code for an access token. - **callbackURL**: The URL to which Google will redirect users after they have authorized your application. - **scope**: The permissions your application is requesting from the user. Make sure to replace `YOUR_GOOGLE_CLIENT_ID` and `YOUR_GOOGLE_CLIENT_SECRET` with your actual Google OAuth client ID and client secret. This change will configure your application to use Google OAuth for user authentication. The other files mentioned in the plan will handle the routing, front-end integration, and event handling to complete the OAuth flow.
Changes on routes/userRoutes.js To implement Google OAuth login and callback handling in the `routes/userRoutes.js` file, follow these steps: 1. **Install Necessary Dependencies**: Ensure you have the required dependencies installed. You will need `passport`, `passport-google-oauth20`, and `express-session`. ```bash npm install passport passport-google-oauth20 express-session ``` 2. **Configure Passport for Google OAuth**: Create a new file `config/passport.js` to configure Passport with Google OAuth strategy. ```javascript const passport = require('passport'); const GoogleStrategy = require('passport-google-oauth20').Strategy; const User = require('../models/user'); passport.use(new GoogleStrategy({ clientID: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, callbackURL: "/auth/google/callback" }, async (token, tokenSecret, profile, done) => { try { let user = await User.findOne({ googleId: profile.id }); if (!user) { user = new User({ googleId: profile.id, name: profile.displayName, email: profile.emails[0].value }); await user.save(); } return done(null, user); } catch (error) { return done(error, null); } } )); passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (error) { done(error, null); } }); module.exports = passport; ``` 3. **Update `routes/userRoutes.js`**: Modify the `routes/userRoutes.js` file to include routes for Google OAuth login and callback handling. ```javascript const express = require('express'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const passport = require('passport'); const User = require('../models/user'); const router = express.Router(); // Initialize Passport require('../config/passport'); // User registration with request body validation router.post('/register', async (req, res) => { // Validate request body here try { const user = new User(req.body); await user.save(); const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); res.status(201).send({ user, token }); } catch (error) { res.status(400).send(error); } }); // User login with request body validation router.post('/login', async (req, res) => { // Validate request body here try { const user = await User.findByCredentials(req.body.email, req.body.password); const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); res.send({ user, token }); } catch (error) { res.status(400).send(error); } }); // Google OAuth login route router.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] })); // Google OAuth callback route router.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/login' }), (req, res) => { // Successful authentication, redirect home. res.redirect('/'); } ); module.exports = router; ``` 4. **Configure Express Session**: Ensure your main server file (e.g., `app.js` or `server.js`) is configured to use sessions and initialize Passport. ```javascript const express = require('express'); const session = require('express-session'); const passport = require('passport'); const userRoutes = require('./routes/userRoutes'); const app = express(); // Middleware for sessions app.use(session({ secret: 'your_secret_key', resave: false, saveUninitialized: true })); // Initialize Passport app.use(passport.initialize()); app.use(passport.session()); // Use user routes app.use('/api', userRoutes); // Other middleware and routes... app.listen(3000, () => { console.log('Server is running on port 3000'); }); ``` By following these steps, you will have added routes for Google OAuth login and callback handling in the `routes/userRoutes.js` file, and implemented the logic to authenticate users using Google OAuth tokens.
Changes on main.js To implement the Google sign-in functionality in `main.js`, you need to add an event listener for the Google sign-in button. This event listener will initiate the OAuth flow by redirecting the user to the Google OAuth login page. Here is a step-by-step guide to achieve this: 1. **Add a Google Sign-In Button**: Ensure that there is a Google sign-in button in your HTML file (`index.html`). This button should have an ID that you can reference in your JavaScript code. 2. **Add Event Listener for Google Sign-In Button**: In `main.js`, add an event listener for the Google sign-in button. When the button is clicked, it should redirect the user to the Google OAuth login page. 3. **Initiate OAuth Flow**: Redirect the user to the Google OAuth login page. This typically involves constructing a URL with the necessary query parameters, including your client ID, redirect URI, and scopes. Here is the updated `main.js` with the necessary changes: ```javascript document.addEventListener('DOMContentLoaded', function() { const taskForm = document.getElementById('taskForm'); const taskList = document.getElementById('taskList'); const googleSignInButton = document.getElementById('googleSignInButton'); // Add this line // Event listener for form submission taskForm.addEventListener('submit', function(e) { e.preventDefault(); const title = document.getElementById('taskTitle').value; const description = document.getElementById('taskDescription').value; const dueDate = document.getElementById('taskDueDate').value; // POST request to add a new task fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title, description, dueDate }) }) .then(response => response.json()) .then(addTaskToList) .catch(error => console.error('Error:', error)); taskForm.reset(); }); // Function to add a task to the DOM function addTaskToList(task) { const div = document.createElement('div'); div.innerHTML = `

${task.title}

${task.description}

Due: ${new Date(task.dueDate).toDateString()}

`; taskList.appendChild(div); } // Load existing tasks on page load fetch('/api/tasks') .then(response => response.json()) .then(tasks => tasks.forEach(addTaskToList)) .catch(error => console.error('Error:', error)); // Event listener for Google sign-in button googleSignInButton.addEventListener('click', function() { const clientId = 'YOUR_GOOGLE_CLIENT_ID'; // Replace with your actual client ID const redirectUri = 'YOUR_REDIRECT_URI'; // Replace with your actual redirect URI const scope = 'profile email'; const responseType = 'token'; const oauthUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=${responseType}`; // Redirect to Google OAuth login page window.location.href = oauthUrl; }); }); ``` ### Explanation: 1. **Google Sign-In Button**: Ensure you have a button with the ID `googleSignInButton` in your HTML file. 2. **Event Listener**: Add an event listener for the Google sign-in button. When clicked, it constructs the OAuth URL with the necessary parameters and redirects the user to the Google OAuth login page. Make sure to replace `'YOUR_GOOGLE_CLIENT_ID'` and `'YOUR_REDIRECT_URI'` with your actual Google client ID and redirect URI. This change will initiate the OAuth flow when the Google sign-in button is clicked, redirecting the user to the Google OAuth login page.