Matrix movie still

Storing JSON in .env Files: A Laravel Developer’s Guide to Managing Google Service Account Keys

When working with Laravel and Google Cloud services, you’ll often need to manage service account credentials that come as JSON files. These credentials are sensitive and shouldn’t be committed to version control, making .env files the natural choice for storage. However, .env files are designed for simple key-value pairs, not complex JSON structures. Let’s explore the best practices for handling this common challenge.

The Challenge

Google service account keys typically look like this:

{
    "type": "service_account",
    "project_id": "your-project-id",
    "private_key_id": "key-id",
    "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
    "client_email": "[email protected]",
    "client_id": "123456789",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/..."
}

The challenge is that .env files don’t natively support multi-line values or complex structures, and the private key contains newline characters that can break the parsing.

🤓😎 More and more people are getting our Geek, Privacy, Dev & Lifestyle Tips

Want to receive the latest Geek, Privacy, Dev & Lifestyle blogs? Subscribe to our newsletter.

Solution 1: Base64 Encoding (Recommended)

The most reliable approach is to encode your JSON as a base64 string. This eliminates issues with special characters, newlines, and quotes.

Implementation Steps

Step 1: Encode your JSON file

# On Linux/Mac
base64 -i credentials.json -o encoded.txt

# Or using PHP
php -r "echo base64_encode(file_get_contents('credentials.json'));"

Step 2: Add to your .env file

GOOGLE_SERVICE_ACCOUNT_JSON=eyJ0eXBlIjoic2VydmljZV9hY2NvdW50IiwicHJvamVjdF9pZCI6...

Step 3: Decode in your Laravel application

// config/services.php
return [
    'google' => [
        'service_account' => env('GOOGLE_SERVICE_ACCOUNT_JSON') 
            ? json_decode(base64_decode(env('GOOGLE_SERVICE_ACCOUNT_JSON')), true)
            : null,
    ],
];

Step 4: Use in your application

use Google\Client;

class GoogleServiceProvider
{
    public function getClient(): Client
    {
        $client = new Client();
        $serviceAccount = config('services.google.service_account');
        
        if ($serviceAccount) {
            $client->setAuthConfig($serviceAccount);
            $client->setScopes(['https://www.googleapis.com/auth/drive']);
        }
        
        return $client;
    }
}

Solution 2: Individual Environment Variables

For simpler cases or when you only need specific values, you can extract individual fields:

GOOGLE_PROJECT_ID=your-project-id
GOOGLE_CLIENT_EMAIL=service-account@project.iam.gserviceaccount.com
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

Then reconstruct the array in your config:

// config/services.php
return [
    'google' => [
        'project_id' => env('GOOGLE_PROJECT_ID'),
        'client_email' => env('GOOGLE_CLIENT_EMAIL'),
        'private_key' => str_replace('\\n', "\n", env('GOOGLE_PRIVATE_KEY')),
    ],
];

Solution 3: JSON String with Escaped Characters

You can store the JSON as a single-line string with escaped characters:

GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"..."}'

However, this approach becomes unwieldy with private keys containing newlines and requires careful escaping.

Best Practices

1. Never Commit Credentials to Version Control

Always include your .env file in .gitignore:

.env
.env.backup
.env.production
credentials.json
*.json  # If storing service account files

2. Use Laravel’s Configuration Caching

Cache your configuration in production to improve performance:

php artisan config:cache

Remember to clear the cache when updating environment variables:

php artisan config:clear

3. Validate Configuration on Application Boot

Create a service provider to validate your Google credentials early:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class GoogleCredentialsServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if (app()->environment('production')) {
            $this->validateGoogleCredentials();
        }
    }
    
    private function validateGoogleCredentials(): void
    {
        $credentials = config('services.google.service_account');
        
        if (!$credentials) {
            throw new \RuntimeException('Google service account credentials not configured');
        }
        
        $requiredKeys = ['type', 'project_id', 'private_key', 'client_email'];
        
        foreach ($requiredKeys as $key) {
            if (!isset($credentials[$key])) {
                throw new \RuntimeException("Missing required Google credential: {$key}");
            }
        }
    }
}

4. Use Environment-Specific Files

Laravel supports environment-specific .env files:

  • .env.local – Local development
  • .env.testing – Testing environment
  • .env.staging – Staging environment
  • .env.production – Production environment

5. Implement Key Rotation

Create a command to help rotate service account keys:

namespace App\Console\Commands;

use Illuminate\Console\Command;

class RotateGoogleCredentials extends Command
{
    protected $signature = 'google:rotate-credentials {path : Path to new JSON file}';
    protected $description = 'Rotate Google service account credentials';
    
    public function handle(): int
    {
        $path = $this->argument('path');
        
        if (!file_exists($path)) {
            $this->error("File not found: {$path}");
            return 1;
        }
        
        $json = file_get_contents($path);
        $encoded = base64_encode($json);
        
        $this->info('Add this to your .env file:');
        $this->line("GOOGLE_SERVICE_ACCOUNT_JSON={$encoded}");
        
        // Optionally validate the new credentials
        $credentials = json_decode($json, true);
        if (!$credentials) {
            $this->error('Invalid JSON file');
            return 1;
        }
        
        $this->info('✓ Credentials validated successfully');
        return 0;
    }
}

6. Use Laravel Encryption for Extra Security

For highly sensitive environments, consider encrypting the credentials:

// Storing encrypted credentials
$encrypted = encrypt(file_get_contents('credentials.json'));
// Store $encrypted in .env

// Retrieving and decrypting
$credentials = json_decode(decrypt(env('GOOGLE_SERVICE_ACCOUNT_JSON_ENCRYPTED')), true);

7. Document Your Approach

Always document your chosen approach in your project’s README:

## Google Service Account Setup

1. Obtain your service account JSON file from Google Cloud Console
2. Encode the file: `base64 -i credentials.json`
3. Copy the output to `.env`: `GOOGLE_SERVICE_ACCOUNT_JSON=<encoded_string>`
4. Clear config cache: `php artisan config:clear`
5. Test the connection: `php artisan google:test-connection`

Security Considerations

  1. Principle of Least Privilege: Only grant the minimum required permissions to your service account
  2. Regular Audits: Periodically review which service accounts have access to your resources
  3. Monitor Usage: Use Google Cloud’s audit logs to monitor service account activity
  4. Secure Storage: In production, consider using:
    • AWS Secrets Manager
    • Google Secret Manager
    • Azure Key Vault
    • HashiCorp Vault

Alternative: File-Based Approach

For some scenarios, keeping the JSON file separate might be cleaner:

// config/services.php
return [
    'google' => [
        'service_account_path' => env('GOOGLE_APPLICATION_CREDENTIALS', storage_path('credentials/google.json')),
    ],
];

// Usage
$client = new Client();
$client->setAuthConfig(config('services.google.service_account_path'));

Then in .env:

GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json

Conclusion

While .env files weren’t designed for complex JSON structures, the base64 encoding approach provides a reliable, secure, and maintainable solution for storing Google service account credentials in Laravel applications. By following these best practices, you can ensure your credentials remain secure while maintaining a clean and professional codebase.

Remember that security is not a one-time setup but an ongoing process. Regularly review your credential management practices, keep your dependencies updated, and always follow the principle of least privilege when configuring service accounts.

For production environments, consider moving beyond .env files to dedicated secret management solutions that provide better security features like automatic rotation, audit logging, and fine-grained access control.

Last Updated on 8 September 2025

Leave a Comment

Your email address will not be published. Required fields are marked *

en_USEnglish
Scroll to Top