Skip to content

Enhancing multi tenant sample #206

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

Merged
merged 33 commits into from
Nov 27, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Registering the Azure Active Directory applications and updating the configuration files for this sample using PowerShell scripts
# Registering the sample apps with Microsoft Identity Platform and updating the configuration files using PowerShell scripts

## Overview

Expand All @@ -9,7 +9,7 @@
```PowerShell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
```
1. Run the script to create your Azure AD application and configure the code of the sample application accordinly. (Other ways of running the scripts are described below)
1. Run the script to create your Azure AD application and configure the code of the sample application accordingly. (Other ways of running the scripts are described below)
```PowerShell
.\AppCreationScripts\Configure.ps1
```
Expand Down
23 changes: 15 additions & 8 deletions 1-WebApp-OIDC/1-2-AnyOrg/AppCreationScripts/Cleanup.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ param(
[string] $tenantId
)

if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null) {
if ($null -eq (Get-Module -ListAvailable -Name "AzureAD")) {
Install-Module "AzureAD" -Scope CurrentUser
}
Import-Module AzureAD
$ErrorActionPreference = 'Stop'
$ErrorActionPreference = "Stop"

Function Cleanup
{
Expand Down Expand Up @@ -44,20 +44,27 @@ This function removes the Azure AD applications for the sample. These applicatio
$tenantId = $creds.Tenant.Id
}
$tenant = Get-AzureADTenantDetail
$tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name
$tenantName = ($tenant.VerifiedDomains | Where-Object { $_._Default -eq $True }).Name

# Removes the applications
Write-Host "Cleaning-up applications from tenant '$tenantName'"

Write-Host "Removing 'webApp' (WebApp) if needed"
$app=Get-AzureADApplication -Filter "DisplayName eq 'WebApp'"
Get-AzureADApplication -Filter "DisplayName eq 'WebApp'" | ForEach-Object {Remove-AzureADApplication -ObjectId $_.ObjectId }
$apps = Get-AzureADApplication -Filter "DisplayName eq 'WebApp'"
if ($apps)
{
Remove-AzureADApplication -ObjectId $apps.ObjectId
}

if ($app)
foreach ($app in $apps)
{
Remove-AzureADApplication -ObjectId $app.ObjectId
Write-Host "Removed."
Write-Host "Removed WebApp.."
}

# also remove service principals of this app
Get-AzureADServicePrincipal -filter "DisplayName eq 'WebApp'" | ForEach-Object {Remove-AzureADServicePrincipal -ObjectId $_.Id -Confirm:$false}

}

Cleanup -Credential $Credential -tenantId $TenantId
Cleanup -Credential $Credential -tenantId $TenantId
118 changes: 112 additions & 6 deletions 1-WebApp-OIDC/1-2-AnyOrg/AppCreationScripts/Configure.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,87 @@ param(
There are four ways to run this script. For more information, read the AppCreationScripts.md file in the same folder as this script.
#>

# Create a password that can be used as an application key
Function ComputePassword
{
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
$aesManaged.BlockSize = 128
$aesManaged.KeySize = 256
$aesManaged.GenerateKey()
return [System.Convert]::ToBase64String($aesManaged.Key)
}

# Create an application key
# See https://www.sabin.io/blog/adding-an-azure-active-directory-application-and-key-using-powershell/
Function CreateAppKey([DateTime] $fromDate, [double] $durationInYears, [string]$pw)
{
$endDate = $fromDate.AddYears($durationInYears)
$keyId = (New-Guid).ToString();
$key = New-Object Microsoft.Open.AzureAD.Model.PasswordCredential
$key.StartDate = $fromDate
$key.EndDate = $endDate
$key.Value = $pw
$key.KeyId = $keyId
return $key
}

# Adds the requiredAccesses (expressed as a pipe separated string) to the requiredAccess structure
# The exposed permissions are in the $exposedPermissions collection, and the type of permission (Scope | Role) is
# described in $permissionType
Function AddResourcePermission($requiredAccess, `
$exposedPermissions, [string]$requiredAccesses, [string]$permissionType)
{
foreach($permission in $requiredAccesses.Trim().Split("|"))
{
foreach($exposedPermission in $exposedPermissions)
{
if ($exposedPermission.Value -eq $permission)
{
$resourceAccess = New-Object Microsoft.Open.AzureAD.Model.ResourceAccess
$resourceAccess.Type = $permissionType # Scope = Delegated permissions | Role = Application permissions
$resourceAccess.Id = $exposedPermission.Id # Read directory data
$requiredAccess.ResourceAccess.Add($resourceAccess)
}
}
}
}

#
# Example: GetRequiredPermissions "Microsoft Graph" "Graph.Read|User.Read"
# See also: http://stackoverflow.com/questions/42164581/how-to-configure-a-new-azure-ad-application-through-powershell
Function GetRequiredPermissions([string] $applicationDisplayName, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions, $servicePrincipal)
{
# If we are passed the service principal we use it directly, otherwise we find it from the display name (which might not be unique)
if ($servicePrincipal)
{
$sp = $servicePrincipal
}
else
{
$sp = Get-AzureADServicePrincipal -Filter "DisplayName eq '$applicationDisplayName'"
}
$appid = $sp.AppId
$requiredAccess = New-Object Microsoft.Open.AzureAD.Model.RequiredResourceAccess
$requiredAccess.ResourceAppId = $appid
$requiredAccess.ResourceAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]

# $sp.Oauth2Permissions | Select Id,AdminConsentDisplayName,Value: To see the list of all the Delegated permissions for the application:
if ($requiredDelegatedPermissions)
{
AddResourcePermission $requiredAccess -exposedPermissions $sp.Oauth2Permissions -requiredAccesses $requiredDelegatedPermissions -permissionType "Scope"
}

# $sp.AppRoles | Select Id,AdminConsentDisplayName,Value: To see the list of all the Application permissions for the application
if ($requiredApplicationPermissions)
{
AddResourcePermission $requiredAccess -exposedPermissions $sp.AppRoles -requiredAccesses $requiredApplicationPermissions -permissionType "Role"
}
return $requiredAccess
}


Function UpdateLine([string] $line, [string] $value)
{
$index = $line.IndexOf('=')
Expand Down Expand Up @@ -56,13 +137,16 @@ Function UpdateTextFile([string] $configFilePath, [System.Collections.HashTable]
Set-Content -Value "<html><body><table>" -Path createdApps.html
Add-Content -Value "<thead><tr><th>Application</th><th>AppId</th><th>Url in the Azure portal</th></tr></thead><tbody>" -Path createdApps.html

$ErrorActionPreference = "Stop"

Function ConfigureApplications
{
<#.Description
This function creates the Azure AD applications for the sample in the provided Azure AD tenant and updates the
configuration files in the client and service project of the visual studio solution (App.Config and Web.Config)
so that they are consistent with the Applications parameters
#>
$commonendpoint = "common"

# $tenantId is the Active Directory Tenant. This is a GUID which represents the "Directory ID" of the AzureAD tenant
# into which you want to create the apps. Look it up in the Azure portal in the "Properties" of the Azure AD.
Expand Down Expand Up @@ -93,52 +177,74 @@ Function ConfigureApplications
$tenant = Get-AzureADTenantDetail
$tenantName = ($tenant.VerifiedDomains | Where { $_._Default -eq $True }).Name

# Get the user running the script
# Get the user running the script to add the user as the app owner
$user = Get-AzureADUser -ObjectId $creds.Account.Id

# Create the webApp AAD application
Write-Host "Creating the AAD application (WebApp)"
# Get a 2 years application key for the webApp Application
$pw = ComputePassword
$fromDate = [DateTime]::Now;
$key = CreateAppKey -fromDate $fromDate -durationInYears 2 -pw $pw
$webAppAppKey = $pw
# create the application
$webAppAadApplication = New-AzureADApplication -DisplayName "WebApp" `
-HomePage "https://localhost:44321/" `
-LogoutUrl "https://localhost:44321/signout-oidc" `
-ReplyUrls "https://localhost:44321/", "https://localhost:44321/signin-oidc" `
-IdentifierUris "https://$tenantName/WebApp" `
-AvailableToOtherTenants $True `
-PasswordCredentials $key `
-Oauth2AllowImplicitFlow $true `
-PublicClient $False

# create the service principal of the newly created application
$currentAppId = $webAppAadApplication.AppId
$webAppServicePrincipal = New-AzureADServicePrincipal -AppId $currentAppId -Tags {WindowsAzureActiveDirectoryIntegratedApp}

# add the user running the script as an app owner if needed
$owner = Get-AzureADApplicationOwner -ObjectId $webAppAadApplication.ObjectId
if ($owner -eq $null)
{
Add-AzureADApplicationOwner -ObjectId $webAppAadApplication.ObjectId -RefObjectId $user.ObjectId
Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($webAppServicePrincipal.DisplayName)'"
Add-AzureADApplicationOwner -ObjectId $webAppAadApplication.ObjectId -RefObjectId $user.ObjectId
Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($webAppServicePrincipal.DisplayName)'"
}


Write-Host "Done creating the webApp application (WebApp)"

# URL of the AAD application in the Azure portal
# Future? $webAppPortalUrl = "https://portal.azure.com/#@"+$tenantName+"/blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/Overview/appId/"+$webAppAadApplication.AppId+"/objectId/"+$webAppAadApplication.ObjectId+"/isMSAApp/"
$webAppPortalUrl = "https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/CallAnAPI/appId/"+$webAppAadApplication.AppId+"/objectId/"+$webAppAadApplication.ObjectId+"/isMSAApp/"
Add-Content -Value "<tr><td>webApp</td><td>$currentAppId</td><td><a href='$webAppPortalUrl'>WebApp</a></td></tr>" -Path createdApps.html

$requiredResourcesAccess = New-Object System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]

# Add Required Resources Access (from 'webApp' to 'Microsoft Graph')
Write-Host "Getting access from 'webApp' to 'Microsoft Graph'"
$requiredPermissions = GetRequiredPermissions -applicationDisplayName "Microsoft Graph" `
-requiredDelegatedPermissions "Directory.Read.All" `

$requiredResourcesAccess.Add($requiredPermissions)


Set-AzureADApplication -ObjectId $webAppAadApplication.ObjectId -RequiredResourceAccess $requiredResourcesAccess
Write-Host "Granted permissions."

# Update config file for 'webApp'
$configFile = $pwd.Path + "\..\appsettings.json"
Write-Host "Updating the sample code ($configFile)"
$dictionary = @{ "ClientId" = $webAppAadApplication.AppId;"TenantId" = "organizations";"Domain" = $tenantName };
$dictionary = @{ "ClientId" = $webAppAadApplication.AppId;"TenantId" = "organizations";"Domain" = $tenantName;"ClientSecret" = $webAppAppKey };
UpdateTextFile -configFilePath $configFile -dictionary $dictionary

Add-Content -Value "</tbody></table></body></html>" -Path createdApps.html
}

# Pre-requisites
if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null) {
Install-Module "AzureAD" -Scope CurrentUser
}
}

Import-Module AzureAD

# Run interactively (will ask you for the tenant ID)
Expand Down
11 changes: 11 additions & 0 deletions 1-WebApp-OIDC/1-2-AnyOrg/AppCreationScripts/sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
"HomePage": "https://localhost:44321/",
"ReplyUrls": "https://localhost:44321/, https://localhost:44321/signin-oidc",
"LogoutUrl": "https://localhost:44321/signout-oidc",
"PasswordCredentials": "Auto",
"RequiredResourcesAccess": [
{
"Resource": "Microsoft Graph",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chapter 1 is about signing-in to a web app? not calling graph?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jmprieur are you sure you have the latest version? I reverted this change already.

"DelegatedPermissions": [ "Directory.Read.All" ]
}
]
}
],

Expand All @@ -46,6 +53,10 @@
{
"key": "Domain",
"value": "$tenantName"
},
{
"key": "ClientSecret",
"value": ".AppKey"
}
]
}
Expand Down
14 changes: 14 additions & 0 deletions 1-WebApp-OIDC/1-2-AnyOrg/BLL/IMSGraphService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Graph;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApp_OpenIDConnect_DotNet.BLL
{
public interface IMSGraphService
{
Task<IEnumerable<User>> GetUsersAsync(string accessToken);
}
}
18 changes: 18 additions & 0 deletions 1-WebApp-OIDC/1-2-AnyOrg/BLL/ITodoItemService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using WebApp_OpenIDConnect_DotNet.Models;

namespace WebApp_OpenIDConnect_DotNet.BLL
{
public interface ITodoItemService
{
Task<TodoItem> Create(string text, ClaimsPrincipal user);
Task<IEnumerable<TodoItem>> List(bool showAll, ClaimsPrincipal user);
Task<TodoItem> Get(int id, ClaimsPrincipal user);
Task Delete(int id, ClaimsPrincipal user);
Task<TodoItem> Edit(TodoItem todoItem, ClaimsPrincipal user);
}
}
74 changes: 74 additions & 0 deletions 1-WebApp-OIDC/1-2-AnyOrg/BLL/MSGraphService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Graph;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace WebApp_OpenIDConnect_DotNet.BLL
{
public class MSGraphService : IMSGraphService
{
// the Graph SDK's GraphServiceClient
private GraphServiceClient graphServiceClient;

/// <summary>
/// Gets the users in a tenant.
/// </summary>
/// <param name="accessToken">The access token for MS Graph.</param>
/// <returns>
/// A list of users
/// </returns>
public async Task<IEnumerable<User>> GetUsersAsync(string accessToken)
{
IGraphServiceUsersCollectionPage users = null;

try
{
PrepareAuthenticatedClient(accessToken);
users = await graphServiceClient.Users.Request()
.Filter($"accountEnabled eq true")
.Select("id, userPrincipalName")
.GetAsync();

if (users?.CurrentPage.Count > 0)
{
return users;
}
}
catch (ServiceException e)
{
Debug.WriteLine("We could not retrieve the user's list: " + $"{e}");
return null;
}

return users;
}

/// <summary>
/// Prepares the authenticated client.
/// </summary>
/// <param name="accessToken">The access token.</param>
private void PrepareAuthenticatedClient(string accessToken)
{
try
{
graphServiceClient = new GraphServiceClient("https://graph.microsoft.com/beta",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
await Task.Run(() =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
});
}));
}
catch (Exception ex)
{
Debug.WriteLine($"Could not create a graph client {ex}");
}
}
}
}
Loading