Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
147016 stories
·
33 followers

Securing Aspire Apps with Microsoft Entra ID

1 Share

About the Author

This is a guest post from Jean-Marc Prieur from Microsoft’s 1ES (One Engineering System) team. We help reduce developer toil across Microsoft by making it easy to build services that are secure and compliant from the start, for instance with Aspire and Entra ID.

TL;DR: When you create an Aspire app with aspire new aspire-starter, you get a Blazor frontend and API backend—but no authentication. This post shows two ways to fix that: the quick way (using AI skills with GitHub Copilot) and the detailed way (understanding every line of code).

The problem

You’ve just created your first Aspire application:

aspire new aspire-starter --name MyService

You’ve got a beautiful Blazor frontend talking to an API backend, all orchestrated by Aspire. But there’s a problem: anyone can access your API. No authentication. No authorization.

The questions pile up:

  • How do I sign users in?
  • How do I protect my APIs?
  • How do I pass tokens between services?
  • How does this work with Aspire’s service discovery?

The quick way: AI skills (5 minutes)

If you’re using GitHub Copilot, Claude, or another AI coding assistant, you can add Entra ID authentication in minutes using AI skills—an open standard for sharing domain-specific knowledge with AI assistants.

Overview

Watch the live demo of the Aspire+Entra ID agent skills from February 3rd Microsoft Reactor episode How Microsoft uses Agentic AI to accelerate software delivery.

How Microsoft uses Agentic AI to accelerate software delivery

If you want to try yourself:

Step 1: Add the skills to your repo

# From your repo root
mkdir -p .github/skills # or .copilot/skills or what your coding agent understand
cd .github/skills

# Download the skills (or copy the  repo)
curl -O https://aka.ms/msidweb/aspire/entra-id-code-skill
curl -O https://aka.ms/msidweb/aspire/entra-id-provisioning-skill

Or simply copy the entra-id-aspire-authentication and entra-id-aspire-provisioning folders from the Entra ID aspire skills on github.

Step 2: Ask your AI assistant

Open GitHub Copilot Chat and say:

You: “Add Entra ID authentication to my Aspire app”

Copilot will detect the skill and walk you through it:

Copilot: “I detected your Aspire solution with:

  • Web App (Blazor): MyService.Web
  • API: MyService.ApiService

I’ll implement authentication in two phases:

Phase 1 (now): Add authentication code with placeholder values. The app will build but won’t run until app registrations are configured.

Phase 2 (after): Use the provisioning skill to create Entra ID app registrations.

Ready to proceed?”

You: “Yes”

Copilot then:

  1. Adds Microsoft.Identity.Web packages
  2. Updates appsettings.json with Azure AD config (placeholders)
  3. Updates Program.cs in both projects
  4. Copies helper files for login/logout and Blazor auth handling
  5. Creates the login button UI component
  6. Adds proper exception handling for Conditional Access

Step 3: Provision app registrations

Copilot: “I’m now ready to provision the Entra ID apps. Shall I proceed?”

You: “Yes please!”

Copilot: “I’ll use Microsoft Graph PowerShell to create the app registrations. First, let me check your tenant connection…”

Copilot runs PowerShell commands to:

  1. Create the API app registration with access_as_user scope
  2. Create the Web app registration with redirect URIs
  3. Generate a client secret and store it in user-secrets
  4. Update your appsettings.json with real values

Step 4: Run and test

aspire run

Open the Aspire dashboard, navigate to your Blazor app, and click Login. That’s it! 🎉


What just happened?

The skills automated what would take at least 30-60 minutes manually, or even days without the detailed instructions:

Aspire app architecture

The flow:

  1. User visits Blazor app → redirected to Entra ID
  2. User signs in → app receives tokens + establishes session
  3. User requests data → MicrosoftIdentityMessageHandler automatically acquires access token
  4. Token attached to API call → API validates JWT → returns data

The detailed way: understanding the code

Want to know what the skills actually did? Or prefer to implement manually? Here’s the simplified breakdown. The full article is available from Manually add Entra ID authentication and authorization to an Aspire App

Files modified

Project File Changes
ApiService Program.cs JWT Bearer auth, authorization middleware
appsettings.json Azure AD configuration
Web Program.cs OIDC auth, token acquisition, message handler
appsettings.json Azure AD config, downstream API scopes
LoginLogoutEndpointRouteBuilderExtensions.cs Login/logout with incremental consent (copy from skill)
BlazorAuthenticationChallengeHandler.cs Auth challenge handler (copy from skill)
Components/UserInfo.razor Login button UI (new)

Part 1: Protecting the API

Let’s start with the API since it’s simpler. We need to validate incoming JWT tokens.

Add Microsoft.Identity.Web to the API

cd MyService.ApiService
dotnet add package Microsoft.Identity.Web

Configure Azure AD settings

Add to appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_API_CLIENT_ID",
    "Audiences": ["api://YOUR_API_CLIENT_ID"]
  }
}

Wire up authentication

Update Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// 👇 Add this: JWT Bearer authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization();

var app = builder.Build();

// 👇 Add middleware (order matters!)
app.UseAuthentication();
app.UseAuthorization();

// 👇 Protect your endpoints
app.MapGet("/weatherforecast", () =>
{
    // ... your logic
})
.RequireAuthorization();

app.Run();

That’s it for the API. Without a valid token, callers get a 401.

Part 2: Blazor frontend authentication

This is where the magic happens. We need to:

  1. Sign users in with OIDC
  2. Acquire access tokens for the API
  3. Automatically attach tokens to HTTP requests

Add Microsoft.Identity.Web

cd MyService.Web
dotnet add package Microsoft.Identity.Web

Configure Azure AD + downstream API

Add to appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com",
    "Domain": "yourtenant.onmicrosoft.com",
    "TenantId": "YOUR_TENANT_ID",
    "ClientId": "YOUR_WEB_APP_CLIENT_ID",
    "CallbackPath": "/signin-oidc",
    "ClientCredentials": [
      {
        "SourceType": "ClientSecret",
        "ClientSecret": "YOUR_SECRET"
      }
    ]
  },
  "WeatherApi": {
    "Scopes": ["api://YOUR_API_CLIENT_ID/.default"]
  }
}

⚠ Production tip: Use managed identity or certificates instead of client secrets. See the certificateless authentication guide.

Wire up authentication + token acquisition

Here’s the full Program.cs:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// 1⃣ OIDC authentication + token acquisition
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

builder.Services.AddCascadingAuthenticationState();
builder.Services.AddRazorComponents().AddInteractiveServerComponents();

// 👇 Handles incremental consent & Conditional Access in Blazor Server
builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>();

// 2⃣ HttpClient with automatic token attachment
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
    // Aspire service discovery! 🎉
    client.BaseAddress = new("https+http://apiservice");
})
.AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi"));

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();

app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
app.MapGroup("/authentication").MapLoginAndLogout();

app.Run();

The magic: MicrosoftIdentityMessageHandler

The key insight is .AddMicrosoftIdentityMessageHandler(). This DelegatingHandler:

  1. Intercepts outgoing HTTP requests
  2. Checks the token cache for a valid access token
  3. Silently acquires a new token if needed (using refresh token)
  4. Attaches the Authorization: Bearer header
  5. Handles Conditional Access challenges automatically

You write normal HttpClient code. Tokens just appear. ✨

📚 Deep dive: MicrosoftIdentityMessageHandler documentation

public class WeatherApiClient(HttpClient httpClient)
{
    public async Task<WeatherForecast[]?> GetForecastAsync()
    {
        // No token handling code needed!
        return await httpClient.GetFromJsonAsync<WeatherForecast[]>("/weatherforecast");
    }
}

The Aspire advantage

Notice how we’re using Aspire’s service discovery:

client.BaseAddress = new("https+http://apiservice");

At runtime, Aspire resolves "apiservice" to the actual endpoint. This works seamlessly whether you’re running:

  • Locally with the Aspire dashboard
  • In Docker containers
  • In Kubernetes
  • In Azure Container Apps

Zero hardcoded URLs. Zero environment-specific config.

Adding login/logout UI

The login/logout endpoints need to support incremental consent and Conditional Access. Copy these helper files from the skill folder or use the code below:

// LoginLogoutEndpointRouteBuilderExtensions.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace Microsoft.Identity.Web;

public static class LoginLogoutEndpointRouteBuilderExtensions
{
    public static IEndpointConventionBuilder MapLoginAndLogout(
        this IEndpointRouteBuilder endpoints)
    {
        var group = endpoints.MapGroup("");

        // Enhanced login with incremental consent support
        group.MapGet("/login", (
            string? returnUrl,
            string? scope,      // For incremental consent
            string? loginHint,  // Pre-fill username
            string? domainHint, // Skip home realm discovery
            string? claims) =>  // Conditional Access
        {
            var properties = GetAuthProperties(returnUrl);

            if (!string.IsNullOrEmpty(scope))
            {
                var scopes = scope.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                properties.SetParameter(OpenIdConnectParameterNames.Scope, scopes);
            }
            if (!string.IsNullOrEmpty(loginHint))
                properties.SetParameter(OpenIdConnectParameterNames.LoginHint, loginHint);
            if (!string.IsNullOrEmpty(domainHint))
                properties.SetParameter(OpenIdConnectParameterNames.DomainHint, domainHint);
            if (!string.IsNullOrEmpty(claims))
                properties.Items["claims"] = claims;

            return TypedResults.Challenge(properties, [OpenIdConnectDefaults.AuthenticationScheme]);
        }).AllowAnonymous();

        group.MapPost("/logout", async (HttpContext context) =>
        {
            string? returnUrl = null;
            if (context.Request.HasFormContentType)
            {
                var form = await context.Request.ReadFormAsync();
                returnUrl = form["ReturnUrl"];
            }
            return TypedResults.SignOut(GetAuthProperties(returnUrl),
                [CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme]);
        }).DisableAntiforgery();

        return group;
    }

    private static AuthenticationProperties GetAuthProperties(string? returnUrl)
    {
        const string pathBase = "/";
        if (string.IsNullOrEmpty(returnUrl)) returnUrl = pathBase;
        else if (returnUrl.StartsWith("//", StringComparison.Ordinal)) returnUrl = pathBase;
        else if (!Uri.IsWellFormedUriString(returnUrl, UriKind.Relative)) returnUrl = new Uri(returnUrl, UriKind.Absolute).PathAndQuery;
        else if (returnUrl[0] != '/') returnUrl = $"{pathBase}{returnUrl}";
        return new AuthenticationProperties { RedirectUri = returnUrl };
    }
}

Then add a Blazor component for the login button (Components/UserInfo.razor):

@using Microsoft.AspNetCore.Components.Authorization

<AuthorizeView>
    <Authorized>
        <span>Hello, @context.User.Identity?.Name</span>
        <form action="/authentication/logout" method="post">
            <AntiforgeryToken />
            <button type="submit">Logout</button>
        </form>
    </Authorized>
    <NotAuthorized>
        <a href="/authentication/login?returnUrl=/">Login</a>
    </NotAuthorized>
</AuthorizeView>

Handling consent & Conditional Access

⚠ This is critical for Blazor Server! You must handle exceptions on pages that call APIs.

The BlazorAuthenticationChallengeHandler (copy from skill folder) handles MicrosoftIdentityWebChallengeUserException. Use it on every page calling a protected API:

@page "/weather"
@attribute [Authorize]
@inject WeatherApiClient WeatherApi
@inject BlazorAuthenticationChallengeHandler ChallengeHandler

@code {
    private WeatherForecast[]? forecasts;
    private string? errorMessage;

    protected override async Task OnInitializedAsync()
    {
        if (!await ChallengeHandler.IsAuthenticatedAsync())
        {
            await ChallengeHandler.ChallengeUserWithConfiguredScopesAsync("WeatherApi:Scopes");
            return;
        }

        try
        {
            forecasts = await WeatherApi.GetForecastAsync();
        }
        catch (Exception ex)
        {
            // Handles incremental consent / Conditional Access
            if (!await ChallengeHandler.HandleExceptionAsync(ex))
            {
                errorMessage = $"Error: {ex.Message}";
            }
        }
    }
}

Common scenarios

Protecting Blazor pages

@page "/weather"
@attribute [Authorize]

<!-- Only authenticated users see this -->

Scope validation in the API

Ensure tokens have specific scopes:

app.MapGet("/sensitive-data", () => { /* ... */ })
    .RequireAuthorization()
    .RequireScope("data.read");

Service-to-service (no user)

For daemon scenarios:

.AddMicrosoftIdentityMessageHandler(options =>
{
    options.Scopes.Add("api://client-id/.default");
    options.RequestAppToken = true; // App-only token
});

Per-request scope override

var request = new HttpRequestMessage(HttpMethod.Get, "/admin-endpoint")
    .WithAuthenticationOptions(options =>
    {
        options.Scopes.Clear();
        options.Scopes.Add("api://client-id/admin.write");
    });

Troubleshooting tips

Symptom Check This
401 Unauthorized Are scopes correct? Does the token audience match?
OIDC redirect fails Is /signin-oidc in your app registration’s redirect URIs?
Token not attached Is AddMicrosoftIdentityMessageHandler on the HttpClient?
AADSTS65001 Admin consent needed—grant it in Azure Portal
No login button Is UserInfo.razor included in your layout?
404 on /MicrosoftIdentity/Account/Challenge Use BlazorAuthenticationChallengeHandler instead of old MicrosoftIdentityConsentHandler
Consent loop Add try/catch with HandleExceptionAsync on all API-calling pages

Pro tip: Enable logging to see token acquisition details:

builder.Services.AddLogging(options =>
{
    options.AddFilter("Microsoft.Identity", LogLevel.Debug);
});

Production checklist

Before going live:

  • [ ] Replace client secrets with managed identity or certificates
  • [ ] Configure distributed token cache (Redis, SQL) instead of in-memory
  • [ ] Set up proper redirect URIs for all deployment environments
  • [ ] Grant admin consent for required scopes
  • [ ] Enable Conditional Access policies as needed
  • [ ] Ensure BlazorAuthenticationChallengeHandler is used.
  • [ ] Add try/catch with HandleExceptionAsync on all pages calling APIs

Conclusion

Securing Aspire apps with Entra ID has never been easier:

🚀 The quick way: Use the AI skills with GitHub Copilot or Claude. Add authentication in 5 minutes with a conversation.

📖 The detailed way: Understand every line by following the manual implementation guide.

Either way, you get:

  • API protection: JWT Bearer authentication with 5 lines of code
  • User sign-in: Standard OIDC with automatic token acquisition
  • Service calls: Tokens attached automatically via MicrosoftIdentityMessageHandler
  • Aspire integration: Works seamlessly with service discovery

The combination of Aspire’s orchestration, Microsoft.Identity.Web’s auth handling, and AI skills means you can go from zero to authenticated in minutes.

📖 Full guide: Aspire Integration Guide — Comprehensive documentation with all configuration options and advanced scenarios.


Resources


Summary

You’ve learned two ways to secure your Aspire apps with Entra ID:

  1. The quick way — AI skills automate the entire process
  2. The detailed way — Understand exactly what’s happening under the hood

Try the AI skills in your next project! Have questions? Found an issue? Let me know in the comments or open an issue on the repo!

The post Securing Aspire Apps with Microsoft Entra ID appeared first on Aspire Blog.

Read the whole story
alvinashcraft
14 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

.NET Framework 3.5 Moves to Standalone Deployment in new versions of Windows

1 Share

The deployment model of .NET Framework 3.5 for future versions of Windows is changing. Starting with Windows 11 Insider Preview Build 27965, .NET Framework 3.5 must be obtained as a standalone installer and is no longer included as an optional Windows component.

.NET Framework 3.5 is now available as a standalone installer for legacy applications that require it on newer major versions of Windows.

This change applies to Insider Preview Build 27965 and future platform releases of Windows. It does not affect Windows 10 or earlier Windows 11 releases through 25H2.

This change aligns with the product’s lifecycle as .NET Framework 3.5 approaches end of support on January 9, 2029, and encourages customers to begin planning migrations to newer supported versions of .NET.

If your application depends on .NET Framework 3.5, we’ve published detailed guidance—including installers, compatibility notes, and recommended migration paths—on Microsoft Learn.

The post .NET Framework 3.5 Moves to Standalone Deployment in new versions of Windows appeared first on .NET Blog.

Read the whole story
alvinashcraft
20 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Replicating a Valentine-Inspired Login UI in .NET MAUI

1 Share

Learn how to build a Valentine-inspired login interface with .NET MAUI. We’ll go through the process step by step!

We find love in many forms: in the people around us, in that hobby we enjoy so much, in wearing clothes that make us feel good … and also in every step of our professional growth.

Yes, even in that love for creating beautiful, functional and well-thought-out user interfaces using XAML.

For many, designing UI can be a challenge. But like everything in development, that challenge is overcome by learning, practicing and daring to create. That’s why in this article we’re going to build a Valentine’s-inspired UI, taking as reference a design found on Dribbble and adapting it into an implementation using .NET MAUI.

We’ll build step by step a registration and a login screen. We’ll break down each section with its corresponding code blocks, so you can walk away not only with a visually appealing interface, but also with practical ideas you can apply to your own projects.

So get ready, because today we’re putting a little love into code. ❤️

Explanation & Insights

Before we get started, let’s go over a few key points that will help you better understand and replicate the UI:

  • Visual guide: Before we begin, I’ll share a preview of the UI we want to achieve. This image will be divided into color-coded blocks to make it easier to identify each section throughout the article.
  • Code blocks: Each block will be explained separately. Every section will include a textual explanation, a code snippet with the exact code you need to write to achieve the result, and a screenshot showing how the UI evolves step by step.
  • No styles: For the moment, we won’t use styles. This way, even developers who are just starting to build UIs can follow along more easily and understand the core structure without extra complexity. ❤️

Breaking Down the Dribbble Design into Blocks

To make this easier to follow, I’ve divided the original design into clear blocks. We’ll build each part one at a time, in the order you see below.

Main Structure

To create the first screen, we’ll start by creating a file called SignInPage.xaml. This file will contain the elements corresponding to blocks one and two of the block-based structure shown earlier.

Before we begin adding elements, it’s important to define an efficient structure, and this is where the layout comes into play. Choosing the right layout allows us to organize elements more easily within the screen. Since this is a simple example with a single vertical column of elements, we’ll use VerticalStackLayout as the main layout.

The VerticalStackLayout allows the elements to be vertically centered. Additionally, I added 30 of horizontal padding and a spacing of 40 between all the elements contained on this screen.

<ScrollView> 
    <VerticalStackLayout 
    VerticalOptions="Center" 
    Padding="30,0" 
    Spacing="40">
     
    <!-- Add all the code here →
     
    </VerticalStackLayout> 
</ScrollView>

Tip: We’ll also wrap this layout inside a ScrollView, so that on devices with smaller screens, users can scroll through the content without it being cut off or overflowing the screen.

If you want to know more information about VerticalStackLayout, I invite you to read the article Exploring Layout Options in .NET MAUI.

Adding a background to the ContentPage: To give the page a uniform background color, we add the BackgroundColor property to the ContentPage and assign it the value #FFF8F8.

Header Block

Now we’re ready! Let’s start building the first block.

Before creating a UI, I like to analyze all the elements that make it up. This helps me clearly identify which components I’ll need and avoid having to rewrite code later on.

In this first block, the elements we see on the screen are:

  • A horizontally centered image → we’ll use an <Image> component
  • A red title → we’ll use a <Label>
  • A description or subtitle in black → we’ll use a <Label>

Let’s start by adding the image

<Image 
    Source="heart" 
    HeightRequest="100" 
    Aspect="AspectFit" 
    SemanticProperties.Description="Header image" />

<!-- Add the next code block below -->

Here are a few important points to highlight:

AspectFit: We added the image using the Aspect property with the value AspectFit, which allows the image to fit entirely within the available display area without being distorted. You can learn more about the differences between the various image aspect options in this article.

SemanticProperties: We added a semantic description, which helps screen readers understand what element is being displayed on the screen. You can read more about this topic in the article Creating Accessible Apps with Semantic Properties in .NET MAUI.

Let’s continue adding the Label with the title:

<Label 
    Text="Sign In" 
    FontSize="20" 
    FontAttributes="Bold" 
    TextColor="#d14361" 
    SemanticProperties.Description="Sign In" /> 
<!-- Add the next code block below -->

And to finish this first block, let’s add the description or subtitle:

<Label 
    Text=”Welcome! Please sign in to access your account and explore a world of possibilities." 
    TextColor="#121111" 
    FontSize="15" 
    SemanticProperties.Description="Welcome!"/> 
<!-- Add the next code block below -->

At this point, your UI should look similar to the following example:

Sign-in Content Block

Continuing with the second block, let’s take a look at the elements that make up this component:

  • Two input fields: One for the username and one for the password
  • Two buttons: The first with the fields, “Login”; and the second for “Create an account”
  • Two labels: One for the “Forgot your password” message and another for “Don’t have an account”

Let’s start with the two fields for the username and password

Here, in addition to the username Entry, we add a password Entry and set the IsPassword property to True so the characters are hidden as the user types.

<Entry Placeholder="Username"/> 
<Entry IsPassword="True" Placeholder="Password"/>
<!-- Add the next code block below -->

The Login Button

<Button BackgroundColor="#d14361" 
    CornerRadius="50" 
    FontAttributes="Bold" 
    HeightRequest="50" 
    Text="LOGIN"/>
<!-- Add the next code block below -->

Forgot Your Password Label

This text is composed of two different styles, and since a simple <Label> cannot handle multiple styles at the same time, we’ll use <Label.FormattedText> to achieve this.

<Label> 
    <Label.FormattedText> 
    <FormattedString> 
    <Span Text="Forgot password? " TextColor="#555351" /> 
    <Span Text=" Reset here" TextColor="#d14361" FontAttributes="Bold"/> 
    </FormattedString> 
    </Label.FormattedText> 
</Label> 
<!-- Add the next code block below -->

Don’t Have an Account Label

<Label Text=" Don't have an account?" 
    HorizontalOptions="Center" 
    TextColor="Gray"/> 
<!-- Add the next code block below -->

Create an Account Button

<Button BackgroundColor="#9A8687" 
    CornerRadius="50" 
    HeightRequest="50" 
    FontAttributes="Bold" 
    Text="CREATE AN ACCOUNT"/>

And that’s it! We’ve completed this second section, which means the first screen is now finished. At this point, your result should look like the following:

Create Account Page

We’ve reached the final stretch! And the last screen!

Since this is a new screen, we’ll create a file called CreateAccountPage.xaml. Just like on the previous screen, we’ll use a VerticalStackLayout as the main layout.

<ScrollView> 
    <VerticalStackLayout 
    VerticalOptions="Center" 
    Padding="30,0" 
    Spacing="40"> 
    
    <!-- Add all the code here →
     
    </VerticalStackLayout> 
</ScrollView>

✍️ Here, we will also add the BackgroundColor property to the ContentPage, setting its value to #FFF8F8.

This screen contains several elements—let’s start with the title and the description labels:

<Label 
    Text="Create an account" 
    FontSize="20" 
    FontAttributes="Bold" 
    TextColor="#d14361"/> 
    
<Label 
    Text="Join us on an exciting journey! Create your account and embark on a path filled with discovery and opportunities." 
    TextColor="#121111" 
    FontSize="15"/>

<!-- Add the next code block below -->

Let’s continue with the username, email and password input fields. To keep this example simple, we won’t add a show/hide password feature.

<Entry Placeholder="Username"/>

<Entry Placeholder="Email"/>

<Entry IsPassword="True" Placeholder="Password"/>

Finally, let’s add a button, text for the terms and conditions, and another button to navigate to the Sign Up action.

<Button BackgroundColor="#d14361" 
    CornerRadius="50" 
    FontAttributes="Bold" 
    HeightRequest="50" 
    Text="SIGN UP"/>
 
<Label> 
    <Label.FormattedText> 
    <FormattedString> 
    <Span Text="By tapping 'Sign In' you accept out " TextColor="#555351" /> 
    <Span Text=" terms" TextColor="#d14361" FontAttributes="Bold"/> 
    <Span Text=" and " TextColor="#555351" /> 
    <Span Text=" conditions" TextColor="#d14361" FontAttributes="Bold"/> 
    </FormattedString> 
    </Label.FormattedText> 
</Label>
 
<Label> 
    <Label.FormattedText> 
    <FormattedString> 
    <Span Text="Already have an account? " TextColor="#555351" /> 
    <Span Text=" Sign Up here" TextColor="#d14361" FontAttributes="Bold"/> 
    </FormattedString> 
    </Label.FormattedText> 
</Label>

And the final result should look like the following:

With these simple steps, you’ve learned how to work with various .NET MAUI components and have successfully built your first two screens!

Conclusion

And that’s it! In this article, we explored how to build a Valentine-inspired login and sign-up UI using .NET MAUI with XAML. By breaking the UI into clear blocks and working step by step, you were able to focus on layout, and components without adding unnecessary complexity.

Remember, creating beautiful interfaces is not just about visuals—it’s about crafting experiences users will enjoy interacting with.

If you have any questions or would like me to dive deeper into specific topics, feel free to leave a comment—I’ll be happy to help!

See you in the next article! ‍♀️

Read the whole story
alvinashcraft
25 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

How can I prevent the user from changing the widths of ListView columns in version 5 of the common controls?

2 Shares

Last time, we saw how to prevent the user from changing the widths of ListView columns, but the technique required version 6 of the common controls. What if you’re stuck in the dark ages and have to use version 5?

You can deny the ability to change the width of a header item by listening for HDN_ITEM­CHANGING and returning 1 to deny the change if there is a change to the width.

case WM_NOTIFY:
    {
        auto hdr = (NMHDR*)lParam;
        if (hdr->code == HDN_ITEMCHANGING) {
            auto header = (NMHEADER*)lParam;
            if (header->pitem->mask & HDI_WIDTH) {
                return 1;
            }
        }
    }
    return 0;

The above code assumes that it is running in a window procedure. If it’s running in a dialog procedure, then you need to set the dialog message result.

case WM_NOTIFY:
    {
        auto hdr = (NMHDR*)lParam;
        if (hdr->code == HDN_ITEMCHANGING) {
            auto header = (NMHEADER*)lParam;
            if (header->pitem->mask & HDI_WIDTH) {
                SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1);
                return TRUE;                              
            }
        }
    }
    return FALSE;

Note that if somebody tries to change both the width and the text, this will reject the entire change. There is, unfortunately, no way to selectively reject the change: Modifications to header->pitem->mask are ignored.¹

However, all is not lost. Even though changes to the mask are ignored, changes to the pitem->cxy are still honored, so we can just set the width back to whatever the width is right now.

case WM_NOTIFY:
    {
        auto hdr = (NMHDR*)lParam;
        if (hdr->code == HDN_ITEMCHANGING) {
            auto header = (NMHEADER*)lParam;
            if (header->pitem->mask & HDI_WIDTH) {
                HDITEM item;                                             
                item.mask = HDI_WIDTH;                                   
                if (Header_GetItem(nm->hdr.hwndFrom, nm->iItem, &item)) {
                {                                                        
                    header->pitem->cxy = item.cxy;                       
                }                                                        
            }
        }
    }
    return 0;

One thing we haven’t fixed, though, is that the mouse cursor changes to a resize cursor when it is on the border between two column headers, even though resizing has been disabled. We’ll try to fix that next time.

¹ This is arguably a bug in the version 5 header control, but there’s no point trying to fix it now. There may be code that relies on the fact that changes to the mask have no effect, and besides, this is the old and busted version 5 control.²

² The version 6 control has the same bug, but again, there’s no point trying to fix it now because it will almost certainly break someone. The version 6 common controls are 25 years old, and it’s probably safe to assume that every possible change will probably break someone

³ Once, I helped fixed a memory leak in the common controls, but we had to back it out because it broke a major application. We couldn’t figure out why it broke the program, so we couldn’t put together a shim. We just had to restore the leak. My guess is that the developers of the program had discovered the leak on their own and was somehow working around it, and our fix broke their workaround.

The post How can I prevent the user from changing the widths of ListView columns in version 5 of the common controls? appeared first on The Old New Thing.

Read the whole story
alvinashcraft
33 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Does code obfuscation affect performance? What developers need to know

1 Share

The digital landscape offers unprecedented opportunities but also presents increasing security challenges. Studies reveal that a new cyberattack occurs every 39 seconds, with many targeting software vulnerabilities. For developers, protecting code against tampering, reverse engineering, and unauthorized access is critical to maintaining security and intellectual property.

Code obfuscation has become an integral defense strategy. By making source code harder to understand, obfuscation slows down attackers and shields sensitive information. But does this added layer of security come at the cost of performance? Let’s explore the methods, benefits, and tradeoffs of code obfuscation to understand its role in secure development.

What Is Code Obfuscation?

Code obfuscation is a security technique that transforms code into a more complex and less readable form while maintaining its functionality. The goal is to make the code difficult for attackers to analyze and exploit. Leaving code unprotected exposes applications to reverse engineering and intellectual property theft, leading to consequences like financial loss and reputation damage.

Common obfuscation methods include techniques developers use to create barriers that increase the time and resources required for reverse engineering.:

  • Renaming: Disguises variables, methods, and class names without affecting functionality.
  • Control Flow Obfuscation: Alters the logical flow of a program, making it harder to deduce its intent.
  • String Encryption: Encrypts readable strings in the code, decrypting them when needed at runtime.
  • Data Obfuscation: Protects sensitive data within the application by encrypting or aggregating it.

What Code Obfuscation Actually Does, & Why it Matters

Code obfuscation makes it difficult for humans and AI tools to read and understand your code. Cyber attacks often work by reverse engineering code, identifying its internal logic so it can be rapidly scanned for vulnerabilities. Code obfuscation gets in the way of that process, by making your code appear too complex and confusing for hackers to unscramble.

Code obfuscation is an important way to protect your network from data breaches and ransomware attacks. Today, most organizations handle massive quantities of data which must be protected from hackers. Obfuscation is one of the best tools for doing that. 

Code Obfuscation and Risk Prevention
As the online space grows riskier, code obfuscation is taking on more importance as a means to mitigate risk. Code obfuscation can help prevent intellectual property theft, so that your business secrets stay safe. If you handle private client data, obfuscation can help keep that data safe. Code obfuscation can also help prevent fraud and app tampering. 

Using Code Obfuscation Across Platforms

Code obfuscation is not limited to just one platform or browser. The strategy is widely used across.NET and Java. It’s also applied on Android and across multiplatform applications.

Done right, obfuscation will help keep end users’ data safe even as they move from smartphone to laptop and then desktop. Modern obfuscation tools are designed to reflect the way people use devices today. 

Different Obfuscation Techniques and Their Performance Impact 

It’s helpful to understand the main techniques used in obfuscation and their impact on application runtime, as follows:

Renaming 

As the name suggests, renaming takes personal, identifiable information (like names and addresses) and replaces them with random letters or numbers. 

Renaming does not impact the application performance of the runtime. That’s because code execution doesn’t normally depend on personal identifiers, so changing those identifiers doesn’t impact speed or performance.

Control Flow Obfuscation 

Control flow obfuscation scrambles the sequence of code, changing the application’s “flow” so that it’s much more difficult to read. Control flow obfuscation usually entails inserting breaks or “junk” items into the code. 

Control flow execution causes code to execute at a slightly slower speed. This is especially true in tight loops or loops that iterate repeatedly. Any software with recursive patterns will likely be slowed down, to some limited degree, by control flow obfuscation. However, although there is an impact, it is minimal.

String encryption

String encryption scrambles “strings” of texts and scrambles them, either by renaming keywords or by encrypting whole strings of text. String encryption does impact performance by adding additional CPU overhead during the application’s runtime. However, the impact is fairly negligible and in many cases will go unnoticed. 

Resource Encryption and Metadata Pruning

Resource encryption encodes elements of a software application, like text files or images. The process significantly increases the computing power needed to run the application, which impacts performance.

Metadata pruning is a technique for optimizing application performance by filtering out irrelevant information. Metadata pruning can work well alongside of a resource encryption process.

Layered Obfuscation and How Tools Optimize It

Layered obfuscation uses strategic obfuscation techniques together to create a complex defense against attackers. 

By using the right suite of automated tools, developers can achieve the right balance between security and application performance. The best modern tools are capable of analyzing code and determining which parts of code are the best targets for particular obfuscation and encryption strategies.

Does Obfuscation Impact Performance?

The effectiveness of obfuscation often sparks a debate about its impact on performance. While it’s true that some techniques can add complexity, the extent of the impact varies depending on the method:

  • Renaming: This method has virtually no effect on performance since it only changes the semantic structure of the code.
  • Control Flow and String Encryption: These techniques can introduce additional processing requirements, especially when used intensively. Applications may experience a slight slowdown as the program works through additional layers of obfuscation during execution.
  • Combined Techniques: While layering multiple obfuscation methods enhances security, it can increase resource demands. The tradeoff is often a small performance impact in exchange for significantly greater protection.

Benefits of Code Obfuscation

Despite minor performance tradeoffs, the advantages of obfuscation far outweigh its drawbacks:

  • Protects Intellectual Property: Obfuscation prevents unauthorized access and reverse engineering of proprietary code.
  • Enhances Security: Obfuscation mitigates risks from static analysis tools often used by attackers.
  • Reduces Debugging: By disguising code structure, obfuscation eliminates many common debugging vulnerabilities.
  • Slows Attackers: The complexity introduced by obfuscation forces attackers to invest significant effort, deterring many attempts.

How to Measure the Performance Impact of Obfuscation 

It’s important to implement data-driven strategies for measuring the impact of obfuscation. Here are some of the best ways to do that.

Benchmark Frequently

Benchmark before/after builds. Track your metrics and measure the impact of every coding change so you can pinpoint and quantify changes in performance.

Profile Performance-Critical Methods

Identify bottlenecks in your encryption and obfuscation processes so you can streamline workflows and minimize impact on software performance.

Optimize JIT Tools

JIT, or Just-In-Time tools, boost software performance by selecting and optimizing the most critical sections of code for speed, based on application performance during runtime. Developers can use profiling tools to ensure that JIT tools are performing as well as possible in  .NET and JVM.

Conserve Your Computational Resources

Developers sometimes say, “Avoid applying heavy transformations to hot paths.” This means that you shouldn’t perform frequent, complex computations (heavy transformations) on heavily-used code paths (hot paths). To avoid slowing down application performance, do your computing offline, reducing stress on your software.

Best Practices to Minimize Performance Overhead

Tune Rule Sets and Adjust System Parameters

Check to make sure that your software is optimized for speed and reliability. The “rule sets” or parameters need to be examined periodically to eliminate cumbersome, outdated rules and break down complex rules into suites of straightforward parameters.

Exclude Performance-Critical Methods

Excluding performance critical methods typically means instructing virus-scanning or other security software to overlook certain applications. The goal is to allow those applications to run as quickly and smoothly as possible. 

This must be done with care to prevent network attacks or viruses. It’s a good practice to consult with experts before beginning.

Apply Control Flow Obfuscation Selectively

Control flow obfuscation is a powerful technique for making code difficult to read and reverse engineer. It’s often a good idea to apply the technique to parts of your code, rather than to the whole software application. This gives you a layer of protection without significant impact on the software’s speed and performance.

Use Situation-Specific Configurations

Software has different runtime needs based on the scenario: different workloads and environments will require either more or less computing power. 

Configure your software to optimize it under each possible scenario. Developers can create specific “profiles” based on scenarios, with different network settings and memory allocation based on each profile’s likely needs.

Integrate Performance Testing into CI/CD
Integrating performance testing into the CI/CD pipeline delivers performance insights at the earliest possible stage, so that developers have plenty of time to make the relevant changes. 

Why the Code Obfuscation Security Benefits Outweigh the Tradeoffs

Code obfuscation delivers significant benefits in return for very minor impact on software performance. It’s hard to overstate the value and ROI of code obfuscation. Here are the most important advantages it offers:

Preventing Intellectual Property (IP) theft

Code obfuscation makes it much more difficult for cyberthieves to access intellectual property and business secrets. 

Reducing the Risk of Reverse-Engineering

Hackers want to reverse-engineer your code to find its internal logic and assess its vulnerabilities. Code obfuscation makes that process much, much harder. 

Protect Hardcoded Logic and Secrets

Encryption keys and other secrets are safer when you apply code obfuscation. 

Slow Attackers Significantly

Obfuscation can’t stop every attack, but it can slow down attackers significantly. This leads to many attackers getting discouraged and giving up. For those who don’t give up, the slowdown gives your network security tools more time to detect them.

Achieve Regulatory Compliance 

Obfuscation makes it easier to comply with data privacy regulations, from HIPAA to GDPR, by protecting your sensitive information and any customer data you handle.

How PreEmptive tools keep performance overhead low

PreEmptive tools use data-driven strategies to minimize the performance impact of obfuscation. Our approach is based on carefully identifying needs and striking the right balance between security and performance.

We use highly optimized control flow transformations to minimize the performance impact of obfuscation. Instead of a blanket approach, our tools analyze each application to discover bottlenecks and determine which code is security-critical. Then we apply highly complex obfuscation elements into the code to protect private data.

We are highly selective about our use of heavy transformations, or encryptions, in order to maximize the performance and speed of software at all times.

See Obfuscation In Action

While obfuscation may introduce minor performance tradeoffs, the protection it provides is invaluable in today’s cybersecurity landscape. PreEmptive provides industry-leading obfuscation solutions that secure applications without compromising performance. Tools like Dotfuscator for .NET and DashO for Java and Android offer protection, combining renaming, control flow obfuscation, string encryption, and other techniques to achieve the balance of performance and protection. Request a free trial today.

Read the whole story
alvinashcraft
37 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing the "LaunchyBar" Visual Studio extension!

1 Share

Introducing "LaunchyBar", an extension for Visual Studio 2022 (and 2026!) that brings the beloved Activity Bar concept from VS Code and JetBrains IDEs right into your Visual Studio workflow.

If you've spent any time in VS Code, you're probably familiar with that narrow vertical strip of icons on the left side - the Activity Bar. It gives you quick, single-click access to common tools like the file explorer, search, source control, and more. I always missed that when switching back to Visual Studio, so I decided to build it myself.

A Word of Warning

Before you get too excited, I need to be upfront: this extension is entirely experimental. I'm doing some things that aren't exactly "supported" by the Visual Studio extensibility APIs - specifically, injecting the toolbar into Visual Studio's main window shell. It works, but it's definitely pushing the boundaries of what extensions are supposed to do.

What does that mean for you? It means this extension could break at any moment - a Visual Studio update, a theme change, or just the stars aligning wrong could cause issues. If you're okay with that and want to live on the edge, keep reading!

What Does It Do?

LaunchyBar docks to the left side of Visual Studio and gives you instant access to frequently used tools and commands. Out of the box, it comes pre-configured with some essentials:

  • Solution Explorer - Jump straight to your project structure
  • Find in Files - Quick search access
  • Git Changes - Check your source control status
  • Debug - Start or stop debugging with a single click (it even changes the icon based on the current debugger state!)
  • Terminal - Pop open the integrated terminal
  • Settings - Jump into Visual Studio settings

The best part? Tool windows like Solution Explorer and Terminal toggle on each click - click once to show, click again to hide. No more hunting through menus.

LaunchyBar in action

Feel free to check it out, and let me know if you have any suggestions for it - I have some ideas for making the bar customizable in the future, but I'd love to hear what features you'd find useful!

And, of course, it's open source and issues / PRs are happily accepted, if you're into that sort of thing.



Read the whole story
alvinashcraft
46 seconds ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories