---
url: 'https://www.corbado.com/blog/angular-passkeys'
title: 'Building an Angular Passkey Login Page with TypeScript'
description: 'Angular & Passkeys: This tutorial teaches developers how to implement passkeys in Angular for secure, scalable user authentication.'
lang: 'en'
author: 'Vincent Delitz'
date: '2023-07-09T00:00:00.000Z'
lastModified: '2026-08-28T15:31:52.633Z'
keywords: 'angular'
category: 'Passkeys Implementation'
---

# Building an Angular Passkey Login Page with TypeScript

> ⚠️ **Archived tutorial - outdated integration**
>
> This article uses Corbado Complete, a discontinued product. Corbado now offers
> **[passkey observability and authentication for enterprise CIAM](https://www.corbado.com/features)**
>
> _The tutorial below is preserved for reference only and is no longer maintained._

## Key Facts

- The tutorial uses **Angular version 15.2.9** with the `@corbado/web-js` package to add
  passkey authentication to an Angular app.
- Two Angular components are required: a **login component** that mounts the Corbado auth
  UI and a profile component displaying user data post-authentication.
- The **`Corbado.load()`** call initializes the project; the app conditionally renders the
  router only after initialization completes.
- The **Angular ViewChild decorator** is used to reference the auth UI div where Corbado
  mounts the passkey login interface after view initialization.
- **Corbado session management** exposes the authenticated user's ID and email via the
  `Corbado.user` object immediately after login.

## 1. Introduction

The final result of this tutorial looks as follows:

![Result: Angular Passkeys Login Page](https://www.corbado.com/website-assets/64aaa5050b9982d5e0a9c59b_running_app_eb33dece50.png)

## 2. Prerequisites

This tutorial assumes basic familiarity with Angular, HTML, CSS and JavaScript /
TypeScript. Lets dive in! Moreover, you need to have Node and NPM installed on your
machine. To facilitate things and follow this tutorial, we recommend to install the
Angular CLI as well:

```bash
npm install -g @angular/cli
```

## 3. Repository structure

Lets first discuss the structure of our project
([full GitHub repo](https://github.com/corbado/example-passkeys-angular)):

```
.
├── .angular
├── angular.json
├── package.json
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── src
    ├── app
    │   ├── login
    │   │   ├── login.component.css
    │   │   ├── login.component.html
    │   │   ├── login.component.spec.ts
    │   │   └── login.component.ts
    │   ├── profile
    │   │   ├── profile.component.css
    │   │   ├── profile.component.html
    │   │   ├── profile.component.spec.ts
    │   │   └── profile.component.ts
    │   ├── app.component.html
    │   ├── app.component.ts
    │   ├── app.module.ts
    │   ├── app.component.css
    │   └── app-routing.module.ts
    └── style.css
```

The rest of the files of this project can be ignored for the purpose of this tutorial.

## 4. Set up the Angular project

In the following, we explain step-by-step what needs to be done to successfully set up the
Angular project. If you want to see the finished code, please have a look at our
[sample application repository](https://github.com/corbado/example-passkeys-angular).

Lets start out by initializing a new Angular project. In this tutorial, we're using
Angular version 15.2.9:

```bash
ng new passkeys-demo-angular
```

In the installation guide steps, we select the following:

- Share pseudonymous usage data: No
- Angular routing: Yes
- Stylesheet format: CSS
- Enable SSR: No (Choose yes if it's required for your application)

If you run

```bash
ng serve
```

the sample Angular skeleton application starts at
[http://localhost:4200](http://localhost:4200).

## 5. Set up the Corbado UI component for passkey authentication

### 5.1 Set up your Corbado account and project

Visit the [Corbado developer panel](https://app.corbado.com/signin) to sign up and create
your account (you'll see the passkey sign-up in action here!).

![Angular Corbado Developer Panel Sign-up](https://www.corbado.com/website-assets/dev_panel_d7f6cc96ed.png)

In the project setup wizard, begin by selecting an appropriate name for your project. For
the product selection, opt for "Corbado Complete". Subsequently, specify your technology
stack and select "DEV along with "Corbado session management" options. Afterwards, youll
get more foundational setup guidance.

Next, choose "Web app" as an application type and [Vue](https://www.corbado.com/blog/vuejs-passkeys) as your
framework. In the application settings, define your application url and
[relying party](https://www.corbado.com/glossary/relying-party) id as follows:

![App settings](https://www.corbado.com/website-assets/Screenshot_2024_05_02_at_08_09_52_d3aa9e3bec.png)

- **Application URL:** Provide the URL where you embedded the UI component, here:
  [http://localhost:4200](http://localhost:4200).
- [**Relying Party ID**](https://www.corbado.com/blog/webauthn-relying-party-id-rpid-passkeys)**:** Provide the
  domain (no protocol, no port and no path) where passkeys should be bound to, here:
  localhost

### 5.2 Embed the UI component in the frontend

First, let's install all packages required for our implementation. Namly, this will
include the Corbado web-js package as well as it's corresponding types and to fix some
typescript issues, we'll also include [react](https://www.corbado.com/blog/react-passkeys) and ua-parser-js
types.

```bash
npm i @corbado/web-js
```

```bash
npm i -D @corbado/types @types/react @types/ua-parser-js
```

Navigate to the root directory of the project:

```bash
cd passkeys-demo-angular
```

Retrieve your Corbado Project's ID, which we'll need in the following. You can find it
[here](https://app.corbado.com/app/settings/general/project-info) under Corbado API
access.

Next, navigate to the app.component.ts file. We'll use it as a boundary to only render our
app once the Corbado project has initialized.

```ts
import { Component, OnInit } from "@angular/core";
import Corbado from "@corbado/web-js";

@Component({
    selector: "app-root",
    templateUrl: "./app.component.html",
    styleUrls: ["./app.component.css"],
})
export class AppComponent implements OnInit {
    title = "passkeys-demo-angular";
    isInitialized = false;

    constructor() {}

    ngOnInit(): void {
        this.initialize();
    }

    async initialize() {
        try {
            await Corbado.load({
                projectId: "<Your Corbado Project ID>",
                darkMode: "off",
            });
            this.isInitialized = true;
        } catch (error) {
            console.error("Initialization failed:", error);
        }
    }
}
```

Once our project has initialized, we'll set the corresponding state and in our App we'll
conditionally render our router depending on this `isInitialized` state inside the
\`app.component.html file.

Most of the Corbado configuration can be done in the `Corbado.load` call.

Replace all content with the following:

```html
<ng-container *ngIf="isInitialized">
    <router-outlet></router-outlet>
</ng-container>
```

Next, we create two new components, one where we display our
[passkey login](https://www.corbado.com/blog/passkey-login-best-practices) UI component and one that displays
basic user information after we have successfully authenticated.

```bash
ng generate component login
ng generate component profile
```

Open [src](https://www.corbado.com/glossary/src)/app/app-routing.module.ts and add the routes for the login and
profile component. The login component should be reachable at
[http://localhost:4200](http://localhost:4200), while the profile component is reachable
at [http://localhost:4200/profile](http://localhost:4200/profile):

```ts
// src/app/app-routing.module.ts

import { NgModule } from "@angular/core";
import { ProfileComponent } from "./profile/profile.component";
import { RouterModule, Routes } from "@angular/router";
import { LoginComponent } from "./login/login.component";

const routes: Routes = [
    { path: "profile", component: ProfileComponent },
    { path: "login", component: LoginComponent },
    { path: "", component: LoginComponent },
    { path: "**", redirectTo: "/" },
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
})
export class AppRoutingModule {}
```

Let's now set up our auth page. For this we'll need a reference to a div element on our
page, which should be used to inject the auth UI into. We establish this reference with
Angular ViewChild decorator. After the view has initialized, we'll use the global Corbado
object again to mount the auth UI and define a callback to be executed once the user has
successfully logged in or signed up. In our case, we'll redirect to the `/profile` page.
Let's set this up in our `login.component.ts` file.

```ts
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit } from "@angular/core";
import { Router } from "@angular/router";
import Corbado from "@corbado/web-js";

@Component({
    selector: "app-login",
    templateUrl: "./login.component.html",
    styleUrls: ["./login.component.css"],
})
export class LoginComponent implements OnInit, AfterViewInit {
    @ViewChild("authElement", { static: false }) authElement!: ElementRef; // Access the element

    constructor(private router: Router) {}

    ngOnInit() {
        if (Corbado.user) {
            this.router.navigate(["/profile"]);
        }
    }

    ngAfterViewInit() {
        // Mount the Corbado auth UI after the view initializes
        Corbado.mountAuthUI(this.authElement.nativeElement, {
            onLoggedIn: () => this.router.navigate(["/profile"]), // Use Angular's router instead of window.location
        });
    }
}
```

To establish the reference to our auth UI div we'll put the following into the
`login.component.html`:

```html
<div #authElement></div>
```

### 5.3 Set up the profile page

After successful authentication, the Corbado UI component redirects the user to the
provided Redirect URL ([https://localhost:4200/profile](https://localhost:4200/profile)).
This page renders basic user information ([user ID](https://www.corbado.com/blog/webauthn-user-id-userhandle) and
email) and provides a button to logout. If the user isn't logged in, it will provide him
with the option to go back to the home page.

Because we only render components once Corbado has loaded, the setup of our
`profile.components.ts` file is very straight forward. We can just set the user state to
the `Corbado.user` value to be able to use it in our template. If the user is not logged
in, this value will be `undefined`.

We'll also define a `handleLogout` callback to allow the user to log out in the template.

This is how our `profile.component.ts` file should look like.

```ts
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import Corbado from "@corbado/web-js";

@Component({
    selector: "app-profile",
    templateUrl: "./profile.component.html",
    styleUrls: ["./profile.component.css"],
})
export class ProfileComponent {
    user = Corbado.user;

    constructor(private router: Router) {}

    async handleLogout() {
        await Corbado.logout();
        await this.router.navigate(["/"]);
    }
}
```

In the `profile.component.html` we'll use the user state to determine if he's logged in
and conditionally render our content:

```html
<div>
    <ng-container *ngIf="user; else notLoggedIn">
        <div>
            <h1>Profile Page</h1>
            <div>
                <p>
                    User-ID: {{user.sub}}
                    <br />
                    Email: {{user.email}}
                </p>
            </div>
            <button (click)="handleLogout()">Logout</button>
        </div>
    </ng-container>

    <ng-template #notLoggedIn>
        <div>
            <p>You're not logged in.</p>
            <p>Please go back to <a routerLink="/">home</a> to log in.</p>
        </div>
    </ng-template>
</div>
```

### 5.4 Run the application

If everything is set up and installed, run the application with

```bash
ng serve
```

You should see the following screen:

![Angular Passkeys Login Page](https://www.corbado.com/website-assets/Screenshot_2024_04_25_at_18_38_50_c2cafffc15.png)

After successful sign up / login, you see the profile page:

![Angular Passkeys Profile Page](https://www.corbado.com/website-assets/Svelte_Corbado_Profile_Page_8d2d4ebb3e.png)

## 6. How Corbado can help

Most Angular apps that want passkeys already have users, a login route and a session that
works. [Corbado Connect](https://www.corbado.com/connect) adds passkeys to that app rather than replacing it, and
it mounts the way any Angular integration does: take the element reference out of the
template with `ViewChild`, wait for the SDK to load, mount into that element.
`@corbado/connect-web-js` exposes `mountCorbadoConnectLogin`, `mountCorbadoConnectAppend`
and `mountCorbadoConnectPasskeyList`, each taking a plain `HTMLElement` plus its options,
with a matching unmount call for `ngOnDestroy`.

[Video: Passkey first components](https://www.corbado.com/videos/connect/passkey-first-components.mp4) ([See how Corbado Connect ships this](https://www.corbado.com/connect))

The shape of the integration is what matters here. Connect is built to sit in front of an
authentication system you already run, so the login component owns the passkey ceremony
and hands off to your existing password or social path when there is no passkey to use.
Your user store stays where it is, which is how Connect reaches up to 10x higher passkey
adoption without a migration. Appearance is styling rather than configuration: the
components render under the `cb-connect` class and you override from
`cb-connect-custom-style` in your own stylesheet.

If you are starting an Angular app from scratch today, the same three mount calls apply,
and the passkey list is what the profile page in section 5.4 would become.

## 7. Conclusion

This tutorial showed how easy it is to add
[passwordless authentication](https://www.corbado.com/glossary/passwordless-authentication) with passkeys to an
Angular app using Corbado. Besides the passkey-first authentication, Corbado provides
simple session management, that we used for a retrieval of basic user data.

## Frequently Asked Questions

### What packages do I need to implement passkeys in Angular with Corbado?

You need `@corbado/web-js` as a runtime dependency and `@corbado/types`, `@types/react`
and `@types/ua-parser-js` as dev dependencies. The dev packages provide TypeScript types
and fix TypeScript compatibility issues that arise from the Corbado web component.

### How do I mount the Corbado passkey UI inside an Angular component?

Use Angular's `ViewChild` decorator to get a reference to a div element in your login
template, then call `Corbado.mountAuthUI()` inside `ngAfterViewInit` passing that element
and an `onLoggedIn` callback. The callback should use Angular's `Router` to navigate to a
protected route rather than manipulating `window.location` directly.

### Why does my Angular app need to wait for Corbado to initialize before rendering routes?

The `Corbado.load()` call must resolve before the Corbado UI components are available for
mounting. Wrapping the `<router-outlet>` in an `*ngIf="isInitialized"` guard prevents
rendering errors and ensures the passkey interface mounts correctly after the SDK is
ready.

### How do I handle logout after passkey authentication in an Angular app?

Call `await Corbado.logout()` in an async method on your profile component, then use
Angular's `Router` to navigate back to the root path. The `Corbado.user` value will be
`undefined` after logout, which can drive conditional rendering in your profile template
to handle the unauthenticated state.
