simonrob / email-oauth2-proxy

An IMAP/POP/SMTP proxy that transparently adds OAuth 2.0 authentication for email clients that don't support this method.
Apache License 2.0
828 stars 89 forks source link

Grant flow Bad Request #72

Closed D4rki91 closed 2 years ago

D4rki91 commented 2 years ago

Hi Simon,

I want to try the "grant flow" support. But Im stuck and not sure what Im doing wrong.

I did all of the required steps on AAD and the Powershell cmdlets described on: https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#use-client-credentials-grant-flow-to-authenticate-imap-and-pop-connections

My account setup in the emailproxy.config:

[testuser@domain.com] permission_url = token_url = https://login.microsoftonline.com/common/oauth2/v2.0/token oauth2_scope = https://outlook.office365.com/.default https://outlook.office365.com/IMAP.AccessAsUser.All https://outlook.office365.com/POP.AccessAsUser.All https://outlook.office365.com/SMTP.Send offline_access redirect_uri = http://localhost client_id = xxxxxxxxxxxxxxxxxxx client_secret = yyyyyyyyyyyyyyyy

Not sure if I have to add the ".default" URI into the oauth2_scope.

When I start the emailproxy.py --no-gui , telnet localhost 1995 and provide my testuser credentials following happens.

Caught exception while requesting OAuth 2.0 credentials for testuser@domain.com: <HTTPError 400: 'Bad Request'>

Any idea which step Im probably missing?

simonrob commented 2 years ago

Please try the setup script linked from the readme: https://github.com/simonrob/email-oauth2-proxy/issues/61#issuecomment-1259110336.

D4rki91 commented 2 years ago

Hi, thanks!

My config looks good now. But still get an error when I try to connect over telnet.

2022-10-06 11:43:11: Caught exception while requesting OAuth 2.0 credentials for testuser@domain.tld: <HTTPError 401: 'Unauthorized'>

Im pretty sure I did the steps on AAD correctly.

Do I have to set : [emailproxy] encrypt_client_secret_on_first_use = False to "True" in this scenario or doesn't it matter cause the secret is already encrypted through the script?

simonrob commented 2 years ago

It's hard to diagnose issues with the client credentials grant flow because the setup is so opaque. But I'd assume that here there is some AAD misconfiguration causing the authorisation failure. Note that this error is raised by the proxy when requesting credentials, rather than actually using them, so check you've properly set up and given admin authorisation for your AAD client.

The encrypt_client_secret_on_first_use option is only needed if you are starting with an unencrypted secret. The proxy will automatically use the encrypted secret value if it is present.

D4rki91 commented 2 years ago

Hi Simon,

Its working now, Im not sure where the issue was but I wrote a Powershell script to configure all of this in AAD so I dont forget any steps on AAD in the future.

Maybe the script is useful for someone else too. It's not beautiful and some if statements to prevent obvious errors like "duplicate appname etc." are missing yet but its working at least.

# Requires AzureADPreview Module;
# Install-Module AzureADPreview
# Install-Module -Name AzureRM
# User must be able to create apps and consent (Global Admin, ...)

Param (
$appName = "YourAppName",
$redirecturi = "http://localhost",
$fullaccessuser = ('test1@tld','test2@tld')
)

#Login to Azure Active Directory
$credential = Get-Credential
Connect-AzureAd -Credential $credential;

#MS Graph stuff:

#Microsoft Graph delegated permissions
$servicePrincipalNameGraph = "Microsoft Graph"; 
$servicePrincipalNameOauth2PermissionsGraph = @("IMAP.AccessAsUser.All", "POP.AccessAsUser.All", "SMTP.Send");

# Get MS Graph
$servicePrincipalGraph = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameGraph };

#MS Graph permissions
$reqGraph = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqGraph.ResourceAppId = $servicePrincipalGraph.AppId;

$servicePrincipalGraph.Oauth2Permissions | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsGraph} | ForEach-Object {
    $permission = $_
    $delPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Scope" #delegate permission (oauth) are always "Scope"
    $reqGraph.ResourceAccess += $delPermission
}

#EXO stuff:

#Office 365 Exchange Online app permissions
$servicePrincipalNameExchangeOnline = "Office 365 Exchange Online"; 
$servicePrincipalNameOauth2PermissionsExchangeOnline = @("IMAP.AccessAsApp", "POP.AccessAsApp");

#Get Office 365 Exchange Online
$servicePrincipalExchangeOnline = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameExchangeOnline };

#Exchange Online permissions
$reqExchangeOnline = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqExchangeOnline.ResourceAppId = $servicePrincipalExchangeOnline.AppId;

$servicePrincipalExchangeOnline.AppRoles | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsExchangeOnline} | ForEach-Object {
    $permission = $_
    $appPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Role"
    $reqExchangeOnline.ResourceAccess += $appPermission
}

#Define the Clientsecret here
$secretstartdate = Get-Date
$secretenddate = $secretstartdate.AddMonths(24)
$description = "OAuth_secret"

New-AzureADApplication -DisplayName $appName -AvailableToOtherTenants:$true -PublicClient:$false -ReplyUrls $redirecturi -RequiredResourceAccess $reqGraph, $reqExchangeOnline;
$newapp = Get-AzureADApplication -SearchString $appName;
$clientsecret = New-AzureADApplicationPasswordCredential -ObjectId $newapp.ObjectId -StartDate $secretstartdate -EndDate $secretenddate -CustomKeyIdentifier $description;

"`n"

"ClientId: " + $newapp.AppId;
"ClientSecret: " + $clientsecret.Value
"TenantId: " + (Get-AzureADTenantDetail).ObjectId;

"`n"

"You can check your AAD app here: https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/" + $newapp.AppId + "/objectId/" + $newapp.ObjectId + "/isMSAApp/";

#Give it some time to appear in azuread so the next step will run successfull
Start-Sleep -Seconds 60

#Add you as owner
$AppOwner = get-azureaduser -all $true -Filter "UserPrincipalName eq '$($credential.UserName)'"
Add-AzureADApplicationOwner -ObjectId $($newapp.ObjectId) -RefObjectId $($AppOwner.ObjectId)

#Grant Admin consent
Login-AzureRmAccount -Credential $credential
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $TenantId, $null, "Never", $null, "74658136-14ec-4630-ad9b-26e160ff0fc6")
$headers = @{
  'Authorization' = 'Bearer ' + $token.AccessToken
  'X-Requested-With'= 'XMLHttpRequest'
  'x-ms-client-request-id'= [guid]::NewGuid()
  'x-ms-correlation-id' = [guid]::NewGuid()}

$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$($newapp.AppId)/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $headers -Method POST -ErrorAction Stop

#Register Service principal
Connect-ExchangeOnline -Credential $credential
$serviceprincipalexo = New-ServicePrincipal -AppId $newapp.AppId -ServiceId $newapp.ObjectId -Displayname $newapp.DisplayName -Organization $tenantId

#Grant your application's service principal full access to the specified mailboxes.
ForEach ($fullaccessuser in $fullaccessusers) {
Add-MailboxPermission -Identity "$fullaccessusers" -User $serviceprincipalexo.Id -AccessRights FullAccess
}
simonrob commented 2 years ago

Thanks for the contribution!

(I edited it slightly to make a code block rather than individual lines – you can use three backticks to do this if needed)

sflamm commented 1 year ago

@D4rki91

`#EXO stuff:

Office 365 Exchange Online app permissions

$servicePrincipalNameExchangeOnline = "Office 365 Exchange Online"; $servicePrincipalNameOauth2PermissionsExchangeOnline = @("IMAP.AccessAsApp", "POP.AccessAsApp");

Get Office 365 Exchange Online

$servicePrincipalExchangeOnline = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameExchangeOnline };

Exchange Online permissions

$reqExchangeOnline = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"; $reqExchangeOnline.ResourceAppId = $servicePrincipalExchangeOnline.AppId;

... }`

Are you purposefully configuring your registered application to support both 'Delegation' (Graph) and 'Flow' (Exchange) permissions? If you are using only one - do you need to configure the application to have both sets of permissions? Or does the 'Flow' still require the 'Delegation' (Graph) permissions as well?

Based on this comment I believe the Graph permissions are not being used in the Flow mode: If your Office 365 configuration uses the client credentials grant flow rather than interactive authentication, add your account entry as normal, but do not add apermission_urlvalue (it does not apply in this mode, and its absence signals to the proxy to use the appropriate token retrieval mechanism

Thanks in advance for the clarification

sflamm commented 1 year ago

@D4rki91

Is there a mistake in this script

`Param ( $appName = "YourAppName", $redirecturi = "http://localhost", $fullaccessuser = ('test1@tld','test2@tld') )

Grant your application's service principal full access to the specified mailboxes.

ForEach ($fullaccessuser in $fullaccessusers) { Add-MailboxPermission -Identity "$fullaccessusers" -User $serviceprincipalexo.Id -AccessRights FullAccess `

should be

`Param ( $appName = "YourAppName", $redirecturi = "http://localhost", $fullaccessusers = ('test1@tld','test2@tld') )

Grant your application's service principal full access to the specified mailboxes.

ForEach ($fullaccessuser in $fullaccessusers) { Add-MailboxPermission -Identity "$fullaccessuser" -User $serviceprincipalexo.Id -AccessRights FullAccess`

sflamm commented 1 year ago

@D4rki91

Instead of creating a specific list of users... and granting full permissions to all of them... would it work instead to add "full_access_as_ap" into your Exchange Online Permissions"? If so this would make it more maintainable too...

full_access_as_app

D4rki91 commented 1 year ago

Hey,

Thanks for your input. I know you don't have to configure both. :) It was just a quick and dirty script from me, so my colleagues can register Enterprise Applications faster and without asking me or reading my "HowTo".

I made a simple if statement to configure either this Grant Flow stuff or only the MS Graph permissions. The script is still not pretty I know, but all of these weird AzureAD and AzureRM cmdlets are confusing. :)

As long as it works Im happy.

# Requires AzureADPreview Module;
# Install-Module AzureADPreview
# Install-Module -Name AzureRM
# User must be able to create apps and consent (Global Admin, ...)

Param (
$appName = "YourAppName",
$redirecturi = "http://localhost",
$fullaccessuser = ('test1@tld','test2@tld'),
$grantflowrequirements = $false
)

#Login to Azure Active Directory
$credential = Get-Credential
Connect-AzureAd -Credential $credential;

#MS Graph stuff:

#Microsoft Graph delegated permissions
$servicePrincipalNameGraph = "Microsoft Graph"; 
$servicePrincipalNameOauth2PermissionsGraph = @("IMAP.AccessAsUser.All", "POP.AccessAsUser.All", "SMTP.Send");

# Get MS Graph
$servicePrincipalGraph = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameGraph };

#MS Graph permissions
$reqGraph = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqGraph.ResourceAppId = $servicePrincipalGraph.AppId;

$servicePrincipalGraph.Oauth2Permissions | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsGraph} | ForEach-Object {
    $permission = $_
    $delPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Scope" #delegate permission (oauth) are always "Scope"
    $reqGraph.ResourceAccess += $delPermission
}

If($grantflowrequirements -eq $true){
#EXO stuff:

#Office 365 Exchange Online app permissions
$servicePrincipalNameExchangeOnline = "Office 365 Exchange Online"; 
$servicePrincipalNameOauth2PermissionsExchangeOnline = @("IMAP.AccessAsApp", "POP.AccessAsApp");

#Get Office 365 Exchange Online
$servicePrincipalExchangeOnline = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameExchangeOnline };

#Exchange Online permissions
$reqExchangeOnline = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqExchangeOnline.ResourceAppId = $servicePrincipalExchangeOnline.AppId;

$servicePrincipalExchangeOnline.AppRoles | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsExchangeOnline} | ForEach-Object {
    $permission = $_
    $appPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Role"
    $reqExchangeOnline.ResourceAccess += $appPermission
}
    }
    else{Write-Host "Grantflow requirements disabled"}
#Define the Clientsecret here
$secretstartdate = Get-Date
$secretenddate = $secretstartdate.AddMonths(24)
$description = "OAuth_secret"

New-AzureADApplication -DisplayName $appName -AvailableToOtherTenants:$true -PublicClient:$false -ReplyUrls $redirecturi -RequiredResourceAccess $reqGraph, $reqExchangeOnline;
$newapp = Get-AzureADApplication -SearchString $appName;
$clientsecret = New-AzureADApplicationPasswordCredential -ObjectId $newapp.ObjectId -StartDate $secretstartdate -EndDate $secretenddate -CustomKeyIdentifier $description;

"`n"

"ClientId: " + $newapp.AppId;
"ClientSecret: " + $clientsecret.Value
"TenantId: " + (Get-AzureADTenantDetail).ObjectId;

"`n"

"You can check your AAD app here: https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/" + $newapp.AppId + "/objectId/" + $newapp.ObjectId + "/isMSAApp/";

#Give it some time to appear in azuread so the next step will run successfull
Start-Sleep -Seconds 60

#Add you as owner
$AppOwner = get-azureaduser -all $true -Filter "UserPrincipalName eq '$($credential.UserName)'"
Add-AzureADApplicationOwner -ObjectId $($newapp.ObjectId) -RefObjectId $($AppOwner.ObjectId)

#Grant Admin consent
Login-AzureRmAccount -Credential $credential
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $TenantId, $null, "Never", $null, "74658136-14ec-4630-ad9b-26e160ff0fc6")
$headers = @{
  'Authorization' = 'Bearer ' + $token.AccessToken
  'X-Requested-With'= 'XMLHttpRequest'
  'x-ms-client-request-id'= [guid]::NewGuid()
  'x-ms-correlation-id' = [guid]::NewGuid()}

$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$($newapp.AppId)/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $headers -Method POST -ErrorAction Stop

If($grantflowrequirements -eq $true){
#Register Service principal
Connect-ExchangeOnline -Credential $credential
$serviceprincipalexo = New-ServicePrincipal -AppId $newapp.AppId -ServiceId $newapp.ObjectId -Displayname $newapp.DisplayName -Organization $tenantId

#Grant your application's service principal full access to the specified mailboxes.
ForEach ($fullaccessuser in $fullaccessusers) {
Add-MailboxPermission -Identity "$fullaccessusers" -User $serviceprincipalexo.Id -AccessRights FullAccess
}
    }
    else{Write-Host "Grantflow requirements disabled"}
D4rki91 commented 1 year ago

@D4rki91

Instead of creating a specific list of users... and granting full permissions to all of them... would it work instead to add "full_access_as_ap" into your Exchange Online Permissions"? If so this would make it more maintainable too...

full_access_as_app

Hey, I'm pretty sure I've tried that and it did not work. :(

sflamm commented 1 year ago

@D4rki91

You are correct - it will not work ... It only applies to EWS and not POP or IMAP

sflamm commented 1 year ago

@D4rki91

Simple "if" works fine... just wanted to clarify what is required for which scenario... script is nice to automate and not use the UI.

From here: https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access

There are scenarios where administrators may want to limit an app to only specific mailboxes and not all Exchange Online mailboxes in the organization. **Administrators can identify the set of mailboxes to permit access by putting them in a mail-enabled security group. Administrators can then limit third-party app access to only that set of mailboxes by creating an application access policy for access to that group**

Even though the link is about EWS - we should be able to make use of the idea for our needs by:

  1. create a Mail Enabled Group which will contain only the users we want the principal to have access to
  2. assign the principal access to the Mail Enabled Group (not to individual users)
  3. add/remove users from the group --> no need to change/maintain the principals ongoing permissions. AD itself can be used to manage the Mail Enabled Group membership

Can you add this to your script? (creating a mail enabled security group and assigning the principal permissions to it)

sflamm commented 1 year ago

@D4rki91

In your script:

Param (
$appName = "YourAppName",
$redirecturi = **"http://localhost",**
$fullaccessuser = ('test1@tld','test2@tld'),
$grantflowrequirements = $false
)

The redirectURL will only work where the proxy is deployed on a location where port 80 traffic is not already in use... (quite commonly used)

Can you update the script so $redirectURI takes a DomainName and Port in the params ? e.g. http://domainName:Port)

sflamm commented 1 year ago

@D4rki91

My understanding is that when creating/registering an application the redirectURI is optional... and only used to make sure traffic is restricted to that URI only.... but in general the proxy choose the redirectURL for the application... would it be better not to define it in the application at all? Does it work not to put one in the application and let the proxy dictate the redirectURL?

D4rki91 commented 1 year ago

@D4rki91

In your script:

Param (
$appName = "YourAppName",
$redirecturi = **"http://localhost",**
$fullaccessuser = ('test1@tld','test2@tld'),
$grantflowrequirements = $false
)

The redirectURL will only work where the proxy is deployed on a location where port 80 traffic is not already in use... (quite commonly used)

Can you update the script so $redirectURI takes a DomainName and Port in the params ? e.g. http://domainName:Port)

Sure I can take a look at it, so it will accept DomainName and Port. We are using the external auth method so in our construct it's not required. (Disadvantage here is, that the Server the proxy is running on has a own LocalAdmin User and autologin enabled and the Proxy has a start up scheduled task configured. So we can restart the Server for Updates without have to care.)

The STunnel on the Client System is setting up a secure connection to our public FQDN "OAuth.domain.tld" and there is a SNAT in our Watchguard Firewall that will redirect it to the Proxy Server. Then I just have to send the User the "login.microsoft" URI and after he successful logged in my ".php.ini" has a simple script that will write the "https://oauth.domain.tld/?code=xyz" part into a CSV file on the Server so I just have to enter it into the proxy application.

It's a really comfortable way to authenticate Usermailboxes in our scenario and of course we restricted the acces with proxy actions like pattern matches on our Firewall to make it as secure as we can. :)

To make sure STunnel can set up a secure connection we use "winacme" on the proxy server to autorenewal the certificate .pem files the Proxy needs.

sflamm commented 1 year ago

@D4rki91

Thanks for the detailed response... still coming up to speed on all of the OAuth flow nuances... would you mind creating a diagram for me so I can better understand?

Here is a diagram I have created for the Client Flow configuration

oauth-client-flow-diagram

D4rki91 commented 1 year ago

I've tested it, it works with DomainName and Port like that. There was a missing if statement so the script will register the Application with "grant flow = $false". Before it ran into a variable = "null" error.

Also I corrected the "fullaccesuser" mistake inside the foreach loop.


# Requires AzureADPreview Module;
# Install-Module AzureADPreview
# Install-Module -Name AzureRM
# User must be able to create apps and consent (Global Admin, ...)

Param (
$appName = "TestApp",
$redirecturi = "http://localhost:8081",
$fullaccessuser = ('test1@tld','test2@tld'),
$grantflowrequirements = $false
)

#Login to Azure Active Directory
$credential = Get-Credential
Connect-AzureAd -Credential $credential;

#MS Graph stuff:

#Microsoft Graph delegated permissions
$servicePrincipalNameGraph = "Microsoft Graph"; 
$servicePrincipalNameOauth2PermissionsGraph = @("IMAP.AccessAsUser.All", "POP.AccessAsUser.All", "SMTP.Send");

# Get MS Graph
$servicePrincipalGraph = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameGraph };

#MS Graph permissions
$reqGraph = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqGraph.ResourceAppId = $servicePrincipalGraph.AppId;

$servicePrincipalGraph.Oauth2Permissions | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsGraph} | ForEach-Object {
    $permission = $_
    $delPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Scope" #delegate permission (oauth) are always "Scope"
    $reqGraph.ResourceAccess += $delPermission
}

If($grantflowrequirements -eq $true){
#EXO stuff:

#Office 365 Exchange Online app permissions
$servicePrincipalNameExchangeOnline = "Office 365 Exchange Online"; 
$servicePrincipalNameOauth2PermissionsExchangeOnline = @("IMAP.AccessAsApp", "POP.AccessAsApp");

#Get Office 365 Exchange Online
$servicePrincipalExchangeOnline = Get-AzureADServicePrincipal -All $true | Where-Object { $_.DisplayName -eq $servicePrincipalNameExchangeOnline };

#Exchange Online permissions
$reqExchangeOnline = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess";
$reqExchangeOnline.ResourceAppId = $servicePrincipalExchangeOnline.AppId;

$servicePrincipalExchangeOnline.AppRoles | Where-Object { $_.Value -in $servicePrincipalNameOauth2PermissionsExchangeOnline} | ForEach-Object {
    $permission = $_
    $appPermission = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" -ArgumentList $permission.Id,"Role"
    $reqExchangeOnline.ResourceAccess += $appPermission
}
    }
    else{Write-Host "Grantflow requirements disabled"}
#Define the Clientsecret here
$secretstartdate = Get-Date
$secretenddate = $secretstartdate.AddMonths(24)
$description = "OAuth_secret"

If($grantflowrequirements -eq $true){
New-AzureADApplication -DisplayName $appName -AvailableToOtherTenants:$true -PublicClient:$false -ReplyUrls $redirecturi -RequiredResourceAccess $reqGraph, $reqExchangeOnline;
    }
    else
{New-AzureADApplication -DisplayName $appName -AvailableToOtherTenants:$true -PublicClient:$false -ReplyUrls $redirecturi -RequiredResourceAccess $reqGraph;}

$newapp = Get-AzureADApplication -SearchString $appName;
$clientsecret = New-AzureADApplicationPasswordCredential -ObjectId $newapp.ObjectId -StartDate $secretstartdate -EndDate $secretenddate -CustomKeyIdentifier $description;

"`n"

"ClientId: " + $newapp.AppId;
"ClientSecret: " + $clientsecret.Value
"TenantId: " + (Get-AzureADTenantDetail).ObjectId;

"`n"

"You can check your AAD app here: https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/" + $newapp.AppId + "/objectId/" + $newapp.ObjectId + "/isMSAApp/";

#Give it some time to appear in azuread so the next step will run successfull
Start-Sleep -Seconds 60

#Add you as owner
$AppOwner = get-azureaduser -all $true -Filter "UserPrincipalName eq '$($credential.UserName)'"
Add-AzureADApplicationOwner -ObjectId $($newapp.ObjectId) -RefObjectId $($AppOwner.ObjectId)

#Grant Admin consent
Login-AzureRmAccount -Credential $credential
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $TenantId, $null, "Never", $null, "74658136-14ec-4630-ad9b-26e160ff0fc6")
$headers = @{
  'Authorization' = 'Bearer ' + $token.AccessToken
  'X-Requested-With'= 'XMLHttpRequest'
  'x-ms-client-request-id'= [guid]::NewGuid()
  'x-ms-correlation-id' = [guid]::NewGuid()}

$url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$($newapp.AppId)/Consent?onBehalfOfAll=true"
Invoke-RestMethod -Uri $url -Headers $headers -Method POST -ErrorAction Stop

If($grantflowrequirements -eq $true){
#Register Service principal
Connect-ExchangeOnline -Credential $credential
$serviceprincipalexo = New-ServicePrincipal -AppId $newapp.AppId -ServiceId $newapp.ObjectId -Displayname $newapp.DisplayName -Organization $tenantId

#Grant your application's service principal full access to the specified mailboxes.
ForEach ($fullaccessuser in $fullaccessusers) {
Add-MailboxPermission -Identity "$fullaccessuser" -User $serviceprincipalexo.Id -AccessRights FullAccess
}
    }
    else{Write-Host "Grantflow requirements disabled"} ```
D4rki91 commented 1 year ago

Hey, I tried to create a diagram. I'm very bad at this and also haven't a propper tool for that right now. :D But I think it should clarify our solution.

OAuth