Last active
January 20, 2026 22:34
-
-
Save rbrayb/18889762d062e758b600c4611c55a9d9 to your computer and use it in GitHub Desktop.
Azure AD B2C to Entra External ID (EEID) Migration Kit - Configure-ExternalIdJit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # FormatPemWithSlashN.ps1 - Convert PEM key to JSON-safe format | |
| # | |
| # This script reads a PEM-encoded private key file and converts it to a format | |
| # that can be safely embedded in JSON configuration files (e.g., appsettings.json). | |
| # It replaces actual newlines with the literal string "\n" so the multi-line key | |
| # becomes a single-line string suitable for JSON values. | |
| # Path to the PEM file containing the private key | |
| $pemPath = ".\jit-private-key.pem" # Change this to your actual PEM file path | |
| # Read the entire PEM file as a single string (preserves original line endings) | |
| $pem = Get-Content $pemPath -Raw | |
| # Convert line endings to JSON-safe escape sequences | |
| # This replaces Windows (CRLF) and Unix (LF) line endings with the literal string "\n" | |
| $escaped = $pem -replace "`r`n", "\n" -replace "`n", "\n" | |
| # Copy the formatted string to clipboard for easy pasting | |
| $escaped | Set-Clipboard | |
| # Display success message | |
| Write-Host "`n✓ Converted key copied to clipboard!" -ForegroundColor Green | |
| Write-Host "`nPaste into: Migration__JitAuthentication__InlineRsaPrivateKey value" -ForegroundColor Cyan | |
| # Output the escaped content to console for verification | |
| Write-Host "`nFormatted key:`n" -ForegroundColor Yellow | |
| Write-Host $escaped | |
| Write-Host "`n" | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Test-JIT-Function.ps1 - Test JIT Authentication Function locally | |
| # Developer Guide: Step 5: Test JIT Flow: Test with HTTP Client: | |
| # Configuration | |
| $ngrokUrl = "https://96dcf877ef56.ngrok-free.app" # Change to your actual ngrok domain | |
| $endpoint = "$ngrokUrl/api/JitAuthentication" | |
| # Test payload matching External ID Custom Authentication Extension format | |
| $payload = @{ | |
| type = "customAuthenticationExtension" | |
| data = @{ | |
| authenticationContext = @{ | |
| correlationId = "test-12345" | |
| user = @{ | |
| id = "user-object-id-from-external-id" | |
| userPrincipalName = "testuser@yourdomain.com" | |
| } | |
| } | |
| passwordContext = @{ | |
| userPassword = "RealB2CPassword123!" | |
| nonce = "1234567890" | |
| } | |
| } | |
| } | |
| Write-Host "" | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host " Testing JIT Authentication Function" -ForegroundColor Cyan | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host "" | |
| Write-Host "Endpoint: " -NoNewline | |
| Write-Host $endpoint -ForegroundColor Yellow | |
| Write-Host "User: " -NoNewline | |
| Write-Host $payload.data.authenticationContext.user.userPrincipalName -ForegroundColor Yellow | |
| Write-Host "" | |
| try { | |
| # Send POST request | |
| $response = Invoke-RestMethod -Uri $endpoint ` | |
| -Method Post ` | |
| -Body ($payload | ConvertTo-Json -Depth 10) ` | |
| -ContentType "application/json" ` | |
| -TimeoutSec 30 | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Green | |
| Write-Host " ✓ SUCCESS - Response Received" -ForegroundColor Green | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Response:" -ForegroundColor Cyan | |
| $response | ConvertTo-Json -Depth 10 | Write-Host -ForegroundColor White | |
| Write-Host "" | |
| } | |
| catch { | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Red | |
| Write-Host " ✗ ERROR - Request Failed" -ForegroundColor Red | |
| Write-Host "═══════════════════════════════════════════════" -ForegroundColor Red | |
| Write-Host "" | |
| Write-Host "Error Message:" -ForegroundColor Yellow | |
| Write-Host $_.Exception.Message -ForegroundColor Red | |
| Write-Host "" | |
| if ($_.Exception.Response) { | |
| Write-Host "Status Code:" -ForegroundColor Yellow | |
| Write-Host $_.Exception.Response.StatusCode -ForegroundColor Red | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Test-JitRsaEncryption.ps1 | |
| <# | |
| .SYNOPSIS | |
| Tests RSA encryption/decryption using JIT certificate and private key. | |
| .DESCRIPTION | |
| Encrypts a random string with the public key (from certificate) | |
| and decrypts it with the private key to verify the key pair works. | |
| .PARAMETER CertificatePath | |
| Path to jit-certificate.txt file (public key) | |
| .PARAMETER PrivateKeyPath | |
| Path to jit-private-key.pem file (private key) | |
| .PARAMETER TestString | |
| String to encrypt/decrypt. If not provided, generates a random string. | |
| .EXAMPLE | |
| .\Test-JitRsaEncryption.ps1 | |
| .EXAMPLE | |
| .\Test-JitRsaEncryption.ps1 -TestString "MySecretPassword123!" | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(Mandatory = $false)] | |
| [string]$CertificatePath = ".\jit-certificate.txt", | |
| [Parameter(Mandatory = $false)] | |
| [string]$PrivateKeyPath = ".\jit-private-key.pem", | |
| [Parameter(Mandatory = $false)] | |
| [string]$TestString | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| Write-Host "" | |
| Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host " RSA Encryption/Decryption Test" -ForegroundColor Cyan | |
| Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host "" | |
| # Generate random test string if not provided | |
| if (-not $TestString) { | |
| $TestString = "TestPassword_" + [Guid]::NewGuid().ToString().Substring(0, 8) | |
| Write-Host "Generated random test string" -ForegroundColor Gray | |
| } | |
| Write-Host "Test String: " -NoNewline -ForegroundColor Cyan | |
| Write-Host $TestString -ForegroundColor Yellow | |
| Write-Host "" | |
| # ============================================================================ | |
| # Load Certificate (Public Key) | |
| # ============================================================================ | |
| Write-Host "Loading public key from certificate..." -ForegroundColor Cyan | |
| if (-not (Test-Path $CertificatePath)) { | |
| Write-Host "✗ Certificate file not found: $CertificatePath" -ForegroundColor Red | |
| exit 1 | |
| } | |
| try { | |
| # Read certificate base64 | |
| $certBase64 = Get-Content $CertificatePath -Raw | |
| $certBytes = [Convert]::FromBase64String($certBase64) | |
| # Load certificate | |
| $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($certBytes) | |
| Write-Host "✓ Certificate loaded" -ForegroundColor Green | |
| Write-Host " Subject: $($cert.Subject)" -ForegroundColor Gray | |
| Write-Host " Valid: $($cert.NotBefore.ToString('yyyy-MM-dd')) to $($cert.NotAfter.ToString('yyyy-MM-dd'))" -ForegroundColor Gray | |
| # Get RSA public key from certificate | |
| $rsaPublic = $cert.PublicKey.Key | |
| } | |
| catch { | |
| Write-Host "✗ Failed to load certificate: $_" -ForegroundColor Red | |
| exit 1 | |
| } | |
| # ============================================================================ | |
| # Load Private Key | |
| # ============================================================================ | |
| Write-Host "" | |
| Write-Host "Loading private key from PEM file..." -ForegroundColor Cyan | |
| if (-not (Test-Path $PrivateKeyPath)) { | |
| Write-Host "✗ Private key file not found: $PrivateKeyPath" -ForegroundColor Red | |
| exit 1 | |
| } | |
| try { | |
| # Read PEM file | |
| $pemContent = Get-Content $PrivateKeyPath -Raw | |
| # Remove PEM headers/footers and whitespace | |
| $pemContent = $pemContent -replace "-----BEGIN PRIVATE KEY-----", "" | |
| $pemContent = $pemContent -replace "-----END PRIVATE KEY-----", "" | |
| $pemContent = $pemContent -replace "\s+", "" | |
| # Decode base64 | |
| $privateKeyBytes = [Convert]::FromBase64String($pemContent) | |
| # Import PKCS#8 private key | |
| $rsaPrivate = [System.Security.Cryptography.RSA]::Create() | |
| $rsaPrivate.ImportPkcs8PrivateKey($privateKeyBytes, [ref]$null) | |
| Write-Host "✓ Private key loaded" -ForegroundColor Green | |
| Write-Host " Key Size: $($rsaPrivate.KeySize) bits" -ForegroundColor Gray | |
| } | |
| catch { | |
| Write-Host "✗ Failed to load private key: $_" -ForegroundColor Red | |
| exit 1 | |
| } | |
| # ============================================================================ | |
| # Encrypt with Public Key | |
| # ============================================================================ | |
| Write-Host "" | |
| Write-Host "Encrypting with public key..." -ForegroundColor Cyan | |
| try { | |
| # Convert string to bytes | |
| $plainBytes = [System.Text.Encoding]::UTF8.GetBytes($TestString) | |
| # Encrypt using RSA-OAEP with SHA-256 | |
| $encryptedBytes = $rsaPublic.Encrypt($plainBytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA256) | |
| # Convert to base64 for display | |
| $encryptedBase64 = [Convert]::ToBase64String($encryptedBytes) | |
| Write-Host "✓ Encryption successful" -ForegroundColor Green | |
| Write-Host " Encrypted (base64): $($encryptedBase64.Substring(0, [Math]::Min(60, $encryptedBase64.Length)))..." -ForegroundColor Gray | |
| Write-Host " Size: $($encryptedBytes.Length) bytes" -ForegroundColor Gray | |
| } | |
| catch { | |
| Write-Host "✗ Encryption failed: $_" -ForegroundColor Red | |
| exit 1 | |
| } | |
| # ============================================================================ | |
| # Decrypt with Private Key | |
| # ============================================================================ | |
| Write-Host "" | |
| Write-Host "Decrypting with private key..." -ForegroundColor Cyan | |
| try { | |
| # Decrypt using RSA-OAEP with SHA-256 | |
| $decryptedBytes = $rsaPrivate.Decrypt($encryptedBytes, [System.Security.Cryptography.RSAEncryptionPadding]::OaepSHA256) | |
| # Convert bytes back to string | |
| $decryptedString = [System.Text.Encoding]::UTF8.GetString($decryptedBytes) | |
| Write-Host "✓ Decryption successful" -ForegroundColor Green | |
| Write-Host " Decrypted: " -NoNewline -ForegroundColor Gray | |
| Write-Host $decryptedString -ForegroundColor Yellow | |
| } | |
| catch { | |
| Write-Host "✗ Decryption failed: $_" -ForegroundColor Red | |
| exit 1 | |
| } | |
| # ============================================================================ | |
| # Verify | |
| # ============================================================================ | |
| Write-Host "" | |
| Write-Host "Verifying..." -ForegroundColor Cyan | |
| if ($TestString -eq $decryptedString) { | |
| Write-Host "✓ SUCCESS! Original and decrypted strings match!" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host " Original: $TestString" -ForegroundColor White | |
| Write-Host " Decrypted: $decryptedString" -ForegroundColor White | |
| Write-Host "" | |
| Write-Host "✓ RSA key pair is working correctly!" -ForegroundColor Green | |
| } else { | |
| Write-Host "✗ FAILURE! Strings do not match!" -ForegroundColor Red | |
| Write-Host " Original: $TestString" -ForegroundColor White | |
| Write-Host " Decrypted: $decryptedString" -ForegroundColor White | |
| exit 1 | |
| } | |
| Write-Host "" | |
| Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host " Test Summary" -ForegroundColor Cyan | |
| Write-Host "═══════════════════════════════════════════════════════" -ForegroundColor Cyan | |
| Write-Host "" | |
| Write-Host "Certificate: $CertificatePath" -ForegroundColor Gray | |
| Write-Host "Private Key: $PrivateKeyPath" -ForegroundColor Gray | |
| Write-Host "Key Size: $($rsaPrivate.KeySize) bits" -ForegroundColor Gray | |
| Write-Host "Padding: OAEP-SHA256" -ForegroundColor Gray | |
| Write-Host "Test String: $TestString" -ForegroundColor Gray | |
| Write-Host "Status: " -NoNewline -ForegroundColor Gray | |
| Write-Host "PASSED ✓" -ForegroundColor Green | |
| Write-Host "" | |
| # Cleanup | |
| $rsaPublic.Dispose() | |
| $rsaPrivate.Dispose() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://medium.com/the-new-control-plane/azure-ad-b2c-to-entra-external-id-eeid-migration-kit-configure-externalidjit-217f14471f75