Monthly Archives: December 2025

Auto-discovery using Vite (and React)

I’m messing around with a Shell application in React and wanting to load information from apps/components that are added to the shell dynamically, i.e. via auto-discovery.

Now, we’re using React for this example so we can create React apps using Vite for what we will call, our components, as these can be loaded into React as components. The reason to create these as standalone apps would be for use to develop against.

So let’s assume I have a main application, the Shell app and this can get routes for functionality (our apps/components) which may get added later on in the development process, basically a pluggable type of architecture which just uses React components along with those components supplying routing information to allow us to plug them into the Shell.

To give it more context, I build a Shell application with Search and later on want to add a Document UI, so it’d be nice if the Document can be worked on separately and when ready, deployed to a specific locations where the Shell (upon refresh) can discover the new component and wire into the Shell.

Vite has a feature import.meta.glob which we can use to discover our routes.tsx that are deployed to a specific path, i.e.

const modules = import.meta.glob<Record<string, unknown>>(
  '../../apps/*/src/routes.tsx', { eager: true }
);

This will return keys for the file paths and values being the functions that import from the files or modules. For example it might locate components such as

apps/search/src/routes.tsx
apps/document/src/routes.tsx
apps/export/src/routes.tsx
apps/settings/src/routes.tsx

If eager is missing (or false) then Vite lazy imports the functions and you’d then need to call them yourself, with eager:true, Vite imports the modules immediately.

If we use something like this


import type { RouteObject } from 'react-router-dom';

const modules = import.meta.glob<Record<string, unknown>>(
  '../../apps/*/src/routes.tsx', { eager: true }
);

export const allAppRoutes: RouteObject[] = Object.values(modules)
  .flatMap((mod) => {
    const arr = Object.values(mod).find(
      (v): v is RouteObject[] => Array.isArray(v) && 
        v.every(item => typeof item === 'object' && 
           item !== null && 'path' in item && 'element' in item)
    );
    return arr || [];
  });

Then we could use the routes via the react-router-dom in App.tsx, like this

const baseRoutes = [
  { path: '/', element: <div>Welcome to the Shell</div> },
];

const routes = [
  ...baseRoutes,
  ...allAppRoutes,
];
return (
  <AuthContext.Provider value={auth}>
    <ShellLayout>
      <Suspense fallback={<div>Loading…</div>}>
        <Routes>
          {routes.map(({ path, element }) => (
            <Route key={path} path={path} element={element} />
          ))}
        </Routes>
      </Suspense>
    </ShellLayout>
  </AuthContext.Provider>
);

and finally here’s an example routes.tsx file for our components

import SearchApp from './App';

export const searchRoutes = [
  { path: '/search/*', element: <SearchApp /> },
];

Looking at security features and the web

Let’s take a look at various security features around web technologies, although I’ll we concentrating on their use in ASP.NET, but the information should be valid for other frameworks etc.

Note: We’ll look at some code for implementing this in a subsequent set of posts.

Authentication and Authorization

We’re talking Identity, JWT, OAuth, Open ID Connect.

Obviously the use of proper authentication and authorisation ensure only legitimate users have access to resources and forcing a least privilege and role based access ensures authenticated users can only access resources befitting their privileges.

OWASP risks mitigation:

  • A01 Broken Access Control and improper enforcement of permissions
  • A07 Identification and Authentication failures, weak of missing authentication flows
    • Data protection API (DPAPI) / ASP.NET Core Data Protection

      This is designed to protect “data at reset”, such as cookies, tokens CSRF keys etc. and providers key rotation and encryption services.

      OWASP risks mitigation:

      • A02 Cryptographic failures, weak or missing encryption of sensitive data
        • HTTPS Enforcement and HSTS

          This forces encrypted transport layers and prevents protocol downgrade attacks.

          OWASP risks mitigation:

          • A02 Cryptographic failures, sensitive data exposure
          • A05 Security misconfigurations, missing TLS or insecure defaults
            • Anti-Forgery Tokens (CSRF Protection)

              This prevents cross site request forgery by validation of user intent.

              OWASP risks mitigation:

              • A01 Broken access control
              • A05 Security misconfigurations
              • A08 Software and Data integrity failures such as session integrity
                • Input Validation and Model Binding Validation

                  This prevents malformed or malicious input from reaching the business logic.

                  OWASP risks mitigation:

                  • A03 Injection, such as SQL, NoSQL and command injections
                  • A04 Insecure design, lacking validation rules
                  • A05 Security misconfigurations
                    • Output Encoding

                      This prevents untrusted data from being rendered, for example covers things like Razor, Tag Helpers, HTML Encoders.

                      OWASP risks mitigation:

                      • A03 Injection
                      • A05 Security misconfigurations
                      • A06 Vulnerable and outdated components
                        • Security Headers

                          Covers things such as CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and mitigates XSS, click jacking, MIME sniffing and data leakage.

                          OWASP providers explicit guidance on recommended headers.

                          OWASP risks mitigation:

                          • A03 Injection, CSP reduces XSS
                          • A05 Security misconfigurations, missing headers
                          • A09 Security logging and monitoring failures, via reporting endpoints
                            • Rate limiting and throttling

                              This is included, but need to be enabled as ASP.NET built in middleware.

                              This prevents brute force, credential stuffing and resource exhaustion attacks.

                              OWASP risks mitigation:

                              • A07 Identification and Authentication failures
                              • A10 Server side request forgery (SSRF) limit abuse
                              • A04 Insecure design, lack of abuse protection
                                • CORS (Cross‑Origin Resource Sharing)

                                  This controls which origins can access API’s and prevents unauthorized cross-site API calls.

                                  OWASP risks mitigation:

                                  • A05 Security misconfiguration
                                  • A01 Broken access control
                                    • Cookie Security

                                      Protects session cookies from theft or misuse.

                                      OWASP risks mitigation:

                                      • A07 Identification and Authentication failures
                                      • A02 Cryptographic Failures
                                      • A01 Broken access control
                                        • Dependency Management

                                          When using third party dependencies via NuGet, NPM etc. we need to ensure libraries are patched and up to date.

                                          OWASP risks mitigation:

                                          • A06 Vulnerable and outdated components
                                            • Logging and Monitoring

                                              This covers things like Serilog, Application Insights and built-in logging etc.

                                              Used to detect suspicious activites, as well as support incident response.

                                              OWASP risks mitigation:

                                              • A09 Security Logging and monitoring failures
                                                • Secure deployment and configuration

                                                  This covers all forms of configuration, including appsettings.json, key vault, environment seperation etc.

                                                  Here we want to prevent secrets being exposed and enforce secure defaults.

                                                  OWASP risks mitigation:

                                                  • A05 Security misconfiguration
                                                  • A02 Cryptographic Failures

Generating QR code with Javascript

We can generate QR codes using the package qrcode. If we just want this as tooling, such as manually generating, then we can add the following

npm install qrcode --save-dev

Now update your packages.json with this script

"generate:qrs": "node ./scripts/generate-qrs.mjs",

As yo can see, add a scripts folder and create the file generates-qrs.mjs with the following code

import QRCode from 'qrcode';
import fs from 'fs/promises';
import path from 'path';

const outDir = path.join(process.cwd(), 'public', 'qrcodes');
await fs.mkdir(outDir, { recursive: true });

const sites = [
  { name: 'www.mywebsite.co.uk', url: 'https://www.mywebsite.co.uk' },
];

for (const site of sites) {
  const pngPath = path.join(outDir, `${site.name}.png`);
  const svgPath = path.join(outDir, `${site.name}.svg`);

  console.log(`Generating ${pngPath} and ${svgPath}`);

  // PNG (300x300)
  await QRCode.toFile(pngPath, site.url, { width: 300 });

  // SVG
  const svgString = await QRCode.toString(site.url, { type: 'svg' });
  await fs.writeFile(svgPath, svgString, 'utf8');
}

console.log(`QR codes generated in ${outDir}`);

In the above, the sites include a name, this is the name of the file ad we’ll create a file of type and with suffix PNG and SVG. The url is what’s embedded into the QR code.

Bringing markdown into your React app.

I’m working on a little website and I wanted to store some markdown files in a /content folder which allows a user to create their content as markdown without ever needing to change the website – think of this as a very simple file based CMS.

So the idea is we use git to handle versioning via revisions. The user can just change their markdown files for their content and the website will rebuild in something like GitHub actions and the content is then used by the site.

Depending on what we’re doing we’ve got a couple of options to include the markdown.

The first is simply import the markdown file using

import contactContent from '../../content/contact.md?raw';

export default function Contact() {
  return (
     <div>
        {contactContent}
     </div>
  );
}

The ?raw informs Vite or the likes to not try to parse this file, just supply it as raw text as a string.

This is fine if you’re just reading the test but if you want to render markdown you can use react-markdown.

Add the package (and the vite-plugin-markdown) using

npm install react-markdown
npm install vite-plugin-markdown

You’ll likely want these plugins as well

npm install rehype-raw
npm install remark-gfm

Now we can use the ReactMarkdown component like this

<ReactMarkdown 
  remarkPlugins={[remarkGfm]}
  rehypePlugins={[rehypeRaw]}
>
  {content}
</ReactMarkdown>

In this example we’d import the markdown as we did earlier, in this can we’re passing by props (potentially) as a content string.

In my case, I’m using Vite, and the import with the ?raw tells the Vite tooling to essentially treat this as a plain text file and it’ll get bundled and hence not visible in the dist folder. This is great because we can change the markdown and hot reload will update redisplay the changes.

Azure Static Web App Preview Environments

If you’re using something like GitHub actions to deploy your static web app to Azure, you might not realise that you can have the PR’s deployed to “Preview” Environments.

Go to your static web app and select Settings | Environment and your PR’s will have a deployment listed in the preview environment section.

If the URL for your site is something like this

https://green-bat-01ba34c03-26.westeurope.3.azurestaticapps.net

The 26 is the PR number so

https://green-bat-01ba34c03-{PR Number}.westeurope.3.azurestaticapps.net

And you can simply open that PR via your browser and verify all looks/works before merging to main.

If you’ve set your PR close to correctly delete these preview environments, for example from GitHub actions

- name: Close Pull Request
  id: closepullrequest
  uses: Azure/static-web-apps-deploy@v1
  with:
    azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
    app_location: "./dist"
    action: "close"

then this will be delete the preview envirnoment once your PR is merged.

However as I found, if you PR close is not working correctly these preview environments increase until you get an error trying to build the PR and the PR cannot be deployed to the preview environment until you select and delete them, i.e. you’ve reached the max number of preview environments.