A Laravel 11 application that allows users to search Google Workspace groups and view a flattened list of all members, including those from nested groups.
- Google OAuth Authentication: Users sign in with their Google account
- Authorization Check: Only users in the database can access the application
- Admin User Management: Admin users can create, edit, and delete users via a web interface
- Recursive Group Expansion: Automatically expands nested groups to show all members
- Duplicate Removal: Removes duplicate users and shows which groups they belong to
- Pagination: Results are paginated (50 per page) for better performance
- Client-Side Search: Search across all members by name, email, title, department, or source group
- Session Caching: Results are cached in session to avoid re-fetching on pagination
- Domain Auto-Append: Optionally configure EMAIL_DOMAIN to allow users to search without typing the full email
- CSV Export: Download group member lists as CSV files
- User Information: Displays name, email, source group, title, and department
- Ubuntu Server (20.04 LTS or later recommended)
- PHP 8.2 or higher
- Apache 2.4+ with SSL support
- MySQL 8.0+ or MariaDB 10.6+
- Composer
- Domain name with DNS pointing to your server (required for SSL)
- Google Workspace account with admin access
- Google OAuth credentials
- Google Service Account with domain-wide delegation
Important: This application requires SSL/HTTPS and must run on port 443. You must have a valid domain name configured before installation.
This guide assumes you're starting with a fresh Ubuntu server installation.
sudo apt update
sudo apt upgrade -ysudo apt install apache2 -y
sudo systemctl enable apache2
sudo systemctl start apache2Verify Apache is running:
sudo systemctl status apache2sudo apt install mysql-server -y
sudo systemctl enable mysql
sudo systemctl start mysqlSecure MySQL installation (follow prompts):
sudo mysql_secure_installationCreate a database and user for the application:
sudo mysql -u root -pIn the MySQL prompt:
CREATE DATABASE groupmembers CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'groupmembers'@'localhost' IDENTIFIED BY 'your_strong_password_here';
GRANT ALL PRIVILEGES ON groupmembers.* TO 'groupmembers'@'localhost';
FLUSH PRIVILEGES;
EXIT;sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
sudo apt install php8.2 php8.2-cli php8.2-common php8.2-mysql php8.2-zip php8.2-gd php8.2-mbstring php8.2-curl php8.2-xml php8.2-bcmath php8.2-fpm php8.2-intl -yVerify PHP installation:
php -vcd ~
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composerVerify Composer installation:
composer --versionsudo apt install git -ysudo a2enmod rewrite
sudo a2enmod headers
sudo a2enmod ssl
sudo systemctl restart apache2Note: This application requires SSL/HTTPS and must run on port 443. You must have a domain name configured and pointing to your server's IP address.
Install Certbot:
sudo apt install certbot python3-certbot-apache -yObtain SSL certificate (replace your-domain.com with your actual domain):
sudo certbot certonly --standalone -d your-domain.com -d www.your-domain.comFollow the prompts to complete SSL certificate generation. You'll need to provide an email address and agree to the terms of service.
Create a new Apache virtual host configuration for HTTPS (port 443):
sudo nano /etc/apache2/sites-available/groupmembers-ssl.confAdd the following configuration (replace your-domain.com with your actual domain):
<VirtualHost *:443>
ServerName your-domain.com
ServerAlias www.your-domain.com
DocumentRoot /var/www/groupmembers/public
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/your-domain.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
<Directory /var/www/groupmembers/public>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/groupmembers_ssl_error.log
CustomLog ${APACHE_LOG_DIR}/groupmembers_ssl_access.log combined
</VirtualHost>Create HTTP to HTTPS redirect configuration:
sudo nano /etc/apache2/sites-available/groupmembers.confAdd the following to redirect all HTTP traffic to HTTPS:
<VirtualHost *:80>
ServerName your-domain.com
ServerAlias www.your-domain.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>Enable the sites and disable the default site:
sudo a2ensite groupmembers.conf
sudo a2ensite groupmembers-ssl.conf
sudo a2dissite 000-default.conf
sudo systemctl restart apache2Verify SSL is working by checking Apache status:
sudo systemctl status apache2
sudo apache2ctl -Scd /var/www
sudo git clone https://github.com/childrda/GroupMembers.git groupmembers
sudo chown -R www-data:www-data /var/www/groupmembers
cd groupmemberssudo -u www-data composer install --no-dev --optimize-autoloadersudo -u www-data cp example.env .env
sudo nano .envUpdate the following in .env:
APP_NAME- Your application nameAPP_URL- Your domain URL must use HTTPS (e.g.,https://your-domain.com)DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=groupmembersDB_USERNAME=groupmembersDB_PASSWORD=your_strong_password_here
Important: The APP_URL must use https:// as the application requires SSL.
sudo -u www-data php artisan key:generatesudo chown -R www-data:www-data /var/www/groupmembers
sudo chmod -R 755 /var/www/groupmembers
sudo chmod -R 775 /var/www/groupmembers/storage
sudo chmod -R 775 /var/www/groupmembers/bootstrap/cachesudo -u www-data php artisan migrateLet's Encrypt certificates expire every 90 days. Set up automatic renewal:
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timerTest the renewal process:
sudo certbot renew --dry-runThis ensures your SSL certificate will be automatically renewed before expiration.
If you've already completed the server installation above, you can skip to step 3. Otherwise, follow these steps:
- Clone the repository:
git clone https://github.com/childrda/GroupMembers.git
cd GroupMembers- Install dependencies:
composer install- Copy the environment file:
cp example.env .env- Generate application key:
php artisan key:generate- Run migrations:
php artisan migrate- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google+ API
- Create OAuth 2.0 credentials (Web application)
- Add authorized redirect URI:
http://your-domain/auth/google/callback - Update your
.envfile:
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=http://your-domain/auth/google/callback
-
Enable Admin SDK API:
- Go to Google Cloud Console
- Navigate to APIs & Services → Library
- Search for "Admin SDK API"
- Click on it and press Enable
- Wait a few minutes for the API to propagate
-
In Google Cloud Console, create a Service Account
-
Enable Domain-Wide Delegation
-
Download the JSON key file
-
Place the JSON file at:
storage/app/google-credentials.json -
In Google Admin Console, authorize the service account with these scopes:
https://www.googleapis.com/auth/admin.directory.group.readonlyhttps://www.googleapis.com/auth/admin.directory.group.member.readonlyhttps://www.googleapis.com/auth/admin.directory.user.readonly
-
Update your
.envfile:
GOOGLE_ADMIN_EMAIL=admin@yourdomain.com
EMAIL_DOMAIN=yourdomain.com
Note: EMAIL_DOMAIN is optional. If set, users can search for groups by entering just the group name (e.g., "all-elementary-staff") instead of the full email address.
Add authorized users to the users table. You can do this via MySQL:
mysql -u groupmembers -p groupmembersINSERT INTO users (name, email, email_verified_at, created_at, updated_at)
VALUES ('John Doe', 'john@yourdomain.com', NOW(), NOW(), NOW());Or use the admin interface (if you have an admin user) or create an admin user directly:
INSERT INTO users (name, email, email_verified_at, isadmin, created_at, updated_at)
VALUES ('Admin User', 'admin@yourdomain.com', NOW(), 1, NOW(), NOW());Note: The first admin user must be created via SQL. After that, admin users can manage other users through the web interface at /admin/users.
If you've set up Apache as described above, navigate to your domain using HTTPS:
https://your-domain.com
Note: HTTP traffic will automatically redirect to HTTPS. The application only accepts connections on port 443 (HTTPS).
For local development:
php artisan serveThen navigate to http://localhost:8000
- Navigate to your application URL
- Click "Sign in with Google"
- If your email is in the database, you'll be redirected to the dashboard
- Admin Users: Access the "Admin" link in the navigation to manage users
- Go to "Groups" to search for a Google Workspace group
- Enter a group email (e.g.,
all-elementary-staff@lcps.k12.va.us) or just the group name ifEMAIL_DOMAINis configured - View the flattened member list with pagination
- Use the search box to filter members across all results
- Navigate between pages using pagination controls
- Download results as CSV if needed
Admin users (those with isadmin = true) can:
- View all users at
/admin/users - Create new users
- Edit existing users (including admin status)
- Delete users (cannot delete themselves)
- Access is protected by middleware - only admin users can access these routes
The application requires the following Google Directory API scopes:
https://www.googleapis.com/auth/admin.directory.group.readonlyhttps://www.googleapis.com/auth/admin.directory.group.member.readonlyhttps://www.googleapis.com/auth/admin.directory.user.readonly
- The service account JSON file should be kept secure and never committed to version control
- Only authorized users (those in the database) can access the application
- The application uses Laravel's built-in session-based authentication
- Admin routes are protected by middleware - only users with
isadmin = truecan access them - Keep your
.envfile secure and never commit it to version control - Use strong passwords for your MySQL database user
- Regularly update your system packages:
sudo apt update && sudo apt upgrade - Consider setting up a firewall (UFW) to restrict access to necessary ports only
- SSL/HTTPS is mandatory - The application only accepts secure connections on port 443
- Keep SSL certificates up to date - Let's Encrypt certificates auto-renew every 90 days
- Ensure your domain's DNS is properly configured before setting up SSL
"403 Forbidden" error:
- Check file permissions:
sudo chown -R www-data:www-data /var/www/groupmembers - Verify Apache can read the directory:
sudo chmod -R 755 /var/www/groupmembers - Ensure
mod_rewriteis enabled:sudo a2enmod rewrite && sudo systemctl restart apache2
"500 Internal Server Error":
- Check Apache error logs:
sudo tail -f /var/log/apache2/error.log - Check Laravel logs:
tail -f /var/www/groupmembers/storage/logs/laravel.log - Verify file permissions on
storageandbootstrap/cachedirectories
SSL Certificate Issues:
- Verify SSL module is enabled:
sudo a2enmod ssl && sudo systemctl restart apache2 - Check certificate paths in virtual host config match your domain
- Verify certificate files exist:
sudo ls -la /etc/letsencrypt/live/your-domain.com/ - Test SSL configuration:
sudo apache2ctl configtest - Check if port 443 is open:
sudo ufw status(if firewall is enabled) - Ensure DNS is properly configured and pointing to your server IP
"SQLSTATE[HY000] [2002] No such file or directory":
- Ensure MySQL is running:
sudo systemctl status mysql - Check your
.envfile has correct database credentials - Verify database exists:
mysql -u root -p -e "SHOW DATABASES;"
- Ensure the JSON file is placed at
storage/app/google-credentials.json - Check file permissions:
sudo chmod 600 storage/app/google-credentials.json - Verify ownership:
sudo chown www-data:www-data storage/app/google-credentials.json
- Add your email to the
userstable in the database - Verify the email matches exactly (case-sensitive)
"The stream or file could not be opened":
- Fix storage permissions:
sudo chmod -R 775 storage bootstrap/cache - Fix ownership:
sudo chown -R www-data:www-data storage bootstrap/cache
"Admin SDK API has not been used" or "SERVICE_DISABLED"
- Enable the Admin SDK API in Google Cloud Console:
- Go to APIs & Services → Library
- Search for "Admin SDK API"
- Click Enable
- Wait a few minutes for propagation
"invalid_grant" or "Invalid email or User ID"
- Verify domain-wide delegation is enabled for the service account
- Check that all required scopes are authorized in Google Admin Console
- Ensure the admin email in
.envmatches a valid admin account - Verify the email exists in your Google Workspace domain
- Framework: Laravel 11
- PHP: 8.2+
- Authentication: Laravel Socialite (Google OAuth)
- Google APIs: Google API Client Library
- Frontend: Tailwind CSS (via CDN)
group-members/
├── app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── Admin/
│ │ │ │ └── AdminUserController.php
│ │ │ ├── Auth/
│ │ │ │ └── GoogleAuthController.php
│ │ │ ├── DashboardController.php
│ │ │ └── GroupController.php
│ │ └── Middleware/
│ │ ├── AdminMiddleware.php
│ │ └── CheckAuthorizedUser.php
│ ├── Models/
│ │ └── User.php
│ └── Services/
│ └── GoogleDirectoryService.php
├── config/
│ └── services.php (Google OAuth & Service Account config)
├── database/
│ └── migrations/
│ ├── 0001_01_01_000000_create_users_table.php
│ └── 2024_01_01_000003_add_isadmin_to_users_table.php
├── resources/
│ └── views/
│ ├── admin/
│ │ └── users/ (index, create, edit)
│ ├── auth/ (login, unauthorized)
│ ├── groups/ (index, results)
│ └── layouts/ (app.blade.php)
└── routes/
└── web.php
Contributions are welcome! Please feel free to submit a Pull Request.
This project is open-sourced software licensed under the MIT license.