- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Build Secure Applications with Angular
In this Code Lab, learners secure an Angular application by adding protections for routing, user input, dynamic content, and API communication. They implement route and view access controls, validate untrusted input with forms and custom validators, safely handle dynamic content with Angular’s built-in security mechanisms, and centralize authorization header handling with an HTTP interceptor. The lab focuses on practical implementation of maintainable security patterns in Angular applications.
Lab Info
Table of Contents
-
Challenge
Introduction
Welcome to the Build Secure Applications with Angular Code Lab. In this hands-on lab, you analyze a customer portal application with security gaps, apply route and view access controls, validate untrusted user input, safely render dynamic content, and centralize authorization with an HTTP interceptor to harden the application against common client-side risks.
About the tools and concepts
-
Route guards are functions Angular calls before activating a route. A guard returns
trueto allow navigation,falseto block it, or aUrlTreeto redirect. Guards are the standard way to gate access to authenticated or admin-only views. -
Reactive form validators are functions that inspect a control's value and return either
nullfor valid input or an error object for invalid input. Custom validators let you enforce rules that the built-in validators cannot, like rejecting characters that commonly appear in injection payloads. -
Angular sanitization runs automatically on interpolated values and property bindings. The
DomSanitizerexposes explicitbypassSecurityTrust*methods for the rare cases where you must mark a value as safe --- those calls should be guarded by a trust check on the input itself. -
HTTP interceptors run on every outgoing request, which makes them the right place to attach authorization headers in a single, auditable location instead of scattering header logic across services.
Prerequisites
Before starting this lab, you should be comfortable with:
- Angular basics --- components, templates, routing, and HTTP
- TypeScript basics --- classes, interfaces, and imports
- Forms basics --- user input, validation, and submission
- Web security basics --- authentication, authorization, and common client-side risks
The lab environment is ready to use. Run
ng buildfrom inside theSecureAppfolder at any time to verify your changes compile.The Scenario
You are a frontend developer at SecureApp Inc. working on the customer portal. A recent security audit identified four findings: routes that anyone can navigate to, forms that accept any input, dynamic content rendered without trust checks, and authorization headers added inconsistently across HTTP calls. Your job is to add route guards and conditional rendering, build custom form validators, route dynamic content through Angular's sanitization correctly, and centralize the authorization header in an HTTP interceptor.
The Application Structure
Key files in the lab environment
SecureApp/src/app/services/auth.service.ts--- an authentication service that exposes login state, the current user, and the auth tokenSecureApp/src/app/services/data.service.ts--- an existing API service that currently attaches theAuthorizationheader manually on every callSecureApp/src/app/guards/auth.guard.ts--- route guards that need to enforce authentication and admin roleSecureApp/src/app/components/dashboard/--- a dashboard that needs to show or hide sections based on the current userSecureApp/src/app/validators/input.validators.ts--- custom validators that need to reject unsafe input and weak passwordsSecureApp/src/app/components/announcement/announcement.component.ts--- an announcement view that displays dynamic text and an embedded iframeSecureApp/src/app/interceptors/auth.interceptor.ts--- an HTTP interceptor that needs to attach the auth token to outgoing API calls
Complete the tasks in order. Each task builds on the previous one.
Run the build at any point with:
cd SecureApp && ng buildNote: The first time you run
ng build, the CLI asks whether you want to enable autocompletion. TypeNand pressEnter.info> If you get stuck, you can refer to the provided solution code for each task, available in the
solutionsfolder. -
-
Challenge
Protecting Routes and Views
Understanding Route Guards
Before any work happens inside a protected component, the router asks a guard whether navigation is allowed. A guard is a plain function that returns
trueto permit the navigation,falseto block it, or a redirect target. This means the component never even loads when access is denied --- the protection happens at the router boundary, not inside the component.The
authGuardchecks whether a user is logged in and redirects to the login page when they are not. TheadminGuardruns alongsideauthGuardon admin-only routes and adds a role check on top of the authentication check. Wiring both guards into a route gives you layered protection: the auth check guarantees there is a user, and the role check guarantees that user has the right permissions. ### Understanding Conditional RenderingRoute guards stop unauthorized users from reaching a view, but a single view often contains sections that should only appear for certain roles --- admin controls inside an otherwise public dashboard, for example. Angular's
*ngIfdirective removes an element from the rendered DOM entirely when the bound expression isfalse, which is the right tool for hiding role-specific UI.To keep the template simple, the component class exposes plain getters that return the boolean values the template needs. Each getter delegates straight to
AuthService, so login and role state stay in one place and the template stays focused on layout. -
Challenge
Validating Untrusted User Input
Understanding Custom Validators
Angular's
Validatorsnamespace covers common cases like required fields and minimum length, but security rules usually need custom validators --- functions that inspect the control value and return eithernullfor valid input or an error object that the template can display. A validator factory is a function that returns the actual validator, which lets you parameterize the rule and reuse it across forms.For input that flows into application logic or API calls, the safest approach is to reject characters commonly used in injection payloads at the form layer. The
noScriptCharactersValidatordoes this with a single regular expression, returning a typed error key the template uses to show a message. ### Understanding Composite RulesReal-world validators usually check several rules at once and report a single error when any of them fail. The
strongPasswordValidatorenforces three rules --- minimum length, at least one uppercase letter, at least one digit --- and returns a singleweakPassworderror key when any rule fails. The template only needs to handle one error case, which keeps the UI simple.Each rule is computed into a separate boolean before the final return. This makes the logic easy to read and easy to extend: adding a fourth rule means adding one more boolean and including it in the final
ifcheck. -
Challenge
Safely Rendering Dynamic Content
Understanding Angular's Default Escaping
Angular's interpolation syntax {{ value }} automatically escapes HTML characters before inserting them into the DOM. A user who types
<script>alert(1)</script>into a field that flows back into an interpolated binding sees the text as-is --- the script tag is rendered as visible characters, not executed. This is the default behavior, and it is the right behavior for any text that originated from user input. The mistake to avoid is reaching for[innerHTML]orbypassSecurityTrustHtmlwhen interpolation would do the job. Exposing the value through a plain getter keeps the template on the safe interpolation path and makes the trust boundary explicit in the component class. -
Challenge
Centralizing Authorization with an Interceptor
Understanding HTTP Interceptors
The starter
DataServiceattaches theAuthorizationheader manually on every API call. That works, but the rule has to be repeated in every method, and any new service that forgets the header silently sends unauthenticated requests. An HTTP interceptor moves that rule to a single function the framework runs on every outgoing request --- services drop the header code and callhttp.getandhttp.postwith plain URLs.An interceptor can inspect the request, modify it by cloning, and forward the result to the next handler in the chain. Centralizing the auth header in an interceptor means the rest of the application makes plain HTTP calls --- no service has to remember to add the header --- and the rule for which requests get the header lives in exactly one place.
A safe interceptor never adds the auth token to arbitrary URLs. Sending an internal token to an external host is a credential leak, so the first thing the interceptor does is confirm the request targets a trusted API origin. Requests outside the allowlist get forwarded unchanged. ### Understanding Request Cloning
HttpRequestinstances are immutable, so adding a header means callingreq.clone({ setHeaders: { ... } })to produce a new request with the merged headers. Forwarding this cloned request (not the original) is what actually sends the header on the wire.Before cloning, the interceptor reads the current token from
AuthServiceand short-circuits when no token is available. This handles the case of an anonymous user calling a public API endpoint that happens to live at a trusted origin: the request still goes through, just without anAuthorizationheader. -
Challenge
Conclusion
Congratulations on completing the Build Secure Applications with Angular lab! You have added route and view access controls, validated untrusted input with custom validators, routed dynamic content through Angular's sanitization correctly, and centralized authorization in an HTTP interceptor.
What You Have Accomplished
- Implemented Route Guards --- Completed
authGuardandadminGuardso the router redirects anonymous users to the login page and blocks non-admins from admin-only routes. - Added Conditional Rendering --- Exposed login and role state through component getters and used
*ngIfto hide user-only and admin-only sections from users who should not see them. - Built an Unsafe Characters Validator --- Created a custom validator that rejects characters commonly used in script injection attempts.
- Built a Strong Password Validator --- Composed three rules into a single validator that enforces minimum length and character variety.
- Routed Dynamic Text Through Safe Interpolation --- Exposed announcement text through a getter so Angular's default escaping handles the trust boundary.
- Added a Trust Check Around Bypassed Sanitization --- Validated embed URLs against an allowlisted prefix before calling
bypassSecurityTrustResourceUrl. - Filtered Requests by Trusted Origin --- Limited the auth interceptor to allowlisted API origins so external URLs never receive the auth token.
- Attached the Auth Header to Trusted Requests --- Cloned each trusted request with a
Bearertoken fromAuthService, centralizing the header logic in one place.
Key Takeaways
- Route guards stop unauthorized users at the router boundary, before any component code runs.
- Custom validators are the right place to enforce input rules that protect downstream code from unsafe values.
- Angular's interpolation already escapes HTML characters; reach for
bypassSecurityTrust*only when you have a separate trust check on the input. - Centralizing auth headers in an interceptor turns a per-call concern into a single, auditable rule --- and limiting that rule to a trusted-origin allowlist prevents credential leaks to external hosts.
Experiment Before You Go
You still have time in the lab environment. Try these explorations:
- Add a
roleGuard(role: string)factory that produces a guard for any role, not justadmin - Extend
noScriptCharactersValidatorto accept a custom regular expression as a parameter - Add a second trusted prefix to the announcement embed allowlist and confirm both prefixes pass the trust check
- Add a refresh-token branch to the auth interceptor that retries the original request after a 401 response
- Implemented Route Guards --- Completed
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.