Post

Messkit Series: Working with Google Authenticator

Messkit Series: Working with Google Authenticator

Introduction

In an era where cybersecurity breaches make headlines daily, implementing robust authentication mechanisms is no longer optional—it’s essential. Two-Factor Authentication (2FA) using Time-based One-Time Passwords (TOTP) has become common for securing digital accounts, with Google Authenticator serving as one of the top 5 most utilized authentication apps.

Today, we’ll explore two powerful MessKit functions that bring Google Authenticator functionality directly into your PowerShell environment: New-MKGoogleAuthSecret and Get-MKGoogleAuthPin. Whether you’re a system administrator setting up 2FA for users, a developer integrating authentication into applications, or a security professional testing TOTP implementations, these functions provide the tools you need.

Understanding TOTP and Google Authenticator

Before diving into the functions, let’s understand what we’re working with:

Time-based One-Time Password (TOTP) is an algorithm that generates a unique numeric code based on:

  • A shared secret key
  • The current time
  • A predefined time window (usually 30 seconds)

Google Authenticator implements the TOTP standard (RFC 6238) and generates 6-digit codes that refresh every 30 seconds. The magic happens through cryptographic operations using HMAC-SHA1, ensuring that both the server and authenticator app generate identical codes when synchronized.

The MessKit Google Authenticator Functions

Our MessKit module provides two complementary functions:

  1. New-MKGoogleAuthSecret - Creates new TOTP secrets with QR codes for easy setup
  2. Get-MKGoogleAuthPin - Generates current PIN codes from existing secrets

These functions follow the same TOTP standards used by Google Authenticator, Microsoft Authenticator, and other major authentication apps, ensuring full compatibility.

Getting Started: Creating Your First Secret

Let’s start by generating a new Google Authenticator secret:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create a new secret for a user account
$auth = New-MKGoogleAuthSecret -AccountName "john.doe@company.com" -Issuer "MyCompany"

# Display the secret information
$auth | Format-List

Secret      : MFRGG2LTMREXGMDJOBZWE4LOMNQXIZLFOR2GCY3PNZWWK
AccountName : john.doe@company.com
Issuer      : MyCompany
Algorithm   : SHA1
Digits      : 6
Period      : 30
KeyUri      : otpauth://totp/MyCompany:john.doe@company.com?secret=MFRGG...
QrCodeUri   : https://api.qrserver.com/v1/create-qr-code/?size=300x300...

The function generates a cryptographically secure 20-byte (160-bit) secret and provides everything needed for setup, including a QR code URL that users can scan with their authenticator app.

Generating PIN Codes

Once you have a secret, generating the current PIN is straightforward:

1
2
3
4
5
6
7
8
9
10
11
12
# Generate the current PIN
$pin = Get-MKGoogleAuthPin -Secret $auth.Secret

# Display the result
$pin

PinCode          : 123 456
RawPin           : 123456
SecondsRemaining : 23
ExpiresAt        : 1/18/2025 2:15:30 PM
Algorithm        : SHA1
Digits           : 6

The function returns both a formatted PIN (with space for readability) and the raw PIN for programmatic use, along with timing information to know when the code expires.

Understanding the Parameters

New-MKGoogleAuthSecret Parameters

  • -AccountName (Required): Usually an email address that identifies the account
  • -Issuer (Required): Your company or service name
  • -SecretLength: Secret size in bytes (default: 20 for enhanced security)
  • -Algorithm: HMAC algorithm - SHA1 (default), SHA256, or SHA512
  • -Digits: PIN length - 6 (default) or 8 digits
  • -Period: Time window in seconds (default: 30)
  • -Online: Automatically opens QR code in browser
  • -UseThisSecretCode: Use an existing secret instead of generating new

Get-MKGoogleAuthPin Parameters

  • -Secret (Required): The BASE32 encoded secret
  • -TimeWindow: Time window in seconds (default: 30)
  • -Algorithm: Must match the secret’s algorithm
  • -Digits: Must match the secret’s digit count
  • -NoFormatting: Return PIN without space formatting

Real-World Usage Scenarios

Scenario 1: Setting Up 2FA for New Employees

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Bulk setup for new employees
$newEmployees = @(
    @{Email="alice@company.com"; Department="IT"}
    @{Email="bob@company.com"; Department="Sales"}
    @{Email="carol@company.com"; Department="HR"}
)

$employees = $newEmployees | ForEach-Object {
    $auth = New-MKGoogleAuthSecret -AccountName $_.Email -Issuer "CompanyPortal"
    [PSCustomObject]@{
        Employee = $_.Email
        Department = $_.Department
        Secret = $auth.Secret
        QRCode = $auth.QrCodeUri
        SetupUri = $auth.KeyUri
    }
}

# Export setup information for IT team
$employees | Export-Csv "2FA_Setup_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation

# Display QR codes for immediate setup
$employees | ForEach-Object {
    Write-Host "Setup 2FA for $($_.Employee):" -ForegroundColor Green
    Write-Host "QR Code: $($_.QRCode)" -ForegroundColor Cyan
    Write-Host "Manual Entry: $($_.Secret)" -ForegroundColor Yellow
    Write-Host "---"
}

Scenario 2: PIN Monitoring and Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Monitor PIN changes in real-time
function Start-PINMonitor {
    param([string]$Secret, [string]$AccountName)

    Write-Host "Monitoring TOTP codes for: $AccountName" -ForegroundColor Green
    Write-Host "Press Ctrl+C to stop`n" -ForegroundColor Yellow

    while ($true) {
        $pin = Get-MKGoogleAuthPin -Secret $Secret
        Clear-Host

        Write-Host "Account: $AccountName" -ForegroundColor Cyan
        Write-Host "Current PIN: $($pin.PinCode)" -ForegroundColor Green -NoNewline

        # Color code based on time remaining
        $color = if ($pin.SecondsRemaining -le 5) { "Red" }
                elseif ($pin.SecondsRemaining -le 10) { "Yellow" }
                else { "Green" }

        Write-Host " (Expires in $($pin.SecondsRemaining)s)" -ForegroundColor $color
        Write-Host "Next refresh: $($pin.ExpiresAt.ToString('HH:mm:ss'))" -ForegroundColor Gray

        Start-Sleep 1
    }
}

# Start monitoring
Start-PINMonitor -Secret "JBSWY3DPEHPK3PXP" -AccountName "test@example.com"

Scenario 3: Batch PIN Generation for Multiple Accounts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Validate multiple accounts at once
$accounts = @(
    @{Name="Work Email"; Secret="T5UOE4YLIFYTZQA2"}
    @{Name="Personal Gmail"; Secret="JBSWY3DPEHPK3PXP"}
    @{Name="Azure Account"; Secret="MFRGG2LTMREXGMDJ"}
)

$accounts | ForEach-Object {
    $pin = Get-MKGoogleAuthPin -Secret $_.Secret
    [PSCustomObject]@{
        Account = $_.Name
        PIN = $pin.RawPin
        ExpiresAt = $pin.ExpiresAt.ToString("HH:mm:ss")
        TimeRemaining = "$($pin.SecondsRemaining)s"
    }
} | Format-Table -AutoSize

Account         PIN    ExpiresAt TimeRemaining
-------         ---    --------- -------------
Work Email      123456 14:32:15  23s
Personal Gmail  789012 14:32:15  23s
Azure Account   345678 14:32:15  23s

Scenario 4: Automated PIN to Clipboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Save PIN to clipboard for easy pasting
function Copy-TOTPPin {
    param([string]$Secret, [string]$AccountName)

    $pin = Get-MKGoogleAuthPin -Secret $Secret
    $pin.RawPin | Set-Clipboard

    Write-Host "✓ PIN $($pin.PinCode) copied to clipboard" -ForegroundColor Green
    Write-Host "  Account: $AccountName" -ForegroundColor Cyan
    Write-Host "  Expires in: $($pin.SecondsRemaining) seconds" -ForegroundColor Yellow

    return $pin
}

# Usage
Copy-TOTPPin -Secret "JBSWY3DPEHPK3PXP" -AccountName "GitHub"

Advanced Features and Customization

Custom Time Windows and Algorithms

Some services use non-standard TOTP configurations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Create secret with 60-second time window and SHA256
$customAuth = New-MKGoogleAuthSecret `
    -AccountName "admin@secure-service.com" `
    -Issuer "SecureService" `
    -Period 60 `
    -Algorithm "SHA256" `
    -Digits 8

# Generate PIN with matching parameters
$customPin = Get-MKGoogleAuthPin `
    -Secret $customAuth.Secret `
    -TimeWindow 60 `
    -Algorithm "SHA256" `
    -Digits 8

Write-Host "8-digit PIN with 60s window: $($customPin.RawPin)"

Working with Existing Secrets

Sometimes you need to work with secrets generated elsewhere:

1
2
3
4
5
6
7
8
9
10
# Use an existing secret from another system
$existingSecret = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"

$auth = New-MKGoogleAuthSecret `
    -UseThisSecretCode $existingSecret `
    -AccountName "migrated-user@company.com" `
    -Issuer "MigratedSystem"

# Generate QR code for the existing secret
Write-Host "QR Code for existing secret: $($auth.QrCodeUri)"

Built-in PIN Generation Method

The secret object includes a convenient method for PIN generation:

1
2
3
4
5
6
# Create secret
$auth = New-MKGoogleAuthSecret -AccountName "test@example.com" -Issuer "TestApp"

# Use built-in method (equivalent to Get-MKGoogleAuthPin)
$pin = $auth.GetCurrentPin()
Write-Host "Current PIN: $($pin.PinCode)"

Integration with Password Managers and Scripts

Creating Setup Documents

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# Generate comprehensive setup documentation
function New-2FASetupDoc {
    param([string]$AccountName, [string]$Issuer)

    $auth = New-MKGoogleAuthSecret -AccountName $AccountName -Issuer $Issuer

    $doc = @"
# 2FA Setup Instructions for $AccountName

## Account Information
- **Account**: $($auth.AccountName)
- **Service**: $($auth.Issuer)
- **Algorithm**: $($auth.Algorithm)
- **Digits**: $($auth.Digits)
- **Period**: $($auth.Period) seconds

## Setup Methods

### Option 1: QR Code (Recommended)
1. Open your authenticator app
2. Scan this QR code: $($auth.QrCodeUri)

### Option 2: Manual Entry
1. Open your authenticator app
2. Choose "Enter setup key manually"
3. Enter this secret: `$($auth.Secret)`

## Backup Information
- **Secret Key**: $($auth.Secret)
- **Setup URI**: $($auth.KeyUri)

## Test Code
Current PIN: $(($auth.GetCurrentPin()).PinCode)
(Generated at $(Get-Date))

---
*Keep this information secure and accessible only to authorized personnel.*
"@

    return $doc
}

# Generate setup documentation
$setupDoc = New-2FASetupDoc -AccountName "ceo@company.com" -Issuer "ExecutivePortal"
$setupDoc | Out-File "2FA_Setup_CEO.md" -Encoding UTF8

Automated Testing and Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Test TOTP implementation against known test vectors
function Test-TOTPImplementation {
    # RFC 6238 test vectors
    $testVectors = @(
        @{Secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; Time=59; Expected="94287082"}
        @{Secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; Time=1111111109; Expected="07081804"}
        @{Secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; Time=1111111111; Expected="14050471"}
    )

    foreach ($vector in $testVectors) {
        # Note: This requires modifying the function to accept specific timestamps
        # for testing purposes (not shown in the main implementation)
        Write-Host "Testing vector for time $($vector.Time)..." -ForegroundColor Yellow
        Write-Host "Expected: $($vector.Expected)" -ForegroundColor Gray
        # Implementation would generate PIN for specific timestamp here
    }
}

Security Considerations and Best Practices

Secret Storage and Handling

  • Never log secrets in plain text - Use secure storage mechanisms
  • Implement proper access controls - Limit who can generate/view secrets
  • Use secure random generation - The functions use cryptographically secure RNG
  • Consider secret rotation policies - Regularly update secrets for high-security accounts

Backup and Recovery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Create encrypted backup of 2FA secrets
function Backup-2FASecrets {
    param([array]$Accounts, [string]$BackupPath)

    $backup = $Accounts | ConvertTo-Json -Depth 3
    $secureBackup = $backup | ConvertTo-SecureString -AsPlainText -Force
    $encryptedBackup = $secureBackup | ConvertFrom-SecureString

    $encryptedBackup | Out-File $BackupPath
    Write-Host "✓ Encrypted backup saved to: $BackupPath" -ForegroundColor Green
}

# Usage
$accounts = @(
    @{Name="Service1"; Secret="SECRET1"; Account="user@domain.com"}
    @{Name="Service2"; Secret="SECRET2"; Account="admin@domain.com"}
)

Backup-2FASecrets -Accounts $accounts -BackupPath "2FA_Backup_$(Get-Date -Format 'yyyyMMdd').txt"

Troubleshooting Common Issues

Clock Synchronization

TOTP codes are time-sensitive. If codes don’t match:

1
2
3
4
5
6
7
8
9
# Check time synchronization
$ntpTime = (Get-Date)
Write-Host "System time: $ntpTime"
Write-Host "Verify this matches your authenticator app's time"

# Generate PIN with time tolerance
$pin = Get-MKGoogleAuthPin -Secret "YOUR_SECRET"
Write-Host "Current window PIN: $($pin.RawPin)"
Write-Host "Consider ±30 second tolerance for clock drift"

Secret Validation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Validate BASE32 secret format
function Test-Base32Secret {
    param([string]$Secret)

    $cleanSecret = $Secret.ToUpper() -replace '[^A-Z2-7]', ''

    if ($cleanSecret.Length -eq 0) {
        Write-Warning "Invalid secret: Contains no valid BASE32 characters"
        return $false
    }

    if ($Secret -ne $cleanSecret) {
        Write-Warning "Secret contains invalid characters, cleaned: $cleanSecret"
    }

    Write-Host "✓ Valid BASE32 secret: $cleanSecret" -ForegroundColor Green
    return $true
}

# Test a secret
Test-Base32Secret -Secret "JBSWY3DPEHPK3PXP"

Conclusion

The New-MKGoogleAuthSecret and Get-MKGoogleAuthPin functions bring enterprise-grade TOTP functionality directly to your PowerShell environment. Whether you’re automating 2FA deployment, integrating authentication into applications, or managing security for multiple accounts, these tools provide the reliability and compatibility you need.

Key benefits include:

  • Full RFC 6238 compliance ensuring compatibility with all major authenticator apps
  • Cryptographically secure secret generation using proper random number generators
  • Flexible configuration supporting different algorithms, time windows, and PIN lengths
  • Easy QR code generation for seamless user onboarding
  • Comprehensive output providing both human-readable and programmatic interfaces

Next Steps

  • Experiment with different TOTP configurations to understand their impact
  • Integrate these functions into your user onboarding workflows
  • Build monitoring tools to track 2FA adoption across your organization
  • Consider creating wrapper functions for your specific organizational needs

Security Reminder

Always handle TOTP secrets with the same care as passwords. Store them securely, limit access appropriately, and consider implementing audit logging for secret generation and access.


This post is part of my PowerShell MessKit module series. Stay tuned for more articles as I continue exploring the security and utility functions that make system administration more efficient and secure.

This post is licensed under CC BY 4.0 by the author.