Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grant flow Bad Request #72

Closed
D4rki91 opened this issue Oct 5, 2022 · 18 comments
Closed

Grant flow Bad Request #72

D4rki91 opened this issue Oct 5, 2022 · 18 comments

Comments

@D4rki91
Copy link

D4rki91 commented Oct 5, 2022

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
Copy link
Owner

simonrob commented Oct 5, 2022

Please try the setup script linked from the readme: #61 (comment).

@D4rki91
Copy link
Author

D4rki91 commented Oct 6, 2022

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
Copy link
Owner

simonrob commented Oct 7, 2022

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
Copy link
Author

D4rki91 commented Oct 10, 2022

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
Copy link
Owner

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
Copy link

sflamm commented Oct 20, 2022

@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 a permission_url value (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
Copy link

sflamm commented Oct 20, 2022

@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
Copy link

sflamm commented Oct 20, 2022

@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
Copy link
Author

D4rki91 commented Oct 21, 2022

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
Copy link
Author

D4rki91 commented Oct 21, 2022

@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
Copy link

sflamm commented Oct 21, 2022

@D4rki91

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

@sflamm
Copy link

sflamm commented Oct 22, 2022

@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
Copy link

sflamm commented Oct 22, 2022

@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
Copy link

sflamm commented Oct 22, 2022

@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
Copy link
Author

D4rki91 commented Oct 23, 2022

@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
Copy link

sflamm commented Oct 25, 2022

@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
Copy link
Author

D4rki91 commented Oct 25, 2022

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
Copy link
Author

D4rki91 commented Oct 25, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants