B2C.ps1

# Creates a new B2CToken
# Sep 12th 2023
Function New-B2CToken
{
    [cmdletbinding()]

    param(
        [Parameter(Mandatory=$True)]
        [string]$Tenant,
        [Parameter(Mandatory=$True)]
        [string]$Policy,
        [Parameter(Mandatory=$True)]
        [guid]$ClientId,
        [Parameter(Mandatory=$True)]
        [guid]$UserId,

        [Parameter(Mandatory=$False)]
        [ValidateSet("authorization_code","refresh_token")]
        [string]$Type = "refresh_token",

        [Parameter(Mandatory=$False)]
        [System.Security.Cryptography.RSA]$PublicKey,

        [Parameter(Mandatory=$False)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,

        [Parameter(Mandatory=$False)]
        [string]$PfxFileName,
        [Parameter(Mandatory=$False)]
        [string]$PfxPassword,
        [Parameter(Mandatory=$False)]
        [string]$KeyId,
        [Parameter(Mandatory=$False)]
        [System.Collections.Hashtable]$Claims,

        [Parameter(Mandatory=$True)]
        [DateTime]$NotBefore,
        [Parameter(Mandatory=$True)]
        [DateTime]$ExpiresOn
    )
    process
    {
        # Get the public key from certificate if not provided
        if(!$PublicKey)
        {
            # Load certificate if not provided
            if(!$Certificate)
            {
                $Certificate = Load-Certificate -FileName $PfxFileName -Password $PfxPassword -Exportable
            }

            $PublicKey = $Certificate.PublicKey.Key
        }

        # Get the tenant name
        if(($parts = $Tenant.Split(".")).Count -gt 1)
        {
            $Tenant = $parts[0]
        }

        # Type
        # 1 = authorization_code
        # 2 = refresh_token
        $t = 1
        if($Type -eq "refresh_token")
        {
            $t = 2
        }

        # Create the claims block
        $claimValues = @()
        if($Claims)
        {
            foreach($key in $Claims.Keys)
            {
                $claimValues += @{
                    "claimTypeId" = $key
                    "value"       = $Claims[$key] 
                }
            }
        }

        # Create the token (minimal working)
        $B2Ctoken = [ordered]@{
            "tid"   = "$Tenant.onmicrosoft.com"
            "pid"   = $Policy
            "t"     = $t
            "cls"   = @{
                "`$id" = "1"
                "`$values" = $claimValues
                }
            "o_aud" = $ClientId.ToString()
            "o_iat" = [int]($NotBefore-$epoch).TotalSeconds
            "iat"   = [int]($NotBefore-$epoch).TotalSeconds
            "exp"   = [int]($ExpiresOn-$epoch).TotalSeconds
            "avm"   = "V2.0"
            "rcc"   = $true
            "uid"   = $UserId
        }

        # Create the payload: convert to unicode and deflate
        $payload = Get-DeflatedByteArray -byteArray ([text.encoding]::Unicode.getBytes( ($B2Ctoken | ConvertTo-Json -Depth 10 -Compress ))) 

        # Create the header
        $header = [ordered]@{
            "kid"="$KeyId"
            "ver"="1.0"
            "zip"= "Deflate"
            "ser" ="1.0"
        }

        # Create the JWE
        New-JWE -PublicKey $publicKey -Payload $payload -Header ($header | ConvertTo-Json -Depth 10 -Compress)
    }
}


# Creates a new B2C refresh token
# Sep 12th 2023
Function New-B2CRefreshToken
{
<#
    .SYNOPSIS
    Creates a new B2C refresh token using the provided public key.
 
    .DESCRIPTION
    Creates a new B2C refresh token using the provided public key.
     
    .Parameter Certificate
    A certificate which public key is used to encrypt the refresh token.
 
    .PARAMETER Claims
    A hashtable of claims (key & value) to be added to the refresh token.
 
    .PARAMETER ClientId
    Client id of the application
 
    .PARAMETER ExpiresOn
    Date time when the refresh token expires
 
    .PARAMETER KeyId
    Id of the public key.
 
    .PARAMETER NotBefore
    Date time after when the refresh token is active
 
    .PARAMETER PfxFileName
    File name of the certificate .pfx file
 
    .PARAMETER PfxPassword
    Password of the certificate .pfx file
 
    .PARAMETER Policy
    Policy id of the Identity Experience Framework policy.
 
    .PARAMETER Tenant
    Name of the B2C (without .b2clogin.com)
 
    .PARAMETER UserId
    User's Entra ID object ID
 
    .Example
    $keys = Get-AADIntB2CEncryptionKeys
    PS C:\>$refresh_token = New-AADIntB2CRefreshToken -Tenant "companyb2c" -ClientId "00364d2a-695e-49e6-b5ef-377276103dc2" -UserId "910e4c2f-1396-434c-aa8e-1bcf8883376a" -Policy "B2C_1A_signup_signin" -PublicKey $keys[1].Key -KeyId $keys[1].Id
#>

    [cmdletbinding()]

    param(
        [Parameter(Mandatory=$True)]
        [string]$Tenant,
        [Parameter(Mandatory=$True)]
        [string]$Policy,
        [Parameter(Mandatory=$True)]
        [guid]$ClientId,
        [Parameter(Mandatory=$True)]
        [guid]$UserId,

        [Parameter(ParameterSetName='PublicKey',Mandatory=$True)]
        [System.Security.Cryptography.RSA]$PublicKey,

        [Parameter(ParameterSetName='Certificate',Mandatory=$True)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,

        [Parameter(ParameterSetName='FileAndPassword',Mandatory=$True)]
        [string]$PfxFileName,
        [Parameter(ParameterSetName='FileAndPassword',Mandatory=$False)]
        [string]$PfxPassword,
        [Parameter(Mandatory=$False)]
        [string]$KeyId,
        [Parameter(Mandatory=$False)]
        [System.Collections.Hashtable]$Claims,

        [Parameter(Mandatory=$False)]
        [DateTime]$NotBefore = ((Get-Date).ToUniversalTime()),
        [Parameter(Mandatory=$False)]
        [DateTime]$ExpiresOn = ($NotBefore.AddDays(14))
    )
    process
    {
        $arguments = @{ 
            "Tenant"      = $Tenant
            "ClientId"    = $ClientId
            "UserId"      = $UserId
            "Policy"      = $Policy
            "Certificate" = $Certificate
            "PfxFileName" = $PfxFileName
            "PfxPassword" = $PfxPassword
            "KeyId"       = $KeyId
            "Type"        = "refresh_token"
            "Claims"      = $Claims
            "NotBefore"   = $NotBefore
            "ExpiresOn"   = $ExpiresOn
            "PublicKey"   = $PublicKey
        }

        New-B2CToken @arguments
    }
}

# Creates a new B2C refresh token
# Sep 12th 2023
Function New-B2CAuthorizationCode
{
<#
    .SYNOPSIS
    Creates a new B2C authorization code using the provided public key.
 
    .DESCRIPTION
    Creates a new B2C authorization code using the provided public key.
     
    .Parameter Certificate
    A certificate which public key is used to encrypt the authorization code.
 
    .PARAMETER Claims
    A hashtable of claims (key & value) to be added to the authorization code.
 
    .PARAMETER ClientId
    Client id of the application
 
    .PARAMETER ExpiresOn
    Date time when the authorization code expires
 
    .PARAMETER KeyId
    Id of the public key.
 
    .PARAMETER NotBefore
    Date time after when the authorization code is active
 
    .PARAMETER PfxFileName
    File name of the certificate .pfx file
 
    .PARAMETER PfxPassword
    Password of the certificate .pfx file
 
    .PARAMETER Policy
    Policy id of the Identity Experience Framework policy.
 
    .PARAMETER Tenant
    Name of the B2C (without .b2clogin.com)
 
    .PARAMETER UserId
    User's Entra ID object ID
 
    .Example
    $keys = Get-AADIntB2CEncryptionKeys
    PS C:\>$authorization_code = New-AADIntB2CAuthorizationCode -Tenant "companyb2c" -ClientId "00364d2a-695e-49e6-b5ef-377276103dc2" -UserId "910e4c2f-1396-434c-aa8e-1bcf8883376a" -Policy "B2C_1A_signup_signin" -PublicKey $keys[1].Key -KeyId $keys[1].Id
#>

    [cmdletbinding()]

    param(
        [Parameter(Mandatory=$True)]
        [string]$Tenant,
        [Parameter(Mandatory=$True)]
        [string]$Policy,
        [Parameter(Mandatory=$True)]
        [guid]$ClientId,
        [Parameter(Mandatory=$True)]
        [guid]$UserId,

        [Parameter(ParameterSetName='PublicKey',Mandatory=$True)]
        [System.Security.Cryptography.RSA]$PublicKey,

        [Parameter(ParameterSetName='Certificate',Mandatory=$True)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,

        [Parameter(ParameterSetName='FileAndPassword',Mandatory=$True)]
        [string]$PfxFileName,
        [Parameter(ParameterSetName='FileAndPassword',Mandatory=$False)]
        [string]$PfxPassword,
        [Parameter(Mandatory=$False)]
        [string]$KeyId,
        [Parameter(Mandatory=$False)]
        [System.Collections.Hashtable]$Claims,

        [Parameter(Mandatory=$False)]
        [DateTime]$NotBefore = ((Get-Date).ToUniversalTime()),
        [Parameter(Mandatory=$False)]
        [DateTime]$ExpiresOn = ($NotBefore.AddDays(14))
    )
    process
    {
        $arguments = @{ 
            "Tenant"      = $Tenant
            "ClientId"    = $ClientId
            "UserId"      = $UserId
            "Policy"      = $Policy
            "Certificate" = $Certificate
            "PfxFileName" = $PfxFileName
            "PfxPassword" = $PfxPassword
            "KeyId"       = $KeyId
            "Type"        = "authorization_code"
            "Claims"      = $Claims
            "NotBefore"   = $NotBefore
            "ExpiresOn"   = $ExpiresOn
            "PublicKey"   = $PublicKey
        }

        New-B2CToken @arguments
    }
}