Skip to content

Commit c0c027f

Browse files
authored
Add files via upload
1 parent 31976d5 commit c0c027f

3 files changed

Lines changed: 230 additions & 0 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<#
2+
.SYNOPSIS
3+
Generates a constrained multi-tenant OAuth authorization URL for an authorized consent-screen training demonstration.
4+
5+
.DESCRIPTION
6+
Builds an OAuth 2.0 authorization URL for a multi-tenant Microsoft Entra application owned by the operator.
7+
It demonstrates that an application registered in one tenant can request sign-in or consent from users in another.
8+
The application must already be configured for multiple organizations and the redirect URI must already be registered.
9+
The script does not create or modify applications, open a browser, deliver the URL, exchange an authorization code,
10+
acquire tokens, or request mail, file, directory, chat, Teams, site, calendar, write, or persistent-access scopes.
11+
#>
12+
[CmdletBinding()]
13+
param(
14+
[Parameter(Mandatory)]
15+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
16+
[string]$ClientId,
17+
18+
[ValidateSet('organizations','common')]
19+
[string]$Authority = 'organizations',
20+
21+
[ValidateScript({
22+
$parsed = $null
23+
if (-not [uri]::TryCreate($_, [UriKind]::Absolute, [ref]$parsed)) { throw 'RedirectUri must be absolute.' }
24+
if ($parsed.Scheme -eq 'https' -or ($parsed.Scheme -eq 'http' -and $parsed.IsLoopback)) { return $true }
25+
throw 'RedirectUri must use HTTPS or HTTP loopback.'
26+
})]
27+
[string]$RedirectUri = 'https://example.invalid/oauth-training-multitenant.html',
28+
29+
[ValidateSet('openid','profile','email','User.Read')]
30+
[string[]]$Scopes = @('openid','profile','User.Read'),
31+
32+
[switch]$CopyToClipboard,
33+
34+
[Parameter(Mandatory)]
35+
[switch]$AcknowledgeAuthorizedTraining
36+
)
37+
38+
Set-StrictMode -Version Latest
39+
$ErrorActionPreference = 'Stop'
40+
if (-not $AcknowledgeAuthorizedTraining) { throw 'Authorized-training acknowledgement is required.' }
41+
42+
$scopesNormalized = @('openid') + @($Scopes | Where-Object { $_ -ne 'openid' })
43+
$scopesNormalized = @($scopesNormalized | Sort-Object -Unique)
44+
$stateBytes = [byte[]]::new(32)
45+
[Security.Cryptography.RandomNumberGenerator]::Fill($stateBytes)
46+
$state = [Convert]::ToBase64String($stateBytes).TrimEnd('=').Replace('+','-').Replace('/','_')
47+
$query = [ordered]@{
48+
client_id = $ClientId
49+
response_type = 'code'
50+
redirect_uri = $RedirectUri
51+
response_mode = 'query'
52+
scope = ($scopesNormalized -join ' ')
53+
state = $state
54+
prompt = 'consent'
55+
}
56+
$encoded = @($query.GetEnumerator() | ForEach-Object {
57+
'{0}={1}' -f [uri]::EscapeDataString([string]$_.Key), [uri]::EscapeDataString([string]$_.Value)
58+
}) -join '&'
59+
$url = "https://login.microsoftonline.com/$Authority/oauth2/v2.0/authorize?$encoded"
60+
61+
[pscustomobject]@{
62+
Scenario = 'Multi-tenant application'
63+
Authority = $Authority
64+
ClientId = $ClientId
65+
RedirectUri = $RedirectUri
66+
Scopes = $scopesNormalized -join ' '
67+
TokenAcquisition = 'Not implemented'
68+
AuthorizationUrl = $url
69+
} | Format-List
70+
71+
if ($CopyToClipboard) {
72+
if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
73+
Set-Clipboard $url
74+
Write-Host 'URL copied.' -ForegroundColor Cyan
75+
} else {
76+
Write-Warning 'Set-Clipboard is unavailable.'
77+
}
78+
}
79+
Write-Warning 'Training URL only. No browser launch, delivery, code exchange, or token acquisition is implemented.'
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<#
2+
.SYNOPSIS
3+
Generates a constrained OAuth URL that intentionally uses an unregistered redirect URI to demonstrate AADSTS50011.
4+
5+
.DESCRIPTION
6+
Builds an authorization URL for an application the operator owns while intentionally supplying a redirect URI
7+
that is not registered on that application. Microsoft Entra should reject the request with AADSTS50011. This safely
8+
teaches that app display names and genuine Microsoft sign-in pages do not override redirect-URI validation.
9+
The script does not use a Microsoft-owned client ID, acquire tokens, modify applications, open a browser, or deliver a URL.
10+
#>
11+
[CmdletBinding()]
12+
param(
13+
[Parameter(Mandatory)]
14+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
15+
[string]$TenantId,
16+
17+
[Parameter(Mandatory)]
18+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
19+
[string]$ClientId,
20+
21+
[ValidateScript({
22+
$parsed = $null
23+
if (-not [uri]::TryCreate($_, [UriKind]::Absolute, [ref]$parsed)) { throw 'The redirect URI must be absolute.' }
24+
if ($parsed.Scheme -ne 'https') { throw 'The demonstration redirect URI must use HTTPS.' }
25+
return $true
26+
})]
27+
[string]$IntentionallyUnregisteredRedirectUri = 'https://example.invalid/not-registered',
28+
29+
[switch]$CopyToClipboard,
30+
31+
[Parameter(Mandatory)]
32+
[switch]$AcknowledgeAuthorizedTraining
33+
)
34+
35+
Set-StrictMode -Version Latest
36+
$ErrorActionPreference = 'Stop'
37+
if (-not $AcknowledgeAuthorizedTraining) { throw 'Authorized-training acknowledgement is required.' }
38+
39+
$stateBytes = [byte[]]::new(32)
40+
[Security.Cryptography.RandomNumberGenerator]::Fill($stateBytes)
41+
$state = [Convert]::ToBase64String($stateBytes).TrimEnd('=').Replace('+','-').Replace('/','_')
42+
$query = [ordered]@{
43+
client_id = $ClientId
44+
response_type = 'code'
45+
redirect_uri = $IntentionallyUnregisteredRedirectUri
46+
response_mode = 'query'
47+
scope = 'openid profile'
48+
state = $state
49+
}
50+
$encoded = @($query.GetEnumerator() | ForEach-Object {
51+
'{0}={1}' -f [uri]::EscapeDataString([string]$_.Key), [uri]::EscapeDataString([string]$_.Value)
52+
}) -join '&'
53+
$url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?$encoded"
54+
55+
[pscustomobject]@{
56+
Scenario = 'Intentional redirect mismatch'
57+
ExpectedResult = 'AADSTS50011'
58+
ClientOwnership = 'Operator-owned or explicitly authorized application only'
59+
TokenAcquisition = 'Not implemented'
60+
AuthorizationUrl = $url
61+
} | Format-List
62+
63+
if ($CopyToClipboard) {
64+
if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
65+
Set-Clipboard $url
66+
Write-Host 'URL copied.' -ForegroundColor Cyan
67+
} else {
68+
Write-Warning 'Set-Clipboard is unavailable.'
69+
}
70+
}
71+
Write-Warning 'Expected result is redirect rejection. Do not register the demonstration redirect URI.'
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<#
2+
.SYNOPSIS
3+
Generates a constrained tenant-specific OAuth authorization URL for an authorized consent-screen training demonstration.
4+
5+
.DESCRIPTION
6+
Builds an OAuth 2.0 authorization URL for a single-tenant Microsoft Entra application owned by the operator.
7+
The script does not create or modify an application, open a browser, deliver the URL, exchange an authorization
8+
code, acquire tokens, or access user data. The redirect URI must already be registered on the application.
9+
Only the low-impact training scopes openid, profile, email, and User.Read are permitted.
10+
#>
11+
[CmdletBinding()]
12+
param(
13+
[Parameter(Mandatory)]
14+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
15+
[string]$TenantId,
16+
17+
[Parameter(Mandatory)]
18+
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')]
19+
[string]$ClientId,
20+
21+
[ValidateScript({
22+
$parsed = $null
23+
if (-not [uri]::TryCreate($_, [UriKind]::Absolute, [ref]$parsed)) { throw 'RedirectUri must be absolute.' }
24+
if ($parsed.Scheme -eq 'https' -or ($parsed.Scheme -eq 'http' -and $parsed.IsLoopback)) { return $true }
25+
throw 'RedirectUri must use HTTPS or HTTP loopback.'
26+
})]
27+
[string]$RedirectUri = 'https://example.invalid/oauth-training-success.html',
28+
29+
[ValidateSet('openid','profile','email','User.Read')]
30+
[string[]]$Scopes = @('openid','profile','User.Read'),
31+
32+
[switch]$CopyToClipboard,
33+
34+
[Parameter(Mandatory)]
35+
[switch]$AcknowledgeAuthorizedTraining
36+
)
37+
38+
Set-StrictMode -Version Latest
39+
$ErrorActionPreference = 'Stop'
40+
if (-not $AcknowledgeAuthorizedTraining) { throw 'Authorized-training acknowledgement is required.' }
41+
42+
$scopesNormalized = @('openid') + @($Scopes | Where-Object { $_ -ne 'openid' })
43+
$scopesNormalized = @($scopesNormalized | Sort-Object -Unique)
44+
$stateBytes = [byte[]]::new(32)
45+
[Security.Cryptography.RandomNumberGenerator]::Fill($stateBytes)
46+
$state = [Convert]::ToBase64String($stateBytes).TrimEnd('=').Replace('+','-').Replace('/','_')
47+
48+
$query = [ordered]@{
49+
client_id = $ClientId
50+
response_type = 'code'
51+
redirect_uri = $RedirectUri
52+
response_mode = 'query'
53+
scope = ($scopesNormalized -join ' ')
54+
state = $state
55+
prompt = 'consent'
56+
}
57+
$encoded = @($query.GetEnumerator() | ForEach-Object {
58+
'{0}={1}' -f [uri]::EscapeDataString([string]$_.Key), [uri]::EscapeDataString([string]$_.Value)
59+
}) -join '&'
60+
$url = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/authorize?$encoded"
61+
62+
[pscustomobject]@{
63+
Scenario = 'Organization-only application'
64+
Authority = $TenantId
65+
ClientId = $ClientId
66+
RedirectUri = $RedirectUri
67+
Scopes = $scopesNormalized -join ' '
68+
TokenAcquisition = 'Not implemented'
69+
AuthorizationUrl = $url
70+
} | Format-List
71+
72+
if ($CopyToClipboard) {
73+
if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
74+
Set-Clipboard $url
75+
Write-Host 'URL copied.' -ForegroundColor Cyan
76+
} else {
77+
Write-Warning 'Set-Clipboard is unavailable.'
78+
}
79+
}
80+
Write-Warning 'Training URL only. No browser launch, delivery, code exchange, or token acquisition is implemented.'

0 commit comments

Comments
 (0)