Skip to content

Update blazor templates with Microsoft.Identity.Web APIs #24268

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 9 commits into from
Jul 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@
<IdentityServer4StoragePackageVersion>3.0.0</IdentityServer4StoragePackageVersion>
<IdentityServer4EntityFrameworkStoragePackageVersion>3.0.0</IdentityServer4EntityFrameworkStoragePackageVersion>
<MessagePackPackageVersion>2.1.90</MessagePackPackageVersion>
<MicrosoftIdentityWebPackageVersion>0.2.0-preview</MicrosoftIdentityWebPackageVersion>
<MicrosoftIdentityWebUIPackageVersion>0.2.0-preview</MicrosoftIdentityWebUIPackageVersion>
<MicrosoftIdentityWebPackageVersion>0.2.1-preview</MicrosoftIdentityWebPackageVersion>
<MicrosoftIdentityWebUIPackageVersion>0.2.1-preview</MicrosoftIdentityWebUIPackageVersion>
<MicrosoftGraphPackageVersion>3.8.0</MicrosoftGraphPackageVersion>
<MessagePackAnalyzerPackageVersion>$(MessagePackPackageVersion)</MessagePackAnalyzerPackageVersion>
<MoqPackageVersion>4.10.0</MoqPackageVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,30 @@ private void TestBasicNavigation()
Browser.Exists(By.CssSelector("table>tbody>tr"));
Browser.Equal(5, () => Browser.FindElements(By.CssSelector("p+table>tbody>tr")).Count);
}

[Theory]
[QuarantinedTest]
[InlineData("IndividualB2C", null)]
[InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", null)]
[InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })]
[InlineData("SingleOrg", new string[] { "--calls-graph" })]
public async Task BlazorServerTemplat_IdentityWeb_BuildAndPublish(string auth, string[] args)
{
Project = await ProjectFactory.GetOrCreateProject("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLower(), Output);

var createResult = await Project.RunDotNetNewAsync("blazorserver", auth: auth, args: args);
Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult));

var publishResult = await Project.RunDotNetPublishAsync();
Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", Project, publishResult));

// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
// later, while the opposite is not true.

var buildResult = await Project.RunDotNetBuildAsync();
Assert.True(0 == buildResult.ExitCode, ErrorMessages.GetFailedProcessMessage("build", Project, buildResult));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,27 @@ public async Task BlazorWasmStandaloneTemplate_IndividualAuth_Works()
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmhostedaadgraph", "-ho",
"-au", "SingleOrg",
"--calls-graph",
"--domain", "my-domain",
"--tenant-id", "tenantId",
"--client-id", "clientId",
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmhostedaadapi", "-ho",
"-au", "SingleOrg",
"--called-api-url", "\"https://graph.microsoft.com\"",
"--called-api-scopes", "user.readwrite",
"--domain", "my-domain",
"--tenant-id", "tenantId",
"--client-id", "clientId",
"--default-scope", "full",
"--app-id-uri", "ApiUri",
"--api-client-id", "1234123413241324"),
new TemplateInstance(
"blazorwasmstandaloneaadb2c",
"-au", "IndividualB2C",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
<!--#endif -->
<!--#if (IndividualAuth || OrganizationalAuth) -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion}" Condition="'$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="${MicrosoftAspNetCoreDiagnosticsEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="${MicrosoftAspNetCoreIdentityEntityFrameworkCorePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="${MicrosoftAspNetCoreIdentityUIPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="${MicrosoftEntityFrameworkCoreSqlServerPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="${MicrosoftEntityFrameworkCoreSqlitePackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="${MicrosoftEntityFrameworkCoreToolsPackageVersion}" Condition=" '$(IndividualLocalAuth)' == 'True' " />
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" Condition=" '$(IndividualB2CAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
</ItemGroup>

<!--#endif -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@
<!--#endif -->
<!--#if (OrganizationalAuth || IndividualB2CAuth) -->
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADUIPackageVersion}" Condition="'$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureADB2C.UI" Version="${MicrosoftAspNetCoreAuthenticationAzureADB2CUIPackageVersion}" Condition="'$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.Identity.Web" Version="${MicrosoftIdentityWebPackageVersion}" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="${MicrosoftIdentityWebUIPackageVersion}" />
<PackageReference Include="Microsoft.Graph" Version="${MicrosoftGraphPackageVersion}" Condition=" '$(GenerateGraph)' == 'True' "/>
</ItemGroup>
<!--#endif -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@
"NoHttps": {
"longName": "no-https",
"shortName": ""
},
"CalledApiUrl": {
"longName": "called-api-url",
"shortName": ""
},
"CalledApiScopes": {
"longName": "called-api-scopes",
"shortName": ""
},
"CallsMicrosoftGraph": {
"longName": "calls-graph",
"shortName": ""
}
},
"usageExamples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,52 @@
"Shared/LoginDisplay.IndividualB2CAuth.razor",
"Shared/LoginDisplay.OrganizationalAuth.razor"
]
},
{
"condition": "(!GenerateApi)",
"exclude": [
"Services/DownstreamWebApi.cs",
"Pages/CallWebApi.razor"
]
},
{
"condition": "(!GenerateGraph)",
"exclude": [
"Services/MicrosoftGraphServiceExtensions.cs",
"Services/TokenAcquisitionCredentialProvider.cs",
"Shared/NavMenu.CallsMicrosoftGraph.razor",
"Pages/ShowProfile.razor"
]
},
{
"condition": "(!GenerateApiOrGraph)",
"rename": {
"Shared/NavMenu.NoGraphOrApi.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.CallsMicrosoftGraph.razor",
"Shared/NavMenu.CallsWebApi.razor"
]
},
{
"condition": "(GenerateGraph)",
"rename": {
"Shared/NavMenu.CallsMicrosoftGraph.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.NoGraphOrApi.razor",
"Shared/NavMenu.CallsWebApi.razor"
]
},
{
"condition": "(GenerateApi)",
"rename": {
"Shared/NavMenu.CallsWebApi.razor": "Shared/NavMenu.razor"
},
"exclude": [
"Shared/NavMenu.NoGraphOrApi.razor",
"Shared/NavMenu.CallsMicrosoftGraph.razor"
]
}
]
}
Expand Down Expand Up @@ -174,21 +220,28 @@
"SignUpSignInPolicyId": {
"type": "parameter",
"datatype": "string",
"defaultValue": "",
"defaultValue": "b2c_1_susi",
"replaces": "MySignUpSignInPolicyId",
"description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)."
},
"SignedOutCallbackPath": {
"type": "parameter",
"datatype": "string",
"defaultValue": "/signout/B2C_1_susi",
"replaces": "/signout/MySignUpSignInPolicyId",
"description": "The global signout callback (use with IndividualB2C auth)."
},
"ResetPasswordPolicyId": {
"type": "parameter",
"datatype": "string",
"defaultValue": "",
"defaultValue": "b2c_1_reset",
"replaces": "MyResetPasswordPolicyId",
"description": "The reset password policy ID for this project (use with IndividualB2C auth)."
},
"EditProfilePolicyId": {
"type": "parameter",
"datatype": "string",
"defaultValue": "",
"defaultValue": "b2c_1_edit_profile",
"replaces": "MyEditProfilePolicyId",
"description": "The edit profile policy ID for this project (use with IndividualB2C auth)."
},
Expand Down Expand Up @@ -352,6 +405,37 @@
"format": "yyyy"
}
},
"CalledApiUrl": {
"type": "parameter",
"datatype": "string",
"replaces": "[WebApiUrl]",
"defaultValue" : "https://graph.microsoft.com/beta",
"description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C is specified."
},
"CallsMicrosoftGraph": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"description": "Specifies if the web app calls Microsoft Graph. This option only applies if --auth SingleOrg or --auth MultiOrg is specified."
},
"CalledApiScopes": {
"type": "parameter",
"datatype": "string",
"replaces" : "user.read",
"description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C is specified."
},
"GenerateApi": {
"type": "computed",
"value": "((IndividualB2CAuth || OrganizationalAuth) && (CalledApiUrl != \"https://graph.microsoft.com/beta\" || CalledApiScopes != \"user.read\"))"
},
"GenerateGraph": {
"type": "computed",
"value": "(OrganizationalAuth && CallsMicrosoftGraph)"
},
"GenerateApiOrGraph": {
"type": "computed",
"value": "(GenerateApi || GenerateGraph)"
},
"skipRestore": {
"type": "parameter",
"datatype": "bool",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
@if (SignInManager.IsSignedIn(User))
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
</li>
<li class="nav-item">
<form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
<form class="form-inline" asp-area="MicrosoftIdentity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
<button type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Register">Register</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@page "/callwebapi"

@using BlazorServerWeb_CSharp
@using Microsoft.Identity.Web

@inject IDownstreamWebApi downstreamAPI
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler

<h1>Call an API</h1>

<p>This component demonstrates fetching data from a Web API.</p>

@if (apiResult == null)
{
<p><em>Loading...</em></p>
}
else
{
<h2>API Result</h2>
@apiResult
}

@code {
private string apiResult;

protected override async Task OnInitializedAsync()
{
try
{
apiResult = await downstreamAPI.CallWebApiAsync("me");
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@page "/showprofile"

@using Microsoft.Identity.Web
@using Microsoft.Graph
@inject Microsoft.Graph.GraphServiceClient GraphServiceClient
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler

<h1>Me</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (user == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table table-striped table-condensed" style="font-family: monospace">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
<tr>
<td>Name</td>
<td>@user.DisplayName</td>
</tr>
<tr>
<td>Photo</td>
<td>
@{
if (photo != null)
{
<img style="margin: 5px 0; width: 150px" src="data:image/jpeg;base64, @photo" />
}
else
{
<h3>NO PHOTO</h3>
<p>Check user profile in Azure Active Directory to add a photo.</p>
}
}
</td>
</tr>
</table>
}

@code {
User user;
string photo;

protected override async Task OnInitializedAsync()
{
try
{
user = await GraphServiceClient.Me.Request().GetAsync();
photo = await GetPhoto();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}

protected async Task<string> GetPhoto()
{
string photo;

try
{
using (var photoStream = await GraphServiceClient.Me.Photo.Content.Request().GetAsync())
{
byte[] photoByte = ((System.IO.MemoryStream)photoStream).ToArray();
photo = Convert.ToBase64String(photoByte);
this.StateHasChanged();
}

}
catch (Exception)
{
photo = null;
}
return photo;
}

}
Loading