Skip to content

Commit

Permalink
Improved comments and added new PasswordCredential creation flow.
Browse files Browse the repository at this point in the history
  • Loading branch information
karok2m committed Nov 4, 2019
1 parent aec696d commit 571a53d
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,6 @@ namespace Microsoft.Azure.IIoT.Deployment {

class AuthenticationManager {

//// ClientId of AzureIndustrialIoTUser Application
//public const string AzureIndustrialIoTUserClientID = "68a4084c-d8c4-4fef-9190-03442be954a2";

//public static readonly string[] MicrosoftGraphUserScopes = new string[] {
// "https://graph.microsoft.com/User.Read",
// "https://graph.microsoft.com/Directory.AccessAsUser.All"
// };

//public static readonly string[] AzureManagementUserScopes = new string[] {
// "https://management.azure.com/user_impersonation"
// };


// ClientId of AzureIndustrialIoTIAI Application
public const string AzureIndustrialIoTIAIClientID = "fb2ca262-60d8-4167-ac33-1998d6d5c50b";

Expand All @@ -53,9 +40,6 @@ class AuthenticationManager {
private IAccount _account;
private Guid _tenantId;

// ToDo: Check recommended call pattern in public client applications for AcquireTokenSilent
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token


public AuthenticationManager(
AzureEnvironment azureEnvironment,
Expand Down Expand Up @@ -129,6 +113,8 @@ string tenant
public async Task<AuthenticationResult> AcquireMicrosoftGraphTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(MicrosoftGraphIAIScopes, _account)
.ExecuteAsync(cancellationToken);
Expand All @@ -139,6 +125,8 @@ string tenant
public async Task<AuthenticationResult> AcquireAzureManagementTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(AzureManagementIAIScopes, _account)
.ExecuteAsync(cancellationToken);
Expand All @@ -149,6 +137,8 @@ string tenant
public async Task<AuthenticationResult> AcquireKeyVaultTokenAsync(
CancellationToken cancellationToken = default
) {
// Fetch AccessToken from cache
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-a-cached-token
var authenticationResult = await _publicClientApplication
.AcquireTokenSilent(KeyVaultIAIScopes, _account)
.ExecuteAsync(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ IEnumerable<AzureEnvironment> azureEnvironments
public ISubscription SelectSubscription(
IEnumerable<ISubscription> subscriptionsList
) {
// ToDo: Handle exceptions when user does npot have enough permissions to list subscriptions.
var subscriptionsCount = subscriptionsList.Count();

ISubscription subscription;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,12 +860,15 @@ await _iotHubManagementClient
cancellationToken
);

// This will point to PublicIP address of Ingress.
var remoteEndpoint = "";

var webSiteCreationTask = _webSiteManagementClient
.CreateSiteAsync(
_resourceGroup,
appServicePlan,
_azureWebsiteName,
"", // !!!!! ToDo: Add connection string to PublicIP address of Ingress !!!!!
remoteEndpoint,
_webAppX509Certificate,
_defaultTagsDict,
cancellationToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ RestClient restClient
//DatabaseAccountOfferType = "Standard",
Kind = DatabaseAccountKind.GlobalDocumentDB,

// !!!!! ToDo: Add selection
ConsistencyPolicy = new ConsistencyPolicy {
DefaultConsistencyLevel = DefaultConsistencyLevel.Strong,
MaxStalenessPrefix = 10,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ RestClient restClient
Location = resourceGroup.RegionName,
Tags = tags,

// !!!!! ToDo: Add selection.
Sku = new Sku {
Name = SkuName.Basic,
Tier = SkuTier.Basic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ VaultInner keyVault

return x509Certificate2;
} else {
// ToDo: Add exporting of PEM certificates.
throw new NotImplementedException("Exporting PEM certificates is not supported.");
}
}
Expand All @@ -120,6 +119,9 @@ VaultInner keyVault
try {
Log.Information($"Adding certificate to Azure KeyVault: {certificateName} ...");

// ToDo: Add support of PEM certificate creation: CertificateContentType.Pem
var contentType = CertificateContentType.Pfx;

var certificatePolicy = new CertificatePolicy {
KeyProperties = new KeyProperties {
Exportable = true,
Expand All @@ -128,7 +130,7 @@ VaultInner keyVault
ReuseKey = false
},
SecretProperties = new SecretProperties {
ContentType = CertificateContentType.Pfx // ToDo: Try CertificateContentType.Pem !!!!!
ContentType = contentType
},
X509CertificateProperties = new X509CertificateProperties {
Subject = $"CN={certificateCN}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ RestClient restClient

Log.Information($"Creating Azure IoT Hub: {iotHubName} ...");

// !!!!! ToDo: Add selection.
var iotHubSkuInfo = new IotHubSkuInfo(
name: "S1",
tier: IotHubSkuTier.Standard,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ RestClient restClient
EnabledForDiskEncryption = false,
TenantId = tenantIdGuid,
Sku = new Sku {
Name = SkuName.Premium, // !!!!!
//Family = "A" !!!!!
Name = SkuName.Premium,
//Family = "A"
},
AccessPolicies = keyVaultAccessPolicies
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,24 +158,12 @@ class MicrosoftGraphServiceClient {
Oauth2PermissionScopes = oauth2Permissions
};

// !!!!! Oauth2AllowImplicitFlow !!!!!
var serviceApplicationWebApplication = new WebApplication {
//Oauth2AllowImplicitFlow = false,
ImplicitGrantSettings = new ImplicitGrantSettings {
EnableIdTokenIssuance = true
}
};

var serviceApplicationPasswordCredentials = new List<PasswordCredential> {
new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes("Service Key"),
DisplayName = "Service Key",
SecretText = GeneratePassword()
}
};

var serviceApplicationIdentifierUri = $"https://{_tenantGuid.ToString()}/{servicesApplicationName}";

var serviceApplicationRequest = new Application {
Expand All @@ -188,14 +176,30 @@ class MicrosoftGraphServiceClient {
RequiredResourceAccess = serviceApplicationRequiredResourceAccess,
Api = serviceApplicationApiApplication,
Web = serviceApplicationWebApplication,
PasswordCredentials = serviceApplicationPasswordCredentials
PasswordCredentials = new List<PasswordCredential> { }
};

var serviceApplication = await _graphServiceClient
.Applications
.Request()
.AddAsync(serviceApplicationRequest, cancellationToken);

// Add Service Key PasswordCredential
var serviceKeyName = "Service Key";

var serviceApplicationServiceKeyPasswordCredentialDefinition = new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes(serviceKeyName),
DisplayName = serviceKeyName
};

await _graphServiceClient
.Applications[serviceApplication.Id]
.AddPassword(serviceApplicationServiceKeyPasswordCredentialDefinition)
.Request()
.PostAsync(cancellationToken);

// We need to create ServicePrincipal for this application.
var serviceApplicationSP = await CreateServicePrincipalAsync(
serviceApplication,
Expand All @@ -205,7 +209,6 @@ class MicrosoftGraphServiceClient {

var me = Me(cancellationToken);

// !!!!! App role assignment is not supported yet, i.e. adding new app role assignments !!!!!
var approverAppRoleAssignmentRequest = new AppRoleAssignment {
//PrincipalDisplayName = "",
PrincipalType = "User",
Expand Down Expand Up @@ -236,9 +239,10 @@ class MicrosoftGraphServiceClient {
AppRoleId = serviceApplicationAdministratorRoleIdGuid
};

//// !!!!! AddAsync() is not defined !!!!!
//var appRoleAssignment = graphServiceClient
// .ServicePrincipals["{id}"]
//// ToDo: Use AddAsync() instead of the workaround bellow
//// when AddAsync() is added.
//var appRoleAssignment = _graphServiceClient
// .ServicePrincipals[serviceApplicationSP.Id]
// .AppRoleAssignments
// .Request()
// .AddAsync(appRoleAssignmentRequest);
Expand Down Expand Up @@ -311,8 +315,10 @@ class MicrosoftGraphServiceClient {
}
};

// !!!!! Oauth2AllowImplicitFlow = true !!!!!
// !!!!! Oauth2AllowUrlPathMatching = true !!!!!
// Note: Oauth2AllowImplicitFlow will be enabled automatically since both
// EnableIdTokenIssuance and EnableAccessTokenIssuance are set to true.

// ToDo: RedirectUris should be set after App Service is deployed.
var clientApplicationWebApplicatoin = new WebApplication {
RedirectUris = new List<string> {
$"https://{azureWebsiteName}.azurewebsites.net/",
Expand All @@ -321,24 +327,14 @@ class MicrosoftGraphServiceClient {
$"https://{azureWebsiteName}.azurewebsites.net/history/",
$"https://{azureWebsiteName}.azurewebsites.net/ua/",
$"https://{azureWebsiteName}.azurewebsites.net/vault/"
}, // ToDo: This should be set after App Service is deployed.
},
//Oauth2AllowImplicitFlow = true,
ImplicitGrantSettings = new ImplicitGrantSettings {
EnableIdTokenIssuance = true,
EnableAccessTokenIssuance = true
}
};

var clientApplicationPasswordCredentials = new List<PasswordCredential> {
new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes("Client Key"),
DisplayName = "Client Key",
SecretText = GeneratePassword()
}
};

var clientApplicationRequest = new Application {
DisplayName = clientsApplicationName,
IsFallbackPublicClient = true,
Expand All @@ -347,14 +343,30 @@ class MicrosoftGraphServiceClient {
RequiredResourceAccess = clientApplicationRequiredResourceAccess,
PublicClient = clientApplicationPublicClientApplication,
Web = clientApplicationWebApplicatoin,
PasswordCredentials = clientApplicationPasswordCredentials
PasswordCredentials = new List<PasswordCredential> { }
};

var clientApplication = await _graphServiceClient
.Applications
.Request()
.AddAsync(clientApplicationRequest, cancellationToken);

// Add Client Key PasswordCredential
var clientKeyName = "Client Key";

var clientApplicationClientKeyPasswordCredentialDefinition = new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes(clientKeyName),
DisplayName = clientKeyName
};

await _graphServiceClient
.Applications[clientApplication.Id]
.AddPassword(clientApplicationClientKeyPasswordCredentialDefinition)
.Request()
.PostAsync(cancellationToken);

// We need to create ServicePrincipal for this application.
await CreateServicePrincipalAsync(
clientApplication,
Expand Down Expand Up @@ -392,26 +404,12 @@ class MicrosoftGraphServiceClient {
Oauth2PermissionScopes = aksOauth2Permissions
};

// !!!!! Oauth2AllowImplicitFlow !!!!!
var aksApplicationWebApplication = new WebApplication {
//Oauth2AllowImplicitFlow = false,
ImplicitGrantSettings = new ImplicitGrantSettings {
EnableIdTokenIssuance = true
}
};

var aksApplicationPasswordCredentialRbacSecret = GeneratePassword();

var aksApplicationPasswordCredentials = new List<PasswordCredential> {
new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes("rbac"),
DisplayName = "rbac",
SecretText = aksApplicationPasswordCredentialRbacSecret
}
};

var aksApplicationIdentifierUri = $"https://{_tenantGuid.ToString()}/{aksApplicationName}";

var aksApplicationDefinition = new Application {
Expand All @@ -424,22 +422,47 @@ class MicrosoftGraphServiceClient {
RequiredResourceAccess = new List<RequiredResourceAccess>(),
Api = aksApplicationApiApplication,
Web = aksApplicationWebApplication,
PasswordCredentials = aksApplicationPasswordCredentials
PasswordCredentials = new List<PasswordCredential> { }
};

var aksApplication = await _graphServiceClient
.Applications
.Request()
.AddAsync(aksApplicationDefinition, cancellationToken);

// Add RBAC Key PasswordCredential
var rbacKeyName = "rbac";

var aksApplicationRBACPasswordCredentialDefinition = new PasswordCredential {
StartDateTime = DateTimeOffset.UtcNow,
EndDateTime = DateTimeOffset.UtcNow.AddYears(2),
CustomKeyIdentifier = ToBase64Bytes(rbacKeyName),
DisplayName = rbacKeyName
};

var aksApplicationRBACPasswordCredential = await _graphServiceClient
.Applications[aksApplication.Id]
.AddPassword(aksApplicationRBACPasswordCredentialDefinition)
.Request()
.PostAsync(cancellationToken);

if (string.IsNullOrEmpty(aksApplicationRBACPasswordCredential.SecretText)) {
throw new Exception($"Failed to retrieve password credentials for AKS Application: {rbacKeyName}");
}

var aksApplicationRBACPasswordCredentialSecret = aksApplicationRBACPasswordCredential.SecretText;

// We need to create ServicePrincipal for this application.
await CreateServicePrincipalAsync(
aksApplication,
tags,
cancellationToken
);

var result = new Tuple<Application, string>(aksApplication, aksApplicationPasswordCredentialRbacSecret);
var result = new Tuple<Application, string>(
aksApplication,
aksApplicationRBACPasswordCredentialSecret
);

return result;
}
Expand Down Expand Up @@ -710,11 +733,6 @@ await _graphServiceClient
return System.Text.Encoding.UTF8.GetBytes(message);
}

private static string GeneratePassword() {
// ToDo: Make it cryptographically sound at some point.
return Guid.NewGuid().ToString();
}

private async Task<RequiredResourceAccess> GetRequiredResourceAccessByDisplayNameAsync(
string displayName,
IEnumerable<string> requiredDelegatedPermissions,
Expand All @@ -738,7 +756,6 @@ await _graphServiceClient
var resourceAccesses = new List<ResourceAccess>();

foreach (var requiredDelegatedPermission in requiredDelegatedPermissions) {
// !!!!! ToDo: Use PublishedPermissionScopes instead of oauth2Permissions when available !!!!!
var oauth2Permissions = servicePrincipal
.Oauth2Permissions
.Where(permission => permission.Value == requiredDelegatedPermission)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ RestClient restClient

PublicIPAllocationMethod = IPAllocationMethod.Dynamic,
DnsSettings = new PublicIPAddressDnsSettings {
DomainNameLabel = domainNameLabel // ToDo: We do not have any VMs any more.
DomainNameLabel = domainNameLabel
},
IdleTimeoutInMinutes = 4
};
Expand Down

0 comments on commit 571a53d

Please sign in to comment.