Skip to main content

Command Palette

Search for a command to run...

Integrating OIDC (Feide) as Fallback and Recovery

Combining WebAuthn and Feide OIDC for recovery, federation, and identity continuity in a VueJS + ASP.NET Core PWA

Updated
6 min read
Integrating OIDC (Feide) as Fallback and Recovery

WebAuthn gave us phishing-resistant, device-bound authentication.
But devices get lost. Browsers reset. Users switch laptops. Institutions manage identities centrally.

That’s where OIDC (Feide) enters — not as a competitor to passwordless, but as structural support.

This article walks through my real implementation:

  • Frontend: VueJS PWA

  • Backend: ASP.NET Core

  • Database: SQL Server

  • Passwordless: fido2-net-lib

  • Federation: OpenID Connect (Feide)

  • Session: HTTP-only cookie

And we’ll focus on four things:

  1. What can Feide bring to the table

  2. How OIDC fits without undermining WebAuthn

  3. Security boundaries between IdP and my system

  4. Account linking in practice

Disclaimer

This article describes architectural patterns and technical approaches based on a real-world implementation. All examples, code snippets, and flow descriptions have been generalized and simplified for educational purposes. No proprietary business logic, confidential configurations, credentials, or organization-specific details are disclosed. The focus is strictly on publicly documented standards (WebAuthn, OIDC) and implementation patterns within a standard VueJS + ASP.NET Core + SQL Server stack.


What can Feide bring to the table

Feide is widely used in Norwegian education and research sectors. That matters for three reasons:

1️⃣ Institutional Identity Already Exists

Users already have:

  • A managed identity

  • Centralized credential lifecycle

  • Organizational trust

Recreating identity inside my PWA would be redundant and weaker.

2️⃣ Compliance & Governance

Institutional IdPs typically enforce:

  • MFA policies

  • Password strength

  • Account revocation

  • Auditing

By integrating Feide, my system inherits that upstream assurance without storing passwords.

3️⃣ Recovery and Bootstrap

WebAuthn is device-bound.

Feide provides:

  • Cross-device identity continuity

  • Secure account recovery

  • Bootstrap trust for new devices


How OIDC Fits Without Undermining Passwordless

The common fear:

“If I add OIDC fallback, doesn’t that weaken passwordless?”

Only if fallback is careless.

My architecture enforces this model:

  • WebAuthn = primary authentication

  • Feide OIDC = bootstrap + recovery

  • HTTP-only cookie = session integrity

  • SQL Server = credential persistence

Feide does not authenticate users inside my system directly.

Feide asserts identity.

WebAuthn proves device possession.

Those are different trust layers.


Real OIDC Integration (ASP.NET Core)

My implemented Authorization Code flow with PKCE.

OIDC Configuration

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Lax;
})
.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "https://auth.feide.no";
    options.ClientId = Configuration["Feide:ClientId"];
    options.ClientSecret = Configuration["Feide:ClientSecret"];
    options.ResponseType = "code";
    options.SaveTokens = false;
    options.GetClaimsFromUserInfoEndpoint = true;

    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("email");

    options.TokenValidationParameters.NameClaimType = "name";
});

Important detail:

options.SaveTokens = false;

You do not store IdP tokens in the browser.

You convert identity into a server-controlled session.


OIDC Callback Flow

[HttpGet("callback")]
public async Task<IActionResult> Callback()
{
    var authenticateResult = await HttpContext.AuthenticateAsync("oidc");

    if (!authenticateResult.Succeeded)
        return Unauthorized();

    var externalUserId = authenticateResult.Principal.FindFirst("sub")?.Value;

    var user = await FindOrCreateUser(externalUserId);

    SignInUser(user.Id);

    if (!user.WebAuthnCredentials.Any())
        return Redirect("/enable-passwordless");

    return Redirect("/dashboard");
}

This is critical:

  • Feide proves identity.

  • The system maps that identity to internal user record.

  • The system issues session cookie.

The IdP does not create sessions in my system.


Security Boundaries Between OIDC and My System

Understanding boundaries prevents architectural confusion.

What OIDC Is Responsible For

  • Authenticating the user upstream

  • Issuing ID tokens

  • Managing institutional identity lifecycle

  • Enforcing upstream MFA policies

What My System Is Responsible For

  • Mapping external identity (sub) to internal user

  • Managing WebAuthn credentials

  • Verifying FIDO2 assertions

  • Issuing and invalidating session cookies

  • Authorization within my application

OIDC is not trusted to:

  • Authorize application actions

  • Manage WebAuthn devices

  • Maintain the session integrity

Trust is layered, not delegated.


Account Linking Considerations

This is where real complexity lives.

OIDC provides:

{
  "sub": "abcd1234",
  "email": "user@example.edu"
}

But what if:

  • Email changes?

  • User logs in with different institutional account?

  • Duplicate local account exists?

You must choose a stable linking strategy.

Use sub as the primary external identifier.

Database model:

CREATE TABLE ExternalLogins (
    Id UNIQUEIDENTIFIER PRIMARY KEY,
    UserId UNIQUEIDENTIFIER NOT NULL,
    Provider NVARCHAR(50) NOT NULL,
    ExternalSubject NVARCHAR(255) NOT NULL
);

Mapping logic:

var externalLogin = await _db.ExternalLogins
    .FirstOrDefaultAsync(x =>
        x.Provider == "Feide" &&
        x.ExternalSubject == externalUserId);

if (externalLogin == null)
{
    // First login → create link
    var user = CreateNewUser();
    _db.ExternalLogins.Add(new ExternalLogin {
        UserId = user.Id,
        Provider = "Feide",
        ExternalSubject = externalUserId
    });
}

Never rely solely on email for linking.

Emails change. sub should not.


Recovery Flow Using Feide

Lost device scenario:

  1. User clicks “Login with Feide”

  2. OIDC completes

  3. Identity verified

  4. System invalidates old WebAuthn credentials

  5. User registers new credential

Example revocation:

_db.WebAuthnCredentials.RemoveRange(user.WebAuthnCredentials);
await _db.SaveChangesAsync();

Then redirect to registration.

Recovery is structured. Not improvised.


Why This Does Not Undermine Passwordless

Weak fallback undermines security when:

  • It bypasses verification

  • It skips policy

  • It exists only as emergency shortcut

My implementation ensures:

  • OIDC must complete successfully

  • Session is server-issued

  • WebAuthn remains primary method

  • Registration after OIDC is explicit

This maintains assurance.


VueJS PWA Integration

From frontend:

function loginWithFeide() {
  window.location.href = "/api/auth/feide-login";
}

No tokens stored client-side.
No JWT in localStorage.
No client-managed identity state.

The PWA only reacts to session cookie.

This keeps attack surface small.


What This Architecture Achieves

By combining:

  • WebAuthn (device-bound proof)

  • Feide OIDC (identity continuity)

  • SQL Server (credential persistence)

  • HTTP-only cookies (session security)

You achieve:

  • Phishing resistance

  • Device lifecycle resilience

  • Institutional identity integration

  • Controlled fallback

  • Clear trust boundaries

Most importantly:

You avoid false dichotomy.

This is not:

“Passwordless vs Federation.”

It is:

“Passwordless for authentication. Federation for identity continuity.”


Final Reflection

Integrating OIDC did not weaken the system.

It completed it.

WebAuthn without federation is brittle.
Federation without WebAuthn is phishable.

Together, they form a layered trust architecture.

In the next article, we’ll examine operational lessons learned after deploying this combined system — including monitoring, auditing, and real-world behavioral patterns that only surface after production traffic begins.

Because authentication design doesn’t end at implementation.

It evolves under pressure.


☰ Series Navigation

Core Series

Optional Extras

Passwordless: Modern Authentication Patterns for PWAs

Part 4 of 13

A practical deep dive into modern authentication for Progressive Web Apps. This series starts from principles — identity, trust, and user verification — and moves toward real-world passwordless systems powered by WebAuthn / FIDO2 and OpenID Connect.

Up next

Implementing WebAuthn in Practice

Implementing FIDO2 with VueJS, ASP.NET Core, SQL Server, and fido2-net-lib in production