๐งฉ The Problem and Flow
A MAUI Hybrid Blazor login can coordinate browser JavaScript, a public-IP check, server access control, authentication, and module navigation. Click any step to jump to the related implementation.
Page renders
โ
OnAfterRenderAsync(firstRender)
โ
Register JavaScript interop
โ
JavaScript calls Blazor
โ
Fetch public IP
โ
API checks IP access
โ
Enable login controls
โ
Authenticate user
โ
sessionStorage stores non-sensitive context
โ
Navigate to the target moduleโ๏ธ Step 1: OnAfterRenderAsync
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await RegisterJs();
await Task.Delay(1000);
isVideoReady = true;
await InvokeAsync(StateHasChanged);
}
}๐ Step 2: Register JavaScript Interop
private DotNetObjectReference<Login_DTVS>? objRef;
protected override void OnInitialized()
{
objRef = DotNetObjectReference.Create(this);
}
private async Task RegisterJs()
{
await JS.InvokeVoidAsync("myInterop.register", objRef);
}
public void Dispose()
{
objRef?.Dispose();
}โ๏ธ Step 3: JavaScript Calls Blazor
window.myInterop = {
dotNetHelper: null,
register: function (dotNetHelper) {
this.dotNetHelper = dotNetHelper;
window.hasregistered = true;
},
callBlazor: function () {
this.dotNetHelper.invokeMethodAsync(
"UpdateMessage", "Hello from JavaScript!"
);
}
};๐ Step 4: Fetch the Public IP
async function getPublicIP() {
const response = await fetch('https://api.ipify.org?format=json');
const data = await response.json();
return data.ip;
}
string res = await JS.InvokeAsync<string>("getPublicIP");
clientIp = res;The request is asynchronous, so the C# caller awaits the JavaScript result.
๐ก๏ธ Step 5: API Checks IP Access
var response = await Http.GetFromJsonAsync<AccessResponse>(
"http://serverip:serverport/Auth/check-access/" + res);
if (response != null)
{
hasFullAccess = response.HasAccess;
clientIp = response.IpAddress;
}โ Step 6: Enable Login Controls
<button class="login-btn"
@onclick="HandleLogin"
disabled="@isLoading || !@hasFullAccess">
Sign in
</button>The UI remains gated until the access decision is available.
๐ Step 7: Authenticate the User
var reqq = new AuthApiService.LoginRequest
{
category = selectedPlant == "1108" ? "E" : "S",
UserID = username,
LoginPwd = password,
IpAddress = clientIp
};
var lr = await AuthService.LoginAsync(reqq);๐พ Step 8: Store Non-Sensitive Context
await JS.InvokeVoidAsync("sessionStorage.setItem", "PlantCode", selectedPlant);
await JS.InvokeVoidAsync("sessionStorage.setItem", "LoginId", username);
await JS.InvokeVoidAsync("sessionStorage.setItem", "CompCode", selectedPlant);Keep only non-sensitive context here. Do not store the password in sessionStorage.
๐ฆ Handling Login Errors
if (!string.IsNullOrWhiteSpace(lr.Error))
{
ToastMessage = lr.Error;
ShowToast = true;
StateHasChanged();
await Task.Delay(3000);
ShowToast = false;
return;
}
๐ง Why Timing Matters
Browser-dependent JavaScript should be coordinated with the component lifecycle. The first OnAfterRenderAsync call provides a reliable point to register the JavaScript bridge, while the API remains responsible for authentication and authorization decisions.
๐งน Cleanup
public void Dispose()
{
objRef?.Dispose();
}
๐ Final Pattern
Blazor owns the UI and orchestration, JavaScript supplies browser capabilities such as public-IP lookup and session storage, and the API handles authentication/access decisions. The interactive flow connects the explanation directly to the relevant code.
Comments will appear here when available.