Skip to content

Commit

Permalink
feat(templates): add support for refresh token #6071 (#6072)
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmoradi authored Nov 22, 2023
1 parent 88693a1 commit e277a6a
Show file tree
Hide file tree
Showing 308 changed files with 3,077 additions and 3,775 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ variables:
APP_SERVICE_NAME: 'app-service-td-test'
AZURE_SERVICE_CONNECTION: 'td-test-service-connection' # https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#azure-resource-manager-service-connection
ConnectionStrings.SqlServerConnectionString: $(DB_CONNECTION_STRING)
AppSettings.JwtSettings.IdentityCertificatePassword: $(API_IDENTITY_CERTIFICATE_PASSWORD)
# ApiServerAddress: 'api/'
AppSettings.IdentitySettings.IdentityCertificatePassword: $(API_IDENTITY_CERTIFICATE_PASSWORD)
ApiServerAddress: 'api/'
# // You can also use absolute urls such as https://todob.bitplatform.dev/api/

jobs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ jobs:
with:
node-version: 18

- name: Update appsettings.json api server address
uses: microsoft/variable-substitution@v1
with:
files: 'src/BlazorWeb.Client/appsettings.json'
env:
ApiServerAddress: ${{ env.API_SERVER_ADDRESS }}

- name: Install wasm
run: cd src && dotnet workload install wasm-tools wasm-experimental

Expand Down Expand Up @@ -84,7 +91,7 @@ jobs:
files: 'appsettings.json'
env:
ConnectionStrings.SqlServerConnectionString: ${{ secrets.DB_CONNECTION_STRING }}
AppSettings.JwtSettings.IdentityCertificatePassword: ${{ secrets.API_IDENTITY_CERTIFICATE_PASSWORD }}
AppSettings.IdentitySettings.IdentityCertificatePassword: ${{ secrets.API_IDENTITY_CERTIFICATE_PASSWORD }}

- name: Delete IdentityCertificate.pfx
run: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
<InvariantGlobalization Condition="'$(MultilingualEnabled)' == 'false'">true</InvariantGlobalization>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace System.Collections.Generic;

public static class IListExtensions
public static class ICollectionExtensions
{
// Basically a Polyfill since we now expose IList instead of List
// which is better but IList doesn't have AddRange
Expand All @@ -20,4 +20,14 @@ public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
list.Add(item);
}
}

public static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items, CancellationToken cancellationToken = default)
{
var results = new List<T>();
await foreach (var item in items.WithCancellation(cancellationToken))
{
results.Add(item);
}
return results;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using BlazorWeb.Shared.Dtos.Identity;

namespace Microsoft.JSInterop;

public static class IJSRuntimeExtensions
{
/// <summary>
/// To disable the scrollbar of the body when showing the modal, so the modal can be always shown in the viewport without being scrolled out.
/// </summary>
public static async Task SetBodyOverflow(this IJSRuntime jsRuntime, bool hidden)
{
await jsRuntime.InvokeVoidAsync("App.setBodyOverflow", hidden);
}

public static async Task GoBack(this IJSRuntime jsRuntime)
{
await jsRuntime.InvokeVoidAsync("App.goBack");
}

public static async Task ApplyBodyElementClasses(this IJSRuntime jsRuntime, List<string> cssClasses, Dictionary<string, string> cssVariables)
{
await jsRuntime.InvokeVoidAsync("App.applyBodyElementClasses", cssClasses, cssVariables);
}

public static async Task SetCookie(this IJSRuntime jsRuntime, string key, string value, long expiresIn, bool rememberMe)
{
await jsRuntime.InvokeVoidAsync("App.setCookie", key, value, expiresIn, rememberMe);
}

public static async Task RemoveCookie(this IJSRuntime jsRuntime, string key)
{
await jsRuntime.InvokeVoidAsync("App.removeCookie", key);
}

public static async Task<string?> GetCookie(this IJSRuntime jsRuntime, string key)
{
return await jsRuntime.InvokeAsync<string?>("App.getCookie", key);
}

public static async Task SetLocalStorage(this IJSRuntime jsRuntime, string key, string value, bool rememberMe)
{
await jsRuntime.InvokeVoidAsync($"window.{(rememberMe ? "localStorage" : "sessionStorage")}.setItem", key, value);
}

public static async Task RemoveLocalStorage(this IJSRuntime jsRuntime, string key)
{
await jsRuntime.InvokeVoidAsync("window.sessionStorage.removeItem", key);
await jsRuntime.InvokeVoidAsync("window.localStorage.removeItem", key);
}

public static async Task<string?> GetLocalStorage(this IJSRuntime jsRuntime, string key)
{
return (await jsRuntime.InvokeAsync<string?>("window.localStorage.getItem", key)) ??
(await jsRuntime.InvokeAsync<string?>("window.sessionStorage.getItem", key));
}

public static async Task StoreAuthToken(this IJSRuntime jsRuntime, TokenResponseDto tokenResponse, bool? rememberMe = null)
{
if (rememberMe is null)
{
rememberMe = string.IsNullOrEmpty(await jsRuntime.InvokeAsync<string?>("window.localStorage.getItem", "refresh_token")) is false;
}

await jsRuntime.SetCookie("access_token", tokenResponse.AccessToken!, tokenResponse.ExpiresIn, rememberMe is true);
await jsRuntime.SetLocalStorage("refresh_token", tokenResponse.RefreshToken!, rememberMe is true);
}

public static async Task RemoveAuthTokens(this IJSRuntime jsRuntime)
{
await jsRuntime.RemoveCookie("access_token");
await jsRuntime.RemoveLocalStorage("refresh_token");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//-:cnd:noEmit
using BlazorWeb.Client.Services.HttpMessageHandlers;
using Microsoft.AspNetCore.Components.WebAssembly.Services;

namespace Microsoft.Extensions.DependencyInjection;

Expand All @@ -9,23 +10,23 @@ public static IServiceCollection AddClientSharedServices(this IServiceCollection
{
services.AddSharedServices();

services.AddCascadingAuthenticationState();
services.AddScoped<IPrerenderStateService, PrerenderStateService>();
services.AddScoped<IExceptionHandler, ExceptionHandler>();
services.AddTransient<IPrerenderStateService, PrerenderStateService>();
services.AddTransient<IExceptionHandler, ExceptionHandler>();
services.AddScoped<IPubSubService, PubSubService>();
services.AddBitBlazorUIServices();

services.AddTransient<LocalizationDelegatingHandler>();
services.AddTransient<RequestHeadersDelegationHandler>();
services.AddTransient<AuthDelegatingHandler>();
services.AddTransient<RetryDelegatingHandler>();
services.AddTransient<ExceptionDelegatingHandler>();
services.AddTransient<HttpClientHandler>();

services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
services.AddScoped<IAuthenticationService, AuthenticationService>();
services.AddScoped(sp => (AppAuthenticationStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());

services.AddScoped<MessageBoxService>();
services.AddTransient<MessageBoxService>();

services.AddTransient<LazyAssemblyLoader>();

return services;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@

<div class="form-input-container">
<BitLabel For="catColorInput">@Localizer[nameof(AppStrings.Color)]</BitLabel>
<div class="color-container">
@foreach (var color in new[] { "#FFCD56", "#FF6384", "#4BC0C0", "#FF9124", "#2B88D8", "#C7E0F4" })
<div class="color-container">
@foreach (var color in new[] { "#FFCD56", "#FF6384", "#4BC0C0", "#FF9124", "#2B88D8", "#C7E0F4" })
{
<button @onclick="() => SetCategoryColor(color)"
class="color-btn @(_category.Color == color ? "color-btn--active" : null)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private void PrepareGridDataProvider()

var data = await HttpClient.GetFromJsonAsync(url, AppJsonContext.Default.PagedResultCategoryDto) ?? new();

return BitDataGridItemsProviderResult.From(data.Items!, (int)data.TotalCount);
return BitDataGridItemsProviderResult.From(await data.Items!.ToListAsync()!, (int)data.TotalCount);
}
catch (Exception exp)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ private async Task DeleteAccount()
{
await HttpClient.DeleteAsync("User/Delete");

await AuthenticationService.SignOut();
await JSRuntime.RemoveAuthTokens();

await AuthenticationStateProvider.RaiseAuthenticationStateHasChanged();

await CloseModal();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

<PageTitle>@Localizer[nameof(AppStrings.EditProfileTitle)]</PageTitle>

<div class="page-container">
<div class="content-container profile-panel">
@if (string.IsNullOrEmpty(_editProfileMessage) is false)
<div class="page-container">
<div class="content-container profile-panel">
@if (string.IsNullOrEmpty(_editProfileMessage) is false)
{
<BitMessageBar Class="form-message-bar"
MessageBarType="@_editProfileMessageType"
Expand Down Expand Up @@ -45,16 +45,16 @@
}

<BitLabel>@Localizer[nameof(AppStrings.ProfileImage)]</BitLabel>
<BitFileUpload @onfocus="() => _profileImageError = null"
Label="@Localizer[nameof(AppStrings.UploadNewProfileImage)]"
Accept="image/*"
AutoUploadEnabled="true"
UploadUrl="@_profileImageUploadUrl"
OnUploadComplete="WrapHandled(RefreshProfileData)"
OnUploadFailed="() => _profileImageError = Localizer[nameof(AppStrings.FileUploadFailed)]"
MaxSize="1024 * 1024 * 10"
ChunkedUploadEnabled="false" />
<div class="form-input-error">@_profileImageError</div>
<BitFileUpload @onfocus="() => _profileImageError = null"
Label="@Localizer[nameof(AppStrings.UploadNewProfileImage)]"
Accept="image/*"
AutoUploadEnabled="true"
UploadUrl="@_profileImageUploadUrl"
OnUploadComplete="WrapHandled(RefreshProfileData)"
OnUploadFailed="() => _profileImageError = Localizer[nameof(AppStrings.FileUploadFailed)]"
MaxSize="1024 * 1024 * 10"
ChunkedUploadEnabled="false" />
<div class="form-input-error">@_profileImageError</div>
</div>

<EditForm Model="_userToEdit" OnValidSubmit="WrapHandled(DoSave)" class="edit-profile-form">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,14 @@ protected override async Task OnInitAsync()

private async Task LoadEditProfileData()
{
_user = await PrerenderStateService.GetValue($"{nameof(EditProfilePage)}-{nameof(_user)}", GetCurrentUser) ?? new();
_user = await GetCurrentUser() ?? new();

UpdateEditProfileData();
}

private async Task RefreshProfileData()
{
_user = await GetCurrentUser() ?? new();

UpdateEditProfileData();
await LoadEditProfileData();

PubSubService.Publish(PubSubMessages.PROFILE_UPDATED, _user);
}
Expand All @@ -64,8 +62,7 @@ private void UpdateEditProfileData()
_userToEdit.BirthDate = _user.BirthDate;
}

private Task<UserDto?> GetCurrentUser() => HttpClient.GetFromJsonAsync("User/GetCurrentUser", AppJsonContext.Default.UserDto);

private Task<UserDto?> GetCurrentUser() => PrerenderStateService.GetValue($"{nameof(EditProfilePage)}-{nameof(_user)}", () => HttpClient.GetFromJsonAsync("User/GetCurrentUser", AppJsonContext.Default.UserDto));

private async Task DoSave()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

<PageTitle>@Localizer[nameof(AppStrings.EmailConfirmationTitle)]</PageTitle>

<div class="page-container">
<div class="form">
@if (string.IsNullOrEmpty(_emailConfirmationMessage) is false)
<div class="page-container">
<div class="form">
@if (string.IsNullOrEmpty(_resendLinkErrors ?? Errors) is false)
{
<BitMessageBar Class="form-message-bar"
MessageBarType="@_emailConfirmationMessageType"
OnDismiss="() => _emailConfirmationMessage = null">
@_emailConfirmationMessage
OnDismiss="() => _resendLinkErrors = Errors = null">
@(_resendLinkErrors ?? Errors)
</BitMessageBar>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
public partial class EmailConfirmationPage
{
private bool _isLoading;
private string? _emailConfirmationMessage;
private BitMessageBarType _emailConfirmationMessageType;
private string? _resendLinkErrors;
private BitMessageBarType _emailConfirmationMessageType = BitMessageBarType.Error;

[Parameter]
[SupplyParameterFromQuery]
public string? Email { get; set; }
[SupplyParameterFromQuery, Parameter] public string? Email { get; set; }

[Parameter]
[SupplyParameterFromQuery(Name = "email-confirmed")]
public bool EmailConfirmed { get; set; }
/// <summary>
/// Email confirmation errors populated by api/Identity/ConfirmEmail endpoint.
/// </summary>
[SupplyParameterFromQuery, Parameter] public string? Errors { get; set; }

[SupplyParameterFromQuery(Name = "email-confirmed"), Parameter] public bool EmailConfirmed { get; set; }

private void RedirectToSignIn()
{
Expand All @@ -24,21 +25,21 @@ private async Task DoResendLink()
if (_isLoading) return;

_isLoading = true;
_emailConfirmationMessage = null;
_resendLinkErrors = Errors = null;

try
{
await HttpClient.PostAsJsonAsync("Auth/SendConfirmationEmail", new() { Email = Email }, AppJsonContext.Default.SendConfirmationEmailRequestDto);
await HttpClient.PostAsJsonAsync("Identity/SendConfirmationEmail", new() { Email = Email }, AppJsonContext.Default.SendConfirmationEmailRequestDto);

_emailConfirmationMessageType = BitMessageBarType.Success;

_emailConfirmationMessage = Localizer[nameof(AppStrings.ResendConfirmationLinkMessage)];
_resendLinkErrors = Localizer[nameof(AppStrings.ResendConfirmationLinkMessage)];
}
catch (KnownException e)
{
_emailConfirmationMessageType = BitMessageBarType.Error;

_emailConfirmationMessage = e.Message;
_resendLinkErrors = e.Message;
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

<PageTitle>@Localizer[nameof(AppStrings.ForgetPasswordTitle)]</PageTitle>

<div class="page-container">
<EditForm Model="_forgotPasswordModel" OnValidSubmit="WrapHandled(DoSubmit)" class="form">
<AppDataAnnotationsValidator />
<div class="page-container">
<EditForm Model="_forgotPasswordModel" OnValidSubmit="WrapHandled(DoSubmit)" class="form">
<AppDataAnnotationsValidator />

@if (string.IsNullOrEmpty(_forgotPasswordMessage) is false)
@if (string.IsNullOrEmpty(_forgotPasswordMessage) is false)
{
<BitMessageBar Class="form-message-bar"
MessageBarType="@_forgotPasswordMessageType"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ private async Task DoSubmit()

try
{
await HttpClient.PostAsJsonAsync("Auth/SendResetPasswordEmail", _forgotPasswordModel, AppJsonContext.Default.SendResetPasswordEmailRequestDto);
await HttpClient.PostAsJsonAsync("Identity/SendResetPasswordEmail", _forgotPasswordModel, AppJsonContext.Default.SendResetPasswordEmailRequestDto);

_forgotPasswordMessageType = BitMessageBarType.Success;

Expand Down
Loading

0 comments on commit e277a6a

Please sign in to comment.