Overview
BaronGuard is an internal web application I built from the ground up to give RCBC's Student Support, Test Center, and OIT staff a single, guided, auditable workflow for verifying a person's identity and resetting their college credentials. It talks directly to Microsoft Entra ID and the college's student information system, replacing a patchwork process that had quietly become one of the biggest time-sinks in the department.
For years, RCBC had a similar but much simpler tool, provided and maintained by an outside MSP, that gave the Student Help Desk the ability to reset student passwords and clear MFA on their own. It worked — until January 2026, when the college migrated its identity platform from OneLogin to Microsoft Entra. The migration broke the tool. The MSP committed to a fix, hit a snag, and the work stalled out. OIT had already absorbed the helpdesk workload through a shared Google Chat space, which reduced the urgency to resolve it — and the old tool effectively became dead in the water, leaving the college without a proper solution.
BaronGuard is the answer to that. Rather than wait for another vendor engagement, I built it internally so the college owns the source, the data, and the roadmap. The goal from day one was to never again be in a position where a vendor's silence translated into our staff losing the ability to do their jobs.
The real damage of losing the old tool wasn't technical — it was operational. With no self-service interface, the Student Help Desk could no longer help students who couldn't log in. Every password reset, every MFA clear, every locked-out account had to be relayed from Student Support to OIT through a shared Google Chat space.
That chat became the bottleneck for everything. It needed an OIT technician monitoring it constantly — if no one was watching, students sat unresolved while staff caught up. The noise was relentless: routine resets mixed with urgent escalations mixed with unrelated tickets, all in one timeline.
Just opening Student Support's access to Entra wasn't an option either. Entra's permission model is coarse; the smallest role that would let them reset a password would also let them edit groups, view directory data they didn't need, and touch accounts well beyond students. The risk of over-privileging a frontline team that turns over frequently was unacceptable.
Scoping access with Administrative Units could limit which accounts staff could see, but Entra's interface wasn't designed for non-technical users — and putting frontline staff directly in the portal introduced a different risk: account actions could be taken without any structured identity verification. There was also no automation for temporary passwords; staff would have had to set them manually and communicate them out-of-band, which was error-prone and inconsistent.
BaronGuard wraps the entire identity-recovery flow in a four-step, role-gated web workflow: Lookup → Request → Verify → Result. Student Support staff can now resolve the routine cases themselves — safely, with full audit trails, with guiderails that prevent the actions they shouldn't take — and without ever needing direct access to Entra.
OIT gets its time back. Students get faster resolution. The audit log captures every action, by every staff member, for every student. The Google Chat firehose is no longer the critical path for password resets, and OIT technicians can focus on the work that actually requires their skill set.
Role-Based Workflows
BaronGuard's most important design decision is who can do what. Three operational roles cover the people who actually use the tool, and one developer role covers me. Each role gets exactly the privileges its work requires — nothing more, nothing less — and every page in the application enforces this at the framework level, not as an afterthought.
This is the role that motivated the project. Student Support staff handle student walk-ins, phone calls, and the Help Desk chat. They need to resolve routine login issues quickly without escalating every one to OIT, but they shouldn't be able to touch staff accounts, generate persistent credentials, or see other staff members' activity.
✓ Can Do
- Look up student records by ID or name
- Reset a student's password to the default formula
- Clear a student's registered MFA methods
- Use phone, in-person, or video as verification methods
- See the student's account status (registered MFA, last password change)
✗ Cannot Do
- Look up any staff or faculty account
- Generate a Temporary Access Pass
- Skip the verification checklist
- View the audit log or export historical data
- Reach the developer panel or system broadcast tools
Why these limits? A TAP is essentially a temporary credential. If misused or socially engineered, it grants direct sign-in access for up to 24 hours. That's an OIT decision, not a frontline one. Likewise, staff/faculty accounts often have elevated permissions in other college systems — a compromised faculty reset is a much larger blast radius than a student reset. Keeping those flows behind the OIT gate eliminates an entire class of social-engineering attack against the Student Help Desk.
The Test Center has a very specific problem: personal devices are not allowed in the testing area, so students can’t use their usual MFA app to sign in to their exam session. The exam start time is fixed — they can’t wait for an OIT ticket. What they need is a Temporary Access Pass: a one-time, short-duration credential that lets them authenticate without MFA for that one session.
✓ Can Do
- Look up the student physically present at the test center
- Generate a one-time, 60-minute Temporary Access Pass
- Verify identity using the student's physical RCBC ID card (single-point strong verification)
✗ Cannot Do
- Reset passwords or clear MFA — only TAP generation is exposed
- Generate multi-use or extended-duration TAPs (forced to one-time / 60 min)
- Look up staff/faculty accounts
- View the audit log
Why force one-time, 60 minutes? Because the student only needs to sign in to start their exam. Once they’re in, their regular credentials — password and MFA — work normally for everything outside the Test Center. A reusable or extended-duration TAP isn’t necessary and would grant more access than the situation requires.
OIT is the team that already has Entra admin access through Microsoft. BaronGuard doesn't grant them anything they couldn't already do via the admin console — it just gives them a faster, audited workflow for doing it. The benefit isn't capability; it's speed, consistency, and a unified audit trail that doesn't depend on Microsoft's tenant log retention. Crucially, the Office of Information Technology does not have the ability to reset any privileged user accounts, such as system administrators. This restriction is strictly enforced to prevent an OIT staff member from resetting account credentials for someone with privileges they should not have access to.
✓ Full Access
- Everything Student Support can do, for students and staff/faculty
- Generate Temporary Access Passes with configurable lifetime and usability
- Skip the verification checklist when Student Support has pre-verified
- View, filter, and paginate the full audit log
- Delete individual audit entries or clear the entire log when needed
- Access the staff record workflow including position title and department checks
✗ Still Cannot
- Reset privileged user accounts (e.g., admins) to prevent unauthorized privilege escalation
- Bypass the audit log — every action they take is recorded
- Reach the developer panel (separate role, opt-in)
- Send system-wide broadcast messages
Why give OIT the audit log viewer? So they don't need to log into a separate compliance tool to answer the question "who reset student X this month?"
The Developer role is intentionally separate from the operational roles. The developer panel isn’t locked behind having operational permissions — a Developer-only account gets full observability and testing tools with no reset capabilities. Conversely, OIT staff don’t see the panel in their sessions unless they also hold the Developer role. The separation is enforced at the policy level, not via “Developer is a superset of OIT.”
✓ Can Do
- Open the developer panel — live logs, sessions, latency, activity feed, bug reports
- Push a system broadcast banner to all signed-in users via SignalR
- Enable Mock-Graph mode (canned responses, no real Entra calls)
- Inject a forced error to test failure-path UI
- Impersonate any operational role to test what they see — no second test account needed, no role swapping in Entra
✗ Cannot Do
- Perform any reset action unless they also hold an operational role
- Bypass the audit log — impersonated actions are recorded as such
Technology Stack
BaronGuard is built on the modern Microsoft web stack and integrates deeply with the same identity platform it manages. Every layer was chosen for production stability, observability, and a small operational footprint — this is a tool that needs to run quietly on a single internal IIS box and survive nightly reboots without anyone thinking about it.
The Four-Step Workflow
Every successful reset moves through four guided pages. The workflow is deliberately rigid — you can't reset an account without first verifying identity, and you can't verify identity without first specifying what's being requested and why.
Step 1 — Lookup
Staff begin by entering a student or staff ID number. The system pulls the live record from the college's Informer reporting platform and presents it for confirmation. Student Support only sees the Student lookup form — the Staff toggle is gated to OIT.
Student Lookup — search by ID or name (Student Support view)
Step 2 — Request
After confirming the record, staff fill out a structured request: what needs to be reset (password, MFA, Temporary Access Pass), how the person is contacting them (in-person, phone, video), and why. The system also shows a live Account Status panel pulled from Microsoft Graph — which MFA methods are registered and the date of the last password change — so staff have context before they start verifying.
This page also checks the audit log on load. If the same account has been reset within the past 4 hours, a prominent red warning surfaces showing who did it and when. This catches two distinct failure modes: social engineering attempts where someone tries the same story with multiple staff members, and the more common case of two staff members accidentally stepping on each other's toes by resetting the same account back-to-back without realizing the first one already happened.
• Dan Whittaker reset Password + MFA — 47 minutes ago (2:14 PM)
Please make sure account actions are still needed. If unsure, please verify with a supervisor before proceeding with another reset.
Request page with account status, recent-reset warning, and the structured request form for Baron Oxley
Step 3 — Verify
The verification page presents a checklist of identity points to confirm: ID number, address, phone, personal email, last term enrolled, and so on. A physical RCBC ID card counts as a single-item strong verification; otherwise, staff must collect a minimum of three verbally confirmed fields. The page enforces this threshold — you cannot submit until you have checked enough items.
Once submitted, the requested account actions execute against Microsoft Graph. For password resets, BaronGuard uses the SSPR (Self-Service Password Reset) pipeline endpoint specifically so that hybrid AD-synced accounts trigger writeback to on-premises Active Directory via Entra Connect. This is a critical distinction in hybrid environments: the more obvious Graph API password reset endpoint updates the cloud account but leaves the on-premises password stale. The practical result is that a student can sign in once with the new password, but then the “Current Password” prompt during Windows sign-in reports it as incorrect — because the on-prem side never got the update. SSPR writeback avoids this entirely. Identifying this distinction — and knowing which endpoint to use — is what made BaronGuard work where the MSP’s tool couldn’t. The symptom they couldn’t resolve (sign in once, then immediately blocked on password change) is a well-known artifact of using the standard reset endpoint against a hybrid-synced account. Routing through the SSPR pipeline instead resolved it completely.
Verify page — Baron Oxley’s record on the left, checklist on the right with 3 of 3 confirmed
Step 4 — Confirm and Execute Account Actions
After verification passes, the page does not execute anything immediately. Instead it presents a confirmation screen summarizing exactly what is about to happen: the student’s account, the actions selected, and the verification method used. Staff must explicitly confirm before any Graph calls are made. This prevents accidental execution and gives staff one last opportunity to catch a mistake before it is in the audit log.
Confirmation screen — summary of pending actions before any Graph calls are made
Once confirmed, the actions execute and the result page renders immediately. The result varies by what was performed. For password and MFA resets, it shows a checkmark for each completed action. Student Support staff are shown the temporary password formula — a template they can walk the student through using the student’s own information — rather than the raw value, keeping FERPA-sensitive data off the screen. OIT sees the generated value directly. For TAP generation (Test Center and OIT), the one-time code is displayed with a copy button and expiry time; it is shown once and is never retrievable after that. If an active TAP already exists, the confirmation step requires an additional acknowledgment before deleting the existing one and issuing a new one.
Result screens — Password + MFA reset (Student Support / OIT view, left) and TAP generation (Test Center view, right)
Core Features
Beyond the core workflow, BaronGuard ships with operational and safety features that were added in direct response to feedback during the pilot rollout. Each one solves a real problem observed in actual use, not a hypothetical one.
Live Account Status
Pulls real-time MFA methods and last-password-change date from Microsoft Graph so staff see the current state of the account before requesting a reset. Helps spot accounts that don't need to be reset at all.
Recent-Reset Warning
The Request page checks the audit log on load and surfaces a red alert if the same account has been reset within 4 hours, naming who did it and when. Stops both social engineering and accidental double-resets.
Repeat-Lookup Flag
In-memory tracker warns when two different staff members look up the same person within 10 minutes. Catches "I'll just ask the next agent" patterns in real time, before either reset happens.
Temporary Access Pass
OIT and Test Center can generate a Microsoft Entra TAP for proctored exam access or first-time setup. Includes safeguards for existing TAPs and (for OIT) configurable usability and lifetime.
Full MFA Clear
One click removes every authentication method on the account — Authenticator, phone, software OATH, email, FIDO2 keys — in a single audited operation. Recovers students from lost-device situations cleanly.
Hybrid Password Reset
Uses the Entra SSPR pipeline endpoint specifically so password changes write back to on-premises AD via Entra Connect. Critical for hybrid environments where the simpler API call leaves the on-prem password stale.
System Broadcast Banner
Developer-only feature that pushes a real-time banner to all signed-in users via SignalR. Used to announce planned maintenance, urgent alerts, or temporary tool issues without an email blast.
Bug Report Workflow
Floating button (with the RCBC Baron mascot) lets any staff member submit a bug or feedback note. Reports flow into the developer panel with page context, severity, and submitter role auto-attached.
Help Desk Pre-Verified Path
When Student Support has already verified a student via the Google Chat space, OIT can skip the verification checklist with a single radio. The audit entry records this, helping OIT track and manage Chat-originated requests while Student Support is being phased into handling resets on their own.
Security & Audit
Every design decision in BaronGuard answers the question: "if this got abused, what would the audit log show?" Authentication, authorization, action execution, and event recording are all separated and layered so that no single compromise — including a stolen staff session — can produce an unrecorded action.
Authentication
Sign-in is delegated entirely to Microsoft Entra ID via OpenID Connect. There are no local accounts, no service accounts shared between staff, and no API keys floating in config files. Every Graph call to perform a reset uses the signed-in staff member's token — meaning Microsoft's audit log also records who did what, providing a second independent record outside BaronGuard's control.
The sign-in flow explicitly forces the credential prompt every time, defeating browser session reuse so that a sign out or idle timeout actually means a re-authentication.
Authorization
Four roles, mapped from Entra security groups, gate every page:
Policies enforce these at the page level. A Student Support agent literally cannot reach the TAP generation flow, the audit log viewer, or staff/faculty records. The Developer role exists separately from operational roles — the panel isn’t gated behind having reset permissions, and OIT staff don’t see it in their sessions unless they also hold the Developer role.
Audit Trail
Every completed reset writes a structured audit entry to a thread-safe JSON Lines file, and simultaneously mirrors to a SharePoint Online list as a secondary record. The audit entry captures: timestamp (Eastern Time), staff UPN and display name, account type, user ID, user name, verified fields, reset types performed, contact method, and reason. The OIT-only audit viewer in the app supports filtering by date range, staff member, and free-text search.
| Time | Staff | Type | User | Resets | Reason |
|---|---|---|---|---|---|
| 05/17 3:01 PM | Ron Cahall | Student | Baron Oxley | Password + MFA | New / Lost Phone |
| 05/17 2:47 PM | Dan Whittaker | Staff | Pat Faculty | TAP (60min) | First-Time Setup |
| 05/17 2:14 PM | Dan Whittaker | Student | Baron Oxley | Password | Forgot Password |
| 05/17 1:55 PM | Ron Cahall | Student | Sam Demo | MFA | MFA Not Working |
Audit log viewer — OIT-only, with date range filter, staff filter, and free-text search
Rate Limiting
Per-user sliding-window rate limiting (30 lookups per minute, partitioned by UPN) prevents either accidental form-resubmit storms or deliberate enumeration attempts. The limit is high enough to never affect normal work but tight enough to halt a runaway script.
Idle Timeout with Background-Tab Awareness
Idle staff sessions auto-logout after a configurable period (10 minutes by default, with a warning at 2 minutes remaining). The original implementation used a JavaScript interval to count down, which broke when the browser tab was backgrounded — browsers throttle inactive timers. The shipping version uses absolute timestamps plus the Page Visibility API to detect the true elapsed time when the tab becomes visible again, and signs the user out immediately on return if the deadline passed while the tab was hidden.
Developer Tools
Operating a production tool inside an OIT department means I'm both the developer and the on-call. I built a suite of developer-only tools into the application itself so I can observe and diagnose without ever needing to remote into the server or attach a debugger.
The Developer Panel
Visible only to users with the Developer role, the panel slides out from the right side of
any page and shows the application's internal state. The whole panel is a single Razor
partial gated by an IRoleContext.IsDeveloper check — non-Developer
sessions don't even receive the HTML, let alone the data behind it.
| Ron Cahall | Student Support |
| /request/3131313 · 12s ago | |
| Dan Whittaker | OIT DEV |
| /audit · 4m ago | |
| Pat Proctor | Test Center |
| /tap · 1m ago | |
Developer Panel slide-out — role switcher, dev modes, broadcast, sessions, logs, and bug reports in one place
What's in the Panel
- Role & Impersonation — the four-way switcher in the screenshot; tap any role and the entire UI re-renders as that role, with a banner showing what's being impersonated
- Dev Modes — Mock-Graph (canned responses, no real Entra calls) and Force-Error (queue a specific error code for the next Graph call) for testing error-path UI safely
- System Broadcast — push a banner message to every signed-in user instantly via SignalR
- Active Sessions — who's signed in right now, what role, what page they're on, last seen timestamp; auto-purged after 10 minutes idle
- Recent Logs — in-memory ring buffer of the last 500 ILogger entries, filterable by level (Info / Warn / Error / Critical)
- Recent Activity — a separate event stream for things that don't make it to the audit log (failed verifications, cancelled flows, abandoned sessions)
- My Audit Entries — my own audit history with a one-click delete to clean up test entries I made during development
- Audit Log Stats — file size, total entries, SharePoint sink status, and a preview of the most recent audits across all users
- Build Info — assembly version, environment, server time, and audit log path
Role Impersonation
The role switcher in the panel is one of the most useful pieces. Tap "Student Support" and the entire UI re-renders as if I were a Student Support agent — the Staff lookup toggle disappears, the TAP option vanishes from the request page, and so on. Tap "OIT" and it all comes back. The impersonated role is shown as a banner across the top of every page so I can never forget which role is active, and every action I take while impersonating is recorded in the dev activity log with both the real and impersonated roles.
This means I can test what every role sees without needing a second test account, and without having to keep switching role group memberships in Entra. A one-click change in BaronGuard replaces what used to be a multi-minute round-trip through the admin portal.
Mock-Graph & Forced-Error Modes
Two session flags let me exercise the full UI without touching real Entra data. Mock-Graph mode short-circuits every Graph call to return canned success responses — useful when I want to walk through the entire reset flow during a demo without actually resetting anyone. Forced-Error mode queues a specific error code (insufficient permissions, rate limited, transient 503, expired TAP) to be returned on the next real Graph call, so I can test how the UI handles each failure mode on demand.
Architecture Highlights
BaronGuard runs as a single-instance application on an internal IIS host. Most of the engineering complexity is in keeping that single instance fast, observable, and resilient without the operational overhead of a distributed system.
Service Layer
Dependency injection wires up the entire service graph at startup. The major interfaces:
IInformerService— HTTP client for the student information system with timeout and TLS handlingIGraphService— Microsoft Graph integration with auto-retry for transient errors, timed call instrumentation, and dev-mode short-circuitsIAuditService— thread-safe JSON Lines writer with a parallel SharePoint sinkIDevActivityLog— separate-from-audit event recorder for diagnostics (failed verifications, cancellations, etc.)IBroadcastService— in-memory current-message holder with event subscribers for the SignalR hubILookupTracker— concurrent-dictionary backed in-memory tracker for repeat-lookup detectionIGraphLatencyTracker— lock-protected linked-list ring buffer for the dev panel's latency viewIActiveSessionsTracker— in-memory who's-online registry with a cleanup hosted service
Resilience Patterns
Every Graph call passes through an auto-retry wrapper that detects transient errors (HTTP 429, 503, 504, "concurrent request" messages) and retries with backoff — 500ms, then 2 seconds — for up to three attempts. Permanent errors (user not found, insufficient permissions) skip the retry and surface immediately. This is invisible to the operator when it works, and the dev panel shows the retry attempts when it doesn't.
Long-running operations like password reset use the Graph long-running-operation pattern:
BaronGuard receives a 202 Accepted with a polling URL, then polls every second
for up to 30 seconds for the operation to reach a terminal state. This is what lets the UI
wait correctly for hybrid AD writeback to complete before showing "success" to the operator.
Real-Time via SignalR
Two hubs, two purposes. The BroadcastHub is open to all authenticated users and pushes system-wide banner messages. The DevHub is gated to the Developer role and pushes live updates to the dev panel: log entries, session changes, new activity events, and new bug reports. A hosted background service wires singleton event sources (log buffer, session tracker, activity log, bug report service) to the hub broadcaster at startup, with a 2-second coalesce window so a burst of events produces a single push, not dozens.
Caching & Cache Invalidation
Student/staff records pulled from Informer are cached for the duration of a verification flow to avoid hammering the reporting backend, but the cache is explicitly evicted after any reset completes — so the next lookup always sees fresh data, particularly important when an MFA or email field might have just changed.
Path-Base Awareness
The app is deployed as an IIS sub-application at /BaronGuard in production
but runs at the root in dev. Razor's Url.Page() and tag helpers handle this
automatically. Client-side JavaScript needs to do the same — all fetch URLs and
SignalR hub URLs are rendered through Razor into a single window.BaronGuard
config object, so the JS uses path-base-aware URLs without any conditional logic.
Workflow Polish
The difference between a working internal tool and one that staff actively enjoy using is in the small things. These are the touches that came out of pilot feedback — each one removes a moment of friction or confusion that frontline staff hit during real use.
When you check "Password Reset" only, the reasons list automatically hides MFA-only reasons like "New or Lost Phone." Check MFA too and they reappear. Staff don't see options that don't apply to what they just selected.
RCBC uses a unique temporary password formula tied to each student’s own information. Rather than computing it manually, the result page shows Student Support the formula template so staff can walk the student through what their password will be — without ever displaying FERPA-sensitive information directly. OIT sees the generated value in full.
Every step has a one-click way back to the lookup page if the wrong person was selected. The audit log only records action when something actually happens — abandoned flows produce no noise.
TAP can't be combined with Password or MFA — if you tick TAP, the other two grey out with an inline hint explaining why. Stops invalid combinations before submission rather than throwing an error after.
If a student already has an active TAP and OIT requests another, the system pauses and confirms before deleting the old one. Microsoft never returns the original code after creation, so this prevents accidentally invalidating a TAP the student already has in hand.
If Student Support has already verified identity via the Google Chat space, OIT can pick “Student Help Desk Verified” as the contact method and skip the checklist entirely. The audit entry records this so OIT can quickly manage and track Chat-originated requests while Student Support is being phased into handling resets directly.
Every timestamp shown to users — audit entries, broadcast send times, recent-reset warnings — is in Eastern Time, regardless of the server's timezone. No mental conversion needed during a high-pressure walk-in.
A loading indicator appears on every navigation that takes longer than a moment, and form submit buttons disable themselves. Fetch-based forms (broadcast send, bug report) opt out cleanly with a single attribute to keep the loader from sticking.
The Barry Bug Report Button
The floating bug-report button uses RCBC's Baron mascot, "Barry," with a hover-revealed speech bubble. A small touch that turned a generic feedback button into something staff actually click on. Reports flow into the developer panel with page context, severity, and the submitter's role automatically captured — the friction for "I saw something weird" is one click and a short description.
Engineering Takeaways
BaronGuard was my first production ASP.NET Core application of this scope, and the lessons compounded quickly. A few that I'd carry forward:
The dev panel, mock-graph mode, and dev activity log existed before half the user-facing features did. Being able to see exactly what was happening internally — with no external observability tooling — cut my debug cycles by an order of magnitude and made the rollout phase calm instead of panicked.
Once every state-changing action wrote a structured audit entry, I could derive most other features from it cheaply — the recent-reset warning, the staff activity dashboard, the CSV export, the SharePoint mirror. If I'd started with a logging library and "we'll add structured audit later," I would have ended up with two inconsistent records.
Student Support doesn't need staff record access, so they don't get it. Test Center doesn't need multi-use TAPs, so the toggle isn't even rendered for them. Granting only what the workflow requires — not what the role technically could do — made the tool safer to roll out and easier to defend in a security review.
Several of the trickiest production bugs (broadcast not working in IIS, audit delete
redirects 404ing, the path-base issue) all came down to dev/prod environment differences.
The fix wasn't to fight them — it was to write code that respects the platform's
URL generation pipeline (Url.Page, UsePathBase) instead of
hardcoding paths. Once that was systematic, the bugs stopped.
BaronGuard uses in-memory ring buffers, in-memory caches, and in-process SignalR. There's no Redis, no message queue, no second app server. For the scale (about 30 staff, thousands of actions per year), this is the right answer. The simplicity makes the operational story trivial — "if it breaks, recycle the app pool" — which matters more than "scales to a million users" when the user base is the OIT department.
The bug report button isn't a vanity feature — it's the primary feedback channel for the pilot. Half the features described in this document came from a single submitted bug report or a "would it be possible to..." comment. Lowering the friction for "I saw something weird" turned every operator into a usability tester.