AI 资讯
Navigating Microsoft Azure Certifications in 2026: Value, Trends, and Blueprint Strategy
The cloud ecosystem in 2026 isn't just about moving VMs to the public cloud—it's heavily driven by hybrid operations, unified security telemetry, AI integration, and complex governance across multi-region architectures. As enterprise tech stacks evolve, Microsoft Azure certifications remain a primary yardstick for technical competence, but knowing which track to target is where most engineers get stuck. As someone who works closely with cloud certification blueprints and enterprise deployments, I wanted to map out where Microsoft credentials stand today, what the market actually demands, and how specific exams fit real-world scenarios. Market Trends: Why Azure Credentials Still Drive Real ROI in 2026 The value of certification has shifted from basic feature recognition to proving operational problem-solving under real constraints. Hands-on Scenario Focus: Exams increasingly test scenario-based trade-offs—balancing performance, cost, and strict security requirements rather than simple definition checks. Role-Based Specialization: Instead of broad, generic tracks, Microsoft continues to refine specialized pathways for developers, security analysts, and hybrid infrastructure specialists. Continuous Free Renewal: Earning the badge is step one, but maintaining active status requires passing annual, open-book renewal assessments directly through Microsoft Learn, ensuring skills don't stall out. Mapping Azure Exams to Real-World Enterprise Scenarios Depending on your daily engineering focus or career targets, here is how the core role-based tracks align with active projects: App Modernization & Cloud-Native Dev: AZ-204 (Azure Developer Associate) The Scenario: Refactoring monolithic legacy apps into containerized microservices using Azure App Service, Azure Functions, and Cosmos DB while setting up secure authentication via Microsoft Entra ID. Hybrid Infrastructure & Server Ops: AZ-800 (Administering Windows Server Hybrid Core Infrastructure) The Scenario: Managing mixed e
AI 资讯
Cómo solucionar el error “Enable JavaScript and cookies to continue”
Cómo solucionar el error “Enable JavaScript and cookies to continue” Este mensaje aparece cuando Cloudflare (u otro proxy de seguridad similar) bloquea la solicitud porque detecta que el cliente no cumple con los requisitos mínimos de seguridad: JavaScript deshabilitado o cookies deshabilitadas/expiradas . 🔍 Causa técnica Cloudflare implementa mecanismos de protección como: JavaScript Challenge : El navegador debe ejecutar un script para demostrar que no es un bot. Cookie de verificación : Tras superar el desafío, Cloudflare emite una cookie ( __cf_bm o cf_clearance ) que valida la sesión. Si el cliente (navegador o cliente HTTP personalizado) no ejecuta JavaScript o no maneja cookies correctamente, la validación falla y se muestra este mensaje. ✅ Solución definitiva (por escenario) 🌐 Si eres un usuario final (navegador) Habilita JavaScript : Chrome: Configuración > Privacidad y seguridad > Sitios web no seguros > Habilitar JavaScript . Firefox: Preferencias > Privacidad y seguridad > Permisos > Habilitar JavaScript . Habilita cookies de terceros (si usas extensiones como uBlock Origin o Privacy Badger): Añade el dominio a la lista blanca. Desactiva temporalmente los bloqueadores para probar. Borra cookies y caché del dominio afectado. Reinicia el navegador y vuelve a cargar la página. 🧪 Si eres desarrollador (automatización / scraping / cliente HTTP) ❌ No uses requests o curl sin soporte JS/cookies → fallarán siempre . ✅ Opción recomendada: Usa un navegador headless con soporte JS y cookies # Ejemplo con Playwright (recomendado) from playwright.sync_api import sync_playwright with sync_playwright () as p : browser = p . chromium . launch ( headless = True ) context = browser . new_context () page = context . new_page () # Navega a la URL (Cloudflare se resolverá automáticamente) page . goto ( " https://ejemplo.com " , wait_until = " networkidle " ) # Si aún falla, fuerza espera tras el desafío try : page . wait_for_selector ( " #challenge-error-text " , timeout = 5
AI 资讯
AI Code Review at Scale: LinkedIn's Multi-Agent Approach
At LinkedIn's scale, relying solely on human reviewers or simply putting an off-the-shelf AI reviewer in front of GitHub is not an effective way to manage PRs. To address this, LinkedIn engineers built a multi-agent AI code review platform that understands the organization’s coding context, treats code review as production infrastructure, and minimizes hallucinations and low-signal feedback. By Sergio De Simone
AI 资讯
Your TTS shortlist is three shortlists, and they barely intersect
Every "best text-to-speech API" list I have read is ranked. Number one, number two, number three, with a verdict at the bottom. That shape cannot express the actual decision, and I want to show you why with something you can run. The problem is that the three things that decide a TTS vendor are measured in units that do not convert into each other. Price is dollars per million characters. Transport is a shape — held-open socket, chunked body, finished file. Compliance is a document that either exists or does not. There is no exchange rate between them, so there is no ordering. A ranked list has to pick one axis and pretend the others are tiebreakers. They are not tiebreakers. They are filters, and filters compose by intersection. The three sets Price spans about 40x. Google Cloud's legacy voices and Amazon Polly's standard engine sit at $4 per million characters. The mid-market — OpenAI's tts-1 , Deepgram Aura-1, Inworld TTS-2 Flash — clusters at $15. Cartesia runs $37.38 to $50. ElevenLabs is $166.11 at its Scale tier. A million characters is roughly 22 hours of speech, so at prototype volume this axis is noise; at a hundred million characters a year it is the difference between a $400 bill and a $16,600 one. Transport comes in three shapes and the difference is architectural, not incremental. WebSocket streaming holds a connection open and pushes audio as it is synthesised. The first syllable can reach the caller while the model is still working on the sentence. This is what a live agent needs. Chunked REST streams the response body back progressively. OpenAI works this way, and its docs recommend wav or pcm output specifically because those start playing sooner than a compressed container. Meaningfully better than waiting for a whole file; meaningfully worse than a held-open socket. Batch returns a finished file. Correct for narration, e-learning, anything rendered ahead of time. Wrong for conversation. Two entries in that column are routinely stated wrong, so th
AI 资讯
VoidZero Releases Vite+ Beta: A Unified Web Toolchain Behind a Single Command
VoidZero has launched the beta of Vite+, a unified web development toolchain. It combines runtime, package management, and essential frontend tools under a single command. Vite+ supports various projects and is open source. The platform enhances workflow through features such as hot-reloading, format checking, and testing. The team emphasizes community feedback for future updates. By Daniel Curtis
AI 资讯
The Bug That Hid Behind Its Own Comment: Fixing Inconsistent Inference in astroid
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview astroid is the static-analysis engine that powers pylint — one of the most widely used linters in the Python ecosystem. Instead of running your code, astroid builds a model of what your code would do (a process called "inference") so pylint can catch real bugs before you ever hit run. That means astroid's inference logic has to be extremely consistent: if it gets confused about what a piece of code returns, pylint either misses real bugs, or — as in this case — flags perfectly correct code as broken. Bug Fix or Performance Improvement I picked up astroid issue #3077 : identical typing.cast(T, self) expressions were being inferred differently depending only on how the surrounding call was written — even when the code was structurally symmetric. In a class like this: class Base : def __call__ ( self ) -> str : return cast ( str , self ) def run ( self ) -> str : return cast ( str , self ) class IrJoin : separator : Base def __call__ ( self , items ): sep : str = self . separator () # implicit __call__ sugar return sep . join ( items ) def run ( self , items ): sep : str = self . separator . run () # explicit method call return sep . join ( items ) Both self.separator() and self.separator.run() do the exact same thing at runtime — I verified this by actually running the file. But pylint only flagged one of them: $ python -m pylint t5.py t5.py:35:15: E1101: Instance of 'Base' has no 'join' member (no-member) The explicit .run() path got a false positive; the equivalent implicit __call__ path did not, even though sep is a plain str in both cases at runtime. Code PR: https://github.com/pylint-dev/astroid/pull/3242 My Improvements Ruling out the obvious suspect My first hypothesis was infer_typing_cast , the function that handles typing.cast() itself — it seemed like the natural place for a cast-related inconsistency to live. Tested in isolation, though, it behaves identi
AI 资讯
Building a 9-Language Fan Site with Next.js 15 and next-intl (No Middleware)
I recently built a multilingual fan site for The Duskbloods , an upcoming FromSoftware game. The challenge: 9 languages (English, Japanese, Korean, Chinese, Spanish, French, German, Italian, Portuguese), static generation , and no middleware — all deployed on Cloudflare Workers. Here's how I did it and what I learned. The Architecture The site uses Next.js 15 App Router with next-intl v4 for internationalization. The key constraint: I wanted to avoid middleware to keep Cloudflare Worker costs down. src/ ├── app/ │ ├── (root)/ # English at / │ │ ├── gameplay/ │ │ ├── characters/ │ │ └── ... │ └── [locale]/ # Other languages at /zh, /ja, /ko... │ ├── gameplay/ │ ├── characters/ │ └── ... ├── messages/ # Translation files │ ├── en.json │ ├── ja.json │ ├── zh.json │ └── ... └── components/ # Shared components └── views/ Route Groups for Language Separation Instead of using middleware to detect locale, I use route groups : (root) — English content at the root path / [locale] — Other languages at /zh , /ja , /ko , etc. This means English gets clean URLs ( /gameplay ) while other languages get prefixed URLs ( /zh/gameplay ). Good for SEO — English is the default, and other languages have clear URL signals. Why No Middleware? Cloudflare Workers charge per request. Middleware runs on every request. For a static site with 9 languages, that's 9x the middleware invocations for every page load. By handling locale in the route, I skip middleware entirely. // src/app/[locale]/layout.tsx export async function generateStaticParams () { return [ ' ja ' , ' zh ' , ' ko ' , ' es ' , ' fr ' , ' de ' , ' it ' , ' pt ' ]. map ( locale => ({ locale })); } This pre-generates all locale variants at build time. Zero runtime locale detection. The Translation System Message Files Each locale has a JSON message file: // src/messages/zh.json { "gameplay" : { "intro" : { "eyebrow" : "玩法介绍" , "title" : "游戏机制" , "lead" : "深入了黄昏征讨的核心机制。" }, "virtue" : { "title" : "美德" , "types" : [ { "title" : "讨伐之美德
AI 资讯
Your AI doesn't understand design. So I gave it a library it can read.
Ask any LLM to "make this landing page look like a high-end Swiss design studio" and you'll get something that gestures at the idea — a sans-serif font, some whitespace, maybe a red accent because it half-remembers Müller-Brockmann. It looks AI-generated because it is. The model has read a billion words about design but has no grounded, reusable representation of what "Swiss International Style" actually specifies: the exact grid, the type scale, the spacing ramp, the rules for what you must not do. That gap is the whole problem. Models are great at language and bad at design systems, because a design system isn't language — it's a set of constrained values plus the discipline to apply them consistently. So I built the missing piece: a library of real design styles, turned into something a machine can actually consume. It's called Curio . This post is about the part I think is interesting to other builders: making design machine-readable, and publishing the catalog for agents instead of for humans. A design style is just tokens + rules The insight is boring and that's why it works. Pick any coherent visual language — Bauhaus, Memphis, the Edo woodblock palette, Stripe's product aesthetic — and you can decompose it into: Tokens : color families, type families and scale, spacing ramp, radii, shadow/elevation, motion timing. Components : how a button, card, input, nav actually look in this language. Rules : the "always" and the "never." (Swiss: never center body text, never more than two weights. Memphis: never subtle.) Once a style is expressed that way, an AI doesn't have to imagine the look. It interpolates within a fixed, internally-consistent set of values. The output stops looking like a guess because it isn't one. Each style in Curio is packaged exactly like this — tokens, component specs, and an explicit "avoid" list — as a DESIGN.md file — markdown with YAML frontmatter — that a model can read in one shot ( what is DESIGN.md? ). # excerpt of a design package a
AI 资讯
I Built Browser-Local File Tools So Files Don't Need to Be Uploaded
A lot of small file jobs still follow the same awkward pattern: choose a file, upload it to someone else's server, wait for processing, download the result. For some tasks, that server round trip is unnecessary. I have been building FileNest Worktools , a browser-based toolkit for repetitive file work, around a simple constraint: If a task can reasonably be done inside the browser, the file contents should stay on the user's device. What currently runs locally The current FileNest tools cover: batch file renaming sequential, reverse, and custom-order renaming JPG / PNG / WebP conversion image resizing and compression image-to-text OCR with editable review PDF merge, split, extract, and images-to-PDF text export to DOCX, PDF, TXT, Markdown, and HTML CSV / TSV / JSON conversion duplicate-file detection For these current browser-local workflows, the file contents are processed on the device rather than being sent to a conversion server. Why local processing matters Privacy is one reason, but it is not the only one. Many file operations do not actually need server-side infrastructure. Renaming a file is mostly about filenames, order, extensions, and conflict checks. Image resizing can be handled with browser APIs. CSV and JSON conversion is essentially local parsing and serialization. Duplicate detection can compare file fingerprints locally. If those jobs can stay inside the browser, there is less network overhead and one less copy of the user's files being created somewhere else. I also wanted the risky parts to stay visible One thing I dislike about many online file tools is the "click and hope" workflow. So I tried to make FileNest show more information before or after processing: renamed files can be previewed before packaging original files are not silently overwritten image compression reports actual output bytes duplicate detection uses content matching rather than filename guesses OCR output can be reviewed and edited the image enhancer does not claim that shar
AI 资讯
DevOps Questions After We Broke The Release Handshake
Answers from the incident where every dashboard looked politely wrong. The release-api deployment had already been marked complete when the invoice page began returning 503s. The new container was serving traffic, the PostgreSQL migration had committed, and the feature flag was on. A NetworkPolicy added in another repository prevented the new pod from reaching tax-rate-cache . The application team saw errors, the database team saw a clean migration, and Platform saw green nodes. By the time we put all three facts in one incident channel, 63 deployment messages had buried the one that mattered. “Is This Actually A DevOps Failure Or Just One Bad Deploy?” It was a DevOps failure because four teams completed valid local work and nobody owned the release handoff between them. Calling it “just a bad deploy” would have been convenient. We could have fixed the policy, replayed the release, written a short incident note, and carried on pretending that a green Argo CD application means a service is ready for users. The pod was healthy according to Kubernetes. It was also unable to call a dependency required to render an invoice. Both things can be true, which is why a deployment status alone is a fairly poor witness. Our old release process had hidden contracts in too many places: The service repository declared its image and Helm values. The infrastructure repository held network rules. Database migrations ran from a separate GitHub Actions workflow. Feature flags lived in LaunchDarkly, owned by whoever had last touched the feature. The runbook lived in Confluence, where it had last been edited in February. We’ve started putting the release dependencies in the service repository, close to the code that needs them. It is not a clever system. It is a file that a human can read during an incident and a pipeline can check before promotion. release : service : release-api requires : - dependency : tax-rate-cache namespace : finance port : 8080 network_policy : allow-release-api-t
AI 资讯
Kubernetes Basics for DevOps Engineers
Introduction: Kubernetes can feel overwhelming when you first hear terms like Pods, Services, and Deployments thrown around. In this first post of my Kubernetes series, I’ll break down the fundamentals — what Kubernetes actually solves, and the core building blocks you need to understand before going further. What is Kubernetes? Kubernetes is an open-source container orchestration tool , originally developed by Google. It helps manage containerized applications across different environments — physical machines, virtual machines, and cloud environments — which makes it a great fit for hybrid deployment setups. Why Kubernetes? The Problem It Solves To understand why Kubernetes exists, look at the trend that led to it: Applications moved from monolith to microservices. That shift drastically increased the number of containers teams had to manage. Managing hundreds of containers by hand became unsustainable — teams needed a proper way to orchestrate them. Key Features High Availability — no downtime Scalability — scale up or down based on load and performance needs Disaster Recovery — backup and restore built into the ecosystem Main Kubernetes Components Pods: Abstraction over containers Services: Stable networking & communication Ingress: Routes external traffic into the cluster ConfigMaps & Secrets: External configuration Volumes : Data persistence Deployments & StatefulSets: Replication (stateless vs. stateful) DaemonSets: One Pod per node, auto-scaled with the cluster
AI 资讯
FieldOS, Part 1: I Built the Core System and Timed Every Single Hour
A while back, I built a complete field service management platform for the first time. It worked. But building it taught me as much about what I'd do differently as it taught me about field service software itself. So I built it again — FieldOS — the way I'd build it now, with everything I picked up the first time around. And this time I tracked every real hour it took, start to finish. Not to hit a deadline. To finally answer a question I'd always guessed at before: how much time does a project like this actually take, and what is that time genuinely worth to the business paying for it? I do maintenance for a car dealership, an auto parts warehouse, a body shop, and five parts stores in my day job, so I've seen firsthand what off-the-shelf field service software costs a business — and how much of it a smaller crew never touches. FieldOS is built so a business only pays for the pieces it actually needs. But the real question underneath this series isn't what to charge. It's how to price custom work in a way that's honest about what the work is worth, without pricing a small business out of getting it built at all. This series is that experiment, told through the build itself. How the Core System Works Every field service business needs the same five things to run day to day, no matter what they fix or deliver. This is the part of FieldOS nobody gets to skip — the foundation every add-on plugs into later in this series. A Front Door for Every Job: A request comes in, gets logged, and moves through a status list a business defines for itself — not a fixed workflow some software vendor decided everyone needs. A body shop's process doesn't look like an HVAC crew's, so the system shouldn't force them into the same one. Real Parts Tracking: Every part or supply used on a job gets logged against real stock at a real location, and the system flags it automatically the moment something drops below what a crew needs on hand — not after a technician shows up to an empty shelf.
AI 资讯
How to Add AI to Your Existing SaaS Application: A Practical Guide for 2026
"Should we add AI to our product?" isn't really the right question anymore. Most SaaS founders and product teams have moved past whether to add AI and are stuck on how — how to do it without a six-month rebuild, a runaway API bill, or a feature that looks impressive in a demo but nobody actually uses. At Softication Technology Pvt. Ltd., we've worked with SaaS teams integrating AI into products ranging from CRMs to internal tooling to customer support platforms. This guide lays out the practical, engineering-first approach we use — the decisions that actually matter, and the ones that are just noise. Table of Contents Why "Adding AI" Isn't One Thing Step 1: Find the Right Entry Point Step 2: Choose Your Integration Pattern Step 3: Design the Architecture Step 4: Handle Cost, Latency, and Reliability Step 5: Ship Small, Measure, Expand Common Mistakes We See Final Thoughts Why "Adding AI" Isn't One Thing "AI integration" gets used as a catch-all term, but it covers very different engineering problems: Generating or rewriting content Answering questions using your product's own data Classifying, tagging, or routing records automatically Predicting outcomes from historical data Automating multi-step workflows end to end Each of these needs a different technical approach. The biggest mistake teams make is picking a technology (usually "let's use an LLM for everything") before defining which of these problems they're actually solving. Step 1: Find the Right Entry Point Before writing any code, look at your product usage data and support tickets for patterns like: Repetitive manual work — users doing the same categorization, summarization, or data entry over and over Search or discovery friction — users struggling to find information that exists in your product Decision bottlenecks — users waiting on judgment calls that follow a somewhat predictable pattern A good first AI feature is narrow, has a clear success metric, and solves a problem your users already complain abou
AI 资讯
HTML is getting cool again: Meet the Invoker Commands API
For years, frontend development has had a slightly embarrassing relationship with HTML. We all read...
AI 资讯
Tesla’s Door Handles Lead to Its Biggest Recall Yet
A Chinese agency says the two recalls affecting some 3 million vehicles can mostly be fixed by over-the-air updates—but they will also require physical warning stickers and camera-related updates.
AI 资讯
The flaky test was right: a 58%-reproducible race in a scroll-reading pipeline's disk cache
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview The Vesuvius Challenge uses machine learning to read carbonized Herculaneum scrolls, which is 2,000-year-old papyrus that got buried by the eruption of Vesuvius and can never be physically unrolled. Its open-source monorepo, ScrollPrize/villa, contains the vesuvius Python package that researchers use to stream multi-terabyte CT scan volumes and train ink-detection models. I was setting up that package on my Windows 11 machine (the project's CI only tests Ubuntu, and the workflow file literally says "Extend this list once the build scripts for macOS and Windows are confirmed"), working with an AI coding assistant to run the test suite on a platform it had never been tested on. One test failed. Then it passed. Then it failed again. Bug Fix or Performance Improvement The test, test_shared_cache_multiprocess_reads_are_not_torn, spawns four processes that read one scroll volume through a shared on-disk chunk cache. Run it once and you might not see anything wrong. So I ran it twelve times: 7 failures out of 12, all PermissionError: [WinError 5] Access is denied. A 58% flake isn't a flake. It's a bug with a coin flip attached. The cache is on the hot path for real usage. It's the component behind the package's documented volume_cache_dir config and the --cache-dir flag of its inference CLI. Any PyTorch DataLoader with num_workers > 0 puts multiple processes into exactly this concurrent pattern, so on Windows, training runs would randomly die mid-epoch. Once I dug in (a standalone reproducer that propagated full worker tracebacks instead of repr(exc)), the failure turned out to have three separate surfaces, each one hiding behind the previous one: Cache-entry commit. The zarr library commits each cache entry with a write-temp-then-os.replace pattern. On POSIX, rename(2) over a file another process has open is legal. On Windows, MoveFileEx(MOVEFILE_REPLACE_EXISTING) return
开发者
The Optimization That Was Too Good: Why Our Push Notifications Only Worked When You Weren't Looking
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. When I was...
AI 资讯
UNDERSTANDING THE GIT WORKFLOW
Git is a version control system. Version control, also known as source control, is the practice of tracking and managing changes to software code. Version control systems are software tools that help software teams manage changes to source code over time. Git is used for: Tracking code changes Tracking who made changes Coding collaboration Setting up a new Repository A Git repository is a folder that Git tracks for changes. The repository stores all your project's history and versions. Add files to the folder. The following describes how to set up a new repository: Git Init Initializes git user@localhost $ git init This creates a hidden folder called .git inside your project. This is where Git stores all the information it needs to track your files and history. To see which files are in your project folder, use the ls command: user@localhost $ ls To Check if Git is tracking your new files: user@localhost $ git status The files here could either be tracked or untracked:- Untracked Files Files you've created or copied into the folder, but haven't told Git to watch. Tracked Files Files that Git is watching for changes. To make a file tracked, you need to add it to the staging area. Git Staging Tells Git exactly which files you want to include in your next commit. user@localhost $ git add . Common Commands git add . Stages all new, modified, and deleted files in the current directory and its subdirectories. git add <file> Stages a specific file. git add -A (or --all) Stages all changes across the entire repository, regardless of your current folder location. git add -u Stages modifications and deletions of already-tracked files, ignoring completely new (untracked) files. git add *.txt Stages all files matching a specific pattern (e.g., all text files). Git Commit A commit is like a save point in your project. It records a snapshot of your files at a certain time, with a message describing what changed. user@localhost $ git commit -m " Describe your changes" Pushing Chan
AI 资讯
How We Handle Client-Side CSV Merging Without Server Processing
When merging CSVs in the browser, handling mismatched columns and quoted cells changes everything. Here's how filetools does it. Last week we shipped CSV merge/split/transpose tools for filetools, and the most interesting challenge wasn't CSV parsing - it was handling real-world data without a server. Here's how we handle the hard cases. The Problem: CSV files in the wild are messy. Columns don't always match. A cell value contains a comma and that comma is quoted. Headers are sometimes case-sensitive, sometimes not. When you build on a server, you can run a fast library and stream the result. In the browser, you have to make your merge operation deterministic from first load. Our approach: Column matching: Users specify which columns to merge on (e.g., "id" or "email"). We do a case-insensitive first pass, then check for exact matches. If no match exists, we warn the user and ask them to pick from the detected headers. This upfront clarity saves merge errors later. Quoted cell handling: We follow RFC 4180 strictly - a quote inside a quoted field is escaped as a double quote. Most CSV parsers get this wrong when they're quick. We use the csv-parse library (MIT) vendored into the site, same way we do with PDF and ZIP libraries. Column order: The merge operation respects column order from the first file, then appends any new columns from subsequent files. This is deterministic and reproducible. Why this matters for a browser tool: Server-based CSV tools hide their assumptions - you upload, they merge, you download. If a merge fails, you get an error message and no insight into why. Client-side, the user can see the detected headers, approve or correct them, and re-try immediately. That transparency matters when you're dealing with data that represents real records or transactions. What shipped this week: We added merge, split (by row count or column value), transpose, and comparison tools. The same deterministic, transparent approach applies to each one. Next question
开发者
Building a Full Enterprise-Ready React + Spring Boot Auth Flow: An End-to-End Guide
Introduction Authentication is one of those things that looks simple in a tutorial and becomes surprisingly complex in production. Between token storage, CSRF protection, refresh flows, and protected routing, there are many places to get it wrong—and getting it wrong has real security consequences. In two earlier posts, I covered pieces of this puzzle: Enabling CSRF in a JWT-Based React + Spring Boot Application and Storing Personal Information in React: sessionStorage vs Context API . This post ties those threads together into a complete, end-to-end authentication flow you can adapt for enterprise applications. We'll walk through the full journey: login → token issuance → secure storage → protected routes → token refresh → logout. Architecture Overview Before the code, here's the high-level flow: ┌──────────────┐ ┌──────────────────┐ │ React │ │ Spring Boot │ │ Frontend │ │ Backend │ └──────┬───────┘ └────────┬─────────┘ │ 1. POST /login │ │─────────────────────────>│ │ │ validate credentials │ 2. JWT (httpOnly cookie)│ issue access + refresh │<─────────────────────────│ │ │ │ 3. GET /protected │ │ (+ CSRF token) │ │─────────────────────────>│ validate JWT + CSRF │ 4. Protected data │ │<─────────────────────────│ │ │ │ 5. POST /refresh │ │─────────────────────────>│ rotate tokens │ │ │ 6. POST /logout │ │─────────────────────────>│ invalidate session Key Design Decisions Decision Choice Rationale Token storage httpOnly cookies Not accessible to JavaScript → mitigates XSS token theft CSRF protection Double-submit / token pattern Required when using cookies Token type Short-lived access + refresh Limits exposure window State management Context API for auth status Centralized, lightweight Why httpOnly cookies over localStorage? As I discussed in the storage blog, localStorage is readable by any script on the page—making it vulnerable to XSS. httpOnly cookies trade that risk for the need to handle CSRF, which we address below. Step 1: Backend — Login and Token Issuance